From 72e93344b4d1ffc71e9c832ec23de0657e5b04a5 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Queru Date: Thu, 12 Nov 2009 18:45:15 -0800 Subject: [PATCH] eclair snapshot --- README.txt | 12 +- dalvikvm/Main.c | 2 +- dexdump/DexDump.c | 254 +- dexopt/OptMain.c | 2 +- docs/debugger.html | 51 +- docs/dex-format.html | 4 +- docs/embedded-vm-control.html | 58 +- docs/heap-profiling.html | 178 + docs/hello-world.html | 216 + docs/jni-tips.html | 91 +- docs/opcodes/opcode-04-move-wide.html | 2 +- docs/porting-guide.html | 356 + docs/porting-proto.c.txt | 245 + docs/verifier.html | 78 +- dx/README.txt | 3 +- dx/etc/dx | 48 +- dx/etc/dx.bat | 47 +- dx/src/com/android/dx/Version.java | 4 +- .../dx/cf/attrib/AttAnnotationDefault.java | 14 +- dx/src/com/android/dx/cf/attrib/AttCode.java | 34 +- .../dx/cf/attrib/AttConstantValue.java | 22 +- .../android/dx/cf/attrib/AttDeprecated.java | 4 +- .../dx/cf/attrib/AttEnclosingMethod.java | 16 +- .../android/dx/cf/attrib/AttExceptions.java | 14 +- .../android/dx/cf/attrib/AttInnerClasses.java | 10 +- .../dx/cf/attrib/AttLineNumberTable.java | 10 +- .../dx/cf/attrib/AttLocalVariableTable.java | 6 +- .../cf/attrib/AttLocalVariableTypeTable.java | 6 +- .../AttRuntimeInvisibleAnnotations.java | 8 +- ...tRuntimeInvisibleParameterAnnotations.java | 8 +- .../attrib/AttRuntimeVisibleAnnotations.java | 8 +- ...AttRuntimeVisibleParameterAnnotations.java | 8 +- .../android/dx/cf/attrib/AttSignature.java | 10 +- .../android/dx/cf/attrib/AttSourceFile.java | 10 +- .../android/dx/cf/attrib/AttSynthetic.java | 4 +- .../android/dx/cf/attrib/BaseAnnotations.java | 12 +- .../android/dx/cf/attrib/BaseAttribute.java | 4 +- .../dx/cf/attrib/BaseLocalVariables.java | 12 +- .../cf/attrib/BaseParameterAnnotations.java | 12 +- .../android/dx/cf/attrib/InnerClassList.java | 32 +- .../android/dx/cf/attrib/RawAttribute.java | 22 +- .../com/android/dx/cf/code/BaseMachine.java | 70 +- .../com/android/dx/cf/code/BasicBlocker.java | 37 +- dx/src/com/android/dx/cf/code/ByteBlock.java | 30 +- .../com/android/dx/cf/code/ByteBlockList.java | 14 +- .../com/android/dx/cf/code/ByteCatchList.java | 84 +- dx/src/com/android/dx/cf/code/ByteOps.java | 74 +- .../com/android/dx/cf/code/BytecodeArray.java | 132 +- .../android/dx/cf/code/ConcreteMethod.java | 38 +- .../android/dx/cf/code/ExecutionStack.java | 56 +- dx/src/com/android/dx/cf/code/Frame.java | 60 +- .../android/dx/cf/code/LineNumberList.java | 36 +- .../android/dx/cf/code/LocalVariableList.java | 117 +- .../com/android/dx/cf/code/LocalsArray.java | 61 +- .../android/dx/cf/code/LocalsArraySet.java | 46 +- dx/src/com/android/dx/cf/code/Machine.java | 72 +- dx/src/com/android/dx/cf/code/Merger.java | 26 +- .../android/dx/cf/code/OneLocalsArray.java | 8 +- .../com/android/dx/cf/code/ReturnAddress.java | 6 +- dx/src/com/android/dx/cf/code/Ropper.java | 147 +- .../com/android/dx/cf/code/RopperMachine.java | 101 +- dx/src/com/android/dx/cf/code/Simulator.java | 95 +- dx/src/com/android/dx/cf/code/SwitchList.java | 30 +- .../android/dx/cf/code/ValueAwareMachine.java | 3 +- .../android/dx/cf/cst/ConstantPoolParser.java | 24 +- .../com/android/dx/cf/cst/ConstantTags.java | 22 +- .../dx/cf/direct/AnnotationParser.java | 56 +- .../dx/cf/direct/AttributeFactory.java | 22 +- .../dx/cf/direct/AttributeListParser.java | 26 +- .../android/dx/cf/direct/ClassPathOpener.java | 43 +- .../android/dx/cf/direct/CodeObserver.java | 24 +- .../android/dx/cf/direct/DirectClassFile.java | 92 +- .../android/dx/cf/direct/FieldListParser.java | 12 +- .../dx/cf/direct/MemberListParser.java | 42 +- .../dx/cf/direct/MethodListParser.java | 12 +- .../dx/cf/direct/StdAttributeFactory.java | 50 +- dx/src/com/android/dx/cf/iface/Attribute.java | 6 +- .../android/dx/cf/iface/AttributeList.java | 26 +- dx/src/com/android/dx/cf/iface/ClassFile.java | 52 +- dx/src/com/android/dx/cf/iface/Field.java | 6 +- dx/src/com/android/dx/cf/iface/FieldList.java | 10 +- dx/src/com/android/dx/cf/iface/Member.java | 28 +- dx/src/com/android/dx/cf/iface/Method.java | 4 +- .../com/android/dx/cf/iface/MethodList.java | 10 +- .../android/dx/cf/iface/ParseObserver.java | 24 +- .../android/dx/cf/iface/StdAttributeList.java | 6 +- dx/src/com/android/dx/cf/iface/StdField.java | 6 +- .../com/android/dx/cf/iface/StdFieldList.java | 6 +- dx/src/com/android/dx/cf/iface/StdMember.java | 12 +- dx/src/com/android/dx/cf/iface/StdMethod.java | 8 +- .../android/dx/cf/iface/StdMethodList.java | 6 +- dx/src/com/android/dx/command/DxConsole.java | 8 +- dx/src/com/android/dx/command/Main.java | 12 +- .../dx/command/annotool/AnnotationLister.java | 32 +- .../com/android/dx/command/annotool/Main.java | 7 +- dx/src/com/android/dx/command/dexer/Main.java | 168 +- .../android/dx/command/dump/BaseDumper.java | 48 +- .../android/dx/command/dump/BlockDumper.java | 17 +- .../android/dx/command/dump/ClassDumper.java | 4 +- .../android/dx/command/dump/DotDumper.java | 23 +- dx/src/com/android/dx/command/dump/Main.java | 4 +- .../android/dx/command/dump/SsaDumper.java | 76 +- .../dx/dex/cf/AttributeTranslator.java | 82 +- .../com/android/dx/dex/cf/CfTranslator.java | 32 +- .../com/android/dx/dex/cf/CodeStatistics.java | 6 +- .../android/dx/dex/cf/OptimizerOptions.java | 20 +- dx/src/com/android/dx/dex/code/ArrayData.java | 13 +- .../android/dx/dex/code/BlockAddresses.java | 32 +- .../com/android/dx/dex/code/CatchBuilder.java | 4 +- .../android/dx/dex/code/CatchHandlerList.java | 44 +- .../com/android/dx/dex/code/CatchTable.java | 34 +- .../com/android/dx/dex/code/CodeAddress.java | 4 +- dx/src/com/android/dx/dex/code/CstInsn.java | 32 +- dx/src/com/android/dx/dex/code/DalvCode.java | 40 +- dx/src/com/android/dx/dex/code/DalvInsn.java | 84 +- .../com/android/dx/dex/code/DalvInsnList.java | 32 +- dx/src/com/android/dx/dex/code/Dop.java | 24 +- dx/src/com/android/dx/dex/code/Dops.java | 14 +- .../android/dx/dex/code/FixedSizeInsn.java | 8 +- .../dx/dex/code/HighRegisterPrefix.java | 18 +- .../com/android/dx/dex/code/InsnFormat.java | 122 +- dx/src/com/android/dx/dex/code/LocalEnd.java | 10 +- dx/src/com/android/dx/dex/code/LocalList.java | 273 +- .../android/dx/dex/code/LocalSnapshot.java | 10 +- .../com/android/dx/dex/code/LocalStart.java | 14 +- dx/src/com/android/dx/dex/code/OddSpacer.java | 6 +- .../android/dx/dex/code/OutputCollector.java | 22 +- .../android/dx/dex/code/OutputFinisher.java | 86 +- .../com/android/dx/dex/code/PositionList.java | 32 +- dx/src/com/android/dx/dex/code/RopToDop.java | 4 +- .../android/dx/dex/code/RopTranslator.java | 70 +- .../com/android/dx/dex/code/SimpleInsn.java | 6 +- .../android/dx/dex/code/StdCatchBuilder.java | 43 +- .../com/android/dx/dex/code/SwitchData.java | 30 +- .../com/android/dx/dex/code/TargetInsn.java | 30 +- .../android/dx/dex/code/VariableSizeInsn.java | 6 +- .../com/android/dx/dex/code/ZeroSizeInsn.java | 4 +- .../com/android/dx/dex/code/form/Form10t.java | 4 +- .../com/android/dx/dex/code/form/Form10x.java | 4 +- .../com/android/dx/dex/code/form/Form11n.java | 4 +- .../com/android/dx/dex/code/form/Form11x.java | 4 +- .../com/android/dx/dex/code/form/Form12x.java | 4 +- .../com/android/dx/dex/code/form/Form20t.java | 4 +- .../com/android/dx/dex/code/form/Form21c.java | 4 +- .../com/android/dx/dex/code/form/Form21h.java | 4 +- .../com/android/dx/dex/code/form/Form21s.java | 4 +- .../com/android/dx/dex/code/form/Form21t.java | 4 +- .../com/android/dx/dex/code/form/Form22b.java | 4 +- .../com/android/dx/dex/code/form/Form22c.java | 4 +- .../com/android/dx/dex/code/form/Form22s.java | 4 +- .../com/android/dx/dex/code/form/Form22t.java | 4 +- .../com/android/dx/dex/code/form/Form22x.java | 4 +- .../com/android/dx/dex/code/form/Form23x.java | 4 +- .../com/android/dx/dex/code/form/Form30t.java | 4 +- .../com/android/dx/dex/code/form/Form31c.java | 4 +- .../com/android/dx/dex/code/form/Form31i.java | 4 +- .../com/android/dx/dex/code/form/Form31t.java | 4 +- .../com/android/dx/dex/code/form/Form32x.java | 4 +- .../com/android/dx/dex/code/form/Form35c.java | 14 +- .../com/android/dx/dex/code/form/Form3rc.java | 4 +- .../com/android/dx/dex/code/form/Form51l.java | 4 +- .../dx/dex/code/form/SpecialFormat.java | 4 +- .../android/dx/dex/file/AnnotationItem.java | 16 +- .../dx/dex/file/AnnotationSetItem.java | 14 +- .../dx/dex/file/AnnotationSetRefItem.java | 4 +- .../android/dx/dex/file/AnnotationUtils.java | 68 +- .../dx/dex/file/AnnotationsDirectoryItem.java | 46 +- .../com/android/dx/dex/file/CatchStructs.java | 57 +- .../android/dx/dex/file/ClassDataItem.java | 70 +- .../com/android/dx/dex/file/ClassDefItem.java | 70 +- .../android/dx/dex/file/ClassDefsSection.java | 18 +- dx/src/com/android/dx/dex/file/CodeItem.java | 33 +- .../dx/dex/file/DebugInfoConstants.java | 8 +- .../android/dx/dex/file/DebugInfoDecoder.java | 14 +- .../android/dx/dex/file/DebugInfoEncoder.java | 174 +- .../android/dx/dex/file/DebugInfoItem.java | 36 +- dx/src/com/android/dx/dex/file/DexFile.java | 108 +- .../android/dx/dex/file/EncodedArrayItem.java | 6 +- .../com/android/dx/dex/file/EncodedField.java | 6 +- .../android/dx/dex/file/EncodedMember.java | 18 +- .../android/dx/dex/file/EncodedMethod.java | 16 +- .../dx/dex/file/FieldAnnotationStruct.java | 12 +- .../com/android/dx/dex/file/FieldIdItem.java | 4 +- .../android/dx/dex/file/FieldIdsSection.java | 16 +- .../com/android/dx/dex/file/HeaderItem.java | 4 +- .../android/dx/dex/file/HeaderSection.java | 6 +- dx/src/com/android/dx/dex/file/IdItem.java | 6 +- .../com/android/dx/dex/file/IndexedItem.java | 12 +- dx/src/com/android/dx/dex/file/Item.java | 12 +- dx/src/com/android/dx/dex/file/ItemType.java | 8 +- dx/src/com/android/dx/dex/file/MapItem.java | 30 +- .../com/android/dx/dex/file/MemberIdItem.java | 10 +- .../android/dx/dex/file/MemberIdsSection.java | 6 +- .../dx/dex/file/MethodAnnotationStruct.java | 12 +- .../com/android/dx/dex/file/MethodIdItem.java | 4 +- .../android/dx/dex/file/MethodIdsSection.java | 16 +- .../android/dx/dex/file/MixedItemSection.java | 40 +- .../android/dx/dex/file/OffsettedItem.java | 59 +- .../dex/file/ParameterAnnotationStruct.java | 14 +- .../com/android/dx/dex/file/ProtoIdItem.java | 14 +- .../android/dx/dex/file/ProtoIdsSection.java | 16 +- dx/src/com/android/dx/dex/file/Section.java | 56 +- .../com/android/dx/dex/file/Statistics.java | 26 +- .../android/dx/dex/file/StringDataItem.java | 8 +- .../com/android/dx/dex/file/StringIdItem.java | 10 +- .../android/dx/dex/file/StringIdsSection.java | 36 +- .../com/android/dx/dex/file/TypeIdItem.java | 2 +- .../android/dx/dex/file/TypeIdsSection.java | 24 +- .../com/android/dx/dex/file/TypeListItem.java | 6 +- .../dx/dex/file/UniformItemSection.java | 12 +- .../android/dx/dex/file/UniformListItem.java | 24 +- .../com/android/dx/dex/file/ValueEncoder.java | 101 +- .../android/dx/rop/annotation/Annotation.java | 20 +- .../rop/annotation/AnnotationVisibility.java | 4 +- .../dx/rop/annotation/Annotations.java | 24 +- .../dx/rop/annotation/AnnotationsList.java | 20 +- .../dx/rop/annotation/NameValuePair.java | 12 +- .../com/android/dx/rop/code/AccessFlags.java | 60 +- .../com/android/dx/rop/code/BasicBlock.java | 54 +- .../android/dx/rop/code/BasicBlockList.java | 58 +- .../code/ConservativeTranslationAdvice.java | 4 +- dx/src/com/android/dx/rop/code/CstInsn.java | 14 +- .../dx/rop/code/DexTranslationAdvice.java | 6 +- .../com/android/dx/rop/code/Exceptions.java | 50 +- .../dx/rop/code/FillArrayDataInsn.java | 14 +- dx/src/com/android/dx/rop/code/Insn.java | 86 +- dx/src/com/android/dx/rop/code/InsnList.java | 24 +- dx/src/com/android/dx/rop/code/LocalItem.java | 18 +- .../dx/rop/code/LocalVariableExtractor.java | 19 +- .../dx/rop/code/LocalVariableInfo.java | 50 +- .../com/android/dx/rop/code/PlainCstInsn.java | 10 +- dx/src/com/android/dx/rop/code/PlainInsn.java | 16 +- dx/src/com/android/dx/rop/code/RegOps.java | 166 +- .../com/android/dx/rop/code/RegisterSpec.java | 148 +- .../android/dx/rop/code/RegisterSpecList.java | 56 +- .../android/dx/rop/code/RegisterSpecSet.java | 52 +- dx/src/com/android/dx/rop/code/Rop.java | 80 +- dx/src/com/android/dx/rop/code/RopMethod.java | 24 +- dx/src/com/android/dx/rop/code/Rops.java | 856 +- .../android/dx/rop/code/SourcePosition.java | 30 +- .../com/android/dx/rop/code/SwitchInsn.java | 16 +- .../android/dx/rop/code/ThrowingCstInsn.java | 12 +- .../com/android/dx/rop/code/ThrowingInsn.java | 16 +- .../dx/rop/code/TranslationAdvice.java | 14 +- dx/src/com/android/dx/rop/cst/Constant.java | 12 +- .../com/android/dx/rop/cst/ConstantPool.java | 36 +- .../com/android/dx/rop/cst/CstAnnotation.java | 6 +- dx/src/com/android/dx/rop/cst/CstArray.java | 18 +- .../android/dx/rop/cst/CstBaseMethodRef.java | 40 +- dx/src/com/android/dx/rop/cst/CstBoolean.java | 20 +- dx/src/com/android/dx/rop/cst/CstByte.java | 16 +- dx/src/com/android/dx/rop/cst/CstChar.java | 16 +- dx/src/com/android/dx/rop/cst/CstDouble.java | 12 +- dx/src/com/android/dx/rop/cst/CstEnumRef.java | 6 +- .../com/android/dx/rop/cst/CstFieldRef.java | 14 +- dx/src/com/android/dx/rop/cst/CstFloat.java | 14 +- dx/src/com/android/dx/rop/cst/CstInteger.java | 26 +- .../dx/rop/cst/CstInterfaceMethodRef.java | 10 +- .../com/android/dx/rop/cst/CstKnownNull.java | 4 +- .../com/android/dx/rop/cst/CstLiteral32.java | 4 +- .../com/android/dx/rop/cst/CstLiteral64.java | 4 +- .../android/dx/rop/cst/CstLiteralBits.java | 18 +- dx/src/com/android/dx/rop/cst/CstLong.java | 12 +- .../com/android/dx/rop/cst/CstMemberRef.java | 16 +- .../com/android/dx/rop/cst/CstMethodRef.java | 6 +- dx/src/com/android/dx/rop/cst/CstNat.java | 30 +- dx/src/com/android/dx/rop/cst/CstShort.java | 18 +- dx/src/com/android/dx/rop/cst/CstString.java | 10 +- dx/src/com/android/dx/rop/cst/CstType.java | 60 +- dx/src/com/android/dx/rop/cst/CstUtf8.java | 40 +- .../android/dx/rop/cst/StdConstantPool.java | 12 +- .../com/android/dx/rop/cst/TypedConstant.java | 2 +- dx/src/com/android/dx/rop/cst/Zeroes.java | 6 +- dx/src/com/android/dx/rop/package-info.java | 57 +- dx/src/com/android/dx/rop/type/Prototype.java | 54 +- .../com/android/dx/rop/type/StdTypeList.java | 132 +- dx/src/com/android/dx/rop/type/Type.java | 190 +- .../com/android/dx/rop/type/TypeBearer.java | 24 +- dx/src/com/android/dx/rop/type/TypeList.java | 20 +- .../android/dx/ssa/BasicRegisterMapper.java | 30 +- dx/src/com/android/dx/ssa/ConstCollector.java | 146 +- .../com/android/dx/ssa/DeadCodeRemover.java | 61 +- dx/src/com/android/dx/ssa/DomFront.java | 49 +- dx/src/com/android/dx/ssa/Dominators.java | 159 +- .../dx/ssa/InterferenceRegisterMapper.java | 32 +- .../com/android/dx/ssa/LiteralOpUpgrader.java | 6 +- .../dx/ssa/LocalVariableExtractor.java | 18 +- .../com/android/dx/ssa/LocalVariableInfo.java | 50 +- .../com/android/dx/ssa/MoveParamCombiner.java | 4 +- dx/src/com/android/dx/ssa/NormalSsaInsn.java | 43 +- dx/src/com/android/dx/ssa/Optimizer.java | 6 +- dx/src/com/android/dx/ssa/PhiInsn.java | 169 +- dx/src/com/android/dx/ssa/RegisterMapper.java | 20 +- dx/src/com/android/dx/ssa/SCCP.java | 13 +- dx/src/com/android/dx/ssa/SetFactory.java | 12 +- dx/src/com/android/dx/ssa/SsaBasicBlock.java | 421 +- dx/src/com/android/dx/ssa/SsaConverter.java | 141 +- dx/src/com/android/dx/ssa/SsaInsn.java | 103 +- dx/src/com/android/dx/ssa/SsaMethod.java | 307 +- dx/src/com/android/dx/ssa/SsaRenamer.java | 166 +- .../com/android/dx/ssa/_tests/_DomFront.java | 2 +- .../dx/ssa/back/FirstFitAllocator.java | 1 - .../back/FirstFitLocalCombiningAllocator.java | 300 +- .../dx/ssa/back/IdenticalBlockCombiner.java | 45 +- .../dx/ssa/back/InterferenceGraph.java | 19 +- .../android/dx/ssa/back/LivenessAnalyzer.java | 107 +- .../dx/ssa/back/NullRegisterAllocator.java | 9 +- .../dx/ssa/back/RegisterAllocator.java | 71 +- dx/src/com/android/dx/ssa/back/SsaToRop.java | 246 +- dx/src/com/android/dx/ssa/package-info.java | 14 +- .../com/android/dx/util/AnnotatedOutput.java | 12 +- dx/src/com/android/dx/util/BitIntSet.java | 8 +- dx/src/com/android/dx/util/Bits.java | 76 +- dx/src/com/android/dx/util/ByteArray.java | 102 +- .../dx/util/ByteArrayAnnotatedOutput.java | 56 +- .../android/dx/util/ExceptionWithContext.java | 20 +- dx/src/com/android/dx/util/FileUtils.java | 8 +- dx/src/com/android/dx/util/FixedSizeList.java | 46 +- dx/src/com/android/dx/util/Hex.java | 62 +- dx/src/com/android/dx/util/HexParser.java | 6 +- .../com/android/dx/util/IndentingWriter.java | 24 +- dx/src/com/android/dx/util/IntIterator.java | 6 - dx/src/com/android/dx/util/IntList.java | 30 +- dx/src/com/android/dx/util/IntSet.java | 8 +- dx/src/com/android/dx/util/LabeledItem.java | 2 +- dx/src/com/android/dx/util/LabeledList.java | 10 +- dx/src/com/android/dx/util/Leb128Utils.java | 1 - dx/src/com/android/dx/util/ListIntSet.java | 5 - .../android/dx/util/MutabilityControl.java | 8 +- dx/src/com/android/dx/util/Output.java | 44 +- dx/src/com/android/dx/util/ToHuman.java | 4 +- .../com/android/dx/util/TwoColumnOutput.java | 52 +- dx/src/com/android/dx/util/Writers.java | 8 +- dx/src/com/android/dx/util/_tests/_Bits.java | 2 +- .../com/android/dx/util/_tests/_IntList.java | 2 +- dx/tests/111-use-null-as-array/Blort.java | 108 + dx/tests/111-use-null-as-array/expected.txt | 116 + dx/tests/111-use-null-as-array/info.txt | 18 + dx/tests/111-use-null-as-array/run | 19 + libcore/Android.mk | 2 +- .../java/java/lang/annotation/Annotation.java | 26 +- .../annotation/AnnotationFormatError.java | 17 +- .../AnnotationTypeMismatchException.java | 17 +- .../java/java/lang/annotation/Documented.java | 4 +- .../java/lang/annotation/ElementType.java | 4 +- .../IncompleteAnnotationException.java | 16 +- .../java/java/lang/annotation/Inherited.java | 4 +- .../java/java/lang/annotation/Retention.java | 7 +- .../java/lang/annotation/RetentionPolicy.java | 4 +- .../java/java/lang/annotation/Target.java | 4 +- .../AnnotationTypeMismatchExceptionTest.java | 2 +- .../IncompleteAnnotationExceptionTest.java | 2 +- .../main/java/java/util/jar/Attributes.java | 185 +- .../main/java/java/util/jar/InitManifest.java | 373 +- .../src/main/java/java/util/jar/JarEntry.java | 19 +- .../main/java/java/util/jar/JarException.java | 7 +- .../src/main/java/java/util/jar/JarFile.java | 159 +- .../java/java/util/jar/JarInputStream.java | 25 +- .../java/java/util/jar/JarOutputStream.java | 3 - .../main/java/java/util/jar/JarVerifier.java | 213 +- .../src/main/java/java/util/jar/Manifest.java | 289 +- .../src/main/java/java/util/jar/Pack200.java | 76 +- .../src/main/java/java/util/zip/Adler32.java | 10 +- .../src/main/java/java/util/zip/CRC32.java | 11 +- .../java/util/zip/CheckedInputStream.java | 8 - .../java/util/zip/CheckedOutputStream.java | 7 - .../src/main/java/java/util/zip/Checksum.java | 25 +- .../java/util/zip/DataFormatException.java | 7 +- .../src/main/java/java/util/zip/Deflater.java | 94 +- .../java/util/zip/DeflaterOutputStream.java | 15 +- .../java/java/util/zip/GZIPInputStream.java | 137 +- .../java/java/util/zip/GZIPOutputStream.java | 33 +- .../src/main/java/java/util/zip/Inflater.java | 236 +- .../java/util/zip/InflaterInputStream.java | 137 +- .../main/java/java/util/zip/ZipConstants.java | 1 - .../src/main/java/java/util/zip/ZipEntry.java | 74 +- .../main/java/java/util/zip/ZipException.java | 7 +- .../src/main/java/java/util/zip/ZipFile.java | 57 +- .../java/java/util/zip/ZipInputStream.java | 199 +- .../java/java/util/zip/ZipOutputStream.java | 29 +- .../archive/internal/nls/Messages.java | 15 +- .../archive/internal/nls/messages.properties | 13 +- .../org/apache/harmony/archive/util/Util.java | 60 +- .../src/main/native/java_util_zip_Adler32.c | 15 +- .../src/main/native/java_util_zip_CRC32.c | 5 +- .../src/main/native/java_util_zip_Deflater.c | 73 +- .../src/main/native/java_util_zip_Inflater.c | 56 +- libcore/archive/src/main/native/sieb.c | 14 - libcore/archive/src/main/native/sieb.h | 5 +- libcore/archive/src/main/native/sub.mk | 3 +- .../src/main/native/zip.c} | 25 +- libcore/archive/src/main/native/zip.h | 70 + libcore/archive/src/main/native/zipsup.c | 2 +- libcore/archive/src/main/native/zipsup.h | 26 +- .../apache/harmony/archive/util/UtilTest.java | 87 + .../tests/java/util/jar/AttributesTest.java | 23 +- .../tests/java/util/jar/JarEntryTest.java | 42 +- .../tests/java/util/jar/JarFileTest.java | 382 +- .../java/util/jar/JarInputStreamTest.java | 53 +- .../java/util/jar/JarOutputStreamTest.java | 51 +- .../tests/java/util/jar/ManifestTest.java | 721 +- .../util/zip/DeflaterOutputStreamTest.java | 13 +- .../java/util/zip/GZIPInputStreamTest.java | 40 +- .../java/util/zip/GZIPOutputStreamTest.java | 2 - .../util/zip/InflaterInputStreamTest.java | 45 +- .../tests/java/util/zip/InflaterTest.java | 48 +- .../tests/java/util/zip/ZipFileTest.java | 120 +- .../java/util/zip/ZipInputStreamTest.java | 39 +- .../java/util/zip/ZipOutputStreamTest.java | 22 +- .../javax/security/auth/AuthPermission.java | 16 +- .../security/auth/DestroyFailedException.java | 4 +- .../java/javax/security/auth/Destroyable.java | 6 +- .../auth/PrivateCredentialPermission.java | 19 +- .../java/javax/security/auth/Subject.java | 40 +- .../security/auth/SubjectDomainCombiner.java | 8 +- .../security/auth/callback/Callback.java | 2 - .../auth/callback/CallbackHandler.java | 4 +- .../auth/callback/PasswordCallback.java | 12 +- .../UnsupportedCallbackException.java | 8 +- .../security/auth/login/LoginException.java | 4 +- .../security/auth/x500/X500Principal.java | 32 +- .../concurrent/AbstractExecutorService.java | 71 +- .../util/concurrent/ArrayBlockingQueue.java | 341 +- .../java/util/concurrent/BlockingQueue.java | 289 +- .../util/concurrent/CompletionService.java | 53 +- .../util/concurrent/ConcurrentHashMap.java | 1015 +- .../concurrent/ConcurrentLinkedQueue.java | 466 +- .../java/util/concurrent/ConcurrentMap.java | 154 +- .../util/concurrent/CopyOnWriteArrayList.java | 1933 ++-- .../util/concurrent/CopyOnWriteArraySet.java | 340 +- .../java/util/concurrent/CountDownLatch.java | 161 +- .../java/util/concurrent/CyclicBarrier.java | 316 +- .../java/java/util/concurrent/DelayQueue.java | 381 +- .../java/java/util/concurrent/Delayed.java | 12 +- .../java/java/util/concurrent/Exchanger.java | 677 +- .../util/concurrent/ExecutionException.java | 8 +- .../java/java/util/concurrent/Executor.java | 9 +- .../concurrent/ExecutorCompletionService.java | 143 +- .../java/util/concurrent/ExecutorService.java | 259 +- .../java/java/util/concurrent/Executors.java | 295 +- .../java/java/util/concurrent/Future.java | 37 +- .../java/java/util/concurrent/FutureTask.java | 147 +- .../java/util/concurrent/Java6Arrays.java | 77 + .../util/concurrent/LinkedBlockingQueue.java | 203 +- .../concurrent/PriorityBlockingQueue.java | 371 +- .../RejectedExecutionException.java | 2 +- .../concurrent/RejectedExecutionHandler.java | 16 +- .../concurrent/ScheduledExecutorService.java | 108 +- .../ScheduledThreadPoolExecutor.java | 1113 +- .../java/java/util/concurrent/Semaphore.java | 376 +- .../util/concurrent/SynchronousQueue.java | 1193 +- .../java/util/concurrent/ThreadFactory.java | 13 +- .../util/concurrent/ThreadPoolExecutor.java | 1856 +-- .../java/java/util/concurrent/TimeUnit.java | 205 +- .../util/concurrent/atomic/AtomicBoolean.java | 31 +- .../util/concurrent/atomic/AtomicInteger.java | 75 +- .../concurrent/atomic/AtomicIntegerArray.java | 73 +- .../atomic/AtomicIntegerFieldUpdater.java | 187 +- .../util/concurrent/atomic/AtomicLong.java | 103 +- .../concurrent/atomic/AtomicLongArray.java | 79 +- .../atomic/AtomicLongFieldUpdater.java | 232 +- .../atomic/AtomicMarkableReference.java | 36 +- .../concurrent/atomic/AtomicReference.java | 38 +- .../atomic/AtomicReferenceArray.java | 40 +- .../atomic/AtomicReferenceFieldUpdater.java | 180 +- .../atomic/AtomicStampedReference.java | 38 +- .../util/concurrent/atomic/package-info.java | 163 + .../java/util/concurrent/atomic/package.html | 132 - .../locks/AbstractOwnableSynchronizer.java | 56 + .../locks/AbstractQueuedSynchronizer.java | 1427 ++- .../java/util/concurrent/locks/Condition.java | 294 +- .../java/java/util/concurrent/locks/Lock.java | 250 +- .../util/concurrent/locks/LockSupport.java | 163 +- .../util/concurrent/locks/ReadWriteLock.java | 23 +- .../util/concurrent/locks/ReentrantLock.java | 413 +- .../locks/ReentrantReadWriteLock.java | 1102 +- .../util/concurrent/locks/package-info.java | 44 + .../java/util/concurrent/locks/package.html | 44 - .../java/util/concurrent/package-info.java | 229 + .../java/java/util/concurrent/package.html | 125 - .../AbstractQueuedSynchronizerTest.java | 3 +- .../concurrent/ArrayBlockingQueueTest.java | 19 +- .../AtomicIntegerFieldUpdaterTest.java | 22 + .../util/concurrent/AtomicIntegerTest.java | 1 + .../AtomicLongFieldUpdaterTest.java | 41 +- .../java/util/concurrent/AtomicLongTest.java | 1 + .../AtomicReferenceFieldUpdaterTest.java | 36 +- .../util/concurrent/AtomicReferenceTest.java | 4 +- .../concurrent/ConcurrentHashMapTest.java | 132 +- .../util/concurrent/CyclicBarrierTest.java | 253 +- .../java/util/concurrent/DelayQueueTest.java | 224 +- .../ExecutorCompletionServiceTest.java | 8 +- .../java/util/concurrent/ExecutorsTest.java | 94 +- .../java/util/concurrent/JSR166TestCase.java | 110 +- .../concurrent/LinkedBlockingQueueTest.java | 126 +- .../java/util/concurrent/LockSupportTest.java | 27 +- .../concurrent/PriorityBlockingQueueTest.java | 24 +- .../util/concurrent/ReentrantLockTest.java | 244 +- .../ReentrantReadWriteLockTest.java | 549 +- .../util/concurrent/SynchronousQueueTest.java | 1 + .../concurrent/ThreadPoolExecutorTest.java | 66 + .../java/util/concurrent/TimeUnitTest.java | 76 +- .../javax/crypto/BadPaddingException.java | 5 - .../src/main/java/javax/crypto/Cipher.java | 151 +- .../java/javax/crypto/CipherInputStream.java | 42 +- .../java/javax/crypto/CipherOutputStream.java | 24 +- .../src/main/java/javax/crypto/CipherSpi.java | 82 +- .../javax/crypto/EncryptedPrivateKeyInfo.java | 37 +- .../java/javax/crypto/ExemptionMechanism.java | 46 +- .../crypto/ExemptionMechanismException.java | 5 - .../javax/crypto/ExemptionMechanismSpi.java | 21 +- .../crypto/IllegalBlockSizeException.java | 5 - .../main/java/javax/crypto/KeyAgreement.java | 43 +- .../java/javax/crypto/KeyAgreementSpi.java | 24 +- .../main/java/javax/crypto/KeyGenerator.java | 38 +- .../java/javax/crypto/KeyGeneratorSpi.java | 15 +- .../src/main/java/javax/crypto/Mac.java | 61 +- .../src/main/java/javax/crypto/MacSpi.java | 30 +- .../javax/crypto/NoSuchPaddingException.java | 5 - .../main/java/javax/crypto/NullCipher.java | 2 - .../main/java/javax/crypto/SealedObject.java | 27 +- .../src/main/java/javax/crypto/SecretKey.java | 7 +- .../java/javax/crypto/SecretKeyFactory.java | 30 +- .../javax/crypto/SecretKeyFactorySpi.java | 12 +- .../javax/crypto/ShortBufferException.java | 5 - .../java/javax/crypto/interfaces/DHKey.java | 4 +- .../javax/crypto/interfaces/DHPrivateKey.java | 2 - .../javax/crypto/interfaces/DHPublicKey.java | 4 +- .../java/javax/crypto/interfaces/PBEKey.java | 8 +- .../java/javax/crypto/spec/DESKeySpec.java | 12 +- .../java/javax/crypto/spec/DESedeKeySpec.java | 10 +- .../javax/crypto/spec/DHGenParameterSpec.java | 8 +- .../javax/crypto/spec/DHParameterSpec.java | 12 +- .../javax/crypto/spec/DHPrivateKeySpec.java | 11 +- .../javax/crypto/spec/DHPublicKeySpec.java | 10 +- .../javax/crypto/spec/IvParameterSpec.java | 6 +- .../javax/crypto/spec/OAEPParameterSpec.java | 12 +- .../java/javax/crypto/spec/PBEKeySpec.java | 16 +- .../javax/crypto/spec/PBEParameterSpec.java | 10 +- .../main/java/javax/crypto/spec/PSource.java | 15 +- .../javax/crypto/spec/RC2ParameterSpec.java | 16 +- .../javax/crypto/spec/RC5ParameterSpec.java | 20 +- .../java/javax/crypto/spec/SecretKeySpec.java | 16 +- .../javax/crypto/ExemptionMechanismTest.java | 2 + .../dalvik/system/AllocationLimitError.java | 5 +- .../src/main/java/dalvik/system/DexFile.java | 38 +- .../java/dalvik/system/PathClassLoader.java | 12 +- .../dalvik/system/PotentialDeadlockError.java | 5 +- .../java/dalvik/system/SamplingProfiler.java | 346 + .../dalvik/system/StaleDexCacheError.java | 3 + .../dalvik/system/TemporaryDirectory.java | 5 +- .../src/main/java/dalvik/system/TouchDex.java | 9 +- .../src/main/java/dalvik/system/VMDebug.java | 23 +- .../main/java/dalvik/system/VMRuntime.java | 13 +- .../src/main/java/dalvik/system/VMStack.java | 13 +- .../src/main/java/dalvik/system/Zygote.java | 5 +- .../icu4jni/charset/CharsetDecoderICU.java | 15 +- .../java/com/ibm/icu4jni/util/Resources.java | 38 +- .../main/native/DecimalFormatInterface.cpp | 2 +- .../icu/src/main/native/ResourceInterface.cpp | 5 +- .../java/util/logging/ConsoleHandler.java | 13 +- .../java/java/util/logging/ErrorManager.java | 22 +- .../java/java/util/logging/FileHandler.java | 80 +- .../main/java/java/util/logging/Filter.java | 3 - .../java/java/util/logging/Formatter.java | 35 +- .../main/java/java/util/logging/Handler.java | 100 +- .../main/java/java/util/logging/Level.java | 78 +- .../java/java/util/logging/LogManager.java | 229 +- .../java/java/util/logging/LogRecord.java | 111 +- .../main/java/java/util/logging/Logger.java | 320 +- .../java/java/util/logging/LoggingMXBean.java | 17 +- .../java/util/logging/LoggingPermission.java | 16 +- .../java/java/util/logging/MemoryHandler.java | 128 +- .../java/util/logging/SimpleFormatter.java | 13 +- .../java/java/util/logging/SocketHandler.java | 16 +- .../java/java/util/logging/StreamHandler.java | 71 +- .../java/java/util/logging/XMLFormatter.java | 136 +- .../tests/java/util/logging/LoggerTest.java | 24 + .../config/java/util/logging/logging.config | 2 +- .../src/main/java/java/lang/Class.java | 19 +- .../src/main/java/java/lang/Thread.java | 268 +- .../main/native/java_lang_ProcessManager.c | 3 +- .../java/java/io/BufferedInputStream.java | 161 +- .../java/java/io/BufferedOutputStream.java | 84 +- .../src/main/java/java/io/BufferedReader.java | 29 +- .../src/main/java/java/io/BufferedWriter.java | 51 +- .../java/java/io/ByteArrayInputStream.java | 47 +- .../java/java/io/ByteArrayOutputStream.java | 46 +- .../main/java/java/io/CharArrayReader.java | 48 +- .../main/java/java/io/CharArrayWriter.java | 117 +- .../java/java/io/CharConversionException.java | 7 +- .../luni/src/main/java/java/io/Closeable.java | 5 +- .../luni/src/main/java/java/io/DataInput.java | 23 +- .../main/java/java/io/DataInputStream.java | 223 +- .../src/main/java/java/io/DataOutput.java | 52 +- .../main/java/java/io/DataOutputStream.java | 229 +- .../src/main/java/java/io/EOFException.java | 5 - .../src/main/java/java/io/EmulatedFields.java | 256 +- .../src/main/java/java/io/Externalizable.java | 4 - libcore/luni/src/main/java/java/io/File.java | 543 +- .../src/main/java/java/io/FileDescriptor.java | 28 +- .../src/main/java/java/io/FileFilter.java | 7 +- .../main/java/java/io/FileInputStream.java | 43 +- .../java/java/io/FileNotFoundException.java | 5 - .../main/java/java/io/FileOutputStream.java | 49 +- .../src/main/java/java/io/FilePermission.java | 11 +- .../java/io/FilePermissionCollection.java | 2 - .../src/main/java/java/io/FileReader.java | 5 - .../src/main/java/java/io/FileWriter.java | 7 - .../src/main/java/java/io/FilenameFilter.java | 8 +- .../main/java/java/io/FilterInputStream.java | 28 +- .../main/java/java/io/FilterOutputStream.java | 24 +- .../src/main/java/java/io/FilterReader.java | 14 - .../src/main/java/java/io/FilterWriter.java | 23 +- .../luni/src/main/java/java/io/Flushable.java | 5 +- .../src/main/java/java/io/IOException.java | 5 - .../src/main/java/java/io/InputStream.java | 38 +- .../main/java/java/io/InputStreamReader.java | 196 +- .../java/java/io/InterruptedIOException.java | 7 - .../java/java/io/InvalidClassException.java | 7 - .../java/java/io/InvalidObjectException.java | 3 - .../java/java/io/LineNumberInputStream.java | 36 +- .../main/java/java/io/LineNumberReader.java | 49 +- .../main/java/java/io/NotActiveException.java | 5 - .../java/io/NotSerializableException.java | 5 - .../src/main/java/java/io/ObjectInput.java | 9 - .../main/java/java/io/ObjectInputStream.java | 875 +- .../java/java/io/ObjectInputValidation.java | 4 - .../src/main/java/java/io/ObjectOutput.java | 8 - .../main/java/java/io/ObjectOutputStream.java | 733 +- .../main/java/java/io/ObjectStreamClass.java | 502 +- .../java/java/io/ObjectStreamConstants.java | 62 +- .../java/java/io/ObjectStreamException.java | 5 - .../main/java/java/io/ObjectStreamField.java | 103 +- .../java/java/io/OptionalDataException.java | 9 - .../src/main/java/java/io/OutputStream.java | 21 +- .../main/java/java/io/OutputStreamWriter.java | 39 +- .../main/java/java/io/PipedInputStream.java | 169 +- .../main/java/java/io/PipedOutputStream.java | 26 +- .../src/main/java/java/io/PipedReader.java | 36 +- .../src/main/java/java/io/PipedWriter.java | 27 +- .../src/main/java/java/io/PrintStream.java | 143 +- .../src/main/java/java/io/PrintWriter.java | 159 +- .../java/java/io/PushbackInputStream.java | 54 +- .../src/main/java/java/io/PushbackReader.java | 44 +- .../main/java/java/io/RandomAccessFile.java | 173 +- .../luni/src/main/java/java/io/Reader.java | 42 +- .../java/java/io/SequenceInputStream.java | 26 +- .../src/main/java/java/io/Serializable.java | 4 - .../java/io/StreamCorruptedException.java | 5 - .../main/java/java/io/StreamTokenizer.java | 46 +- .../java/java/io/StringBufferInputStream.java | 27 +- .../src/main/java/java/io/StringReader.java | 54 +- .../src/main/java/java/io/StringWriter.java | 70 +- .../java/java/io/SyncFailedException.java | 3 - .../java/java/io/UTFDataFormatException.java | 7 +- .../java/io/UnsupportedEncodingException.java | 10 +- .../java/java/io/WriteAbortedException.java | 7 - .../luni/src/main/java/java/io/Writer.java | 41 +- .../java/java/lang/AbstractMethodError.java | 7 +- .../java/java/lang/AbstractStringBuilder.java | 194 +- .../src/main/java/java/lang/Appendable.java | 12 +- .../java/java/lang/ArithmeticException.java | 7 +- .../lang/ArrayIndexOutOfBoundsException.java | 8 +- .../java/java/lang/ArrayStoreException.java | 7 +- .../main/java/java/lang/AssertionError.java | 13 +- .../luni/src/main/java/java/lang/Boolean.java | 51 +- .../luni/src/main/java/java/lang/Byte.java | 62 +- .../src/main/java/java/lang/CharSequence.java | 14 +- .../src/main/java/java/lang/Character.java | 1421 ++- .../java/java/lang/ClassCastException.java | 10 +- .../java/java/lang/ClassCircularityError.java | 8 +- .../main/java/java/lang/ClassFormatError.java | 7 +- .../java/lang/ClassNotFoundException.java | 14 +- .../java/lang/CloneNotSupportedException.java | 8 +- .../src/main/java/java/lang/Comparable.java | 9 +- .../src/main/java/java/lang/Deprecated.java | 25 +- .../luni/src/main/java/java/lang/Double.java | 133 +- .../luni/src/main/java/java/lang/Enum.java | 29 +- .../lang/EnumConstantNotPresentException.java | 11 +- .../luni/src/main/java/java/lang/Error.java | 6 - .../src/main/java/java/lang/Exception.java | 6 - .../lang/ExceptionInInitializerError.java | 7 - .../luni/src/main/java/java/lang/Float.java | 140 +- .../java/java/lang/IllegalAccessError.java | 6 - .../java/lang/IllegalAccessException.java | 5 - .../java/lang/IllegalArgumentException.java | 11 +- .../lang/IllegalMonitorStateException.java | 5 - .../java/java/lang/IllegalStateException.java | 9 +- .../lang/IllegalThreadStateException.java | 5 - .../lang/IncompatibleClassChangeError.java | 4 - .../java/lang/IndexOutOfBoundsException.java | 5 - .../java/lang/InheritableThreadLocal.java | 47 +- .../java/java/lang/InstantiationError.java | 7 - .../java/lang/InstantiationException.java | 6 - .../luni/src/main/java/java/lang/Integer.java | 239 +- .../main/java/java/lang/InternalError.java | 5 - .../java/java/lang/InterruptedException.java | 5 - .../src/main/java/java/lang/Iterable.java | 5 +- .../src/main/java/java/lang/LinkageError.java | 4 - .../luni/src/main/java/java/lang/Long.java | 73 +- .../luni/src/main/java/java/lang/Math.java | 270 +- .../java/lang/NegativeArraySizeException.java | 5 - .../java/java/lang/NoClassDefFoundError.java | 5 - .../main/java/java/lang/NoSuchFieldError.java | 6 - .../java/java/lang/NoSuchFieldException.java | 5 - .../java/java/lang/NoSuchMethodError.java | 5 - .../java/java/lang/NoSuchMethodException.java | 5 - .../java/java/lang/NullPointerException.java | 5 - .../luni/src/main/java/java/lang/Number.java | 10 - .../java/java/lang/NumberFormatException.java | 5 - .../main/java/java/lang/OutOfMemoryError.java | 5 - .../src/main/java/java/lang/Override.java | 40 +- .../luni/src/main/java/java/lang/Process.java | 12 +- .../main/java/java/lang/ProcessBuilder.java | 37 +- .../src/main/java/java/lang/Readable.java | 3 - .../src/main/java/java/lang/Runnable.java | 4 - .../main/java/java/lang/RuntimeException.java | 7 - .../java/java/lang/RuntimePermission.java | 4 - .../java/java/lang/SecurityException.java | 9 +- .../main/java/java/lang/SecurityManager.java | 137 +- .../luni/src/main/java/java/lang/Short.java | 187 +- .../java/java/lang/StackOverflowError.java | 5 - .../src/main/java/java/lang/StrictMath.java | 442 +- .../luni/src/main/java/java/lang/String.java | 294 +- .../src/main/java/java/lang/StringBuffer.java | 92 +- .../main/java/java/lang/StringBuilder.java | 107 +- .../lang/StringIndexOutOfBoundsException.java | 6 - .../main/java/java/lang/SuppressWarnings.java | 47 +- .../src/main/java/java/lang/ThreadDeath.java | 4 - .../src/main/java/java/lang/ThreadLocal.java | 9 +- .../java/lang/TypeNotPresentException.java | 5 +- .../src/main/java/java/lang/UnknownError.java | 5 - .../java/java/lang/UnsatisfiedLinkError.java | 5 - .../lang/UnsupportedClassVersionError.java | 5 - .../lang/UnsupportedOperationException.java | 12 +- .../src/main/java/java/lang/VerifyError.java | 5 - .../java/java/lang/VirtualMachineError.java | 4 - .../luni/src/main/java/java/lang/Void.java | 10 +- .../java/java/lang/ref/ReferenceQueue.java | 51 +- .../java/lang/reflect/AnnotatedElement.java | 16 +- .../java/lang/reflect/GenericArrayType.java | 9 +- .../java/lang/reflect/GenericDeclaration.java | 7 +- .../reflect/GenericSignatureFormatError.java | 6 +- .../java/lang/reflect/InvocationHandler.java | 4 - .../reflect/InvocationTargetException.java | 12 - .../MalformedParameterizedTypeException.java | 4 +- .../main/java/java/lang/reflect/Member.java | 30 +- .../main/java/java/lang/reflect/Modifier.java | 71 +- .../java/lang/reflect/ParameterizedType.java | 26 +- .../main/java/java/lang/reflect/Proxy.java | 49 +- .../java/lang/reflect/ReflectPermission.java | 8 - .../src/main/java/java/lang/reflect/Type.java | 4 +- .../java/java/lang/reflect/TypeVariable.java | 19 +- .../reflect/UndeclaredThrowableException.java | 10 - .../java/java/lang/reflect/WildcardType.java | 15 +- .../src/main/java/java/net/Authenticator.java | 21 +- .../src/main/java/java/net/BindException.java | 5 - .../src/main/java/java/net/CacheRequest.java | 6 - .../src/main/java/java/net/CacheResponse.java | 5 - .../main/java/java/net/ConnectException.java | 7 +- .../main/java/java/net/ContentHandler.java | 9 +- .../java/java/net/ContentHandlerFactory.java | 2 - .../src/main/java/java/net/CookieHandler.java | 14 +- .../main/java/java/net/DatagramPacket.java | 44 +- .../main/java/java/net/DatagramSocket.java | 220 +- .../java/java/net/DatagramSocketImpl.java | 52 +- .../java/net/DatagramSocketImplFactory.java | 2 - .../src/main/java/java/net/FileNameMap.java | 2 - .../java/java/net/HttpRetryException.java | 13 +- .../main/java/java/net/HttpURLConnection.java | 103 +- .../src/main/java/java/net/Inet4Address.java | 77 +- .../src/main/java/java/net/Inet6Address.java | 92 +- .../src/main/java/java/net/InetAddress.java | 861 +- .../main/java/java/net/InetSocketAddress.java | 47 +- .../main/java/java/net/JarURLConnection.java | 57 +- .../java/java/net/MalformedURLException.java | 4 - .../main/java/java/net/MulticastSocket.java | 122 +- .../main/java/java/net/NegCacheElement.java | 23 +- .../src/main/java/java/net/NegativeCache.java | 103 +- .../src/main/java/java/net/NetPermission.java | 4 - .../main/java/java/net/NetworkInterface.java | 13 +- .../java/java/net/NoRouteToHostException.java | 5 - .../java/java/net/PasswordAuthentication.java | 6 +- .../java/net/PortUnreachableException.java | 3 - .../main/java/java/net/ProtocolException.java | 5 - .../luni/src/main/java/java/net/Proxy.java | 14 +- .../src/main/java/java/net/ProxySelector.java | 15 +- .../main/java/java/net/ProxySelectorImpl.java | 5 +- .../src/main/java/java/net/ResponseCache.java | 12 +- .../java/java/net/SecureCacheResponse.java | 8 - .../src/main/java/java/net/ServerSocket.java | 99 +- .../luni/src/main/java/java/net/Socket.java | 285 +- .../src/main/java/java/net/SocketAddress.java | 4 - .../main/java/java/net/SocketException.java | 5 - .../src/main/java/java/net/SocketImpl.java | 135 +- .../main/java/java/net/SocketImplFactory.java | 5 +- .../src/main/java/java/net/SocketOptions.java | 31 - .../main/java/java/net/SocketPermission.java | 85 +- .../java/net/SocketPermissionCollection.java | 4 - .../java/java/net/SocketTimeoutException.java | 5 - libcore/luni/src/main/java/java/net/URI.java | 99 +- .../main/java/java/net/URIEncoderDecoder.java | 23 +- .../java/java/net/URISyntaxException.java | 8 - libcore/luni/src/main/java/java/net/URL.java | 75 +- .../main/java/java/net/URLClassLoader.java | 1466 ++- .../src/main/java/java/net/URLConnection.java | 266 +- .../src/main/java/java/net/URLDecoder.java | 108 +- .../src/main/java/java/net/URLEncoder.java | 13 +- .../main/java/java/net/URLStreamHandler.java | 31 +- .../java/net/URLStreamHandlerFactory.java | 5 +- .../java/java/net/UnknownHostException.java | 5 - .../java/net/UnknownServiceException.java | 5 - .../java/java/util/AbstractCollection.java | 93 +- .../src/main/java/java/util/AbstractList.java | 104 +- .../src/main/java/java/util/AbstractMap.java | 83 +- .../main/java/java/util/AbstractQueue.java | 11 +- .../java/util/AbstractSequentialList.java | 27 +- .../src/main/java/java/util/AbstractSet.java | 15 +- .../src/main/java/java/util/ArrayList.java | 182 +- .../luni/src/main/java/java/util/Arrays.java | 535 +- .../luni/src/main/java/java/util/BitSet.java | 762 +- .../src/main/java/java/util/Calendar.java | 441 +- .../src/main/java/java/util/Collection.java | 161 +- .../src/main/java/java/util/Collections.java | 466 +- .../java/java/util/ComparableTimSort.java | 2 +- .../src/main/java/java/util/Comparator.java | 12 +- .../src/main/java/java/util/Currency.java | 55 +- .../luni/src/main/java/java/util/Date.java | 189 +- .../src/main/java/java/util/Dictionary.java | 14 +- .../util/DuplicateFormatFlagsException.java | 5 +- .../java/java/util/EmptyStackException.java | 3 +- .../luni/src/main/java/java/util/EnumMap.java | 84 +- .../luni/src/main/java/java/util/EnumSet.java | 77 +- .../src/main/java/java/util/Enumeration.java | 12 +- .../main/java/java/util/EventListener.java | 6 +- .../java/java/util/EventListenerProxy.java | 4 - .../src/main/java/java/util/EventObject.java | 7 - ...ormatFlagsConversionMismatchException.java | 1 - .../src/main/java/java/util/Formattable.java | 6 +- .../main/java/java/util/FormattableFlags.java | 7 - .../src/main/java/java/util/Formatter.java | 572 +- .../java/util/FormatterClosedException.java | 1 - .../java/java/util/GregorianCalendar.java | 279 +- .../luni/src/main/java/java/util/HashMap.java | 1387 ++- .../luni/src/main/java/java/util/HashSet.java | 35 +- .../src/main/java/java/util/Hashtable.java | 1642 +-- .../src/main/java/java/util/HugeEnumSet.java | 8 +- .../main/java/java/util/IdentityHashMap.java | 54 +- .../util/IllegalFormatCodePointException.java | 1 - .../IllegalFormatConversionException.java | 3 +- .../java/util/IllegalFormatException.java | 1 - .../util/IllegalFormatFlagsException.java | 1 - .../util/IllegalFormatPrecisionException.java | 1 - .../util/IllegalFormatWidthException.java | 1 - .../java/util/InputMismatchException.java | 5 +- .../InvalidPropertiesFormatException.java | 2 - .../src/main/java/java/util/Iterator.java | 14 +- .../main/java/java/util/LinkedHashMap.java | 717 +- .../main/java/java/util/LinkedHashSet.java | 12 +- .../src/main/java/java/util/LinkedList.java | 89 +- .../luni/src/main/java/java/util/List.java | 139 +- .../src/main/java/java/util/ListIterator.java | 11 - .../java/java/util/ListResourceBundle.java | 108 +- .../luni/src/main/java/java/util/Locale.java | 407 +- libcore/luni/src/main/java/java/util/Map.java | 29 +- .../src/main/java/java/util/MapEntry.java | 11 +- .../util/MissingFormatArgumentException.java | 1 - .../util/MissingFormatWidthException.java | 1 - .../java/util/MissingResourceException.java | 1 - .../src/main/java/java/util/Observable.java | 75 +- .../src/main/java/java/util/Observer.java | 2 - .../main/java/java/util/PriorityQueue.java | 23 +- .../src/main/java/java/util/Properties.java | 225 +- .../java/java/util/PropertyPermission.java | 14 +- .../util/PropertyPermissionCollection.java | 2 - .../java/util/PropertyResourceBundle.java | 23 +- .../luni/src/main/java/java/util/Queue.java | 10 - .../luni/src/main/java/java/util/Random.java | 28 +- .../src/main/java/java/util/RandomAccess.java | 5 +- .../main/java/java/util/ResourceBundle.java | 87 +- .../luni/src/main/java/java/util/Scanner.java | 351 +- libcore/luni/src/main/java/java/util/Set.java | 19 +- .../main/java/java/util/SimpleTimeZone.java | 343 +- .../src/main/java/java/util/SortedMap.java | 19 +- .../src/main/java/java/util/SortedSet.java | 7 - .../luni/src/main/java/java/util/Stack.java | 58 +- .../main/java/java/util/StringTokenizer.java | 86 +- .../luni/src/main/java/java/util/TimSort.java | 2 +- .../src/main/java/java/util/TimeZone.java | 340 +- .../luni/src/main/java/java/util/Timer.java | 394 +- .../src/main/java/java/util/TimerTask.java | 7 - .../java/util/TooManyListenersException.java | 2 - .../luni/src/main/java/java/util/TreeMap.java | 2743 +++-- .../luni/src/main/java/java/util/TreeSet.java | 93 +- .../luni/src/main/java/java/util/UUID.java | 100 +- .../UnknownFormatConversionException.java | 1 - .../util/UnknownFormatFlagsException.java | 1 - .../luni/src/main/java/java/util/Vector.java | 293 +- .../src/main/java/java/util/WeakHashMap.java | 77 +- .../luni/internal/io/FileCanonPathCache.java | 136 + .../luni/internal/net/www/MimeTable.java | 2 +- .../www/protocol/file/FileURLConnection.java | 25 +- .../net/www/protocol/file/Handler.java | 22 +- .../www/protocol/ftp/FtpURLConnection.java | 10 +- .../net/www/protocol/http/Header.java | 25 +- .../www/protocol/http/HttpConfiguration.java | 8 +- .../net/www/protocol/http/HttpConnection.java | 12 +- .../protocol/http/HttpConnectionManager.java | 8 +- .../www/protocol/http/HttpURLConnection.java | 152 +- .../protocol/https/HttpsURLConnection.java | 29 - .../net/www/protocol/jar/Handler.java | 10 +- .../www/protocol/jar/JarURLConnection.java | 371 +- .../luni/internal/nls/messages.properties | 4 +- .../internal/reflect/ProxyCharArrayCache.java | 133 - .../luni/internal/reflect/ProxyClassFile.java | 937 -- .../internal/reflect/ProxyConstantPool.java | 312 - .../luni/internal/reflect/ProxyConstants.java | 464 - .../luni/internal/reflect/ProxyMethod.java | 132 - .../reflect/ProxyNameAndTypeCache.java | 105 - .../internal/reflect/ProxyObjectCache.java | 106 - .../luni/net/PlainDatagramSocketImpl.java | 7 +- .../luni/net/PlainMulticastSocketImpl.java | 38 - .../luni/net/PlainServerSocketImpl.java | 10 +- .../harmony/luni/net/PlainSocketImpl.java | 64 +- .../harmony/luni/net/PlainSocketImpl2.java | 64 - .../harmony/luni/net/SocketImplProvider.java | 62 - .../luni/platform/AbstractMemorySpy.java | 131 +- .../harmony/luni/platform/AdapterManager.java | 79 - .../harmony/luni/platform/IAdaptable.java | 54 - .../luni/platform/IAdapterFactory.java | 43 - .../harmony/luni/platform/IFileSystem.java | 2 +- .../harmony/luni/platform/IMemorySystem.java | 57 +- .../harmony/luni/platform/INetworkSystem.java | 78 +- .../harmony/luni/platform/OSFileSystem.java | 40 +- .../harmony/luni/platform/OSMemory.java | 597 +- .../luni/platform/OSNetworkSystem.java | 1196 +- .../harmony/luni/platform/Platform.java | 49 +- .../luni/platform/RuntimeMemorySpy.java | 23 +- .../harmony/luni/util/DeleteOnExit.java | 80 + .../util/ExposedByteArrayInputStream.java | 57 + .../luni/util/ExternalMessages.properties | 8 +- .../luni/util/FloatingPointParser.java | 529 +- .../harmony/luni/util/HexStringParser.java | 4 +- .../luni/util/HistoricalNamesUtil.java | 170 + .../apache/harmony/luni/util/Inet6Util.java | 602 +- .../harmony/luni/util/InputStreamExposer.java | 118 + .../org/apache/harmony/luni/util/Sorter.java | 44 - .../harmony/luni/util/ThreadLocalCache.java | 103 + .../harmony/luni/util/TwoKeyHashMap.java | 104 +- .../org/apache/harmony/luni/util/Util.java | 458 +- libcore/luni/src/main/native/cbigint.c | 8 +- .../src/main/native/java_io_FileDescriptor.c | 17 +- .../src/main/native/java_net_InetAddress.cpp | 67 +- .../main/native/java_net_NetworkInterface.c | 5 +- ..._apache_harmony_luni_platform_OSMemory.cpp | 138 +- ..._harmony_luni_platform_OSNetworkSystem.cpp | 1972 ++-- .../org_apache_harmony_luni_util_fltparse.c | 2 +- .../com/google/coretests/CoreTestResult.java | 7 +- .../google/coretests/CoreTestRunnable.java | 14 +- .../harmony/luni/platform/AllTests.java} | 43 +- .../harmony/luni/platform/OSMemoryTest.java | 164 + .../harmony/luni/tests/java/io/AllTests.java | 1 + .../tests/java/io/FileCanonPathCacheTest.java | 120 + .../harmony/luni/tests/java/io/FileTest.java | 5 +- .../luni/tests/java/lang/ClassLoaderTest.java | 13 +- .../luni/tests/java/lang/ClassTest.java | 5 +- .../luni/tests/java/lang/FloatTest.java | 2 - .../luni/tests/java/lang/IntegerTest.java | 59 + .../luni/tests/java/lang/MathTest.java | 4 +- .../luni/tests/java/lang/ObjectTest.java | 2 + .../tests/java/lang/ProcessBuilderTest.java | 10 + .../tests/java/lang/SecurityManagerTest.java | 28 +- .../luni/tests/java/lang/StrictMathTest.java | 6 +- .../tests/java/lang/StringBuffer2Test.java | 1 - .../luni/tests/java/lang/ThreadLocalTest.java | 6 +- .../luni/tests/java/lang/ThreadTest.java | 55 +- .../java/net/ContentHandlerFactoryTest.java | 6 +- .../luni/tests/java/net/Inet6AddressTest.java | 8 +- .../luni/tests/java/net/InetAddressTest.java | 48 +- .../tests/java/net/InetSocketAddressTest.java | 1 - .../tests/java/net/URLConnectionTest.java | 29 +- .../luni/src/test/java/tests/AllTests.java | 3 + .../test/java/tests/api/java/io/FileTest.java | 15 +- .../api/java/io/InputStreamReaderTest.java | 6 +- .../api/java/io/OutputStreamWriterTest.java | 98 +- .../api/java/io/PipedInputStreamTest.java | 2 + .../api/java/io/SerializationStressTest4.java | 59 +- .../api/java/lang/ref/SoftReferenceTest.java | 2 + .../java/lang/reflect/ConstructorTest.java | 38 +- .../reflect/GenericReflectionCornerCases.java | 4 - .../java/lang/reflect/GenericTypesTest.java | 2 - .../api/java/lang/reflect/MethodTest.java | 36 +- .../lang/reflect/ParameterizedTypeTest.java | 4 - .../api/java/net/JarURLConnectionTest.java | 15 +- .../api/java/net/MulticastSocketTest.java | 120 +- .../java/tests/api/java/net/SocketTest.java | 9 +- .../java/net/UnknownServiceExceptionTest.java | 2 +- .../java/util/AbstractSequentialListTest.java | 2 +- .../java/tests/api/java/util/BitSetTest.java | 6 +- .../tests/api/java/util/CollectionsTest.java | 10 +- .../tests/api/java/util/FormatterTest.java | 2 +- .../java/tests/api/java/util/HashMapTest.java | 96 +- .../tests/api/java/util/HashtableTest.java | 267 +- .../api/java/util/LinkedHashMapTest.java | 97 +- .../java/tests/api/java/util/TreeMapTest.java | 5 +- .../src/main/java/java/math/BigDecimal.java | 488 +- .../src/main/java/java/math/BigInteger.java | 144 +- .../src/main/java/java/math/BitLevel.java | 29 +- .../src/main/java/java/math/Conversion.java | 3 - .../src/main/java/java/math/Division.java | 15 +- .../math/src/main/java/java/math/Logical.java | 39 +- .../src/main/java/java/math/MathContext.java | 77 +- .../main/java/java/math/Multiplication.java | 21 +- .../src/main/java/java/math/Primality.java | 24 +- .../src/main/java/java/math/RoundingMode.java | 25 +- .../tests/api/java/math/BigDecimalTest.java | 38 +- .../tests/api/java/math/BigIntegerTest.java | 10 + .../nio/internal/DatagramChannelImpl.java | 204 +- .../nio/internal/ServerSocketChannelImpl.java | 45 +- .../nio/internal/SocketChannelImpl.java | 333 +- .../nio/channels/DatagramChannelTest.java | 2 + .../java/nio/channels/SocketChannelTest.java | 91 + .../java/nio/charset/CharsetProviderTest.java | 4 - .../java/util/prefs/AbstractPreferences.java | 199 +- .../util/prefs/BackingStoreException.java | 23 +- .../prefs/FilePreferencesFactoryImpl.java | 6 +- .../java/util/prefs/FilePreferencesImpl.java | 41 +- .../InvalidPreferencesFormatException.java | 26 +- .../java/java/util/prefs/NodeChangeEvent.java | 37 +- .../java/util/prefs/NodeChangeListener.java | 17 +- .../util/prefs/PreferenceChangeEvent.java | 28 +- .../util/prefs/PreferenceChangeListener.java | 16 +- .../java/java/util/prefs/Preferences.java | 513 +- .../java/util/prefs/PreferencesFactory.java | 18 +- .../main/java/java/util/prefs/XMLParser.java | 147 +- .../harmony/prefs/internal/nls/Messages.java | 14 +- .../util/prefs/AbstractPreferencesTest.java | 42 +- .../util/prefs/FilePreferencesImplTest.java | 18 +- ...InvalidPreferencesFormatExceptionTest.java | 4 +- .../util/prefs/MockAbstractPreferences.java | 9 + .../java/util/prefs/MockSecurityManager.java | 2 + .../java/util/prefs/NodeChangeEventTest.java | 13 + .../util/prefs/NodeChangeListenerTest.java | 7 + .../util/prefs/PreferenceChangeEventTest.java | 4 +- .../prefs/PreferenceChangeListenerTest.java | 1 + .../util/prefs/PreferencesFactoryTest.java | 5 +- .../java/util/prefs/PreferencesTest.java | 65 +- .../src/test/java/tests/prefs/AllTests.java | 3 +- .../prefs/java/util/prefs/preferences.dtd | 56 - .../main/java/java/util/regex/Pattern.java | 59 +- .../tests/java/util/regex/SplitTest.java | 56 +- libcore/run-core-tests | 2 +- libcore/security/src/main/files/cacerts.bks | Bin 51899 -> 55715 bytes .../src/main/files/cacerts/3e7271e8.0 | 85 + .../src/main/files/cacerts/ab86d4de.0 | 104 + .../src/main/files/cacerts/b0f3e76e.0 | 63 +- .../src/main/files/cacerts/bf64f35b.0 | 92 + libcore/security/src/main/files/certimport.sh | 9 + .../security/x501/AttributeTypeAndValue.java | 6 +- .../jce/provider/IndexedPKIXParameters.java | 17 +- .../bouncycastle/jce/provider/AllTests.java | 34 + .../PKIXCertPathValidatorSpiTest.java | 65 + .../test/java/tests/security/AllTests.java | 1 + libcore/sql/src/main/java/java/sql/Array.java | 12 - .../java/java/sql/BatchUpdateException.java | 12 +- libcore/sql/src/main/java/java/sql/Blob.java | 13 +- .../main/java/java/sql/CallableStatement.java | 139 +- libcore/sql/src/main/java/java/sql/Clob.java | 13 - .../src/main/java/java/sql/Connection.java | 78 +- .../main/java/java/sql/DataTruncation.java | 8 - .../main/java/java/sql/DatabaseMetaData.java | 315 +- libcore/sql/src/main/java/java/sql/Date.java | 55 +- .../sql/src/main/java/java/sql/Driver.java | 17 +- .../src/main/java/java/sql/DriverManager.java | 77 +- .../java/java/sql/DriverPropertyInfo.java | 17 +- .../main/java/java/sql/ParameterMetaData.java | 25 - .../main/java/java/sql/PreparedStatement.java | 78 +- libcore/sql/src/main/java/java/sql/Ref.java | 8 +- .../sql/src/main/java/java/sql/ResultSet.java | 194 +- .../main/java/java/sql/ResultSetMetaData.java | 29 - .../sql/src/main/java/java/sql/SQLData.java | 13 +- .../src/main/java/java/sql/SQLException.java | 7 - .../sql/src/main/java/java/sql/SQLInput.java | 32 +- .../sql/src/main/java/java/sql/SQLOutput.java | 30 +- .../src/main/java/java/sql/SQLPermission.java | 3 - .../src/main/java/java/sql/SQLWarning.java | 8 - .../sql/src/main/java/java/sql/Savepoint.java | 4 - .../sql/src/main/java/java/sql/Statement.java | 83 +- .../sql/src/main/java/java/sql/Struct.java | 5 - libcore/sql/src/main/java/java/sql/Time.java | 63 +- .../sql/src/main/java/java/sql/Timestamp.java | 135 +- libcore/sql/src/main/java/java/sql/Types.java | 62 - .../main/java/javax/sql/ConnectionEvent.java | 17 +- .../javax/sql/ConnectionEventListener.java | 6 - .../javax/sql/ConnectionPoolDataSource.java | 13 +- .../src/main/java/javax/sql/DataSource.java | 15 +- .../main/java/javax/sql/PooledConnection.java | 11 - .../sql/src/main/java/javax/sql/RowSet.java | 80 +- .../src/main/java/javax/sql/RowSetEvent.java | 4 - .../main/java/javax/sql/RowSetInternal.java | 12 +- .../main/java/javax/sql/RowSetListener.java | 5 - .../main/java/javax/sql/RowSetMetaData.java | 23 +- .../src/main/java/javax/sql/RowSetReader.java | 3 - .../src/main/java/javax/sql/RowSetWriter.java | 6 +- .../harmony/sql/internal/nls/Messages.java | 1 - .../sql/internal/nls/messages.properties | 19 + .../harmony/sql/tests/java/sql/DateTest.java | 69 +- .../sql/tests/java/sql/DriverManagerTest.java | 74 + .../java/sql/TestHelper_Connection1.java | 15 +- .../tests/java/sql/TestHelper_Driver1.java | 3 +- .../tests/java/sql/TestHelper_Driver3.java | 1 - .../tests/java/sql/TestHelper_Driver4.java | 12 +- .../tests/java/sql/TestMainForDriver.java} | 51 +- .../sql/tests/java/sql/TimestampTest.java | 86 +- .../tests/javax/sql/ConnectionEventTest.java | 64 +- .../javax/sql/Impl_PooledConnection.java | 3 +- .../sql/tests/javax/sql/Impl_RowSet.java | 100 +- .../sql/tests/javax/sql/RowSetEventTest.java | 2 +- .../src/main/java/sun/misc/Unsafe.java | 4 +- .../src/test/java/sun/misc/AllTests.java | 39 + .../src/test/java/sun/misc/UnsafeTest.java | 67 + .../test/java/tests/suncompat/AllTests.java} | 30 +- .../tests/resources/EmptyEntries_signed.jar | Bin 0 -> 1927 bytes .../tests/support/Support_Configuration.java | 14 - .../tests/support/Support_TestWebServer.java | 53 +- .../src/test/java/tests/util/PrefsTester.java | 73 + .../src/main/java/java/text/Annotation.java | 13 +- .../text/AttributedCharacterIterator.java | 69 +- .../main/java/java/text/AttributedString.java | 53 +- .../text/src/main/java/java/text/Bidi.java | 127 +- .../main/java/java/text/BreakIterator.java | 117 +- .../java/java/text/CharacterIterator.java | 20 +- .../src/main/java/java/text/ChoiceFormat.java | 95 +- .../java/text/CollationElementIterator.java | 25 - .../src/main/java/java/text/CollationKey.java | 26 +- .../src/main/java/java/text/Collator.java | 70 +- .../src/main/java/java/text/DateFormat.java | 201 +- .../java/java/text/DateFormatSymbols.java | 117 +- .../main/java/java/text/DecimalFormat.java | 329 +- .../java/java/text/DecimalFormatSymbols.java | 63 +- .../main/java/java/text/FieldPosition.java | 15 - .../text/src/main/java/java/text/Format.java | 82 +- .../main/java/java/text/MessageFormat.java | 171 +- .../src/main/java/java/text/NumberFormat.java | 137 +- .../main/java/java/text/ParseException.java | 4 - .../main/java/java/text/ParsePosition.java | 9 - .../java/java/text/RuleBasedCollator.java | 106 +- .../main/java/java/text/SimpleDateFormat.java | 866 +- .../java/text/StringCharacterIterator.java | 65 +- .../org/apache/harmony/text/BidiWrapper.java | 3 +- .../text/tests/java/text/BidiTest.java | 29 +- .../tests/java/text/DataFormatFieldTest.java | 1 - .../java/text/DateFormatSymbolsTest.java | 63 +- .../text/tests/java/text/DateFormatTest.java | 39 + .../java/text/DecimalFormatSymbolsTest.java | 49 +- .../tests/java/text/DecimalFormatTest.java | 21 +- .../tests/java/text/MessageFormatTest.java | 69 +- .../tests/java/text/NumberFormatTest.java | 2 +- .../tests/java/text/ParsePositionTest.java | 13 +- .../java/text/Support_DecimalFormat.java | 287 + .../text/tests/java/text/Support_Format.java | 143 + .../java/text/Support_MessageFormat.java | 120 + .../java/text/Support_SimpleDateFormat.java | 266 + libcore/tools/integrate/Android.mk | 19 + libcore/tools/integrate/Command.java | 104 + libcore/tools/integrate/Filesystem.java | 63 + libcore/tools/integrate/Git.java | 90 + libcore/tools/integrate/MappedDirectory.java | 41 + libcore/tools/integrate/Module.java | 67 + libcore/tools/integrate/Modules.java | 62 + libcore/tools/integrate/PullHarmonyCode.java | 109 + libcore/tools/integrate/Svn.java | 25 + .../javax/net/DefaultServerSocketFactory.java | 26 +- .../java/javax/net/DefaultSocketFactory.java | 31 +- .../java/javax/net/ServerSocketFactory.java | 55 +- .../main/java/javax/net/SocketFactory.java | 61 +- .../ssl/CertPathTrustManagerParameters.java | 9 +- .../main/java/javax/net/ssl/ContextImpl.java | 33 - .../java/javax/net/ssl/DefaultSSLContext.java | 76 +- .../ssl/DefaultSSLServerSocketFactory.java | 25 +- .../net/ssl/DefaultSSLSocketFactory.java | 52 +- .../net/ssl/HandshakeCompletedEvent.java | 47 +- .../net/ssl/HandshakeCompletedListener.java | 10 +- .../java/javax/net/ssl/HostnameVerifier.java | 11 +- .../javax/net/ssl/HttpsURLConnection.java | 160 +- .../main/java/javax/net/ssl/KeyManager.java | 12 +- .../java/javax/net/ssl/KeyManagerFactory.java | 156 +- .../javax/net/ssl/KeyManagerFactorySpi.java | 24 +- .../net/ssl/KeyStoreBuilderParameters.java | 23 +- .../net/ssl/ManagerFactoryParameters.java | 6 +- .../main/java/javax/net/ssl/SSLContext.java | 96 +- .../java/javax/net/ssl/SSLContextSpi.java | 36 +- .../main/java/javax/net/ssl/SSLEngine.java | 231 +- .../java/javax/net/ssl/SSLEngineResult.java | 175 +- .../main/java/javax/net/ssl/SSLException.java | 12 +- .../javax/net/ssl/SSLHandshakeException.java | 7 +- .../java/javax/net/ssl/SSLKeyException.java | 11 +- .../net/ssl/SSLPeerUnverifiedException.java | 9 +- .../java/javax/net/ssl/SSLPermission.java | 19 +- .../javax/net/ssl/SSLProtocolException.java | 9 +- .../java/javax/net/ssl/SSLServerSocket.java | 66 +- .../javax/net/ssl/SSLServerSocketFactory.java | 58 +- .../main/java/javax/net/ssl/SSLSession.java | 77 +- .../javax/net/ssl/SSLSessionBindingEvent.java | 32 +- .../net/ssl/SSLSessionBindingListener.java | 12 +- .../java/javax/net/ssl/SSLSessionContext.java | 27 +- .../main/java/javax/net/ssl/SSLSocket.java | 130 +- .../java/javax/net/ssl/SSLSocketFactory.java | 114 +- .../main/java/javax/net/ssl/TrustManager.java | 6 +- .../javax/net/ssl/TrustManagerFactory.java | 146 +- .../javax/net/ssl/TrustManagerFactorySpi.java | 16 +- .../javax/net/ssl/X509ExtendedKeyManager.java | 19 +- .../java/javax/net/ssl/X509KeyManager.java | 22 +- .../java/javax/net/ssl/X509TrustManager.java | 15 +- .../xnet/provider/jsse/AlertException.java | 30 +- .../xnet/provider/jsse/AlertProtocol.java | 11 +- .../xnet/provider/jsse/Appendable.java | 13 +- .../provider/jsse/CertificateMessage.java | 11 +- .../provider/jsse/CertificateRequest.java | 11 +- .../xnet/provider/jsse/CertificateVerify.java | 8 +- .../xnet/provider/jsse/CipherSuite.java | 32 +- .../provider/jsse/ClientHandshakeImpl.java | 30 +- .../xnet/provider/jsse/ClientHello.java | 8 +- .../xnet/provider/jsse/ClientKeyExchange.java | 8 +- .../xnet/provider/jsse/ConnectionState.java | 5 - .../provider/jsse/ConnectionStateSSLv3.java | 14 +- .../provider/jsse/ConnectionStateTLS.java | 15 +- .../xnet/provider/jsse/ContentType.java | 5 - .../xnet/provider/jsse/DHParameters.java | 5 - .../xnet/provider/jsse/DataStream.java | 5 - .../xnet/provider/jsse/DelegatedTask.java | 9 +- .../xnet/provider/jsse/DigitalSignature.java | 147 +- .../provider/jsse/EndOfBufferException.java | 15 +- .../provider/jsse/EndOfSourceException.java | 16 +- .../harmony/xnet/provider/jsse/Finished.java | 7 +- .../harmony/xnet/provider/jsse/Handshake.java | 5 - .../provider/jsse/HandshakeIODataStream.java | 14 +- .../xnet/provider/jsse/HandshakeProtocol.java | 18 +- .../xnet/provider/jsse/HelloRequest.java | 7 +- .../xnet/provider/jsse/JSSEProvider.java | 22 +- .../provider/jsse/KeyManagerFactoryImpl.java | 10 +- .../xnet/provider/jsse/KeyManagerImpl.java | 144 +- .../harmony/xnet/provider/jsse/Logger.java | 7 +- .../harmony/xnet/provider/jsse/Message.java | 4 - .../jsse/OpenSSLSocketImplWrapper.java | 55 +- .../harmony/xnet/provider/jsse/PRF.java | 5 - .../xnet/provider/jsse/ProtocolVersion.java | 66 +- .../xnet/provider/jsse/SSLBufferedInput.java | 7 +- .../xnet/provider/jsse/SSLContextImpl.java | 11 +- .../xnet/provider/jsse/SSLEngineAppData.java | 12 +- .../provider/jsse/SSLEngineDataStream.java | 5 - .../xnet/provider/jsse/SSLEngineImpl.java | 34 +- .../xnet/provider/jsse/SSLInputStream.java | 9 +- .../xnet/provider/jsse/SSLParameters.java | 10 +- .../xnet/provider/jsse/SSLRecordProtocol.java | 16 +- .../xnet/provider/jsse/SSLSessionImpl.java | 225 +- .../xnet/provider/jsse/SSLStreamedInput.java | 7 +- .../xnet/provider/jsse/SSLv3Constants.java | 5 - .../provider/jsse/ServerHandshakeImpl.java | 72 +- .../xnet/provider/jsse/ServerHello.java | 7 +- .../xnet/provider/jsse/ServerHelloDone.java | 8 +- .../xnet/provider/jsse/ServerKeyExchange.java | 7 +- .../jsse/TrustManagerFactoryImpl.java | 8 +- .../xnet/provider/jsse/TrustManagerImpl.java | 106 +- ...y_xnet_provider_jsse_OpenSSLSocketImpl.cpp | 20 +- .../xml/parsers/DocumentBuilderTest.java | 1 - libdex/DexFile.c | 44 + libdex/DexFile.h | 22 +- libdex/DexSwapVerify.c | 16 +- libdex/InstrUtils.c | 98 +- libdex/InstrUtils.h | 4 + libdex/Leb128.h | 37 + libdex/OpCode.h | 4 +- libdex/SysUtil.c | 20 + libdex/SysUtil.h | 8 + libdex/ZipArchive.c | 2 + libnativehelper/Android.mk | 71 +- .../include/nativehelper/JNIHelp.h | 2 + tests/003-omnibus-opcodes/expected.txt | 9 +- tests/003-omnibus-opcodes/src/FloatMath.java | 46 + tests/003-omnibus-opcodes/src/IntMath.java | 21 + tests/003-omnibus-opcodes/src/UnresTest2.java | 1 + tests/030-bad-finalizer/info.txt | 9 +- tests/042-new-instance/src/Main.java | 20 +- tests/044-proxy/expected.txt | 4 +- tests/065-mismatched-implements/expected.txt | 4 +- tests/065-mismatched-implements/info.txt | 17 - .../src/Indirect.java | 28 + tests/065-mismatched-implements/src/Main.java | 23 +- tests/066-mismatched-super/expected.txt | 4 +- tests/066-mismatched-super/info.txt | 17 - tests/066-mismatched-super/src/Indirect.java | 28 + tests/066-mismatched-super/src/Main.java | 23 +- tests/068-classloader/expected.txt | 3 + tests/068-classloader/src-ex/AbstractGet.java | 33 + .../src-ex/DoubledExtendOkay.java | 37 + tests/068-classloader/src-ex/GetDoubled.java | 27 + tests/068-classloader/src/BaseOkay.java | 39 + .../src/DoubledExtendOkay.java | 37 + tests/068-classloader/src/FancyLoader.java | 63 +- tests/068-classloader/src/IGetDoubled.java | 23 + tests/068-classloader/src/Main.java | 145 + tests/071-dexfile/info.txt | 2 + tests/071-dexfile/src/Main.java | 47 +- tests/072-precise-gc/expected.txt | 2 + tests/072-precise-gc/info.txt | 1 + tests/072-precise-gc/src/Main.java | 114 + tests/073-mismatched-field/expected.txt | 1 + tests/073-mismatched-field/info.txt | 3 + tests/073-mismatched-field/src/IMain.java | 20 + tests/073-mismatched-field/src/Main.java | 32 + tests/073-mismatched-field/src/SuperMain.java | 20 + tests/073-mismatched-field/src2/IMain.java | 20 + tests/074-gc-thrash/expected.txt | 2 + tests/074-gc-thrash/info.txt | 2 + tests/074-gc-thrash/src/Main.java | 338 + tests/075-verification-error/expected.txt | 12 + tests/075-verification-error/info.txt | 1 + tests/075-verification-error/src/Main.java | 149 + .../src/MaybeAbstract.java | 21 + .../src/other/InaccessibleClass.java | 24 + .../src/other/InaccessibleMethod.java | 22 + .../src/other/Mutant.java | 44 + .../src2/MaybeAbstract.java | 21 + .../src2/other/InaccessibleClass.java | 24 + .../src2/other/InaccessibleMethod.java | 22 + .../src2/other/Mutant.java | 45 + tests/076-boolean-put/expected.txt | 1 + tests/076-boolean-put/info.txt | 3 + tests/076-boolean-put/src/Main.java | 48 + tests/077-method-override/expected.txt | 15 + tests/077-method-override/info.txt | 2 + tests/077-method-override/src/Base.java | 84 + tests/077-method-override/src/Derived.java | 60 + tests/077-method-override/src/Main.java | 54 + tests/077-method-override/src2/Base.java | 83 + tests/078-polymorphic-virtual/expected.txt | 3 + tests/078-polymorphic-virtual/info.txt | 2 + tests/078-polymorphic-virtual/src/Base.java | 32 + .../078-polymorphic-virtual/src/Derived1.java | 21 + .../078-polymorphic-virtual/src/Derived2.java | 21 + .../078-polymorphic-virtual/src/Derived3.java | 21 + tests/078-polymorphic-virtual/src/Main.java | 40 + tests/etc/local-run-test-jar | 23 +- tests/etc/push-and-run-test-jar | 18 +- tests/run-test | 8 + tools/dexcheck | 32 + tools/dexdeps/Android.mk | 44 + tools/dexdeps/README.txt | 28 + tools/dexdeps/etc/dexdeps | 69 + tools/dexdeps/etc/manifest.txt | 1 + tools/dexdeps/src/Android.mk | 32 + .../src/com/android/dexdeps/DexData.java | 585 + .../com/android/dexdeps/DexDataException.java | 24 + .../src/com/android/dexdeps/FieldRef.java | 52 + .../dexdeps/src/com/android/dexdeps/Main.java | 201 + .../src/com/android/dexdeps/MethodRef.java | 88 + .../src/com/android/dexdeps/Output.java | 264 + .../com/android/dexdeps/UsageException.java | 24 + tools/dmtracedump/TraceDump.c | 123 +- tools/gclog.py | 235 + vm/Android.mk | 279 +- vm/CheckJni.c | 441 +- vm/Common.h | 15 +- vm/Dalvik.h | 5 + vm/DalvikVersion.h | 6 +- vm/Debugger.c | 2 +- vm/Dvm.mk | 331 + vm/DvmDex.c | 2 + vm/Exception.c | 38 +- vm/Exception.h | 7 + vm/Globals.h | 148 +- vm/IndirectRefTable.c | 459 + vm/IndirectRefTable.h | 368 + vm/Init.c | 211 +- vm/Init.h | 11 +- vm/InlineNative.c | 174 +- vm/InlineNative.h | 18 + vm/Inlines.c | 1 + vm/JarFile.c | 3 +- vm/Jni.c | 1617 ++- vm/JniInternal.h | 69 +- vm/LinearAlloc.c | 30 +- vm/LinearAlloc.h | 6 + vm/Misc.c | 11 +- vm/Misc.h | 41 +- vm/Native.c | 27 +- vm/Profile.c | 63 +- vm/Properties.c | 10 +- vm/ReferenceTable.c | 25 +- vm/ReferenceTable.h | 18 +- vm/SignalCatcher.c | 17 +- vm/Thread.c | 609 +- vm/Thread.h | 44 +- vm/UtfString.c | 9 + vm/UtfString.h | 5 + vm/alloc/Alloc.c | 60 +- vm/alloc/Alloc.h | 6 +- vm/alloc/DdmHeap.c | 17 +- vm/alloc/Heap.c | 28 +- vm/alloc/HeapDebug.c | 2 +- vm/alloc/HeapSource.c | 76 + vm/alloc/HeapSource.h | 8 + vm/alloc/HeapWorker.c | 25 +- vm/alloc/MarkSweep.c | 91 +- vm/analysis/CodeVerify.c | 1425 ++- vm/analysis/CodeVerify.h | 8 +- vm/analysis/DexOptimize.c | 190 +- vm/analysis/DexOptimize.h | 46 +- vm/analysis/DexVerify.c | 38 +- vm/analysis/RegisterMap.c | 1750 ++- vm/analysis/RegisterMap.h | 228 +- vm/analysis/VerifySubs.c | 2 + vm/analysis/VerifySubs.h | 6 +- vm/arch/arm/CallEABI.S | 7 +- vm/arch/sh/CallSH4ABI.S | 400 + vm/arch/x86/Call386ABI.S | 24 +- vm/compiler/Compiler.c | 256 + vm/compiler/Compiler.h | 125 + vm/compiler/CompilerIR.h | 108 + vm/compiler/CompilerInternals.h | 26 + vm/compiler/CompilerUtility.h | 44 + vm/compiler/Frontend.c | 752 ++ vm/compiler/IntermediateRep.c | 77 + vm/compiler/Utility.c | 212 + vm/compiler/codegen/CompilerCodegen.h | 42 + vm/compiler/codegen/Optimizer.h | 47 + vm/compiler/codegen/arm/ArchUtility.c | 264 + vm/compiler/codegen/arm/ArmLIR.h | 395 + vm/compiler/codegen/arm/Assemble.c | 1204 ++ vm/compiler/codegen/arm/Codegen-armv5te-vfp.c | 29 + vm/compiler/codegen/arm/Codegen-armv5te.c | 29 + vm/compiler/codegen/arm/Codegen-armv7-a.c | 29 + vm/compiler/codegen/arm/Codegen.c | 3337 ++++++ vm/compiler/codegen/arm/Codegen.h | 57 + vm/compiler/codegen/arm/GlobalOptimizations.c | 62 + vm/compiler/codegen/arm/LocalOptimizations.c | 166 + vm/compiler/codegen/arm/Thumb2Util.c | 488 + vm/compiler/codegen/arm/ThumbUtil.c | 337 + .../codegen/arm/armv5te-vfp/ArchVariant.c | 291 + .../codegen/arm/armv5te-vfp/ArchVariant.h | 34 + vm/compiler/codegen/arm/armv5te/ArchVariant.c | 183 + vm/compiler/codegen/arm/armv5te/ArchVariant.h | 34 + vm/compiler/codegen/arm/armv7-a/ArchVariant.c | 319 + vm/compiler/codegen/arm/armv7-a/ArchVariant.h | 34 + vm/compiler/template/Makefile-template | 49 + vm/compiler/template/README.txt | 1 + .../armv5te-vfp/TEMPLATE_ADD_DOUBLE_VFP.S | 2 + .../armv5te-vfp/TEMPLATE_ADD_FLOAT_VFP.S | 2 + .../armv5te-vfp/TEMPLATE_CMPG_DOUBLE_VFP.S | 34 + .../armv5te-vfp/TEMPLATE_CMPG_FLOAT_VFP.S | 32 + .../armv5te-vfp/TEMPLATE_CMPL_DOUBLE_VFP.S | 32 + .../armv5te-vfp/TEMPLATE_CMPL_FLOAT_VFP.S | 32 + .../armv5te-vfp/TEMPLATE_DIV_DOUBLE_VFP.S | 2 + .../armv5te-vfp/TEMPLATE_DIV_FLOAT_VFP.S | 2 + .../TEMPLATE_DOUBLE_TO_FLOAT_VFP.S | 2 + .../armv5te-vfp/TEMPLATE_DOUBLE_TO_INT_VFP.S | 2 + .../TEMPLATE_FLOAT_TO_DOUBLE_VFP.S | 2 + .../armv5te-vfp/TEMPLATE_FLOAT_TO_INT_VFP.S | 2 + .../armv5te-vfp/TEMPLATE_INT_TO_DOUBLE_VFP.S | 2 + .../armv5te-vfp/TEMPLATE_INT_TO_FLOAT_VFP.S | 2 + .../armv5te-vfp/TEMPLATE_MUL_DOUBLE_VFP.S | 2 + .../armv5te-vfp/TEMPLATE_MUL_FLOAT_VFP.S | 2 + .../armv5te-vfp/TEMPLATE_SQRT_DOUBLE_VFP.S | 23 + .../armv5te-vfp/TEMPLATE_SUB_DOUBLE_VFP.S | 2 + .../armv5te-vfp/TEMPLATE_SUB_FLOAT_VFP.S | 2 + .../template/armv5te-vfp/TemplateOpList.h | 52 + vm/compiler/template/armv5te-vfp/fbinop.S | 14 + vm/compiler/template/armv5te-vfp/fbinopWide.S | 14 + vm/compiler/template/armv5te-vfp/funop.S | 15 + .../template/armv5te-vfp/funopNarrower.S | 15 + vm/compiler/template/armv5te-vfp/funopWider.S | 15 + .../template/armv5te/TEMPLATE_CMPG_DOUBLE.S | 1 + .../template/armv5te/TEMPLATE_CMPG_FLOAT.S | 1 + .../template/armv5te/TEMPLATE_CMPL_DOUBLE.S | 39 + .../template/armv5te/TEMPLATE_CMPL_FLOAT.S | 56 + .../template/armv5te/TEMPLATE_CMP_LONG.S | 34 + .../armv5te/TEMPLATE_INVOKE_METHOD_CHAIN.S | 47 + .../armv5te/TEMPLATE_INVOKE_METHOD_NATIVE.S | 51 + .../armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT.S | 47 + .../TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN.S | 42 + .../template/armv5te/TEMPLATE_MUL_LONG.S | 28 + .../template/armv5te/TEMPLATE_RETURN.S | 38 + .../template/armv5te/TEMPLATE_SHL_LONG.S | 15 + .../template/armv5te/TEMPLATE_SHR_LONG.S | 16 + .../template/armv5te/TEMPLATE_USHR_LONG.S | 16 + vm/compiler/template/armv5te/TemplateOpList.h | 37 + vm/compiler/template/armv5te/footer.S | 61 + vm/compiler/template/armv5te/header.S | 93 + vm/compiler/template/armv5te/platform.S | 16 + vm/compiler/template/armv7-a/TemplateOpList.h | 52 + vm/compiler/template/config-armv5te | 45 + vm/compiler/template/config-armv5te-vfp | 56 + vm/compiler/template/config-armv7-a | 56 + vm/compiler/template/gen-template.py | 422 + .../out/CompilerTemplateAsm-armv5te-vfp.S | 1064 ++ .../out/CompilerTemplateAsm-armv5te.S | 798 ++ .../out/CompilerTemplateAsm-armv7-a.S | 1064 ++ vm/compiler/template/rebuild.sh | 23 + vm/interp/Interp.c | 297 +- vm/interp/Interp.h | 8 + vm/interp/InterpDefs.h | 86 +- vm/interp/Jit.c | 701 ++ vm/interp/Jit.h | 82 + vm/interp/Stack.c | 170 +- vm/interp/Stack.h | 22 +- vm/jdwp/JdwpAdb.c | 74 +- vm/mterp/Mterp.c | 12 +- vm/mterp/Mterp.h | 3 + vm/mterp/arm-vfp/OP_ADD_DOUBLE.S | 2 + vm/mterp/arm-vfp/OP_ADD_DOUBLE_2ADDR.S | 2 + vm/mterp/arm-vfp/OP_ADD_FLOAT.S | 2 + vm/mterp/arm-vfp/OP_ADD_FLOAT_2ADDR.S | 2 + vm/mterp/arm-vfp/OP_CMPG_DOUBLE.S | 43 + vm/mterp/arm-vfp/OP_CMPG_FLOAT.S | 43 + vm/mterp/arm-vfp/OP_CMPL_DOUBLE.S | 43 + vm/mterp/arm-vfp/OP_CMPL_FLOAT.S | 43 + vm/mterp/arm-vfp/OP_DIV_DOUBLE.S | 2 + vm/mterp/arm-vfp/OP_DIV_DOUBLE_2ADDR.S | 2 + vm/mterp/arm-vfp/OP_DIV_FLOAT.S | 2 + vm/mterp/arm-vfp/OP_DIV_FLOAT_2ADDR.S | 2 + vm/mterp/arm-vfp/OP_DOUBLE_TO_FLOAT.S | 2 + vm/mterp/arm-vfp/OP_DOUBLE_TO_INT.S | 2 + vm/mterp/arm-vfp/OP_FLOAT_TO_DOUBLE.S | 2 + vm/mterp/arm-vfp/OP_FLOAT_TO_INT.S | 2 + vm/mterp/arm-vfp/OP_INT_TO_DOUBLE.S | 2 + vm/mterp/arm-vfp/OP_INT_TO_FLOAT.S | 2 + vm/mterp/arm-vfp/OP_MUL_DOUBLE.S | 2 + vm/mterp/arm-vfp/OP_MUL_DOUBLE_2ADDR.S | 2 + vm/mterp/arm-vfp/OP_MUL_FLOAT.S | 2 + vm/mterp/arm-vfp/OP_MUL_FLOAT_2ADDR.S | 2 + vm/mterp/arm-vfp/OP_SUB_DOUBLE.S | 2 + vm/mterp/arm-vfp/OP_SUB_DOUBLE_2ADDR.S | 2 + vm/mterp/arm-vfp/OP_SUB_FLOAT.S | 2 + vm/mterp/arm-vfp/OP_SUB_FLOAT_2ADDR.S | 2 + vm/mterp/arm-vfp/README.txt | 7 + vm/mterp/arm-vfp/fbinop.S | 23 + vm/mterp/arm-vfp/fbinop2addr.S | 21 + vm/mterp/arm-vfp/fbinopWide.S | 23 + vm/mterp/arm-vfp/fbinopWide2addr.S | 22 + vm/mterp/arm-vfp/funop.S | 18 + vm/mterp/arm-vfp/funopNarrower.S | 18 + vm/mterp/arm-vfp/funopWider.S | 18 + vm/mterp/armv5te/OP_DOUBLE_TO_INT.S | 22 +- vm/mterp/armv5te/OP_DOUBLE_TO_LONG.S | 17 +- vm/mterp/armv5te/OP_GOTO.S | 10 +- vm/mterp/armv5te/OP_GOTO_16.S | 9 + vm/mterp/armv5te/OP_GOTO_32.S | 10 +- vm/mterp/armv5te/OP_MONITOR_ENTER.S | 4 +- vm/mterp/armv5te/OP_MOVE_WIDE_16.S | 2 +- vm/mterp/armv5te/OP_NEW_INSTANCE.S | 21 +- vm/mterp/armv5te/OP_PACKED_SWITCH.S | 9 + .../armv5te/OP_THROW_VERIFICATION_ERROR.S | 14 + vm/mterp/armv5te/OP_UNUSED_ED.S | 1 - vm/mterp/armv5te/bincmp.S | 9 +- vm/mterp/armv5te/binop2addr.S | 2 +- vm/mterp/armv5te/debug.c | 16 +- vm/mterp/armv5te/entry.S | 27 + vm/mterp/armv5te/footer.S | 332 +- vm/mterp/armv5te/header.S | 37 +- vm/mterp/armv5te/zcmp.S | 12 +- vm/mterp/armv6/OP_INT_TO_BYTE.S | 2 + vm/mterp/armv6/OP_INT_TO_CHAR.S | 2 + vm/mterp/armv6/OP_INT_TO_SHORT.S | 2 + vm/mterp/armv6t2/OP_ADD_DOUBLE_2ADDR.S | 2 + vm/mterp/armv6t2/OP_ADD_FLOAT_2ADDR.S | 2 + vm/mterp/armv6t2/OP_ADD_INT_2ADDR.S | 2 + vm/mterp/armv6t2/OP_ADD_INT_LIT16.S | 2 + vm/mterp/armv6t2/OP_ADD_LONG_2ADDR.S | 2 + vm/mterp/armv6t2/OP_AND_INT_2ADDR.S | 2 + vm/mterp/armv6t2/OP_AND_INT_LIT16.S | 2 + vm/mterp/armv6t2/OP_AND_LONG_2ADDR.S | 2 + vm/mterp/armv6t2/OP_ARRAY_LENGTH.S | 15 + vm/mterp/armv6t2/OP_CONST_4.S | 10 + vm/mterp/armv6t2/OP_DIV_DOUBLE_2ADDR.S | 2 + vm/mterp/armv6t2/OP_DIV_FLOAT_2ADDR.S | 2 + vm/mterp/armv6t2/OP_DIV_INT_2ADDR.S | 2 + vm/mterp/armv6t2/OP_DIV_INT_LIT16.S | 2 + vm/mterp/armv6t2/OP_DIV_LONG_2ADDR.S | 2 + vm/mterp/armv6t2/OP_DOUBLE_TO_FLOAT.S | 2 + vm/mterp/armv6t2/OP_DOUBLE_TO_INT.S | 55 + vm/mterp/armv6t2/OP_DOUBLE_TO_LONG.S | 54 + vm/mterp/armv6t2/OP_FLOAT_TO_DOUBLE.S | 2 + vm/mterp/armv6t2/OP_FLOAT_TO_INT.S | 41 + vm/mterp/armv6t2/OP_FLOAT_TO_LONG.S | 41 + vm/mterp/armv6t2/OP_IF_EQ.S | 2 + vm/mterp/armv6t2/OP_IF_GE.S | 2 + vm/mterp/armv6t2/OP_IF_GT.S | 2 + vm/mterp/armv6t2/OP_IF_LE.S | 2 + vm/mterp/armv6t2/OP_IF_LT.S | 2 + vm/mterp/armv6t2/OP_IF_NE.S | 2 + vm/mterp/armv6t2/OP_IGET.S | 46 + vm/mterp/armv6t2/OP_IGET_QUICK.S | 16 + vm/mterp/armv6t2/OP_IGET_WIDE.S | 43 + vm/mterp/armv6t2/OP_IGET_WIDE_QUICK.S | 16 + vm/mterp/armv6t2/OP_INT_TO_BYTE.S | 2 + vm/mterp/armv6t2/OP_INT_TO_CHAR.S | 2 + vm/mterp/armv6t2/OP_INT_TO_DOUBLE.S | 2 + vm/mterp/armv6t2/OP_INT_TO_FLOAT.S | 2 + vm/mterp/armv6t2/OP_INT_TO_LONG.S | 2 + vm/mterp/armv6t2/OP_INT_TO_SHORT.S | 2 + vm/mterp/armv6t2/OP_IPUT.S | 46 + vm/mterp/armv6t2/OP_IPUT_QUICK.S | 16 + vm/mterp/armv6t2/OP_IPUT_WIDE.S | 40 + vm/mterp/armv6t2/OP_IPUT_WIDE_QUICK.S | 16 + vm/mterp/armv6t2/OP_LONG_TO_DOUBLE.S | 2 + vm/mterp/armv6t2/OP_LONG_TO_FLOAT.S | 2 + vm/mterp/armv6t2/OP_MOVE.S | 11 + vm/mterp/armv6t2/OP_MOVE_WIDE.S | 13 + vm/mterp/armv6t2/OP_MUL_DOUBLE_2ADDR.S | 2 + vm/mterp/armv6t2/OP_MUL_FLOAT_2ADDR.S | 2 + vm/mterp/armv6t2/OP_MUL_INT_2ADDR.S | 3 + vm/mterp/armv6t2/OP_MUL_INT_LIT16.S | 3 + vm/mterp/armv6t2/OP_MUL_LONG_2ADDR.S | 26 + vm/mterp/armv6t2/OP_NEG_DOUBLE.S | 2 + vm/mterp/armv6t2/OP_NEG_FLOAT.S | 2 + vm/mterp/armv6t2/OP_NEG_INT.S | 2 + vm/mterp/armv6t2/OP_NEG_LONG.S | 2 + vm/mterp/armv6t2/OP_NOT_INT.S | 2 + vm/mterp/armv6t2/OP_NOT_LONG.S | 2 + vm/mterp/armv6t2/OP_OR_INT_2ADDR.S | 2 + vm/mterp/armv6t2/OP_OR_INT_LIT16.S | 2 + vm/mterp/armv6t2/OP_OR_LONG_2ADDR.S | 2 + vm/mterp/armv6t2/OP_REM_DOUBLE_2ADDR.S | 3 + vm/mterp/armv6t2/OP_REM_FLOAT_2ADDR.S | 3 + vm/mterp/armv6t2/OP_REM_INT_2ADDR.S | 3 + vm/mterp/armv6t2/OP_REM_INT_LIT16.S | 3 + vm/mterp/armv6t2/OP_REM_LONG_2ADDR.S | 3 + vm/mterp/armv6t2/OP_RSUB_INT.S | 3 + vm/mterp/armv6t2/OP_SHL_INT_2ADDR.S | 2 + vm/mterp/armv6t2/OP_SHL_LONG_2ADDR.S | 28 + vm/mterp/armv6t2/OP_SHR_INT_2ADDR.S | 2 + vm/mterp/armv6t2/OP_SHR_LONG_2ADDR.S | 28 + vm/mterp/armv6t2/OP_SUB_DOUBLE_2ADDR.S | 2 + vm/mterp/armv6t2/OP_SUB_FLOAT_2ADDR.S | 2 + vm/mterp/armv6t2/OP_SUB_INT_2ADDR.S | 2 + vm/mterp/armv6t2/OP_SUB_LONG_2ADDR.S | 2 + vm/mterp/armv6t2/OP_USHR_INT_2ADDR.S | 2 + vm/mterp/armv6t2/OP_USHR_LONG_2ADDR.S | 28 + vm/mterp/armv6t2/OP_XOR_INT_2ADDR.S | 2 + vm/mterp/armv6t2/OP_XOR_INT_LIT16.S | 2 + vm/mterp/armv6t2/OP_XOR_LONG_2ADDR.S | 2 + vm/mterp/armv6t2/bincmp.S | 31 + vm/mterp/armv6t2/binop2addr.S | 33 + vm/mterp/armv6t2/binopLit16.S | 30 + vm/mterp/armv6t2/binopWide2addr.S | 35 + vm/mterp/armv6t2/unop.S | 20 + vm/mterp/armv6t2/unopNarrower.S | 23 + vm/mterp/armv6t2/unopWide.S | 22 + vm/mterp/armv6t2/unopWider.S | 20 + vm/mterp/c/OP_MONITOR_ENTER.c | 4 +- vm/mterp/c/OP_NEW_INSTANCE.c | 20 +- vm/mterp/c/OP_THROW_VERIFICATION_ERROR.c | 7 + vm/mterp/c/OP_UNUSED_ED.c | 2 - vm/mterp/c/gotoTargets.c | 26 +- vm/mterp/c/header.c | 46 +- vm/mterp/common/FindInterface.h | 37 + vm/mterp/common/asm-constants.h | 62 +- vm/mterp/config-armv5te-vfp | 105 + vm/mterp/config-armv7-a | 167 + vm/mterp/cstubs/stubdefs.c | 7 +- vm/mterp/gen-mterp.py | 10 +- vm/mterp/out/InterpAsm-armv4t.S | 686 +- vm/mterp/out/InterpAsm-armv5te-vfp.S | 9972 +++++++++++++++++ vm/mterp/out/InterpAsm-armv5te.S | 686 +- vm/mterp/out/InterpAsm-armv7-a.S | 9908 ++++++++++++++++ vm/mterp/out/InterpAsm-x86.S | 307 +- vm/mterp/out/InterpC-allstubs.c | 112 +- vm/mterp/out/InterpC-armv4t.c | 69 +- vm/mterp/out/InterpC-armv5te-vfp.c | 1279 +++ vm/mterp/out/InterpC-armv5te.c | 69 +- vm/mterp/out/InterpC-armv7-a.c | 1279 +++ vm/mterp/out/InterpC-portdbg.c | 142 +- vm/mterp/out/InterpC-portstd.c | 134 +- vm/mterp/out/InterpC-x86.c | 79 +- vm/mterp/portable/debug.c | 2 +- vm/mterp/portable/entry.c | 21 + vm/mterp/portable/portdbg.c | 8 + vm/mterp/portable/portstd.c | 2 + vm/mterp/portable/stubdefs.c | 6 +- vm/mterp/rebuild.sh | 2 +- vm/mterp/x86/OP_INVOKE_DIRECT.S | 4 +- vm/mterp/x86/OP_INVOKE_INTERFACE.S | 4 +- vm/mterp/x86/OP_INVOKE_STATIC.S | 8 +- vm/mterp/x86/OP_INVOKE_SUPER.S | 5 +- vm/mterp/x86/OP_INVOKE_SUPER_QUICK.S | 4 +- vm/mterp/x86/OP_INVOKE_VIRTUAL.S | 4 +- vm/mterp/x86/OP_INVOKE_VIRTUAL_QUICK.S | 5 +- vm/mterp/x86/OP_MONITOR_ENTER.S | 4 +- vm/mterp/x86/OP_NEW_INSTANCE.S | 2 + vm/mterp/x86/OP_THROW_VERIFICATION_ERROR.S | 20 + vm/mterp/x86/OP_UNUSED_ED.S | 1 - vm/mterp/x86/footer.S | 209 +- vm/native/InternalNative.c | 2 + vm/native/InternalNativePriv.h | 1 + vm/native/SystemThread.c | 169 + vm/native/SystemThread.h | 41 + vm/native/dalvik_system_DexFile.c | 19 +- vm/native/dalvik_system_SamplingProfiler.c | 647 ++ vm/native/dalvik_system_VMDebug.c | 138 +- vm/native/dalvik_system_VMStack.c | 17 + vm/oo/Class.c | 311 +- vm/oo/Class.h | 15 +- vm/oo/Object.c | 203 +- vm/oo/Object.h | 96 +- vm/oo/Resolve.c | 5 +- vm/reflect/Annotation.c | 9 +- vm/test/AtomicSpeed.c | 115 + vm/test/Test.h | 3 + vm/test/TestHash.c | 4 + vm/test/TestIndirectRefTable.c | 485 + 1712 files changed, 105798 insertions(+), 50236 deletions(-) create mode 100644 docs/heap-profiling.html create mode 100644 docs/hello-world.html create mode 100644 docs/porting-guide.html create mode 100644 docs/porting-proto.c.txt create mode 100644 dx/tests/111-use-null-as-array/Blort.java create mode 100644 dx/tests/111-use-null-as-array/expected.txt create mode 100644 dx/tests/111-use-null-as-array/info.txt create mode 100644 dx/tests/111-use-null-as-array/run rename libcore/{luni/src/main/java/org/apache/harmony/luni/platform/ISystemComponent.java => archive/src/main/native/zip.c} (60%) create mode 100644 libcore/archive/src/main/native/zip.h create mode 100644 libcore/archive/src/test/java-internal/org/apache/harmony/archive/util/UtilTest.java create mode 100644 libcore/concurrent/src/main/java/java/util/concurrent/Java6Arrays.java create mode 100644 libcore/concurrent/src/main/java/java/util/concurrent/atomic/package-info.java delete mode 100644 libcore/concurrent/src/main/java/java/util/concurrent/atomic/package.html create mode 100644 libcore/concurrent/src/main/java/java/util/concurrent/locks/AbstractOwnableSynchronizer.java create mode 100644 libcore/concurrent/src/main/java/java/util/concurrent/locks/package-info.java delete mode 100644 libcore/concurrent/src/main/java/java/util/concurrent/locks/package.html create mode 100644 libcore/concurrent/src/main/java/java/util/concurrent/package-info.java delete mode 100644 libcore/concurrent/src/main/java/java/util/concurrent/package.html create mode 100644 libcore/dalvik/src/main/java/dalvik/system/SamplingProfiler.java create mode 100644 libcore/luni/src/main/java/org/apache/harmony/luni/internal/io/FileCanonPathCache.java delete mode 100644 libcore/luni/src/main/java/org/apache/harmony/luni/internal/reflect/ProxyCharArrayCache.java delete mode 100644 libcore/luni/src/main/java/org/apache/harmony/luni/internal/reflect/ProxyClassFile.java delete mode 100644 libcore/luni/src/main/java/org/apache/harmony/luni/internal/reflect/ProxyConstantPool.java delete mode 100644 libcore/luni/src/main/java/org/apache/harmony/luni/internal/reflect/ProxyConstants.java delete mode 100644 libcore/luni/src/main/java/org/apache/harmony/luni/internal/reflect/ProxyMethod.java delete mode 100644 libcore/luni/src/main/java/org/apache/harmony/luni/internal/reflect/ProxyNameAndTypeCache.java delete mode 100644 libcore/luni/src/main/java/org/apache/harmony/luni/internal/reflect/ProxyObjectCache.java delete mode 100644 libcore/luni/src/main/java/org/apache/harmony/luni/net/PlainMulticastSocketImpl.java delete mode 100644 libcore/luni/src/main/java/org/apache/harmony/luni/net/PlainSocketImpl2.java delete mode 100644 libcore/luni/src/main/java/org/apache/harmony/luni/net/SocketImplProvider.java delete mode 100644 libcore/luni/src/main/java/org/apache/harmony/luni/platform/AdapterManager.java delete mode 100644 libcore/luni/src/main/java/org/apache/harmony/luni/platform/IAdaptable.java delete mode 100644 libcore/luni/src/main/java/org/apache/harmony/luni/platform/IAdapterFactory.java create mode 100644 libcore/luni/src/main/java/org/apache/harmony/luni/util/DeleteOnExit.java create mode 100644 libcore/luni/src/main/java/org/apache/harmony/luni/util/ExposedByteArrayInputStream.java create mode 100644 libcore/luni/src/main/java/org/apache/harmony/luni/util/HistoricalNamesUtil.java create mode 100644 libcore/luni/src/main/java/org/apache/harmony/luni/util/InputStreamExposer.java delete mode 100644 libcore/luni/src/main/java/org/apache/harmony/luni/util/Sorter.java create mode 100644 libcore/luni/src/main/java/org/apache/harmony/luni/util/ThreadLocalCache.java mode change 100755 => 100644 libcore/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp rename libcore/luni/src/{main/java/org/apache/harmony/luni/platform/OSComponentFactory.java => test/java/org/apache/harmony/luni/platform/AllTests.java} (56%) create mode 100644 libcore/luni/src/test/java/org/apache/harmony/luni/platform/OSMemoryTest.java create mode 100644 libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/io/FileCanonPathCacheTest.java delete mode 100644 libcore/prefs/src/test/resources/prefs/java/util/prefs/preferences.dtd create mode 100644 libcore/security/src/main/files/cacerts/3e7271e8.0 create mode 100644 libcore/security/src/main/files/cacerts/ab86d4de.0 create mode 100644 libcore/security/src/main/files/cacerts/bf64f35b.0 create mode 100644 libcore/security/src/test/java/org/bouncycastle/jce/provider/AllTests.java create mode 100644 libcore/security/src/test/java/org/bouncycastle/jce/provider/PKIXCertPathValidatorSpiTest.java rename libcore/{luni/src/main/java/org/apache/harmony/luni/platform/OSComponent.java => sql/src/test/java/org/apache/harmony/sql/tests/java/sql/TestMainForDriver.java} (50%) create mode 100644 libcore/suncompat/src/test/java/sun/misc/AllTests.java create mode 100644 libcore/suncompat/src/test/java/sun/misc/UnsafeTest.java rename libcore/{luni/src/main/java/org/apache/harmony/luni/platform/IAdapterManager.java => suncompat/src/test/java/tests/suncompat/AllTests.java} (59%) create mode 100644 libcore/support/src/test/java/tests/resources/EmptyEntries_signed.jar create mode 100644 libcore/support/src/test/java/tests/util/PrefsTester.java create mode 100644 libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/Support_DecimalFormat.java create mode 100644 libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/Support_Format.java create mode 100644 libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/Support_MessageFormat.java create mode 100644 libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/Support_SimpleDateFormat.java create mode 100644 libcore/tools/integrate/Android.mk create mode 100644 libcore/tools/integrate/Command.java create mode 100644 libcore/tools/integrate/Filesystem.java create mode 100644 libcore/tools/integrate/Git.java create mode 100644 libcore/tools/integrate/MappedDirectory.java create mode 100644 libcore/tools/integrate/Module.java create mode 100644 libcore/tools/integrate/Modules.java create mode 100644 libcore/tools/integrate/PullHarmonyCode.java create mode 100644 libcore/tools/integrate/Svn.java delete mode 100644 libcore/x-net/src/main/java/javax/net/ssl/ContextImpl.java create mode 100644 tests/065-mismatched-implements/src/Indirect.java create mode 100644 tests/066-mismatched-super/src/Indirect.java create mode 100644 tests/068-classloader/src-ex/AbstractGet.java create mode 100644 tests/068-classloader/src-ex/DoubledExtendOkay.java create mode 100644 tests/068-classloader/src-ex/GetDoubled.java create mode 100644 tests/068-classloader/src/BaseOkay.java create mode 100644 tests/068-classloader/src/DoubledExtendOkay.java create mode 100644 tests/068-classloader/src/IGetDoubled.java create mode 100644 tests/072-precise-gc/expected.txt create mode 100644 tests/072-precise-gc/info.txt create mode 100644 tests/072-precise-gc/src/Main.java create mode 100644 tests/073-mismatched-field/expected.txt create mode 100644 tests/073-mismatched-field/info.txt create mode 100644 tests/073-mismatched-field/src/IMain.java create mode 100644 tests/073-mismatched-field/src/Main.java create mode 100644 tests/073-mismatched-field/src/SuperMain.java create mode 100644 tests/073-mismatched-field/src2/IMain.java create mode 100644 tests/074-gc-thrash/expected.txt create mode 100644 tests/074-gc-thrash/info.txt create mode 100644 tests/074-gc-thrash/src/Main.java create mode 100644 tests/075-verification-error/expected.txt create mode 100644 tests/075-verification-error/info.txt create mode 100644 tests/075-verification-error/src/Main.java create mode 100644 tests/075-verification-error/src/MaybeAbstract.java create mode 100644 tests/075-verification-error/src/other/InaccessibleClass.java create mode 100644 tests/075-verification-error/src/other/InaccessibleMethod.java create mode 100644 tests/075-verification-error/src/other/Mutant.java create mode 100644 tests/075-verification-error/src2/MaybeAbstract.java create mode 100644 tests/075-verification-error/src2/other/InaccessibleClass.java create mode 100644 tests/075-verification-error/src2/other/InaccessibleMethod.java create mode 100644 tests/075-verification-error/src2/other/Mutant.java create mode 100644 tests/076-boolean-put/expected.txt create mode 100644 tests/076-boolean-put/info.txt create mode 100644 tests/076-boolean-put/src/Main.java create mode 100644 tests/077-method-override/expected.txt create mode 100644 tests/077-method-override/info.txt create mode 100644 tests/077-method-override/src/Base.java create mode 100644 tests/077-method-override/src/Derived.java create mode 100644 tests/077-method-override/src/Main.java create mode 100644 tests/077-method-override/src2/Base.java create mode 100644 tests/078-polymorphic-virtual/expected.txt create mode 100644 tests/078-polymorphic-virtual/info.txt create mode 100644 tests/078-polymorphic-virtual/src/Base.java create mode 100644 tests/078-polymorphic-virtual/src/Derived1.java create mode 100644 tests/078-polymorphic-virtual/src/Derived2.java create mode 100644 tests/078-polymorphic-virtual/src/Derived3.java create mode 100644 tests/078-polymorphic-virtual/src/Main.java create mode 100755 tools/dexcheck create mode 100644 tools/dexdeps/Android.mk create mode 100644 tools/dexdeps/README.txt create mode 100644 tools/dexdeps/etc/dexdeps create mode 100644 tools/dexdeps/etc/manifest.txt create mode 100644 tools/dexdeps/src/Android.mk create mode 100644 tools/dexdeps/src/com/android/dexdeps/DexData.java create mode 100644 tools/dexdeps/src/com/android/dexdeps/DexDataException.java create mode 100644 tools/dexdeps/src/com/android/dexdeps/FieldRef.java create mode 100644 tools/dexdeps/src/com/android/dexdeps/Main.java create mode 100644 tools/dexdeps/src/com/android/dexdeps/MethodRef.java create mode 100644 tools/dexdeps/src/com/android/dexdeps/Output.java create mode 100644 tools/dexdeps/src/com/android/dexdeps/UsageException.java create mode 100755 tools/gclog.py create mode 100644 vm/Dvm.mk create mode 100644 vm/IndirectRefTable.c create mode 100644 vm/IndirectRefTable.h create mode 100644 vm/arch/sh/CallSH4ABI.S create mode 100644 vm/compiler/Compiler.c create mode 100644 vm/compiler/Compiler.h create mode 100644 vm/compiler/CompilerIR.h create mode 100644 vm/compiler/CompilerInternals.h create mode 100644 vm/compiler/CompilerUtility.h create mode 100644 vm/compiler/Frontend.c create mode 100644 vm/compiler/IntermediateRep.c create mode 100644 vm/compiler/Utility.c create mode 100644 vm/compiler/codegen/CompilerCodegen.h create mode 100644 vm/compiler/codegen/Optimizer.h create mode 100644 vm/compiler/codegen/arm/ArchUtility.c create mode 100644 vm/compiler/codegen/arm/ArmLIR.h create mode 100644 vm/compiler/codegen/arm/Assemble.c create mode 100644 vm/compiler/codegen/arm/Codegen-armv5te-vfp.c create mode 100644 vm/compiler/codegen/arm/Codegen-armv5te.c create mode 100644 vm/compiler/codegen/arm/Codegen-armv7-a.c create mode 100644 vm/compiler/codegen/arm/Codegen.c create mode 100644 vm/compiler/codegen/arm/Codegen.h create mode 100644 vm/compiler/codegen/arm/GlobalOptimizations.c create mode 100644 vm/compiler/codegen/arm/LocalOptimizations.c create mode 100644 vm/compiler/codegen/arm/Thumb2Util.c create mode 100644 vm/compiler/codegen/arm/ThumbUtil.c create mode 100644 vm/compiler/codegen/arm/armv5te-vfp/ArchVariant.c create mode 100644 vm/compiler/codegen/arm/armv5te-vfp/ArchVariant.h create mode 100644 vm/compiler/codegen/arm/armv5te/ArchVariant.c create mode 100644 vm/compiler/codegen/arm/armv5te/ArchVariant.h create mode 100644 vm/compiler/codegen/arm/armv7-a/ArchVariant.c create mode 100644 vm/compiler/codegen/arm/armv7-a/ArchVariant.h create mode 100644 vm/compiler/template/Makefile-template create mode 100644 vm/compiler/template/README.txt create mode 100644 vm/compiler/template/armv5te-vfp/TEMPLATE_ADD_DOUBLE_VFP.S create mode 100644 vm/compiler/template/armv5te-vfp/TEMPLATE_ADD_FLOAT_VFP.S create mode 100644 vm/compiler/template/armv5te-vfp/TEMPLATE_CMPG_DOUBLE_VFP.S create mode 100644 vm/compiler/template/armv5te-vfp/TEMPLATE_CMPG_FLOAT_VFP.S create mode 100644 vm/compiler/template/armv5te-vfp/TEMPLATE_CMPL_DOUBLE_VFP.S create mode 100644 vm/compiler/template/armv5te-vfp/TEMPLATE_CMPL_FLOAT_VFP.S create mode 100644 vm/compiler/template/armv5te-vfp/TEMPLATE_DIV_DOUBLE_VFP.S create mode 100644 vm/compiler/template/armv5te-vfp/TEMPLATE_DIV_FLOAT_VFP.S create mode 100644 vm/compiler/template/armv5te-vfp/TEMPLATE_DOUBLE_TO_FLOAT_VFP.S create mode 100644 vm/compiler/template/armv5te-vfp/TEMPLATE_DOUBLE_TO_INT_VFP.S create mode 100644 vm/compiler/template/armv5te-vfp/TEMPLATE_FLOAT_TO_DOUBLE_VFP.S create mode 100644 vm/compiler/template/armv5te-vfp/TEMPLATE_FLOAT_TO_INT_VFP.S create mode 100644 vm/compiler/template/armv5te-vfp/TEMPLATE_INT_TO_DOUBLE_VFP.S create mode 100644 vm/compiler/template/armv5te-vfp/TEMPLATE_INT_TO_FLOAT_VFP.S create mode 100644 vm/compiler/template/armv5te-vfp/TEMPLATE_MUL_DOUBLE_VFP.S create mode 100644 vm/compiler/template/armv5te-vfp/TEMPLATE_MUL_FLOAT_VFP.S create mode 100644 vm/compiler/template/armv5te-vfp/TEMPLATE_SQRT_DOUBLE_VFP.S create mode 100644 vm/compiler/template/armv5te-vfp/TEMPLATE_SUB_DOUBLE_VFP.S create mode 100644 vm/compiler/template/armv5te-vfp/TEMPLATE_SUB_FLOAT_VFP.S create mode 100644 vm/compiler/template/armv5te-vfp/TemplateOpList.h create mode 100644 vm/compiler/template/armv5te-vfp/fbinop.S create mode 100644 vm/compiler/template/armv5te-vfp/fbinopWide.S create mode 100644 vm/compiler/template/armv5te-vfp/funop.S create mode 100644 vm/compiler/template/armv5te-vfp/funopNarrower.S create mode 100644 vm/compiler/template/armv5te-vfp/funopWider.S create mode 100644 vm/compiler/template/armv5te/TEMPLATE_CMPG_DOUBLE.S create mode 100644 vm/compiler/template/armv5te/TEMPLATE_CMPG_FLOAT.S create mode 100644 vm/compiler/template/armv5te/TEMPLATE_CMPL_DOUBLE.S create mode 100644 vm/compiler/template/armv5te/TEMPLATE_CMPL_FLOAT.S create mode 100644 vm/compiler/template/armv5te/TEMPLATE_CMP_LONG.S create mode 100644 vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_CHAIN.S create mode 100644 vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NATIVE.S create mode 100644 vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_NO_OPT.S create mode 100644 vm/compiler/template/armv5te/TEMPLATE_INVOKE_METHOD_PREDICTED_CHAIN.S create mode 100644 vm/compiler/template/armv5te/TEMPLATE_MUL_LONG.S create mode 100644 vm/compiler/template/armv5te/TEMPLATE_RETURN.S create mode 100644 vm/compiler/template/armv5te/TEMPLATE_SHL_LONG.S create mode 100644 vm/compiler/template/armv5te/TEMPLATE_SHR_LONG.S create mode 100644 vm/compiler/template/armv5te/TEMPLATE_USHR_LONG.S create mode 100644 vm/compiler/template/armv5te/TemplateOpList.h create mode 100644 vm/compiler/template/armv5te/footer.S create mode 100644 vm/compiler/template/armv5te/header.S create mode 100644 vm/compiler/template/armv5te/platform.S create mode 100644 vm/compiler/template/armv7-a/TemplateOpList.h create mode 100644 vm/compiler/template/config-armv5te create mode 100644 vm/compiler/template/config-armv5te-vfp create mode 100644 vm/compiler/template/config-armv7-a create mode 100755 vm/compiler/template/gen-template.py create mode 100644 vm/compiler/template/out/CompilerTemplateAsm-armv5te-vfp.S create mode 100644 vm/compiler/template/out/CompilerTemplateAsm-armv5te.S create mode 100644 vm/compiler/template/out/CompilerTemplateAsm-armv7-a.S create mode 100755 vm/compiler/template/rebuild.sh create mode 100644 vm/interp/Jit.c create mode 100644 vm/interp/Jit.h create mode 100644 vm/mterp/arm-vfp/OP_ADD_DOUBLE.S create mode 100644 vm/mterp/arm-vfp/OP_ADD_DOUBLE_2ADDR.S create mode 100644 vm/mterp/arm-vfp/OP_ADD_FLOAT.S create mode 100644 vm/mterp/arm-vfp/OP_ADD_FLOAT_2ADDR.S create mode 100644 vm/mterp/arm-vfp/OP_CMPG_DOUBLE.S create mode 100644 vm/mterp/arm-vfp/OP_CMPG_FLOAT.S create mode 100644 vm/mterp/arm-vfp/OP_CMPL_DOUBLE.S create mode 100644 vm/mterp/arm-vfp/OP_CMPL_FLOAT.S create mode 100644 vm/mterp/arm-vfp/OP_DIV_DOUBLE.S create mode 100644 vm/mterp/arm-vfp/OP_DIV_DOUBLE_2ADDR.S create mode 100644 vm/mterp/arm-vfp/OP_DIV_FLOAT.S create mode 100644 vm/mterp/arm-vfp/OP_DIV_FLOAT_2ADDR.S create mode 100644 vm/mterp/arm-vfp/OP_DOUBLE_TO_FLOAT.S create mode 100644 vm/mterp/arm-vfp/OP_DOUBLE_TO_INT.S create mode 100644 vm/mterp/arm-vfp/OP_FLOAT_TO_DOUBLE.S create mode 100644 vm/mterp/arm-vfp/OP_FLOAT_TO_INT.S create mode 100644 vm/mterp/arm-vfp/OP_INT_TO_DOUBLE.S create mode 100644 vm/mterp/arm-vfp/OP_INT_TO_FLOAT.S create mode 100644 vm/mterp/arm-vfp/OP_MUL_DOUBLE.S create mode 100644 vm/mterp/arm-vfp/OP_MUL_DOUBLE_2ADDR.S create mode 100644 vm/mterp/arm-vfp/OP_MUL_FLOAT.S create mode 100644 vm/mterp/arm-vfp/OP_MUL_FLOAT_2ADDR.S create mode 100644 vm/mterp/arm-vfp/OP_SUB_DOUBLE.S create mode 100644 vm/mterp/arm-vfp/OP_SUB_DOUBLE_2ADDR.S create mode 100644 vm/mterp/arm-vfp/OP_SUB_FLOAT.S create mode 100644 vm/mterp/arm-vfp/OP_SUB_FLOAT_2ADDR.S create mode 100644 vm/mterp/arm-vfp/README.txt create mode 100644 vm/mterp/arm-vfp/fbinop.S create mode 100644 vm/mterp/arm-vfp/fbinop2addr.S create mode 100644 vm/mterp/arm-vfp/fbinopWide.S create mode 100644 vm/mterp/arm-vfp/fbinopWide2addr.S create mode 100644 vm/mterp/arm-vfp/funop.S create mode 100644 vm/mterp/arm-vfp/funopNarrower.S create mode 100644 vm/mterp/arm-vfp/funopWider.S create mode 100644 vm/mterp/armv5te/OP_THROW_VERIFICATION_ERROR.S delete mode 100644 vm/mterp/armv5te/OP_UNUSED_ED.S create mode 100644 vm/mterp/armv6/OP_INT_TO_BYTE.S create mode 100644 vm/mterp/armv6/OP_INT_TO_CHAR.S create mode 100644 vm/mterp/armv6/OP_INT_TO_SHORT.S create mode 100644 vm/mterp/armv6t2/OP_ADD_DOUBLE_2ADDR.S create mode 100644 vm/mterp/armv6t2/OP_ADD_FLOAT_2ADDR.S create mode 100644 vm/mterp/armv6t2/OP_ADD_INT_2ADDR.S create mode 100644 vm/mterp/armv6t2/OP_ADD_INT_LIT16.S create mode 100644 vm/mterp/armv6t2/OP_ADD_LONG_2ADDR.S create mode 100644 vm/mterp/armv6t2/OP_AND_INT_2ADDR.S create mode 100644 vm/mterp/armv6t2/OP_AND_INT_LIT16.S create mode 100644 vm/mterp/armv6t2/OP_AND_LONG_2ADDR.S create mode 100644 vm/mterp/armv6t2/OP_ARRAY_LENGTH.S create mode 100644 vm/mterp/armv6t2/OP_CONST_4.S create mode 100644 vm/mterp/armv6t2/OP_DIV_DOUBLE_2ADDR.S create mode 100644 vm/mterp/armv6t2/OP_DIV_FLOAT_2ADDR.S create mode 100644 vm/mterp/armv6t2/OP_DIV_INT_2ADDR.S create mode 100644 vm/mterp/armv6t2/OP_DIV_INT_LIT16.S create mode 100644 vm/mterp/armv6t2/OP_DIV_LONG_2ADDR.S create mode 100644 vm/mterp/armv6t2/OP_DOUBLE_TO_FLOAT.S create mode 100644 vm/mterp/armv6t2/OP_DOUBLE_TO_INT.S create mode 100644 vm/mterp/armv6t2/OP_DOUBLE_TO_LONG.S create mode 100644 vm/mterp/armv6t2/OP_FLOAT_TO_DOUBLE.S create mode 100644 vm/mterp/armv6t2/OP_FLOAT_TO_INT.S create mode 100644 vm/mterp/armv6t2/OP_FLOAT_TO_LONG.S create mode 100644 vm/mterp/armv6t2/OP_IF_EQ.S create mode 100644 vm/mterp/armv6t2/OP_IF_GE.S create mode 100644 vm/mterp/armv6t2/OP_IF_GT.S create mode 100644 vm/mterp/armv6t2/OP_IF_LE.S create mode 100644 vm/mterp/armv6t2/OP_IF_LT.S create mode 100644 vm/mterp/armv6t2/OP_IF_NE.S create mode 100644 vm/mterp/armv6t2/OP_IGET.S create mode 100644 vm/mterp/armv6t2/OP_IGET_QUICK.S create mode 100644 vm/mterp/armv6t2/OP_IGET_WIDE.S create mode 100644 vm/mterp/armv6t2/OP_IGET_WIDE_QUICK.S create mode 100644 vm/mterp/armv6t2/OP_INT_TO_BYTE.S create mode 100644 vm/mterp/armv6t2/OP_INT_TO_CHAR.S create mode 100644 vm/mterp/armv6t2/OP_INT_TO_DOUBLE.S create mode 100644 vm/mterp/armv6t2/OP_INT_TO_FLOAT.S create mode 100644 vm/mterp/armv6t2/OP_INT_TO_LONG.S create mode 100644 vm/mterp/armv6t2/OP_INT_TO_SHORT.S create mode 100644 vm/mterp/armv6t2/OP_IPUT.S create mode 100644 vm/mterp/armv6t2/OP_IPUT_QUICK.S create mode 100644 vm/mterp/armv6t2/OP_IPUT_WIDE.S create mode 100644 vm/mterp/armv6t2/OP_IPUT_WIDE_QUICK.S create mode 100644 vm/mterp/armv6t2/OP_LONG_TO_DOUBLE.S create mode 100644 vm/mterp/armv6t2/OP_LONG_TO_FLOAT.S create mode 100644 vm/mterp/armv6t2/OP_MOVE.S create mode 100644 vm/mterp/armv6t2/OP_MOVE_WIDE.S create mode 100644 vm/mterp/armv6t2/OP_MUL_DOUBLE_2ADDR.S create mode 100644 vm/mterp/armv6t2/OP_MUL_FLOAT_2ADDR.S create mode 100644 vm/mterp/armv6t2/OP_MUL_INT_2ADDR.S create mode 100644 vm/mterp/armv6t2/OP_MUL_INT_LIT16.S create mode 100644 vm/mterp/armv6t2/OP_MUL_LONG_2ADDR.S create mode 100644 vm/mterp/armv6t2/OP_NEG_DOUBLE.S create mode 100644 vm/mterp/armv6t2/OP_NEG_FLOAT.S create mode 100644 vm/mterp/armv6t2/OP_NEG_INT.S create mode 100644 vm/mterp/armv6t2/OP_NEG_LONG.S create mode 100644 vm/mterp/armv6t2/OP_NOT_INT.S create mode 100644 vm/mterp/armv6t2/OP_NOT_LONG.S create mode 100644 vm/mterp/armv6t2/OP_OR_INT_2ADDR.S create mode 100644 vm/mterp/armv6t2/OP_OR_INT_LIT16.S create mode 100644 vm/mterp/armv6t2/OP_OR_LONG_2ADDR.S create mode 100644 vm/mterp/armv6t2/OP_REM_DOUBLE_2ADDR.S create mode 100644 vm/mterp/armv6t2/OP_REM_FLOAT_2ADDR.S create mode 100644 vm/mterp/armv6t2/OP_REM_INT_2ADDR.S create mode 100644 vm/mterp/armv6t2/OP_REM_INT_LIT16.S create mode 100644 vm/mterp/armv6t2/OP_REM_LONG_2ADDR.S create mode 100644 vm/mterp/armv6t2/OP_RSUB_INT.S create mode 100644 vm/mterp/armv6t2/OP_SHL_INT_2ADDR.S create mode 100644 vm/mterp/armv6t2/OP_SHL_LONG_2ADDR.S create mode 100644 vm/mterp/armv6t2/OP_SHR_INT_2ADDR.S create mode 100644 vm/mterp/armv6t2/OP_SHR_LONG_2ADDR.S create mode 100644 vm/mterp/armv6t2/OP_SUB_DOUBLE_2ADDR.S create mode 100644 vm/mterp/armv6t2/OP_SUB_FLOAT_2ADDR.S create mode 100644 vm/mterp/armv6t2/OP_SUB_INT_2ADDR.S create mode 100644 vm/mterp/armv6t2/OP_SUB_LONG_2ADDR.S create mode 100644 vm/mterp/armv6t2/OP_USHR_INT_2ADDR.S create mode 100644 vm/mterp/armv6t2/OP_USHR_LONG_2ADDR.S create mode 100644 vm/mterp/armv6t2/OP_XOR_INT_2ADDR.S create mode 100644 vm/mterp/armv6t2/OP_XOR_INT_LIT16.S create mode 100644 vm/mterp/armv6t2/OP_XOR_LONG_2ADDR.S create mode 100644 vm/mterp/armv6t2/bincmp.S create mode 100644 vm/mterp/armv6t2/binop2addr.S create mode 100644 vm/mterp/armv6t2/binopLit16.S create mode 100644 vm/mterp/armv6t2/binopWide2addr.S create mode 100644 vm/mterp/armv6t2/unop.S create mode 100644 vm/mterp/armv6t2/unopNarrower.S create mode 100644 vm/mterp/armv6t2/unopWide.S create mode 100644 vm/mterp/armv6t2/unopWider.S create mode 100644 vm/mterp/c/OP_THROW_VERIFICATION_ERROR.c delete mode 100644 vm/mterp/c/OP_UNUSED_ED.c create mode 100644 vm/mterp/common/FindInterface.h create mode 100644 vm/mterp/config-armv5te-vfp create mode 100644 vm/mterp/config-armv7-a create mode 100644 vm/mterp/out/InterpAsm-armv5te-vfp.S create mode 100644 vm/mterp/out/InterpAsm-armv7-a.S create mode 100644 vm/mterp/out/InterpC-armv5te-vfp.c create mode 100644 vm/mterp/out/InterpC-armv7-a.c create mode 100644 vm/mterp/x86/OP_THROW_VERIFICATION_ERROR.S delete mode 100644 vm/mterp/x86/OP_UNUSED_ED.S create mode 100644 vm/native/SystemThread.c create mode 100644 vm/native/SystemThread.h create mode 100644 vm/native/dalvik_system_SamplingProfiler.c create mode 100644 vm/test/AtomicSpeed.c create mode 100644 vm/test/TestIndirectRefTable.c diff --git a/README.txt b/README.txt index 69559452b..2969180f3 100644 --- a/README.txt +++ b/README.txt @@ -1,5 +1,5 @@ -This directory contains the Dalvik virtual machine and associated -class library. +This directory contains the Dalvik virtual machine and core class library, +as well as related tools, libraries, and tests. A note about the licenses and header comments --------------------------------------------- @@ -50,3 +50,11 @@ Here is the combined header comment: * See the License for the specific language governing permissions and * limitations under the License. */ + + +Native SH call bridge +--------------------- + +Native SH call bridge is written by +Shin-ichiro KAWASAKI +and Contributed to Android by Hitachi, Ltd. and Renesas Solutions Corp. diff --git a/dalvikvm/Main.c b/dalvikvm/Main.c index 70cc61f02..a6439fbd7 100644 --- a/dalvikvm/Main.c +++ b/dalvikvm/Main.c @@ -115,7 +115,7 @@ static int methodIsPublic(JNIEnv* env, jclass clazz, jmethodID methodId) } getModifiersId = (*env)->GetMethodID(env, methodClass, "getModifiers", "()I"); - if (methodClass == NULL) { + if (getModifiersId == NULL) { fprintf(stderr, "Dalvik VM unable to find reflect.Method.getModifiers\n"); goto bail; } diff --git a/dexdump/DexDump.c b/dexdump/DexDump.c index c5714ea68..6fed7aaf4 100644 --- a/dexdump/DexDump.c +++ b/dexdump/DexDump.c @@ -60,10 +60,12 @@ typedef enum OutputFormat { /* command-line options */ struct { + bool checksumOnly; bool disassemble; bool showFileHeaders; bool showSectionHeaders; bool ignoreBadChecksum; + bool dumpRegisterMaps; OutputFormat outputFormat; const char* tempFileName; bool exportsOnly; @@ -85,6 +87,14 @@ static inline u2 get2LE(unsigned char const* pSrc) return pSrc[0] | (pSrc[1] << 8); } +/* + * Get 4 little-endian bytes. + */ +static inline u4 get4LE(unsigned char const* pSrc) +{ + return pSrc[0] | (pSrc[1] << 8) | (pSrc[2] << 16) | (pSrc[3] << 24); +} + /* * Converts a single-character primitive type into its human-readable * equivalent. @@ -589,10 +599,6 @@ const char* getClassDescriptor(DexFile* pDexFile, u4 classIdx) void dumpInstruction(DexFile* pDexFile, const DexCode* pCode, int insnIdx, int insnWidth, const DecodedInstruction* pDecInsn) { - static const float gSpecialTab[16] = { - -2.0f, -1.0f, -0.5f, -0.25f, -0.1f, 0.1f, 0.25f, 0.5f, - 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 10.0f, 100.0f, 1000.0f - }; const u2* insns = pCode->insns; int i; @@ -1197,6 +1203,8 @@ void dumpIField(const DexFile* pDexFile, const DexField* pIField, int i) /* * Dump the class. * + * Note "idx" is a DexClassDef index, not a DexTypeId index. + * * If "*pLastPackage" is NULL or does not match the current class' package, * the value will be replaced with a newly-allocated string. */ @@ -1369,6 +1377,208 @@ bail: free(accessStr); } + +/* + * Advance "ptr" to ensure 32-bit alignment. + */ +static inline const u1* align32(const u1* ptr) +{ + return (u1*) (((int) ptr + 3) & ~0x03); +} + + +/* + * Dump a map in the "differential" format. + * + * TODO: show a hex dump of the compressed data. (We can show the + * uncompressed data if we move the compression code to libdex; otherwise + * it's too complex to merit a fast & fragile implementation here.) + */ +void dumpDifferentialCompressedMap(const u1** pData) +{ + const u1* data = *pData; + const u1* dataStart = data -1; // format byte already removed + u1 regWidth; + u2 numEntries; + + /* standard header */ + regWidth = *data++; + numEntries = *data++; + numEntries |= (*data++) << 8; + + /* compressed data begins with the compressed data length */ + int compressedLen = readUnsignedLeb128(&data); + int addrWidth = 1; + if ((*data & 0x80) != 0) + addrWidth++; + + int origLen = 4 + (addrWidth + regWidth) * numEntries; + int compLen = (data - dataStart) + compressedLen; + + printf(" (differential compression %d -> %d [%d -> %d])\n", + origLen, compLen, + (addrWidth + regWidth) * numEntries, compressedLen); + + /* skip past end of entry */ + data += compressedLen; + + *pData = data; +} + +/* + * Dump register map contents of the current method. + * + * "*pData" should point to the start of the register map data. Advances + * "*pData" to the start of the next map. + */ +void dumpMethodMap(DexFile* pDexFile, const DexMethod* pDexMethod, int idx, + const u1** pData) +{ + const u1* data = *pData; + const DexMethodId* pMethodId; + const char* name; + int offset = data - (u1*) pDexFile->pOptHeader; + + pMethodId = dexGetMethodId(pDexFile, pDexMethod->methodIdx); + name = dexStringById(pDexFile, pMethodId->nameIdx); + printf(" #%d: 0x%08x %s\n", idx, offset, name); + + u1 format; + int addrWidth; + + format = *data++; + if (format == 1) { /* kRegMapFormatNone */ + /* no map */ + printf(" (no map)\n"); + addrWidth = 0; + } else if (format == 2) { /* kRegMapFormatCompact8 */ + addrWidth = 1; + } else if (format == 3) { /* kRegMapFormatCompact16 */ + addrWidth = 2; + } else if (format == 4) { /* kRegMapFormatDifferential */ + dumpDifferentialCompressedMap(&data); + goto bail; + } else { + printf(" (unknown format %d!)\n", format); + /* don't know how to skip data; failure will cascade to end of class */ + goto bail; + } + + if (addrWidth > 0) { + u1 regWidth; + u2 numEntries; + int idx, addr, byte; + + regWidth = *data++; + numEntries = *data++; + numEntries |= (*data++) << 8; + + for (idx = 0; idx < numEntries; idx++) { + addr = *data++; + if (addrWidth > 1) + addr |= (*data++) << 8; + + printf(" %4x:", addr); + for (byte = 0; byte < regWidth; byte++) { + printf(" %02x", *data++); + } + printf("\n"); + } + } + +bail: + //if (addrWidth >= 0) + // *pData = align32(data); + *pData = data; +} + +/* + * Dump the contents of the register map area. + * + * These are only present in optimized DEX files, and the structure is + * not really exposed to other parts of the VM itself. We're going to + * dig through them here, but this is pretty fragile. DO NOT rely on + * this or derive other code from it. + */ +void dumpRegisterMaps(DexFile* pDexFile) +{ + const u1* pClassPool = pDexFile->pRegisterMapPool; + const u4* classOffsets; + const u1* ptr; + u4 numClasses; + int baseFileOffset = (u1*) pClassPool - (u1*) pDexFile->pOptHeader; + int idx; + + if (pClassPool == NULL) { + printf("No register maps found\n"); + return; + } + + ptr = pClassPool; + numClasses = get4LE(ptr); + ptr += sizeof(u4); + classOffsets = (const u4*) ptr; + + printf("RMAP begins at offset 0x%07x\n", baseFileOffset); + printf("Maps for %d classes\n", numClasses); + for (idx = 0; idx < (int) numClasses; idx++) { + const DexClassDef* pClassDef; + const char* classDescriptor; + + pClassDef = dexGetClassDef(pDexFile, idx); + classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx); + + printf("%4d: +%d (0x%08x) %s\n", idx, classOffsets[idx], + baseFileOffset + classOffsets[idx], classDescriptor); + + if (classOffsets[idx] == 0) + continue; + + /* + * What follows is a series of RegisterMap entries, one for every + * direct method, then one for every virtual method. + */ + DexClassData* pClassData; + const u1* pEncodedData; + const u1* data = (u1*) pClassPool + classOffsets[idx]; + u2 methodCount; + int i; + + pEncodedData = dexGetClassData(pDexFile, pClassDef); + pClassData = dexReadAndVerifyClassData(&pEncodedData, NULL); + if (pClassData == NULL) { + fprintf(stderr, "Trouble reading class data\n"); + continue; + } + + methodCount = *data++; + methodCount |= (*data++) << 8; + data += 2; /* two pad bytes follow methodCount */ + if (methodCount != pClassData->header.directMethodsSize + + pClassData->header.virtualMethodsSize) + { + printf("NOTE: method count discrepancy (%d != %d + %d)\n", + methodCount, pClassData->header.directMethodsSize, + pClassData->header.virtualMethodsSize); + /* this is bad, but keep going anyway */ + } + + printf(" direct methods: %d\n", + pClassData->header.directMethodsSize); + for (i = 0; i < (int) pClassData->header.directMethodsSize; i++) { + dumpMethodMap(pDexFile, &pClassData->directMethods[i], i, &data); + } + + printf(" virtual methods: %d\n", + pClassData->header.virtualMethodsSize); + for (i = 0; i < (int) pClassData->header.virtualMethodsSize; i++) { + dumpMethodMap(pDexFile, &pClassData->virtualMethods[i], i, &data); + } + + free(pClassData); + } +} + /* * Dump the requested sections of the file. */ @@ -1382,6 +1592,11 @@ void processDexFile(const char* fileName, DexFile* pDexFile) pDexFile->pHeader->magic +4); } + if (gOptions.dumpRegisterMaps) { + dumpRegisterMaps(pDexFile); + return; + } + if (gOptions.showFileHeaders) dumpFileHeader(pDexFile); @@ -1433,7 +1648,11 @@ int process(const char* fileName) goto bail; } - processDexFile(fileName, pDexFile); + if (gOptions.checksumOnly) { + printf("Checksum verified\n"); + } else { + processDexFile(fileName, pDexFile); + } result = 0; @@ -1453,14 +1672,16 @@ void usage(void) { fprintf(stderr, "Copyright (C) 2007 The Android Open Source Project\n\n"); fprintf(stderr, - "%s: [-d] [-f] [-h] [-i] [-l layout] [-t tempfile] dexfile...\n", + "%s: [-c] [-d] [-f] [-h] [-i] [-l layout] [-m] [-t tempfile] dexfile...\n", gProgName); fprintf(stderr, "\n"); + fprintf(stderr, " -c : verify checksum and exit\n"); fprintf(stderr, " -d : disassemble code sections\n"); fprintf(stderr, " -f : display summary information from file header\n"); fprintf(stderr, " -h : display file header details\n"); fprintf(stderr, " -i : ignore checksum failures\n"); fprintf(stderr, " -l : output layout, either 'plain' or 'xml'\n"); + fprintf(stderr, " -m : dump register maps (and nothing else)\n"); fprintf(stderr, " -t : temp file name (defaults to /sdcard/dex-temp-*)\n"); } @@ -1478,11 +1699,14 @@ int main(int argc, char* const argv[]) gOptions.verbose = true; while (1) { - ic = getopt(argc, argv, "dfhil:t:"); + ic = getopt(argc, argv, "cdfhil:mt:"); if (ic < 0) break; switch (ic) { + case 'c': // verify the checksum then exit + gOptions.checksumOnly = true; + break; case 'd': // disassemble Dalvik instructions gOptions.disassemble = true; break; @@ -1506,6 +1730,9 @@ int main(int argc, char* const argv[]) wantUsage = true; } break; + case 'm': // dump register maps only + gOptions.dumpRegisterMaps = true; + break; case 't': // temp file, used when opening compressed Jar gOptions.tempFileName = optarg; break; @@ -1520,6 +1747,11 @@ int main(int argc, char* const argv[]) wantUsage = true; } + if (gOptions.checksumOnly && gOptions.ignoreBadChecksum) { + fprintf(stderr, "Can't specify both -c and -i\n"); + wantUsage = true; + } + /* initialize some VM tables */ gInstrWidth = dexCreateInstrWidthTable(); gInstrFormat = dexCreateInstrFormatTable(); @@ -1529,12 +1761,14 @@ int main(int argc, char* const argv[]) return 2; } - while (optind < argc) - process(argv[optind++]); + int result = 0; + while (optind < argc) { + result |= process(argv[optind++]); + } free(gInstrWidth); free(gInstrFormat); - return 0; + return (result != 0); } diff --git a/dexopt/OptMain.c b/dexopt/OptMain.c index ef339cdd1..953db0b00 100644 --- a/dexopt/OptMain.c +++ b/dexopt/OptMain.c @@ -337,7 +337,7 @@ static int fromDex(int argc, char* const argv[]) */ GET_ARG(vmBuildVersion, strtol, "bad vm build"); if (vmBuildVersion != DALVIK_VM_BUILD) { - LOGE("Inconsistent build rev: %d vs %d\n", + LOGE("DexOpt: build rev does not match VM: %d vs %d\n", vmBuildVersion, DALVIK_VM_BUILD); goto bail; } diff --git a/docs/debugger.html b/docs/debugger.html index 6e23f0df2..523c7123d 100644 --- a/docs/debugger.html +++ b/docs/debugger.html @@ -41,11 +41,11 @@ only responsible for handling requests from the debugger; VM-initated communication, such as notifying the debugger when the VM has stopped at a breakpoint, are sent from the affected thread.

-When the VM is embedded in the Android framework, -debugging is enabled in the VM unless the system property -ro.secure is set to 1. On these -"secure" devices, debugging is only enabled in app processes whose -manifest contains android:debuggable="true" in the +When the VM is started from the Android app framework, debugging is enabled +for all applications when the system property ro.debuggable +is set to 1 (use adb shell getprop ro.debuggable +to check it). If it's zero, debugging can be enabled via the application's +manifest, which must include android:debuggable="true" in the <application> element.

@@ -194,7 +194,9 @@ The debugger and garbage collector are somewhat loosely integrated at present. The VM currently guarantees that any object the debugger is aware of will not be garbage collected until after the debugger disconnects. This can result in a build-up over time while the -debugger is connected. +debugger is connected. For example, if the debugger sees a running +thread, the associated Thread object will not be collected, even after +the thread terminates.

The situation is exacerbated by a flaw in the exception processing code, which results in nearly all exceptions being added to the "do not discard" @@ -202,6 +204,43 @@ list, even if the debugger never sees them. Having a debugger attached to a program that throws lots of exceptions can result in out-of-memory errors. This will be fixed in a future release.

+The only way to "unlock" the references is to detach and reattach the +debugger. +

+  +

+The translation from Java bytecode to Dalvik bytecode may result in +identical sequences of instructions being combined. This can make it +look like the wrong bit of code is being executed. For example: +

    int test(int i) {
+        if (i == 1) {
+            return 0;
+        }
+        return 1;
+    }
+The Dalvik bytecode uses a common return instruction for both +return statements, so when i is 1 the debugger +will single-step through return 0 and then return 1. +

+  +

+Dalvik handles synchronized methods differently from other VMs. +Instead of marking a method as synchronized and expecting +the VM to handle the locks, dx inserts a "lock" +instruction at the top of the method and an "unlock" instruction in a +synthetic finally block. As a result, when single-stepping +a return statement, the "current line" cursor may jump to +the last line in the method. +

+This can also affect the way the debugger processes exceptions. The +debugger may decide to break on an +exception based on whether that exception is "caught" or "uncaught". To +be considered uncaught, there must be no matching catch block +or finally clause between the current point of execution and +the top of the thread. An exception thrown within or below a synchronized +method will always be considered "caught", so the debugger won't stop +until the exception is re-thrown from the synthetic finally block. +

Copyright © 2009 The Android Open Source Project
diff --git a/docs/dex-format.html b/docs/dex-format.html index 88a7fb0c5..bc69dd099 100644 --- a/docs/dex-format.html +++ b/docs/dex-format.html @@ -2499,12 +2499,12 @@ address += (adjusted_opcode / DBG_LINE_RANGE) count of fields annotated by this item - annotated_methods_off + annotated_methods_size uint count of methods annotated by this item - annotated_parameters_off + annotated_parameters_size uint count of method parameter lists annotated by this item diff --git a/docs/embedded-vm-control.html b/docs/embedded-vm-control.html index f90f0e596..0b279e812 100644 --- a/docs/embedded-vm-control.html +++ b/docs/embedded-vm-control.html @@ -15,6 +15,7 @@
  • Execution Mode
  • Deadlock Prediction
  • Stack Dumps +
  • DEX File Checksums

    Overview

    @@ -35,12 +36,22 @@ effect (adb shell stop; adb shell start). This is because the settings are processed in the "zygote" process, which starts early and stays around "forever". -

    You could also add a line to /data/local.prop that looks like: +

    You may not be able to set this as an unprivileged user. You can use +adb root or run the su command from the device +shell on "userdebug" builds to become root first. When in doubt, +

    adb shell getprop <name>
    +will tell you if the setprop took. + +

    If you don't want the property to evaporate when the device reboots, +add a line to /data/local.prop that looks like:

    <name> = <value>
    -

    Such changes will survive reboots, but will be removed by anything -that wipes the data partition. (Hint: create a local.prop -on your workstation, then adb push local.prop /data .) +

    Such changes will survive reboots, but will be lost if the data +partition is wiped. (Hint: create a local.prop +on your workstation, then adb push local.prop /data . Or, +use one-liners like +adb shell "echo name = value >> /data/local.prop" -- note +the quotes are important.)

    Extended JNI Checks

    @@ -72,7 +83,8 @@ not passed in, and JNI checks will be disabled.

    You can also pass JNI-checking options into the VM through a system property. The value set for dalvik.vm.jniopts will -be passed in as the -Xjniopts argument. +be passed in as the -Xjniopts argument. For example: +

    adb shell setprop dalvik.vm.jniopts forcecopy

    For more information about JNI checks, see JNI Tips. @@ -158,7 +170,12 @@ constrast, the "portable" interpreter is written in C and expected to run on a broad range of platforms. The "debug" interpreter is a variant of "portable" that includes support for profiling and single-stepping. -

    The VM allows you to choose between "fast" and "portable" with an +

    The VM may also support just-in-time compilation. While not strictly +a different interpreter, the JIT compiler may be enabled or disabled +with the same flag. (Check the output of dalvikvm -help to +see if JIT compilation is enabled in your VM.) + +

    The VM allows you to choose between "fast", "portable", and "jit" with an extended form of the -Xint argument. The value of this argument can be set through the dalvik.vm.execution-mode system property. @@ -235,6 +252,35 @@ is passed into the VM via the -Xstacktracefile argument.

    If the property is not defined, the VM will write the stack traces to the Android log when the signal arrives. + +

    DEX File Checksums

    + +

    For performance reasons, the checksum on "optimized" DEX files is +ignored. This is usually safe, because the files are generated on the +device, and have access permissions that prevent modification. + +

    If the storage on a device becomes unreliable, however, data corruption +can occur. This usually manifests itself as a repeatable virtual machine +crash. To speed diagnosis of such failures, the VM provides the +-Xcheckdexsum argument. When set, the checksums on all DEX +files are verified before the contents are used. + +

    The application framework will provide this argument during VM +creation if the dalvik.vm.check-dex-sum property is enabled. + +

    To enable extended DEX checksum verification: +

    adb shell setprop dalvik.vm.check-dex-sum true
    + +

    Incorrect checksums will prevent the DEX data from being used, and will +cause errors to be written to the log file. If a device has a history of +problems it may be useful to add the property to +/data/local.prop. + +

    Note also that the +dexdump tool always verifies DEX checksums, and can be used +to check for corruption in a large set of files. + +

    Copyright © 2008 The Android Open Source Project
    diff --git a/docs/heap-profiling.html b/docs/heap-profiling.html new file mode 100644 index 000000000..e36b60616 --- /dev/null +++ b/docs/heap-profiling.html @@ -0,0 +1,178 @@ + + + Dalvik Heap Profiling + + + +

    Dalvik Heap Profiling

    + +

    +The Dalvik virtual machine can produce a complete dump of the contents +of the virtual heap. This is very useful for debugging memory usage +and looking for memory leaks. Getting at the information can be tricky, +but has become easier in recent releases. + + +

    Getting the data

    +

    +The first step is to cause the VM to dump its status, and then pull the hprof +data off. The exact manner for doing so has changed over time. +

    +There is a runhat shell function, added by +build/envsetup.sh, that partially automates these steps. The +function changes in each release to accommodate newer behavior, so you have +to be careful that you don't use the wrong version. +

    + +

    Early releases (1.0/1.1)

    +

    +You can only generate heap data on the emulator or a device with root +access, because of the way the dump is initiated and where the output +files go. +

    +Get a command shell on the device: +

    +$ adb shell
    +
    +

    +You can verify that you're running as root with the id command. +The response should look like uid=0(root) gid=0(root). If not, +type su and try again. If su fails, you're out +of luck. + +

    +Next, ensure the target directory exists: +

    +# mkdir /data/misc
    +# chmod 777 /data/misc
    +
    + +

    +Use ps or DDMS to determine the process ID of your application, +then send a SIGUSR1 to the target process: + +

    +# kill -10 <pid>
    +
    + +

    +The signal causes a GC, followed by the heap dump (to be completely +accurate, they actually happen concurrently, but the results in the heap +dump reflect the post-GC state). This can take a couple of seconds, +so you have to watch for the GC log message to know when it's complete. +

    +Next: + +

    +# ls /data/misc/heap-dump*
    +# exit
    +
    + +

    +Use ls to check the file names, then exit to quit +the device command shell. + +

    +You should see two output files, named +/data/misc/heap-dump-BLAH-BLAH.hprof and +.hprof-head, where BLAH is a runtime-generated value +that ensures the filename is unique. Pull them off of the device and +remove the device-side copy: + +

    +$ adb pull /data/misc/heap-dump-BLAH-BLAH.hprof tail.hprof
    +$ adb pull /data/misc/heap-dump-BLAH-BLAH.hprof-head head.hprof
    +$ adb shell rm /data/misc/heap-dump-BLAH-BLAH.hprof /data/misc/heap-dump-BLAH-BLAH.hprof-head
    +
    + +

    +Merge them together and remove the intermediates: + +

    +$ cat head.hprof tail.hprof > dump.hprof
    +$ rm head.hprof tail.hprof
    +
    + +

    +You now have the hprof dump in dump.hprof. +

    + + +

    Android 1.5 ("Cupcake")

    +

    +Some steps were taken to make this simpler. Notably, the two output +files are now combined for you, and a new API call was added that allows +a program to write the dump at will to a specific file. If you're not +using the API call, you still need to be on an emulator or running as root. +(For some builds, you can use adb root to restart the adb +daemon as root.) +

    +The basic procedure is the same as for 1.0/1.1, but only one file will +appear in /data/misc (no -head), and upon +completion you will see a log message that says "hprof: heap dump completed". +It looks like this in the log: + +

    +I/dalvikvm(  289): threadid=7: reacting to signal 10
    +I/dalvikvm(  289): SIGUSR1 forcing GC and HPROF dump
    +I/dalvikvm(  289): hprof: dumping VM heap to "/data/misc/heap-dump-tm1240861355-pid289.hprof-hptemp".
    +I/dalvikvm(  289): hprof: dumping heap strings to "/data/misc/heap-dump-tm1240861355-pid289.hprof".
    +I/dalvikvm(  289): hprof: heap dump completed, temp file removed
    +
    + +

    +Summary: as above, use mkdir and chmod +to ensure the directory exists and is writable by your application. +Send the SIGUSR1 or use the API call to initiate a dump. +Use adb pull <dump-file> and adb shell rm +<dump-file> to retrieve the file and remove it from the +device. The concatenation step is not needed. + +

    +The new API is in the android.os.Debug class: +

    +public static void dumpHprofData(String fileName) throws IOException
    +
    +When called, the VM will go through the same series of steps (GC and +generate a .hprof file), but the output will be written to a file of +your choice, e.g. /sdcard/myapp.hprof. Because you're +initiating the action from within the app, and can write the file to +removable storage or the app's private data area, you can do this on a +device without root access. + + + +

    Android 1.6 ("Donut")

    +

    +In 1.6, features were added that allow DDMS to request a heap dump on +demand, and automatically pull the result across. Select your application +and click the "dump HPROF file" button. +

    +However, 1.6 also introduced the WRITE_EXTERNAL_STORAGE +permission, which is required to write data to the SD card. To use +the DDMS feature, which always writes files to the SD card, +you must have a card inserted and the permission enabled in your application. +

    +Otherwise, things are the same as they were in 1.5. + + +

    Examining the data

    +

    +The data file format was augmented slightly from the common hprof format, +and due to licensing restrictions the modified hat tool cannot +be distributed. A conversion tool, hprof-conv, can be used +to strip the Android-specific portions from the output. This tool was +first included in 1.5, but will work with older versions of Android. +

    +The converted output should work with any hprof data analyzer, including +jhat, which is available for free in the Sun JDK, and +Eclipse MAT. + + + +

    +

    Copyright © 2009 The Android Open Source Project
    + + + diff --git a/docs/hello-world.html b/docs/hello-world.html new file mode 100644 index 000000000..dbbaea6a3 --- /dev/null +++ b/docs/hello-world.html @@ -0,0 +1,216 @@ + + + Basic Dalvik VM Invocation + + + +

    Basic Dalvik VM Invocation

    + +

    +On an Android device, the Dalvik virtual machine usually executes embedded +in the Android application framework. It's also possible to run it directly, +just as you would a virtual machine on your desktop system. +

    +After compiling your Java language sources, convert and combine the .class +files into a DEX file, and push that to the device. Here's a simple example: + +

    +% echo 'class Foo {'\
    +> 'public static void main(String[] args) {'\
    +> 'System.out.println("Hello, world"); }}' > Foo.java
    +% javac Foo.java
    +% dx --dex --output=foo.jar Foo.class
    +% adb push foo.jar /sdcard
    +% adb shell dalvikvm -cp /sdcard/foo.jar Foo
    +Hello, world +
    +

    +The -cp option sets the classpath. The initial directory +for adb shell may not be what you expect it to be, so it's +usually best to specify absolute pathnames. + +

    +The dx command accepts lists of individual class files, +directories, or Jar archives. When the --output filename +ends with .jar, .zip, or .apk, +a file called classes.dex is created and stored inside the +archive. +

    +Run adb shell dalvikvm -help to see a list of command-line +options. +

    + + + +

    Using a debugger

    + +

    +You can debug stand-alone applications with any JDWP-compliant debugger. +There are two basic approaches. +

    +The first way is to connect directly through TCP. Add, to the "dalvikvm" +invocation line above, an argument like: +

    +  -agentlib:jdwp=transport=dt_socket,address=8000,server=y,suspend=y +

    +This tells the VM to wait for a debugger to connect to it on TCP port 8000. +You need to tell adb to forward local port 8000 to device port 8000: +

    +% adb forward tcp:8000 tcp:8000 +

    +and then connect to it with your favorite debugger (using jdb +as an example here): +

    +% jdb -attach localhost:8000 +

    +When the debugger attaches, the VM will be in a suspended state. You can +set breakpoints and then tell it to continue. + + +

    +You can also connect through DDMS, like you would for an Android application. +Add, to the "dalvikvm" command line: +

    +  -agentlib:jdwp=transport=dt_android_adb,suspend=y,server=y +

    +Note the transport has changed, and you no longer need to +specify a TCP port number. When your application starts, it will appear +in DDMS, with "?" as the application name. Select it in DDMS, and connect +to it as usual, e.g.: +

    +% jdb -attach localhost:8700 +

    +Because command-line applications don't include the client-side +DDM setup, features like thread monitoring and allocation tracking will not +be available in DDMS. It's strictly a debugger pass-through in this mode. +

    +See Dalvik Debugger Support for more information +about using debuggers with Dalvik. + + + +

    Working with the desktop build

    + + + +

    +The Dalvik VM can also be used directly on the desktop. This is somewhat +more complicated however, because you won't have certain things set up in +your environment, and several native code libraries are required to support +the core Dalvik libs. +

    +Start with: + +

    +  . build/envsetup.sh
    +  lunch sim-eng
    +
    + +You should see something like: + +
    +  ============================================
    +  TARGET_PRODUCT=sim
    +  TARGET_BUILD_VARIANT=eng
    +  TARGET_SIMULATOR=true
    +  TARGET_BUILD_TYPE=debug
    +  TARGET_ARCH=x86
    +  HOST_ARCH=x86
    +  HOST_OS=linux
    +  HOST_BUILD_TYPE=release
    +  BUILD_ID=
    +  ============================================
    +
    + +

    +This configures you to build for the desktop, linking against glibc. +This mode is NOT recommended for anything but experimental use. It +may go away in the future. +

    +You may see TARGET_BUILD_TYPE=release or =debug +or possibly nothing there at all. You may want to replace the +lunch command with +choosecombo Simulator debug sim eng. +

    +Build the world (add a -j4 if you have multiple cores): + +
    +  make
    +
    + +

    +When that completes, you have a working dalvikm on your desktop +machine: + +
    +  % dalvikvm
    +  E/dalvikvm(19521): ERROR: must specify non-'.' bootclasspath
    +  W/dalvikvm(19521): JNI_CreateJavaVM failed
    +  Dalvik VM init failed (check log file)
    +
    + +

    +To actually do something, you need to specify the bootstrap class path +and give it a place to put DEX data that it uncompresses from jar +files. You can do that with a script like this: + +
    +#!/bin/sh
    +
    +# base directory, at top of source tree; replace with absolute path
    +base=`pwd`
    +
    +# configure root dir of interesting stuff
    +root=$base/out/debug/host/linux-x86/product/sim/system
    +export ANDROID_ROOT=$root
    +
    +# configure bootclasspath
    +bootpath=$root/framework
    +export BOOTCLASSPATH=$bootpath/core.jar:$bootpath/ext.jar:$bootpath/framework.jar:$bootpath/android.policy.jar:$bootpath/services.jar
    +
    +# this is where we create the dalvik-cache directory; make sure it exists
    +export ANDROID_DATA=/tmp/dalvik_$USER
    +mkdir -p $ANDROID_DATA/dalvik-cache
    +
    +exec dalvikvm $@
    +
    + +

    +The preparation with dx is the same as before: + +
    +  % cat > Foo.java
    +  class Foo { public static void main(String[] args) {
    +    System.out.println("Hello, world");
    +  } }
    +  (ctrl-D)
    +  % javac Foo.java
    +  % dx --dex --output=foo.jar Foo.class
    +  % ./rund -cp foo.jar Foo
    +  Hello, world
    +
    + +As above, you can get some info about valid arguments like this: + +
    +  % ./rund -help
    +
    + +

    +This also shows what options the VM was configured with. The sim "debug" +build has all sorts of additional assertions and checks enabled, +which slows the VM down, but since this is just for experiments it +doesn't matter. + +

    +All of the above applies to x86 Linux. Anything else will likely +require a porting effort. If libffi supports your system, the amount of +work required should be minor. + +

    +
    Copyright © 2009 The Android Open Source Project
    + + + diff --git a/docs/jni-tips.html b/docs/jni-tips.html index e2c3b8509..e85434b52 100644 --- a/docs/jni-tips.html +++ b/docs/jni-tips.html @@ -14,7 +14,7 @@
  • JavaVM and JNIEnv
  • -
  • jclassID, jmethodID, and jfieldID +
  • jclass, jmethodID, and jfieldID
  • Local vs. Global References
  • @@ -79,7 +79,7 @@ header file requires "#ifdef __cplusplus", you may have to do some extra work if that header refers to JNIEnv.)

    -

    jclassID, jmethodID, and jfieldID

    +

    jclass, jmethodID, and jfieldID

    If you want to access an object's field from native code, you would do the following:

    @@ -104,7 +104,8 @@ to store this data in a static local structure.

    The class references, field IDs, and method IDs are guaranteed valid until the class is unloaded. Classes are only unloaded if all classes associated with a ClassLoader can be garbage collected, -which is rare but will not be impossible in our system. The jclassID +which is rare but will not be impossible in our system. Note however that +the jclass is a class reference and must be protected with a call to NewGlobalRef (see the next section).

    @@ -145,23 +146,40 @@ for an example in our source tree.) Every object that JNI returns is a "local reference". This means that it's valid for the duration of the current native method in the current thread. Even if the object itself continues to live on after the native method returns, the reference is not valid. -This applies to all sub-classes of jobject, including jclass and jarray. -(Dalvik VM will warn you about this when -Xcheck:jni is enabled.) +This applies to all sub-classes of jobject, including +jclass, jstring, and jarray. +(Dalvik VM will warn you about most reference mis-uses when extended JNI +checks are enabled.)

    -If you want to hold on to a reference for a longer period, you must use a "global" reference. -The NewGlobalRef function takes the local reference as -an argument and returns a global one: +If you want to hold on to a reference for a longer period, you must use +a "global" reference. The NewGlobalRef function takes the +local reference as an argument and returns a global one. +The global reference is guaranteed to be valid until you call +DeleteGlobalRef. -

    jobject* localRef = [...];
    -jobject* globalRef;
    -globalRef = env->NewGlobalRef(localRef);
    +

    +This pattern is commonly used when caching copies of class objects obtained +from FindClass, e.g.: +

    jclass* localClass = env->FindClass("MyClass");
    +jclass* globalClass = (jclass*) env->NewGlobalRef(localClass);
     
    -The global reference is guaranteed to be valid until you call -DeleteGlobalRef.

    All JNI methods accept both local and global references as arguments. +It's possible for references to the same object to have different values; +for example, the return values from consecutive calls to +NewGlobalRef on the same object may be different. +To see if two references refer to the same object, +you must use the IsSameObject function. Never compare +references with "==" in native code. +

    +One consequence of this is that you +must not assume object references are constant or unique +in native code. The 32-bit value representing an object may be different +from one invocation of a method to the next, and it's possible that two +different objects could have the same 32-bit value on consecutive calls. Do +not use jobject values as keys.

    Programmers are required to "not excessively allocate" local references. In practical terms this means that if you're creating large numbers of local references, perhaps while running through an array of @@ -179,8 +197,8 @@ and GetByteArrayElements are also not objects. One unusual case deserves separate mention. If you attach a native thread to the VM with AttachCurrentThread, the code you are running will never "return" to the VM until the thread detaches from the VM. Any local -references you create will have to be deleted manually unless the thread -is about to exit or detach. +references you create will have to be deleted manually unless you're going +to detach the thread soon.

    @@ -201,9 +219,10 @@ so you need to hang on to the string length as well as the string pointer.

    -Don't forget to Release the strings you Get. The string functions return jchar* or jbyte*, which -are pointers to primitive types rather than local references. They are -guaranteed valid until Release is called, which means they are not +Don't forget to Release the strings you Get. The +string functions return jchar* or jbyte*, which +are C-style pointers to primitive data rather than local references. They +are guaranteed valid until Release is called, which means they are not released when the native method returns.

    @@ -305,10 +324,10 @@ We can accomplish the same thing with this:

    This accomplishes the same thing, with several advantages:

      -
    • Requires one JNI call instead of 3, reducing overhead. +
    • Requires one JNI call instead of 2, reducing overhead.
    • Doesn't require pinning or extra data copies. -
    • Reduces the risk of programmer error -- no need to match up - Get and Release calls. +
    • Reduces the risk of programmer error -- no risk of forgetting + to call Release after something fails.

    Similarly, you can use the Set<Type>ArrayRegion call @@ -366,7 +385,7 @@ hand to printf or a LOG macro.

    Extended Checking

    -JNI does very little error checking. Calling SetFieldInt +JNI does very little error checking. Calling SetIntField on an Object field will succeed, even if the field is marked private and final. The goal is to minimize the overhead on the assumption that, if you've written it in native code, @@ -377,7 +396,7 @@ puts a different table of functions into the JavaVM and JNIEnv pointers. These an extended series of checks before calling the standard implementation.

    -Some things that may be verified: +Some things that may be checked:

      @@ -420,6 +439,7 @@ flag. Currently supported values include: are over-allocated and surrounded with a guard pattern to help identify code writing outside the buffer, and the contents are erased before the storage is freed to trip up code that uses the data after calling Release. +This will have a noticeable performance impact on some applications.
      warnonly
      By default, JNI "warnings" cause the VM to abort. With this flag it continues on. @@ -434,7 +454,10 @@ You can load native code from shared libraries with the standard preferred way to get at your native code is:

        -
      • Call System.loadLibrary() from a static class initializer. (See the earlier example, where one is used to call nativeClassInit().) The argument is the "undecorated" library name, e.g. to load "libfubar.so" you would pass in "fubar". +
      • Call System.loadLibrary() from a static class +initializer. (See the earlier example, where one is used to call +nativeClassInit().) The argument is the "undecorated" +library name, e.g. to load "libfubar.so" you would pass in "fubar".
      • Provide a native function: jint JNI_OnLoad(JavaVM* vm, void* reserved) @@ -462,17 +485,27 @@ written in C:

    You can also call System.load() with the full path name of the -shared library. For Android apps, you can get the full path to the -application's private data storage area from the context object. +shared library. For Android apps, you may find it useful to get the full +path to the application's private data storage area from the context object.

    -Dalvik does support "discovery" of native methods that are named in a +This is the recommended approach, but not the only approach. The VM does +not require explicit registration, nor that you provide a +JNI_OnLoad function. +You can instead use "discovery" of native methods that are named in a specific way (see - the JNI spec for details), but this is a less desirable -approach. It requires more space in the shared object symbol table, + the JNI spec for details), though this is less desirable. +It requires more space in the shared object symbol table, loading is slower because it requires string searches through all of the loaded shared libraries, and if a method signature is wrong you won't know about it until the first time the method is actually used.

    +One other note about JNI_OnLoad: any FindClass +calls you make from there will happen in the context of the class loader +that was used to load the shared library. Normally FindClass +uses the loader associated with the method at the top of the interpreted +stack, or if there isn't one (because the thread was just attached to +the VM) it uses the "system" class loader. +

    64-bit Considerations

    diff --git a/docs/opcodes/opcode-04-move-wide.html b/docs/opcodes/opcode-04-move-wide.html index 42202be39..e043be9d3 100644 --- a/docs/opcodes/opcode-04-move-wide.html +++ b/docs/opcodes/opcode-04-move-wide.html @@ -9,7 +9,7 @@ -

    move

    +

    move-wide

    Purpose

    diff --git a/docs/porting-guide.html b/docs/porting-guide.html new file mode 100644 index 000000000..b5ac387c7 --- /dev/null +++ b/docs/porting-guide.html @@ -0,0 +1,356 @@ + + + Dalvik Porting Guide + + + +

    Dalvik Porting Guide

    + +

    +The Dalvik virtual machine is intended to run on a variety of platforms. +The baseline system is expected to be a variant of UNIX (Linux, BSD, Mac +OS X) running the GNU C compiler. Little-endian CPUs have been exercised +the most heavily, but big-endian systems are explicitly supported. +

    +There are two general categories of work: porting to a Linux system +with a previously unseen CPU architecture, and porting to a different +operating system. This document covers the former. +

    +Basic familiarity with the Android platform, source code structure, and +build system is assumed. +

    + + +

    Core Libraries

    + +

    +The native code in the core libraries (chiefly dalvik/libcore, +but also dalvik/vm/native) is written in C/C++ and is expected +to work without modification in a Linux environment. Much of the code +comes directly from the Apache Harmony project. +

    +The core libraries pull in code from many other projects, including +OpenSSL, zlib, and ICU. These will also need to be ported before the VM +can be used. +

    + + +

    JNI Call Bridge

    + +

    +Most of the Dalvik VM runtime is written in portable C. The one +non-portable component of the runtime is the JNI call bridge. Simply put, +this converts an array of integers into function arguments of various +types, and calls a function. This must be done according to the C calling +conventions for the platform. The task could be as simple as pushing all +of the arguments onto the stack, or involve complex rules for register +assignment and stack alignment. +

    +To ease porting to new platforms, the +open-source FFI library (Foreign Function Interface) is used when a +custom bridge is unavailable. FFI is not as fast as a native implementation, +and the optional performance improvements it does offer are not used, so +writing a replacement is a good first step. +

    +The code lives in dalvik/vm/arch/*, with the FFI-based version +in the "generic" directory. There are two source files for each architecture. +One defines the call bridge itself: +

    +void dvmPlatformInvoke(void* pEnv, ClassObject* clazz, int argInfo, +int argc, const u4* argv, const char* signature, void* func, +JValue* pReturn) +

    +This will invoke a C/C++ function declared: +

    + return_type func(JNIEnv* pEnv, Object* this [, args])
    +
    or (for a "static" method):
    + return_type func(JNIEnv* pEnv, ClassObject* clazz [, args]) +

    +The role of dvmPlatformInvoke is to convert the values in +argv into C-style calling conventions, call the method, and +then place the return type into pReturn (a union that holds +all of the basic JNI types). The code may use the method signature +(a DEX "shorty" signature, with one character for the return type and one +per argument) to determine how to handle the values. +

    +The other source file involved here defines a 32-bit "hint". The hint +is computed when the method's class is loaded, and passed in as the +"argInfo" argument. The hint can be used to avoid scanning the ASCII +method signature for things like the return value, total argument size, +or inter-argument 64-bit alignment restrictions. + + +

    Interpreter

    + +

    +The Dalvik runtime includes two interpreters, labeled "portable" and "fast". +The portable interpreter is largely contained within a single C function, +and should compile on any system that supports gcc. (If you don't have gcc, +you may need to disable the "threaded" execution model, which relies on +gcc's "goto table" implementation; look for the THREADED_INTERP define.) +

    +The fast interpreter uses hand-coded assembly fragments. If none are +available for the current architecture, the build system will create an +interpreter out of C "stubs". The resulting "all stubs" interpreter is +quite a bit slower than the portable interpreter, making "fast" something +of a misnomer. +

    +The fast interpreter is enabled by default. On platforms without native +support, you may want to switch to the portable interpreter. This can +be controlled with the dalvik.vm.execution-mode system +property. For example, if you: +

    +adb shell "echo dalvik.vm.execution-mode = int:portable >> /data/local.prop" +

    +and reboot, the Android app framework will start the VM with the portable +interpreter enabled. +

    + + +

    Mterp Interpreter Structure

    + +

    +There may be significant performance advantages to rewriting the +interpreter core in assembly language, using architecture-specific +optimizations. In Dalvik this can be done one instruction at a time. +

    +The simplest way to implement an interpreter is to have a large "switch" +statement. After each instruction is handled, the interpreter returns to +the top of the loop, fetches the next instruction, and jumps to the +appropriate label. +

    +An improvement on this is called "threaded" execution. The instruction +fetch and dispatch are included at the end of every instruction handler. +This makes the interpreter a little larger overall, but you get to avoid +the (potentially expensive) branch back to the top of the switch statement. +

    +Dalvik mterp goes one step further, using a computed goto instead of a goto +table. Instead of looking up the address in a table, which requires an +extra memory fetch on every instruction, mterp multiplies the opcode number +by a fixed value. By default, each handler is allowed 64 bytes of space. +

    +Not all handlers fit in 64 bytes. Those that don't can have subroutines +or simply continue on to additional code outside the basic space. Some of +this is handled automatically by Dalvik, but there's no portable way to detect +overflow of a 64-byte handler until the VM starts executing. +

    +The choice of 64 bytes is somewhat arbitrary, but has worked out well for +ARM and x86. +

    +In the course of development it's useful to have C and assembly +implementations of each handler, and be able to flip back and forth +between them when hunting problems down. In mterp this is relatively +straightforward. You can always see the files being fed to the compiler +and assembler for your platform by looking in the +dalvik/vm/mterp/out directory. +

    +The interpreter sources live in dalvik/vm/mterp. If you +haven't yet, you should read dalvik/vm/mterp/README.txt now. +

    + + +

    Getting Started With Mterp

    + +

    +Getting started: +

      +
    1. Decide on the name of your architecture. For the sake of discussion, +let's call it myarch. +
    2. Make a copy of dalvik/vm/mterp/config-allstubs to +dalvik/vm/mterp/config-myarch. +
    3. Create a dalvik/vm/mterp/myarch directory to hold your +source files. +
    4. Add myarch to the list in +dalvik/vm/mterp/rebuild.sh. +
    5. Make sure dalvik/vm/Android.mk will find the files for +your architecture. If $(TARGET_ARCH) is configured this +will happen automatically. +
    +

    +You now have the basic framework in place. Whenever you make a change, you +need to perform two steps: regenerate the mterp output, and build the +core VM library. (It's two steps because we didn't want the build system +to require Python 2.5. Which, incidentally, you need to have.) +

      +
    1. In the dalvik/vm/mterp directory, regenerate the contents +of the files in dalvik/vm/mterp/out by executing +./rebuild.sh. Note there are two files, one in C and one +in assembly. +
    2. In the dalvik directory, regenerate the +libdvm.so library with mm. You can also use +make libdvm from the top of the tree. +
    +

    +This will leave you with an updated libdvm.so, which can be pushed out to +a device with adb sync or adb push. If you're +using the emulator, you need to add make snod (System image, +NO Dependency check) to rebuild the system image file. You should not +need to do a top-level "make" and rebuild the dependent binaries. +

    +At this point you have an "all stubs" interpreter. You can see how it +works by examining dalvik/vm/mterp/cstubs/entry.c. The +code runs in a loop, pulling out the next opcode, and invoking the +handler through a function pointer. Each handler takes a "glue" argument +that contains all of the useful state. +

    +Your goal is to replace the entry method, exit method, and each individual +instruction with custom implementations. The first thing you need to do +is create an entry function that calls the handler for the first instruction. +After that, the instructions chain together, so you don't need a loop. +(Look at the ARM or x86 implementation to see how they work.) +

    +Once you have that, you need something to jump to. You can't branch +directly to the C stub because it's expecting to be called with a "glue" +argument and then return. We need a C stub "wrapper" that does the +setup and jumps directly to the next handler. We write this in assembly +and then add it to the config file definition. +

    +To see how this works, create a file called +dalvik/vm/mterp/myarch/stub.S that contains one line: +

    +/* stub for ${opcode} */
    +
    +Then, in dalvik/vm/mterp/config-myarch, add this below the +handler-size directive: +
    +# source for the instruction table stub
    +asm-stub myarch/stub.S
    +
    +

    +Regenerate the sources with ./rebuild.sh, and take a look +inside dalvik/vm/mterp/out/InterpAsm-myarch.S. You should +see 256 copies of the stub function in a single large block after the +dvmAsmInstructionStart label. The stub.S +code will be used anywhere you don't provide an assembly implementation. +

    +Note that each block begins with a .balign 64 directive. +This is what pads each handler out to 64 bytes. Note also that the +${opcode} text changed into an opcode name, which should +be used to call the C implementation (dvmMterp_${opcode}). +

    +The actual contents of stub.S are up to you to define. +See entry.S and stub.S in the armv5te +or x86 directories for working examples. +

    +If you're working on a variation of an existing architecture, you may be +able to use most of the existing code and just provide replacements for +a few instructions. Look at the armv4t implementation as +an example. +

    + + +

    Replacing Stubs

    + +

    +There are roughly 230 Dalvik opcodes, including some that are inserted by +dexopt and aren't described in the +Dalvik bytecode documentation. Each +one must perform the appropriate actions, fetch the next opcode, and +branch to the next handler. The actions performed by the assembly version +must exactly match those performed by the C version (in +dalvik/vm/mterp/c/OP_*). +

    +It is possible to customize the set of "optimized" instructions for your +platform. This is possible because optimized DEX files are not expected +to work on multiple devices. Adding, removing, or redefining instructions +is beyond the scope of this document, and for simplicity it's best to stick +with the basic set defined by the portable interpreter. +

    +Once you have written a handler that looks like it should work, add +it to the config file. For example, suppose we have a working version +of OP_NOP. For demonstration purposes, fake it for now by +putting this into dalvik/vm/mterp/myarch/OP_NOP.S: +

    +/* This is my NOP handler */
    +
    +

    +Then, in the op-start section of config-myarch, add: +

    +    op OP_NOP myarch
    +
    +

    +This tells the generation script to use the assembly version from the +myarch directory instead of the C version from the c +directory. +

    +Execute ./rebuild.sh. Look at InterpAsm-myarch.S +and InterpC-myarch.c in the out directory. You +will see that the OP_NOP stub wrapper has been replaced with our +new code in the assembly file, and the C stub implementation is no longer +included. +

    +As you implement instructions, the C version and corresponding stub wrapper +will disappear from the output files. Eventually you will have a 100% +assembly interpreter. You may find it saves a little time to examine +the output of your compiler for some of the operations. The +porting-proto.c sample code can be +helpful here. +

    + + +

    Interpreter Switching

    + +

    +The Dalvik VM actually includes a third interpreter implementation: the debug +interpreter. This is a variation of the portable interpreter that includes +support for debugging and profiling. +

    +When a debugger attaches, or a profiling feature is enabled, the VM +will switch interpreters at a convenient point. This is done at the +same time as the GC safe point check: on a backward branch, a method +return, or an exception throw. Similarly, when the debugger detaches +or profiling is discontinued, execution transfers back to the "fast" or +"portable" interpreter. +

    +Your entry function needs to test the "entryPoint" value in the "glue" +pointer to determine where execution should begin. Your exit function +will need to return a boolean that indicates whether the interpreter is +exiting (because we reached the "bottom" of a thread stack) or wants to +switch to the other implementation. +

    +See the entry.S file in x86 or armv5te +for examples. +

    + + +

    Testing

    + +

    +A number of VM tests can be found in dalvik/tests. The most +useful during interpreter development is 003-omnibus-opcodes, +which tests many different instructions. +

    +The basic invocation is: +

    +$ cd dalvik/tests
    +$ ./run-test 003
    +
    +

    +This will run test 003 on an attached device or emulator. You can run +the test against your desktop VM by specifying --reference +if you suspect the test may be faulty. You can also use +--portable and --fast to explictly specify +one Dalvik interpreter or the other. +

    +Some instructions are replaced by dexopt, notably when +"quickening" field accesses and method invocations. To ensure +that you are testing the basic form of the instruction, add the +--no-optimize option. +

    +There is no in-built instruction tracing mechanism. If you want +to know for sure that your implementation of an opcode handler +is being used, the easiest approach is to insert a "printf" +call. For an example, look at common_squeak in +dalvik/vm/mterp/armv5te/footer.S. +

    +At some point you need to ensure that debuggers and profiling work with +your interpreter. The easiest way to do this is to simply connect a +debugger or toggle profiling. (A future test suite may include some +tests for this.) +

    + +

    +

    Copyright © 2009 The Android Open Source Project
    + + + diff --git a/docs/porting-proto.c.txt b/docs/porting-proto.c.txt new file mode 100644 index 000000000..2f72bacdb --- /dev/null +++ b/docs/porting-proto.c.txt @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Dalvik instruction fragments, useful when porting mterp. + * + * Compile this and examine the output to see what your compiler generates. + * This can give you a head start on some of the more complicated operations. + * + * Example: + * % gcc -c -O2 -save-temps -fverbose-asm porting-proto.c + * % less porting-proto.s + */ +#include + +typedef int8_t s1; +typedef uint8_t u1; +typedef int16_t s2; +typedef uint16_t u2; +typedef int32_t s4; +typedef uint32_t u4; +typedef int64_t s8; +typedef uint64_t u8; + +s4 iadd32(s4 x, s4 y) { return x + y; } +s8 iadd64(s8 x, s8 y) { return x + y; } +float fadd32(float x, float y) { return x + y; } +double fadd64(double x, double y) { return x + y; } + +s4 isub32(s4 x, s4 y) { return x - y; } +s8 isub64(s8 x, s8 y) { return x - y; } +float fsub32(float x, float y) { return x - y; } +double fsub64(double x, double y) { return x - y; } + +s4 irsub32lit8(s4 x) { return 25 - x; } + +s4 imul32(s4 x, s4 y) { return x * y; } +s8 imul64(s8 x, s8 y) { return x * y; } +float fmul32(float x, float y) { return x * y; } +double fmul64(double x, double y) { return x * y; } + +s4 idiv32(s4 x, s4 y) { return x / y; } +s8 idiv64(s8 x, s8 y) { return x / y; } +float fdiv32(float x, float y) { return x / y; } +double fdiv64(double x, double y) { return x / y; } + +s4 irem32(s4 x, s4 y) { return x % y; } +s8 irem64(s8 x, s8 y) { return x % y; } + +s4 iand32(s4 x, s4 y) { return x & y; } +s8 iand64(s8 x, s8 y) { return x & y; } + +s4 ior32(s4 x, s4 y) { return x | y; } +s8 ior64(s8 x, s8 y) { return x | y; } + +s4 ixor32(s4 x, s4 y) { return x ^ y; } +s8 ixor64(s8 x, s8 y) { return x ^ y; } + +s4 iasl32(s4 x, s4 count) { return x << (count & 0x1f); } +s8 iasl64(s8 x, s4 count) { return x << (count & 0x3f); } + +s4 iasr32(s4 x, s4 count) { return x >> (count & 0x1f); } +s8 iasr64(s8 x, s4 count) { return x >> (count & 0x3f); } + +s4 ilsr32(s4 x, s4 count) { return ((u4)x) >> (count & 0x1f); } // unsigned +s8 ilsr64(s8 x, s4 count) { return ((u8)x) >> (count & 0x3f); } // unsigned + +s4 ineg32(s4 x) { return -x; } +s8 ineg64(s8 x) { return -x; } +float fneg32(float x) { return -x; } +double fneg64(double x) { return -x; } + +s4 inot32(s4 x) { return x ^ -1; } +s8 inot64(s8 x) { return x ^ -1LL; } + +s4 float2int(float x) { return (s4) x; } +double float2double(float x) { return (double) x; } +s4 double2int(double x) { return (s4) x; } +float double2float(double x) { return (float) x; } + +/* + * ARM lib doesn't clamp large values or NaN the way we want on these two. + * If the simple version isn't correct, use the long version. (You can use + * dalvik/tests/041-narrowing to verify.) + */ +s8 float2long(float x) { return (s8) x; } +s8 float2long_clamp(float x) +{ + static const float kMaxLong = (float)0x7fffffffffffffffULL; + static const float kMinLong = (float)0x8000000000000000ULL; + + if (x >= kMaxLong) { + return 0x7fffffffffffffffULL; + } else if (x <= kMinLong) { + return 0x8000000000000000ULL; + } else if (x != x) { + return 0; + } else { + return (s8) x; + } +} +s8 double2long(double x) { return (s8) x; } +s8 double2long_clamp(double x) +{ + static const double kMaxLong = (double)0x7fffffffffffffffULL; + static const double kMinLong = (double)0x8000000000000000ULL; + + if (x >= kMaxLong) { + return 0x7fffffffffffffffULL; + } else if (x <= kMinLong) { + return 0x8000000000000000ULL; + } else if (x != x) { + return 0; + } else { + return (s8) x; + } +} + +s1 int2byte(s4 x) { return (s1) x; } +s2 int2short(s4 x) { return (s2) x; } +u2 int2char(s4 x) { return (u2) x; } +s8 int2long(s4 x) { return (s8) x; } +float int2float(s4 x) { return (float) x; } +double int2double(s4 x) { return (double) x; } + +s4 long2int(s8 x) { return (s4) x; } +float long2float(s8 x) { return (float) x; } +double long2double(s8 x) { return (double) x; } + +int cmpl_float(float x, float y) +{ + int result; + + if (x == y) + result = 0; + else if (x > y) + result = 1; + else /* (x < y) or NaN */ + result = -1; + return result; +} + +int cmpg_float(float x, float y) +{ + int result; + + if (x == y) + result = 0; + else if (x < y) + result = -1; + else /* (x > y) or NaN */ + result = 1; + return result; +} + +int cmpl_double(double x, double y) +{ + int result; + + if (x == y) + result = 0; + else if (x > y) + result = 1; + else /* (x < y) or NaN */ + result = -1; + return result; +} + +int cmpg_double(double x, double y) +{ + int result; + + if (x == y) + result = 0; + else if (x < y) + result = -1; + else /* (x > y) or NaN */ + result = 1; + return result; +} + +int cmp_long(s8 x, s8 y) +{ + int result; + + if (x == y) + result = 0; + else if (x < y) + result = -1; + else /* (x > y) */ + result = 1; + return result; +} + +/* instruction decoding fragments */ +u1 unsignedAA(u2 x) { return x >> 8; } +s1 signedAA(u2 x) { return (s4)(x << 16) >> 24; } +s2 signedBB(u2 x) { return (s2) x; } +u1 unsignedA(u2 x) { return (x >> 8) & 0x0f; } +u1 unsignedB(u2 x) { return x >> 12; } + +/* some handy immediate constants when working with float/double */ +u4 const_43e00000(u4 highword) { return 0x43e00000; } +u4 const_c3e00000(u4 highword) { return 0xc3e00000; } +u4 const_ffc00000(u4 highword) { return 0xffc00000; } +u4 const_41dfffff(u4 highword) { return 0x41dfffff; } +u4 const_c1e00000(u4 highword) { return 0xc1e00000; } + +/* + * Test for some gcc-defined symbols. If you're frequently switching + * between different cross-compiler architectures or CPU feature sets, + * this can help you keep track of which one you're compiling for. + */ +#ifdef __arm__ +# warning "found __arm__" +#endif +#ifdef __ARM_EABI__ +# warning "found __ARM_EABI__" +#endif +#ifdef __VFP_FP__ +# warning "found __VFP_FP__" /* VFP-format doubles used; may not have VFP */ +#endif +#if defined(__VFP_FP__) && !defined(__SOFTFP__) +# warning "VFP in use" +#endif +#ifdef __ARM_ARCH_5TE__ +# warning "found __ARM_ARCH_5TE__" +#endif +#ifdef __ARM_ARCH_7A__ +# warning "found __ARM_ARCH_7A__" +#endif + diff --git a/docs/verifier.html b/docs/verifier.html index 656b83273..022923bbc 100644 --- a/docs/verifier.html +++ b/docs/verifier.html @@ -33,10 +33,9 @@ Why do it all, when our system relies on UNIX processes for security? error cases because the verifier guarantees that they are impossible. Also, we can optimize the DEX file more aggressively if we start with a stronger set of assumptions about the bytecode. -
  • "Exact" GC. The work peformed during verification has significant - overlap with the work required to compute register use maps for exact - GC. Improper register use, caught by the verifier, could lead to - subtle problems with an "exact" GC. +
  • "Precise" GC. The work peformed during verification has significant + overlap with the work required to compute register use maps for + type-precise GC.
  • Intra-application security. If an app wants to download bits of interpreted code over the network and execute them, it can safely do so using well-established security mechanisms. @@ -84,6 +83,12 @@ There are also some new ones, such as: or filled-new-array instruction. +

    +The VM is permitted but not required to enforce "structured locking" +constraints, which are designed to ensure that, when a method returns, all +monitors locked by the method have been unlocked an equal number of times. +This is not currently implemented. +

    The Dalvik verifier is more restrictive than other VMs in one area: type safety on sub-32-bit integer widths. These additional restrictions @@ -94,24 +99,31 @@ should make it impossible to, say, pass a value outside the range

    Verification Failures

    -When the verifier rejects a class, it always throws a VerifyError. -This is different in some cases from other implementations. For example, -if a class attempts to perform an illegal access on a field, the expected -behavior is to receive an IllegalAccessError at runtime the first time -the field is actually accessed. The Dalvik verifier will reject the -entire class immediately. +The verifier may reject a class immediately, or it may defer throwing +an exception until the code is actually used. For example, if a class +attempts to perform an illegal access on a field, the VM should throw +an IllegalAccessError the first time the instruction is encountered. +On the other hand, if a class contains an invalid bytecode, it should be +rejected immediately with a VerifyError.

    -It's difficult to throw the error on first use in Dalvik. Possible ways -to implement this behavior include: +Immediate VerifyErrors are accompanied by detailed, if somewhat cryptic, +information in the log file. From this it's possible to determine the +exact instruction that failed, and the reason for the failure. + +

    +It's a bit tricky to implement deferred verification errors in Dalvik. +A few approaches were considered:

    1. We could replace the invalid field access instruction with a special instruction that generates an illegal access error, and allow class verification to complete successfully. This type of verification must -often be deferred to first class load, rather than be performed ahead of time -during DEX optimization, which means the bytecode instructions will be -mapped read-only during verification. So this won't work. +be deferred to first class load, rather than be performed ahead of time +during DEX optimization, because some failures will depend on the current +execution environment (e.g. not all classes are available at dexopt time). +At that point the bytecode instructions are mapped read-only during +verification, so rewriting them isn't possible.
    2. We can perform the access checks when the field/method/class is @@ -120,7 +132,7 @@ entry is resolved in the context of the current classfile, but our DEX files combine multiple classfiles together, merging the field/method/class resolution results into a single large table. Once one class successfully resolves the field, every other class in the same DEX file would be able -to access the field. This is bad. +to access the field. This is incorrect.
    3. Perform the access checks on every field/method/class access. @@ -134,25 +146,29 @@ and we don't currently have an optimized form of certain instructions

    -Other implementations are possible, but they all involve allocating -some amount of additional memory or spending additional cycles -on non-DEX-optimized instructions. We don't want to throw an -IllegalAccessError at verification time, since that would indicate that -access to the class being verified was illegal. +In early versions of Dalvik (as found in Android 1.6 and earlier), the verifier +simply regarded all problems as immediately fatal. This generally worked, +but in some cases the VM was rejecting classes because of bits of code +that were never used. The VerifyError itself was sometimes difficult to +decipher, because it was thrown during verification rather than at the +point where the problem was first noticed during execution.

    -One approach that might be worth pursuing: for situations like illegal -accesses, the verifier makes an in-RAM private copy of the method, and -alters the instructions there. The class object is altered to point at -the new copy of the instructions. This requires minimal memory overhead -and provides a better experience for developers. +The current version uses a variation of approach #1. The dexopt +command works the way it did before, leaving the code untouched and +flagging fully-correct classes as "pre-verified". When the VM loads a +class that didn't pass pre-verification, the verifier is invoked. If a +"deferrable" problem is detected, a modifiable copy of the instructions +in the problematic method is made. In that copy, the troubled instruction +is replaced with an "always throw" opcode, and verification continues.

    -The VerifyError is accompanied by detailed, if somewhat cryptic, -information in the log file. From this it's possible to determine the -exact instruction that failed, and the reason for the failure. We can -also constructor the VerifyError with an IllegalAccessError passed in as -the cause. +In the example used earlier, an attempt to read from an inaccessible +field would result in the "field get" instruction being replaced by +"always throw IllegalAccessError on field X". Creating copies of method +bodies requires additional heap space, but since this affects very few +methods overall the memory impact should be minor. +

    Copyright © 2008 The Android Open Source Project
    diff --git a/dx/README.txt b/dx/README.txt index 5421e7ba5..6a20c826e 100644 --- a/dx/README.txt +++ b/dx/README.txt @@ -1,2 +1,3 @@ Home of Dalvik eXchange, the thing that takes in class files and -reformulates them for consumption in the VM. +reformulates them for consumption in the VM. It also does a few other +things; use "dx --help" to see a modicum of self-documentation. diff --git a/dx/etc/dx b/dx/etc/dx index dae587490..f773495f5 100644 --- a/dx/etc/dx +++ b/dx/etc/dx @@ -36,42 +36,50 @@ cd "${oldwd}" jarfile=dx.jar libdir="$progdir" -if [ ! -r "$libdir/$jarfile" ] -then +if [ ! -r "$libdir/$jarfile" ]; then libdir=`dirname "$progdir"`/tools/lib fi -if [ ! -r "$libdir/$jarfile" ] -then + +if [ ! -r "$libdir/$jarfile" ]; then libdir=`dirname "$progdir"`/framework fi -if [ ! -r "$libdir/$jarfile" ] -then + +if [ ! -r "$libdir/$jarfile" ]; then echo `basename "$prog"`": can't find $jarfile" exit 1 fi -javaOpts="" +# By default, give dx a max heap size of 1 gig. This can be overridden +# by using a "-J" option (see below). +defaultMx="-Xmx1024M" + +# The following will extract any initial parameters of the form +# "-J" from the command line and pass them to the Java +# invocation (instead of to dx). This makes it possible for you to add +# a command-line parameter such as "-JXmx256M" in your scripts, for +# example. "java" (with no args) and "java -X" give a summary of +# available options. -# If you want DX to have more memory when executing, uncomment the following -# line and adjust the value accordingly. Use "java -X" for a list of options -# you can pass here. -# -# javaOpts="-Xmx256M" +javaOpts="" -# Alternatively, this will extract any parameter "-Jxxx" from the command line -# and pass them to Java (instead of to dx). This makes it possible for you to -# add a command-line parameter such as "-JXmx256M" in your ant scripts, for -# example. while expr "x$1" : 'x-J' >/dev/null; do - opt=`expr "$1" : '-J\(.*\)'` + opt=`expr "x$1" : 'x-J\(.*\)'` javaOpts="${javaOpts} -${opt}" + if expr "x${opt}" : "xXmx[0-9]" >/dev/null; then + defaultMx="no" + fi shift done -if [ "$OSTYPE" = "cygwin" ] ; then - jarpath=`cygpath -w "$libdir/$jarfile"` +if [ "${defaultMx}" != "no" ]; then + javaOpts="${javaOpts} ${defaultMx}" +fi + +if [ "$OSTYPE" = "cygwin" ]; then + # For Cygwin, convert the jarfile path into native Windows style. + jarpath=`cygpath -w "$libdir/$jarfile"` else - jarpath="$libdir/$jarfile" + jarpath="$libdir/$jarfile" fi exec java $javaOpts -jar "$jarpath" "$@" diff --git a/dx/etc/dx.bat b/dx/etc/dx.bat index 2686d6a3b..1361bb643 100755 --- a/dx/etc/dx.bat +++ b/dx/etc/dx.bat @@ -40,12 +40,47 @@ if exist %frameworkdir%%jarfile% goto JarFileOk set jarpath=%frameworkdir%%jarfile% set javaOpts= +set args= -REM If you want DX to have more memory when executing, uncomment the -REM following line and adjust the value accordingly. Use "java -X" for -REM a list of options you can pass here. -REM -REM set javaOpts=-Xmx256M +REM By default, give dx a max heap size of 1 gig. This can be overridden +REM by using a "-JXmx..." option (see below). +set defaultMx=-Xmx1024M -call java %javaOpts% -Djava.ext.dirs=%frameworkdir% -jar %jarpath% %* +REM capture all arguments to process them below +set params=%* + +:nextArg +if "%params%"=="" goto endArgs + REM Note: advanced substitions don't work on %1..%N. We need to assign to + REM a variable first. + REM We also can't use %1..%N directly because an option such as --output=name + REM gets automagically converted into %1=--output and %2=name (yes, really!) + REM Instead we manually extract the first token from the params variable. + for /F "tokens=1*" %%a in ("%params%") do call :getArg "%%a" "%%b" + + if "%defaultMx%"=="" goto notXmx + if "%A:~0,5%" NEQ "-JXmx" goto notXmx + set defaultMx= + :notXmx + + if "%A:~0,2%" NEQ "-J" goto notJ + set javaOpts=%javaOpts% -%A:~2% + goto nextArg + + :notJ + set args=%args% %A% + goto nextArg + +:getArg + REM this subroutine is called by the for /F with the first argument of params + REM and the rest of the line. The "goto :eof" actually exits the subroutine. + set A=%~1 + set params=%~2 + goto :eof + +:endArgs + +set javaOpts=%javaOpts% %defaultMx% + +call java %javaOpts% -Djava.ext.dirs=%frameworkdir% -jar %jarpath% %args% diff --git a/dx/src/com/android/dx/Version.java b/dx/src/com/android/dx/Version.java index 02dc7b248..4950d39fe 100644 --- a/dx/src/com/android/dx/Version.java +++ b/dx/src/com/android/dx/Version.java @@ -20,6 +20,6 @@ package com.android.dx; * Version number for dx. */ public class Version { - /** non-null; version string */ - public static final String VERSION = "1.2"; + /** {@code non-null;} version string */ + public static final String VERSION = "1.3"; } diff --git a/dx/src/com/android/dx/cf/attrib/AttAnnotationDefault.java b/dx/src/com/android/dx/cf/attrib/AttAnnotationDefault.java index 12e1f74ba..acf5a9ef7 100644 --- a/dx/src/com/android/dx/cf/attrib/AttAnnotationDefault.java +++ b/dx/src/com/android/dx/cf/attrib/AttAnnotationDefault.java @@ -19,24 +19,24 @@ package com.android.dx.cf.attrib; import com.android.dx.rop.cst.Constant; /** - * Attribute class for AnnotationDefault attributes. + * Attribute class for {@code AnnotationDefault} attributes. */ public final class AttAnnotationDefault extends BaseAttribute { - /** non-null; attribute name for attributes of this type */ + /** {@code non-null;} attribute name for attributes of this type */ public static final String ATTRIBUTE_NAME = "AnnotationDefault"; - /** non-null; the annotation default value */ + /** {@code non-null;} the annotation default value */ private final Constant value; - /** >= 0; attribute data length in the original classfile (not + /** {@code >= 0;} attribute data length in the original classfile (not * including the attribute header) */ private final int byteLength; /** * Constructs an instance. * - * @param value non-null; the annotation default value - * @param byteLength >= 0; attribute data length in the original + * @param value {@code non-null;} the annotation default value + * @param byteLength {@code >= 0;} attribute data length in the original * classfile (not including the attribute header) */ public AttAnnotationDefault(Constant value, int byteLength) { @@ -59,7 +59,7 @@ public final class AttAnnotationDefault extends BaseAttribute { /** * Gets the annotation default value. * - * @return non-null; the value + * @return {@code non-null;} the value */ public Constant getValue() { return value; diff --git a/dx/src/com/android/dx/cf/attrib/AttCode.java b/dx/src/com/android/dx/cf/attrib/AttCode.java index f00da2fde..89ba895ad 100644 --- a/dx/src/com/android/dx/cf/attrib/AttCode.java +++ b/dx/src/com/android/dx/cf/attrib/AttCode.java @@ -22,35 +22,35 @@ import com.android.dx.cf.iface.AttributeList; import com.android.dx.util.MutabilityException; /** - * Attribute class for standard Code attributes. + * Attribute class for standard {@code Code} attributes. */ public final class AttCode extends BaseAttribute { - /** non-null; attribute name for attributes of this type */ + /** {@code non-null;} attribute name for attributes of this type */ public static final String ATTRIBUTE_NAME = "Code"; - /** >= 0; the stack size */ + /** {@code >= 0;} the stack size */ private final int maxStack; - /** >= 0; the number of locals */ + /** {@code >= 0;} the number of locals */ private final int maxLocals; - /** non-null; array containing the bytecode per se */ + /** {@code non-null;} array containing the bytecode per se */ private final BytecodeArray code; - /** non-null; the exception table */ + /** {@code non-null;} the exception table */ private final ByteCatchList catches; - /** non-null; the associated list of attributes */ + /** {@code non-null;} the associated list of attributes */ private final AttributeList attributes; /** * Constructs an instance. * - * @param maxStack >= 0; the stack size - * @param maxLocals >= 0; the number of locals - * @param code non-null; array containing the bytecode per se - * @param catches non-null; the exception table - * @param attributes non-null; the associated list of attributes + * @param maxStack {@code >= 0;} the stack size + * @param maxLocals {@code >= 0;} the number of locals + * @param code {@code non-null;} array containing the bytecode per se + * @param catches {@code non-null;} the exception table + * @param attributes {@code non-null;} the associated list of attributes */ public AttCode(int maxStack, int maxLocals, BytecodeArray code, ByteCatchList catches, AttributeList attributes) { @@ -101,7 +101,7 @@ public final class AttCode extends BaseAttribute { /** * Gets the maximum stack size. * - * @return >= 0; the maximum stack size + * @return {@code >= 0;} the maximum stack size */ public int getMaxStack() { return maxStack; @@ -110,7 +110,7 @@ public final class AttCode extends BaseAttribute { /** * Gets the number of locals. * - * @return >= 0; the number of locals + * @return {@code >= 0;} the number of locals */ public int getMaxLocals() { return maxLocals; @@ -119,7 +119,7 @@ public final class AttCode extends BaseAttribute { /** * Gets the bytecode array. * - * @return non-null; the bytecode array + * @return {@code non-null;} the bytecode array */ public BytecodeArray getCode() { return code; @@ -128,7 +128,7 @@ public final class AttCode extends BaseAttribute { /** * Gets the exception table. * - * @return non-null; the exception table + * @return {@code non-null;} the exception table */ public ByteCatchList getCatches() { return catches; @@ -137,7 +137,7 @@ public final class AttCode extends BaseAttribute { /** * Gets the associated attribute list. * - * @return non-null; the attribute list + * @return {@code non-null;} the attribute list */ public AttributeList getAttributes() { return attributes; diff --git a/dx/src/com/android/dx/cf/attrib/AttConstantValue.java b/dx/src/com/android/dx/cf/attrib/AttConstantValue.java index a84da4342..a7436f3c7 100644 --- a/dx/src/com/android/dx/cf/attrib/AttConstantValue.java +++ b/dx/src/com/android/dx/cf/attrib/AttConstantValue.java @@ -24,22 +24,22 @@ import com.android.dx.rop.cst.CstString; import com.android.dx.rop.cst.TypedConstant; /** - * Attribute class for standard ConstantValue attributes. + * Attribute class for standard {@code ConstantValue} attributes. */ public final class AttConstantValue extends BaseAttribute { - /** non-null; attribute name for attributes of this type */ + /** {@code non-null;} attribute name for attributes of this type */ public static final String ATTRIBUTE_NAME = "ConstantValue"; - /** non-null; the constant value */ + /** {@code non-null;} the constant value */ private final TypedConstant constantValue; /** * Constructs an instance. * - * @param constantValue non-null; the constant value, which must - * be an instance of one of: CstString, - * CstInteger, CstLong, - * CstFloat, or CstDouble + * @param constantValue {@code non-null;} the constant value, which must + * be an instance of one of: {@code CstString}, + * {@code CstInteger}, {@code CstLong}, + * {@code CstFloat}, or {@code CstDouble} */ public AttConstantValue(TypedConstant constantValue) { super(ATTRIBUTE_NAME); @@ -65,11 +65,11 @@ public final class AttConstantValue extends BaseAttribute { /** * Gets the constant value of this instance. The returned value - * is an instance of one of: CstString, - * CstInteger, CstLong, - * CstFloat, or CstDouble. + * is an instance of one of: {@code CstString}, + * {@code CstInteger}, {@code CstLong}, + * {@code CstFloat}, or {@code CstDouble}. * - * @return non-null; the constant value + * @return {@code non-null;} the constant value */ public TypedConstant getConstantValue() { return constantValue; diff --git a/dx/src/com/android/dx/cf/attrib/AttDeprecated.java b/dx/src/com/android/dx/cf/attrib/AttDeprecated.java index cd1dd2463..d440aae71 100644 --- a/dx/src/com/android/dx/cf/attrib/AttDeprecated.java +++ b/dx/src/com/android/dx/cf/attrib/AttDeprecated.java @@ -17,10 +17,10 @@ package com.android.dx.cf.attrib; /** - * Attribute class for standard Deprecated attributes. + * Attribute class for standard {@code Deprecated} attributes. */ public final class AttDeprecated extends BaseAttribute { - /** non-null; attribute name for attributes of this type */ + /** {@code non-null;} attribute name for attributes of this type */ public static final String ATTRIBUTE_NAME = "Deprecated"; /** diff --git a/dx/src/com/android/dx/cf/attrib/AttEnclosingMethod.java b/dx/src/com/android/dx/cf/attrib/AttEnclosingMethod.java index 7cccad703..68a24d94b 100644 --- a/dx/src/com/android/dx/cf/attrib/AttEnclosingMethod.java +++ b/dx/src/com/android/dx/cf/attrib/AttEnclosingMethod.java @@ -20,24 +20,24 @@ import com.android.dx.rop.cst.CstNat; import com.android.dx.rop.cst.CstType; /** - * Attribute class for standards-track EnclosingMethod + * Attribute class for standards-track {@code EnclosingMethod} * attributes. */ public final class AttEnclosingMethod extends BaseAttribute { - /** non-null; attribute name for attributes of this type */ + /** {@code non-null;} attribute name for attributes of this type */ public static final String ATTRIBUTE_NAME = "EnclosingMethod"; - /** non-null; the innermost enclosing class */ + /** {@code non-null;} the innermost enclosing class */ private final CstType type; - /** null-ok; the name-and-type of the innermost enclosing method, if any */ + /** {@code null-ok;} the name-and-type of the innermost enclosing method, if any */ private final CstNat method; /** * Constructs an instance. * - * @param type non-null; the innermost enclosing class - * @param method null-ok; the name-and-type of the innermost enclosing + * @param type {@code non-null;} the innermost enclosing class + * @param method {@code null-ok;} the name-and-type of the innermost enclosing * method, if any */ public AttEnclosingMethod(CstType type, CstNat method) { @@ -59,7 +59,7 @@ public final class AttEnclosingMethod extends BaseAttribute { /** * Gets the innermost enclosing class. * - * @return non-null; the innermost enclosing class + * @return {@code non-null;} the innermost enclosing class */ public CstType getEnclosingClass() { return type; @@ -69,7 +69,7 @@ public final class AttEnclosingMethod extends BaseAttribute { * Gets the name-and-type of the innermost enclosing method, if * any. * - * @return null-ok; the name-and-type of the innermost enclosing + * @return {@code null-ok;} the name-and-type of the innermost enclosing * method, if any */ public CstNat getMethod() { diff --git a/dx/src/com/android/dx/cf/attrib/AttExceptions.java b/dx/src/com/android/dx/cf/attrib/AttExceptions.java index 59e624ef8..c592047f3 100644 --- a/dx/src/com/android/dx/cf/attrib/AttExceptions.java +++ b/dx/src/com/android/dx/cf/attrib/AttExceptions.java @@ -20,20 +20,20 @@ import com.android.dx.rop.type.TypeList; import com.android.dx.util.MutabilityException; /** - * Attribute class for standard Exceptions attributes. + * Attribute class for standard {@code Exceptions} attributes. */ public final class AttExceptions extends BaseAttribute { - /** non-null; attribute name for attributes of this type */ + /** {@code non-null;} attribute name for attributes of this type */ public static final String ATTRIBUTE_NAME = "Exceptions"; - /** non-null; list of exception classes */ + /** {@code non-null;} list of exception classes */ private final TypeList exceptions; /** * Constructs an instance. * - * @param exceptions non-null; list of classes, presumed but not - * verified to be subclasses of Throwable + * @param exceptions {@code non-null;} list of classes, presumed but not + * verified to be subclasses of {@code Throwable} */ public AttExceptions(TypeList exceptions) { super(ATTRIBUTE_NAME); @@ -58,9 +58,9 @@ public final class AttExceptions extends BaseAttribute { /** * Gets the list of classes associated with this instance. In * general, these classes are not pre-verified to be subclasses of - * Throwable. + * {@code Throwable}. * - * @return non-null; the list of classes + * @return {@code non-null;} the list of classes */ public TypeList getExceptions() { return exceptions; diff --git a/dx/src/com/android/dx/cf/attrib/AttInnerClasses.java b/dx/src/com/android/dx/cf/attrib/AttInnerClasses.java index df305395b..bd6c7cd6a 100644 --- a/dx/src/com/android/dx/cf/attrib/AttInnerClasses.java +++ b/dx/src/com/android/dx/cf/attrib/AttInnerClasses.java @@ -19,19 +19,19 @@ package com.android.dx.cf.attrib; import com.android.dx.util.MutabilityException; /** - * Attribute class for standard InnerClasses attributes. + * Attribute class for standard {@code InnerClasses} attributes. */ public final class AttInnerClasses extends BaseAttribute { - /** non-null; attribute name for attributes of this type */ + /** {@code non-null;} attribute name for attributes of this type */ public static final String ATTRIBUTE_NAME = "InnerClasses"; - /** non-null; list of inner class entries */ + /** {@code non-null;} list of inner class entries */ private final InnerClassList innerClasses; /** * Constructs an instance. * - * @param innerClasses non-null; list of inner class entries + * @param innerClasses {@code non-null;} list of inner class entries */ public AttInnerClasses(InnerClassList innerClasses) { super(ATTRIBUTE_NAME); @@ -56,7 +56,7 @@ public final class AttInnerClasses extends BaseAttribute { /** * Gets the list of "inner class" entries associated with this instance. * - * @return non-null; the list + * @return {@code non-null;} the list */ public InnerClassList getInnerClasses() { return innerClasses; diff --git a/dx/src/com/android/dx/cf/attrib/AttLineNumberTable.java b/dx/src/com/android/dx/cf/attrib/AttLineNumberTable.java index c5e65e80d..38980be19 100644 --- a/dx/src/com/android/dx/cf/attrib/AttLineNumberTable.java +++ b/dx/src/com/android/dx/cf/attrib/AttLineNumberTable.java @@ -20,19 +20,19 @@ import com.android.dx.cf.code.LineNumberList; import com.android.dx.util.MutabilityException; /** - * Attribute class for standard LineNumberTable attributes. + * Attribute class for standard {@code LineNumberTable} attributes. */ public final class AttLineNumberTable extends BaseAttribute { - /** non-null; attribute name for attributes of this type */ + /** {@code non-null;} attribute name for attributes of this type */ public static final String ATTRIBUTE_NAME = "LineNumberTable"; - /** non-null; list of line number entries */ + /** {@code non-null;} list of line number entries */ private final LineNumberList lineNumbers; /** * Constructs an instance. * - * @param lineNumbers non-null; list of line number entries + * @param lineNumbers {@code non-null;} list of line number entries */ public AttLineNumberTable(LineNumberList lineNumbers) { super(ATTRIBUTE_NAME); @@ -57,7 +57,7 @@ public final class AttLineNumberTable extends BaseAttribute { /** * Gets the list of "line number" entries associated with this instance. * - * @return non-null; the list + * @return {@code non-null;} the list */ public LineNumberList getLineNumbers() { return lineNumbers; diff --git a/dx/src/com/android/dx/cf/attrib/AttLocalVariableTable.java b/dx/src/com/android/dx/cf/attrib/AttLocalVariableTable.java index 893f25477..53ba64fe1 100644 --- a/dx/src/com/android/dx/cf/attrib/AttLocalVariableTable.java +++ b/dx/src/com/android/dx/cf/attrib/AttLocalVariableTable.java @@ -19,16 +19,16 @@ package com.android.dx.cf.attrib; import com.android.dx.cf.code.LocalVariableList; /** - * Attribute class for standard LocalVariableTable attributes. + * Attribute class for standard {@code LocalVariableTable} attributes. */ public final class AttLocalVariableTable extends BaseLocalVariables { - /** non-null; attribute name for attributes of this type */ + /** {@code non-null;} attribute name for attributes of this type */ public static final String ATTRIBUTE_NAME = "LocalVariableTable"; /** * Constructs an instance. * - * @param localVariables non-null; list of local variable entries + * @param localVariables {@code non-null;} list of local variable entries */ public AttLocalVariableTable(LocalVariableList localVariables) { super(ATTRIBUTE_NAME, localVariables); diff --git a/dx/src/com/android/dx/cf/attrib/AttLocalVariableTypeTable.java b/dx/src/com/android/dx/cf/attrib/AttLocalVariableTypeTable.java index 7037b7401..49cdb0c0f 100644 --- a/dx/src/com/android/dx/cf/attrib/AttLocalVariableTypeTable.java +++ b/dx/src/com/android/dx/cf/attrib/AttLocalVariableTypeTable.java @@ -19,16 +19,16 @@ package com.android.dx.cf.attrib; import com.android.dx.cf.code.LocalVariableList; /** - * Attribute class for standard LocalVariableTypeTable attributes. + * Attribute class for standard {@code LocalVariableTypeTable} attributes. */ public final class AttLocalVariableTypeTable extends BaseLocalVariables { - /** non-null; attribute name for attributes of this type */ + /** {@code non-null;} attribute name for attributes of this type */ public static final String ATTRIBUTE_NAME = "LocalVariableTypeTable"; /** * Constructs an instance. * - * @param localVariables non-null; list of local variable entries + * @param localVariables {@code non-null;} list of local variable entries */ public AttLocalVariableTypeTable(LocalVariableList localVariables) { super(ATTRIBUTE_NAME, localVariables); diff --git a/dx/src/com/android/dx/cf/attrib/AttRuntimeInvisibleAnnotations.java b/dx/src/com/android/dx/cf/attrib/AttRuntimeInvisibleAnnotations.java index 583ab17be..e83b76fb3 100644 --- a/dx/src/com/android/dx/cf/attrib/AttRuntimeInvisibleAnnotations.java +++ b/dx/src/com/android/dx/cf/attrib/AttRuntimeInvisibleAnnotations.java @@ -19,18 +19,18 @@ package com.android.dx.cf.attrib; import com.android.dx.rop.annotation.Annotations; /** - * Attribute class for standard RuntimeInvisibleAnnotations + * Attribute class for standard {@code RuntimeInvisibleAnnotations} * attributes. */ public final class AttRuntimeInvisibleAnnotations extends BaseAnnotations { - /** non-null; attribute name for attributes of this type */ + /** {@code non-null;} attribute name for attributes of this type */ public static final String ATTRIBUTE_NAME = "RuntimeInvisibleAnnotations"; /** * Constructs an instance. * - * @param annotations non-null; the list of annotations - * @param byteLength >= 0; attribute data length in the original + * @param annotations {@code non-null;} the list of annotations + * @param byteLength {@code >= 0;} attribute data length in the original * classfile (not including the attribute header) */ public AttRuntimeInvisibleAnnotations(Annotations annotations, diff --git a/dx/src/com/android/dx/cf/attrib/AttRuntimeInvisibleParameterAnnotations.java b/dx/src/com/android/dx/cf/attrib/AttRuntimeInvisibleParameterAnnotations.java index 08865e153..7dfe2069a 100644 --- a/dx/src/com/android/dx/cf/attrib/AttRuntimeInvisibleParameterAnnotations.java +++ b/dx/src/com/android/dx/cf/attrib/AttRuntimeInvisibleParameterAnnotations.java @@ -20,19 +20,19 @@ import com.android.dx.rop.annotation.AnnotationsList; /** * Attribute class for standard - * RuntimeInvisibleParameterAnnotations attributes. + * {@code RuntimeInvisibleParameterAnnotations} attributes. */ public final class AttRuntimeInvisibleParameterAnnotations extends BaseParameterAnnotations { - /** non-null; attribute name for attributes of this type */ + /** {@code non-null;} attribute name for attributes of this type */ public static final String ATTRIBUTE_NAME = "RuntimeInvisibleParameterAnnotations"; /** * Constructs an instance. * - * @param parameterAnnotations non-null; the parameter annotations - * @param byteLength >= 0; attribute data length in the original + * @param parameterAnnotations {@code non-null;} the parameter annotations + * @param byteLength {@code >= 0;} attribute data length in the original * classfile (not including the attribute header) */ public AttRuntimeInvisibleParameterAnnotations( diff --git a/dx/src/com/android/dx/cf/attrib/AttRuntimeVisibleAnnotations.java b/dx/src/com/android/dx/cf/attrib/AttRuntimeVisibleAnnotations.java index c61acb5c2..9de05881d 100644 --- a/dx/src/com/android/dx/cf/attrib/AttRuntimeVisibleAnnotations.java +++ b/dx/src/com/android/dx/cf/attrib/AttRuntimeVisibleAnnotations.java @@ -19,18 +19,18 @@ package com.android.dx.cf.attrib; import com.android.dx.rop.annotation.Annotations; /** - * Attribute class for standard RuntimeVisibleAnnotations + * Attribute class for standard {@code RuntimeVisibleAnnotations} * attributes. */ public final class AttRuntimeVisibleAnnotations extends BaseAnnotations { - /** non-null; attribute name for attributes of this type */ + /** {@code non-null;} attribute name for attributes of this type */ public static final String ATTRIBUTE_NAME = "RuntimeVisibleAnnotations"; /** * Constructs an instance. * - * @param annotations non-null; the list of annotations - * @param byteLength >= 0; attribute data length in the original + * @param annotations {@code non-null;} the list of annotations + * @param byteLength {@code >= 0;} attribute data length in the original * classfile (not including the attribute header) */ public AttRuntimeVisibleAnnotations(Annotations annotations, diff --git a/dx/src/com/android/dx/cf/attrib/AttRuntimeVisibleParameterAnnotations.java b/dx/src/com/android/dx/cf/attrib/AttRuntimeVisibleParameterAnnotations.java index dfe57b2b6..76607c0f4 100644 --- a/dx/src/com/android/dx/cf/attrib/AttRuntimeVisibleParameterAnnotations.java +++ b/dx/src/com/android/dx/cf/attrib/AttRuntimeVisibleParameterAnnotations.java @@ -19,20 +19,20 @@ package com.android.dx.cf.attrib; import com.android.dx.rop.annotation.AnnotationsList; /** - * Attribute class for standard RuntimeVisibleParameterAnnotations + * Attribute class for standard {@code RuntimeVisibleParameterAnnotations} * attributes. */ public final class AttRuntimeVisibleParameterAnnotations extends BaseParameterAnnotations { - /** non-null; attribute name for attributes of this type */ + /** {@code non-null;} attribute name for attributes of this type */ public static final String ATTRIBUTE_NAME = "RuntimeVisibleParameterAnnotations"; /** * Constructs an instance. * - * @param annotations non-null; the parameter annotations - * @param byteLength >= 0; attribute data length in the original + * @param annotations {@code non-null;} the parameter annotations + * @param byteLength {@code >= 0;} attribute data length in the original * classfile (not including the attribute header) */ public AttRuntimeVisibleParameterAnnotations( diff --git a/dx/src/com/android/dx/cf/attrib/AttSignature.java b/dx/src/com/android/dx/cf/attrib/AttSignature.java index 97edbbdd0..b9cb97d1f 100644 --- a/dx/src/com/android/dx/cf/attrib/AttSignature.java +++ b/dx/src/com/android/dx/cf/attrib/AttSignature.java @@ -19,19 +19,19 @@ package com.android.dx.cf.attrib; import com.android.dx.rop.cst.CstUtf8; /** - * Attribute class for standards-track Signature attributes. + * Attribute class for standards-track {@code Signature} attributes. */ public final class AttSignature extends BaseAttribute { - /** non-null; attribute name for attributes of this type */ + /** {@code non-null;} attribute name for attributes of this type */ public static final String ATTRIBUTE_NAME = "Signature"; - /** non-null; the signature string */ + /** {@code non-null;} the signature string */ private final CstUtf8 signature; /** * Constructs an instance. * - * @param signature non-null; the signature string + * @param signature {@code non-null;} the signature string */ public AttSignature(CstUtf8 signature) { super(ATTRIBUTE_NAME); @@ -51,7 +51,7 @@ public final class AttSignature extends BaseAttribute { /** * Gets the signature string. * - * @return non-null; the signature string + * @return {@code non-null;} the signature string */ public CstUtf8 getSignature() { return signature; diff --git a/dx/src/com/android/dx/cf/attrib/AttSourceFile.java b/dx/src/com/android/dx/cf/attrib/AttSourceFile.java index f0872178f..941a2b0f0 100644 --- a/dx/src/com/android/dx/cf/attrib/AttSourceFile.java +++ b/dx/src/com/android/dx/cf/attrib/AttSourceFile.java @@ -19,19 +19,19 @@ package com.android.dx.cf.attrib; import com.android.dx.rop.cst.CstUtf8; /** - * Attribute class for standard SourceFile attributes. + * Attribute class for standard {@code SourceFile} attributes. */ public final class AttSourceFile extends BaseAttribute { - /** non-null; attribute name for attributes of this type */ + /** {@code non-null;} attribute name for attributes of this type */ public static final String ATTRIBUTE_NAME = "SourceFile"; - /** non-null; name of the source file */ + /** {@code non-null;} name of the source file */ private final CstUtf8 sourceFile; /** * Constructs an instance. * - * @param sourceFile non-null; the name of the source file + * @param sourceFile {@code non-null;} the name of the source file */ public AttSourceFile(CstUtf8 sourceFile) { super(ATTRIBUTE_NAME); @@ -51,7 +51,7 @@ public final class AttSourceFile extends BaseAttribute { /** * Gets the source file name of this instance. * - * @return non-null; the source file + * @return {@code non-null;} the source file */ public CstUtf8 getSourceFile() { return sourceFile; diff --git a/dx/src/com/android/dx/cf/attrib/AttSynthetic.java b/dx/src/com/android/dx/cf/attrib/AttSynthetic.java index daa9b0c23..e3841eb86 100644 --- a/dx/src/com/android/dx/cf/attrib/AttSynthetic.java +++ b/dx/src/com/android/dx/cf/attrib/AttSynthetic.java @@ -17,10 +17,10 @@ package com.android.dx.cf.attrib; /** - * Attribute class for standard Synthetic attributes. + * Attribute class for standard {@code Synthetic} attributes. */ public final class AttSynthetic extends BaseAttribute { - /** non-null; attribute name for attributes of this type */ + /** {@code non-null;} attribute name for attributes of this type */ public static final String ATTRIBUTE_NAME = "Synthetic"; /** diff --git a/dx/src/com/android/dx/cf/attrib/BaseAnnotations.java b/dx/src/com/android/dx/cf/attrib/BaseAnnotations.java index 0163e2cb4..4d9201e4b 100644 --- a/dx/src/com/android/dx/cf/attrib/BaseAnnotations.java +++ b/dx/src/com/android/dx/cf/attrib/BaseAnnotations.java @@ -23,19 +23,19 @@ import com.android.dx.util.MutabilityException; * Base class for annotations attributes. */ public abstract class BaseAnnotations extends BaseAttribute { - /** non-null; list of annotations */ + /** {@code non-null;} list of annotations */ private final Annotations annotations; - /** >= 0; attribute data length in the original classfile (not + /** {@code >= 0;} attribute data length in the original classfile (not * including the attribute header) */ private final int byteLength; /** * Constructs an instance. * - * @param attributeName non-null; the name of the attribute - * @param annotations non-null; the list of annotations - * @param byteLength >= 0; attribute data length in the original + * @param attributeName {@code non-null;} the name of the attribute + * @param annotations {@code non-null;} the list of annotations + * @param byteLength {@code >= 0;} attribute data length in the original * classfile (not including the attribute header) */ public BaseAnnotations(String attributeName, Annotations annotations, @@ -64,7 +64,7 @@ public abstract class BaseAnnotations extends BaseAttribute { /** * Gets the list of annotations associated with this instance. * - * @return non-null; the list + * @return {@code non-null;} the list */ public final Annotations getAnnotations() { return annotations; diff --git a/dx/src/com/android/dx/cf/attrib/BaseAttribute.java b/dx/src/com/android/dx/cf/attrib/BaseAttribute.java index ef1c6ac0d..c9c1b33cb 100644 --- a/dx/src/com/android/dx/cf/attrib/BaseAttribute.java +++ b/dx/src/com/android/dx/cf/attrib/BaseAttribute.java @@ -23,13 +23,13 @@ import com.android.dx.cf.iface.Attribute; * the attribute name but leaves the rest up to subclasses. */ public abstract class BaseAttribute implements Attribute { - /** non-null; attribute name */ + /** {@code non-null;} attribute name */ private final String name; /** * Constructs an instance. * - * @param name non-null; attribute name + * @param name {@code non-null;} attribute name */ public BaseAttribute(String name) { if (name == null) { diff --git a/dx/src/com/android/dx/cf/attrib/BaseLocalVariables.java b/dx/src/com/android/dx/cf/attrib/BaseLocalVariables.java index a39e72438..5ba5889cb 100644 --- a/dx/src/com/android/dx/cf/attrib/BaseLocalVariables.java +++ b/dx/src/com/android/dx/cf/attrib/BaseLocalVariables.java @@ -20,18 +20,18 @@ import com.android.dx.cf.code.LocalVariableList; import com.android.dx.util.MutabilityException; /** - * Base attribute class for standard LocalVariableTable - * and LocalVariableTypeTable attributes. + * Base attribute class for standard {@code LocalVariableTable} + * and {@code LocalVariableTypeTable} attributes. */ public abstract class BaseLocalVariables extends BaseAttribute { - /** non-null; list of local variable entries */ + /** {@code non-null;} list of local variable entries */ private final LocalVariableList localVariables; /** * Constructs an instance. * - * @param name non-null; attribute name - * @param localVariables non-null; list of local variable entries + * @param name {@code non-null;} attribute name + * @param localVariables {@code non-null;} list of local variable entries */ public BaseLocalVariables(String name, LocalVariableList localVariables) { @@ -57,7 +57,7 @@ public abstract class BaseLocalVariables extends BaseAttribute { /** * Gets the list of "local variable" entries associated with this instance. * - * @return non-null; the list + * @return {@code non-null;} the list */ public final LocalVariableList getLocalVariables() { return localVariables; diff --git a/dx/src/com/android/dx/cf/attrib/BaseParameterAnnotations.java b/dx/src/com/android/dx/cf/attrib/BaseParameterAnnotations.java index a927e3d5f..1b204b34e 100644 --- a/dx/src/com/android/dx/cf/attrib/BaseParameterAnnotations.java +++ b/dx/src/com/android/dx/cf/attrib/BaseParameterAnnotations.java @@ -23,19 +23,19 @@ import com.android.dx.util.MutabilityException; * Base class for parameter annotation list attributes. */ public abstract class BaseParameterAnnotations extends BaseAttribute { - /** non-null; list of annotations */ + /** {@code non-null;} list of annotations */ private final AnnotationsList parameterAnnotations; - /** >= 0; attribute data length in the original classfile (not + /** {@code >= 0;} attribute data length in the original classfile (not * including the attribute header) */ private final int byteLength; /** * Constructs an instance. * - * @param attributeName non-null; the name of the attribute - * @param parameterAnnotations non-null; the annotations - * @param byteLength >= 0; attribute data length in the original + * @param attributeName {@code non-null;} the name of the attribute + * @param parameterAnnotations {@code non-null;} the annotations + * @param byteLength {@code >= 0;} attribute data length in the original * classfile (not including the attribute header) */ public BaseParameterAnnotations(String attributeName, @@ -65,7 +65,7 @@ public abstract class BaseParameterAnnotations extends BaseAttribute { /** * Gets the list of annotation lists associated with this instance. * - * @return non-null; the list + * @return {@code non-null;} the list */ public final AnnotationsList getParameterAnnotations() { return parameterAnnotations; diff --git a/dx/src/com/android/dx/cf/attrib/InnerClassList.java b/dx/src/com/android/dx/cf/attrib/InnerClassList.java index 3585f1db8..96e1b60e2 100644 --- a/dx/src/com/android/dx/cf/attrib/InnerClassList.java +++ b/dx/src/com/android/dx/cf/attrib/InnerClassList.java @@ -22,7 +22,7 @@ import com.android.dx.util.FixedSizeList; /** * List of "inner class" entries, which are the contents of - * InnerClasses attributes. + * {@code InnerClasses} attributes. */ public final class InnerClassList extends FixedSizeList { /** @@ -37,8 +37,8 @@ public final class InnerClassList extends FixedSizeList { /** * Gets the indicated item. * - * @param n >= 0; which item - * @return null-ok; the indicated item + * @param n {@code >= 0;} which item + * @return {@code null-ok;} the indicated item */ public Item get(int n) { return (Item) get0(n); @@ -47,11 +47,11 @@ public final class InnerClassList extends FixedSizeList { /** * Sets the item at the given index. * - * @param n >= 0, < size(); which class - * @param innerClass non-null; class this item refers to - * @param outerClass null-ok; outer class that this class is a + * @param n {@code >= 0, < size();} which class + * @param innerClass {@code non-null;} class this item refers to + * @param outerClass {@code null-ok;} outer class that this class is a * member of, if any - * @param innerName null-ok; original simple name of this class, + * @param innerName {@code null-ok;} original simple name of this class, * if not anonymous * @param accessFlags original declared access flags */ @@ -64,13 +64,13 @@ public final class InnerClassList extends FixedSizeList { * Item in an inner classes list. */ public static class Item { - /** non-null; class this item refers to */ + /** {@code non-null;} class this item refers to */ private final CstType innerClass; - /** null-ok; outer class that this class is a member of, if any */ + /** {@code null-ok;} outer class that this class is a member of, if any */ private final CstType outerClass; - /** null-ok; original simple name of this class, if not anonymous */ + /** {@code null-ok;} original simple name of this class, if not anonymous */ private final CstUtf8 innerName; /** original declared access flags */ @@ -79,10 +79,10 @@ public final class InnerClassList extends FixedSizeList { /** * Constructs an instance. * - * @param innerClass non-null; class this item refers to - * @param outerClass null-ok; outer class that this class is a + * @param innerClass {@code non-null;} class this item refers to + * @param outerClass {@code null-ok;} outer class that this class is a * member of, if any - * @param innerName null-ok; original simple name of this + * @param innerName {@code null-ok;} original simple name of this * class, if not anonymous * @param accessFlags original declared access flags */ @@ -101,7 +101,7 @@ public final class InnerClassList extends FixedSizeList { /** * Gets the class this item refers to. * - * @return non-null; the class + * @return {@code non-null;} the class */ public CstType getInnerClass() { return innerClass; @@ -110,7 +110,7 @@ public final class InnerClassList extends FixedSizeList { /** * Gets the outer class that this item's class is a member of, if any. * - * @return null-ok; the class + * @return {@code null-ok;} the class */ public CstType getOuterClass() { return outerClass; @@ -119,7 +119,7 @@ public final class InnerClassList extends FixedSizeList { /** * Gets the original name of this item's class, if not anonymous. * - * @return null-ok; the name + * @return {@code null-ok;} the name */ public CstUtf8 getInnerName() { return innerName; diff --git a/dx/src/com/android/dx/cf/attrib/RawAttribute.java b/dx/src/com/android/dx/cf/attrib/RawAttribute.java index b89926d8a..585e5c5a2 100644 --- a/dx/src/com/android/dx/cf/attrib/RawAttribute.java +++ b/dx/src/com/android/dx/cf/attrib/RawAttribute.java @@ -23,11 +23,11 @@ import com.android.dx.util.ByteArray; * Raw attribute, for holding onto attributes that are unrecognized. */ public final class RawAttribute extends BaseAttribute { - /** non-null; attribute data */ + /** {@code non-null;} attribute data */ private final ByteArray data; /** - * null-ok; constant pool to use for resolution of cpis in {@link + * {@code null-ok;} constant pool to use for resolution of cpis in {@link * #data} */ private final ConstantPool pool; @@ -35,9 +35,9 @@ public final class RawAttribute extends BaseAttribute { /** * Constructs an instance. * - * @param name non-null; attribute name - * @param data non-null; attribute data - * @param pool null-ok; constant pool to use for cpi resolution + * @param name {@code non-null;} attribute name + * @param data {@code non-null;} attribute data + * @param pool {@code null-ok;} constant pool to use for cpi resolution */ public RawAttribute(String name, ByteArray data, ConstantPool pool) { super(name); @@ -53,11 +53,11 @@ public final class RawAttribute extends BaseAttribute { /** * Constructs an instance from a sub-array of a {@link ByteArray}. * - * @param name non-null; attribute name - * @param data non-null; array containing the attribute data - * @param offset offset in data to the attribute data + * @param name {@code non-null;} attribute name + * @param data {@code non-null;} array containing the attribute data + * @param offset offset in {@code data} to the attribute data * @param length length of the attribute data, in bytes - * @param pool null-ok; constant pool to use for cpi resolution + * @param pool {@code null-ok;} constant pool to use for cpi resolution */ public RawAttribute(String name, ByteArray data, int offset, int length, ConstantPool pool) { @@ -67,7 +67,7 @@ public final class RawAttribute extends BaseAttribute { /** * Get the raw data of the attribute. * - * @return non-null; the data + * @return {@code non-null;} the data */ public ByteArray getData() { return data; @@ -83,7 +83,7 @@ public final class RawAttribute extends BaseAttribute { * presumably came from the class file that this attribute came * from. * - * @return null-ok; the constant pool + * @return {@code null-ok;} the constant pool */ public ConstantPool getPool() { return pool; diff --git a/dx/src/com/android/dx/cf/code/BaseMachine.java b/dx/src/com/android/dx/cf/code/BaseMachine.java index 430e48be7..b7e700d42 100644 --- a/dx/src/com/android/dx/cf/code/BaseMachine.java +++ b/dx/src/com/android/dx/cf/code/BaseMachine.java @@ -33,44 +33,44 @@ import java.util.ArrayList; * TypeBearer}.

    */ public abstract class BaseMachine implements Machine { - /* non-null; the prototype for the associated method */ + /* {@code non-null;} the prototype for the associated method */ private final Prototype prototype; - /** non-null; primary arguments */ + /** {@code non-null;} primary arguments */ private TypeBearer[] args; - /** >= 0; number of primary arguments */ + /** {@code >= 0;} number of primary arguments */ private int argCount; - /** null-ok; type of the operation, if salient */ + /** {@code null-ok;} type of the operation, if salient */ private Type auxType; - /** auxiliary int argument */ + /** auxiliary {@code int} argument */ private int auxInt; - /** null-ok; auxiliary constant argument */ + /** {@code null-ok;} auxiliary constant argument */ private Constant auxCst; /** auxiliary branch target argument */ private int auxTarget; - /** null-ok; auxiliary switch cases argument */ + /** {@code null-ok;} auxiliary switch cases argument */ private SwitchList auxCases; - /** null-ok; auxiliary initial value list for newarray */ + /** {@code null-ok;} auxiliary initial value list for newarray */ private ArrayList auxInitValues; - /** >= -1; last local accessed */ + /** {@code >= -1;} last local accessed */ private int localIndex; - /** null-ok; local target spec, if salient and calculated */ + /** {@code null-ok;} local target spec, if salient and calculated */ private RegisterSpec localTarget; - /** non-null; results */ + /** {@code non-null;} results */ private TypeBearer[] results; /** - * >= -1; count of the results, or -1 if no results + * {@code >= -1;} count of the results, or {@code -1} if no results * have been set */ private int resultCount; @@ -78,7 +78,7 @@ public abstract class BaseMachine implements Machine { /** * Constructs an instance. * - * @param prototype non-null; the prototype for the associated method + * @param prototype {@code non-null;} the prototype for the associated method */ public BaseMachine(Prototype prototype) { if (prototype == null) { @@ -254,7 +254,7 @@ public abstract class BaseMachine implements Machine { /** * Gets the number of primary arguments. * - * @return >= 0; the number of primary arguments + * @return {@code >= 0;} the number of primary arguments */ protected final int argCount() { return argCount; @@ -264,7 +264,7 @@ public abstract class BaseMachine implements Machine { * Gets the width of the arguments (where a category-2 value counts as * two). * - * @return >= 0; the argument width + * @return {@code >= 0;} the argument width */ protected final int argWidth() { int result = 0; @@ -277,10 +277,10 @@ public abstract class BaseMachine implements Machine { } /** - * Gets the nth primary argument. + * Gets the {@code n}th primary argument. * - * @param n >= 0, < argCount(); which argument - * @return non-null; the indicated argument + * @param n {@code >= 0, < argCount();} which argument + * @return {@code non-null;} the indicated argument */ protected final TypeBearer arg(int n) { if (n >= argCount) { @@ -298,14 +298,14 @@ public abstract class BaseMachine implements Machine { /** * Gets the type auxiliary argument. * - * @return null-ok; the salient type + * @return {@code null-ok;} the salient type */ protected final Type getAuxType() { return auxType; } /** - * Gets the int auxiliary argument. + * Gets the {@code int} auxiliary argument. * * @return the argument value */ @@ -316,7 +316,7 @@ public abstract class BaseMachine implements Machine { /** * Gets the constant auxiliary argument. * - * @return null-ok; the argument value + * @return {@code null-ok;} the argument value */ protected final Constant getAuxCst() { return auxCst; @@ -334,7 +334,7 @@ public abstract class BaseMachine implements Machine { /** * Gets the switch cases auxiliary argument. * - * @return null-ok; the argument value + * @return {@code null-ok;} the argument value */ protected final SwitchList getAuxCases() { return auxCases; @@ -343,7 +343,7 @@ public abstract class BaseMachine implements Machine { /** * Gets the init values auxiliary argument. * - * @return null-ok; the argument value + * @return {@code null-ok;} the argument value */ protected final ArrayList getInitValues() { return auxInitValues; @@ -351,7 +351,7 @@ public abstract class BaseMachine implements Machine { /** * Gets the last local index accessed. * - * @return >= -1; the salient local index or -1 if none + * @return {@code >= -1;} the salient local index or {@code -1} if none * was set since the last time {@link #clearArgs} was called */ protected final int getLocalIndex() { @@ -365,7 +365,7 @@ public abstract class BaseMachine implements Machine { * should be the sole result set by a call to {@link #setResult} (or * the combination {@link #clearResult} then {@link #addResult}. * - * @return null-ok; the salient register spec or null if no + * @return {@code null-ok;} the salient register spec or {@code null} if no * local target was set since the last time {@link #clearArgs} was * called */ @@ -417,7 +417,7 @@ public abstract class BaseMachine implements Machine { *

    Note: If there is more than one result value, the * others may be added by using {@link #addResult}.

    * - * @param result non-null; result value + * @param result {@code non-null;} result value */ protected final void setResult(TypeBearer result) { if (result == null) { @@ -433,7 +433,7 @@ public abstract class BaseMachine implements Machine { * * @see #setResult * - * @param result non-null; result value + * @param result {@code non-null;} result value */ protected final void addResult(TypeBearer result) { if (result == null) { @@ -448,7 +448,7 @@ public abstract class BaseMachine implements Machine { * Gets the count of results. This throws an exception if results were * never set. (Explicitly clearing the results counts as setting them.) * - * @return >= 0; the count + * @return {@code >= 0;} the count */ protected final int resultCount() { if (resultCount < 0) { @@ -462,7 +462,7 @@ public abstract class BaseMachine implements Machine { * Gets the width of the results (where a category-2 value counts as * two). * - * @return >= 0; the result width + * @return {@code >= 0;} the result width */ protected final int resultWidth() { int width = 0; @@ -475,10 +475,10 @@ public abstract class BaseMachine implements Machine { } /** - * Gets the nth result value. + * Gets the {@code n}th result value. * - * @param n >= 0, < resultCount(); which result - * @return non-null; the indicated result value + * @param n {@code >= 0, < resultCount();} which result + * @return {@code non-null;} the indicated result value */ protected final TypeBearer result(int n) { if (n >= resultCount) { @@ -499,7 +499,7 @@ public abstract class BaseMachine implements Machine { * result is stored to that target; otherwise any results are pushed * onto the stack. * - * @param frame non-null; frame to operate on + * @param frame {@code non-null;} frame to operate on */ protected final void storeResults(Frame frame) { if (resultCount < 0) { @@ -529,8 +529,8 @@ public abstract class BaseMachine implements Machine { * Throws an exception that indicates a mismatch in local variable * types. * - * @param found non-null; the encountered type - * @param local non-null; the local variable's claimed type + * @param found {@code non-null;} the encountered type + * @param local {@code non-null;} the local variable's claimed type */ public static void throwLocalMismatch(TypeBearer found, TypeBearer local) { diff --git a/dx/src/com/android/dx/cf/code/BasicBlocker.java b/dx/src/com/android/dx/cf/code/BasicBlocker.java index 82e4a08d0..d67e52549 100644 --- a/dx/src/com/android/dx/cf/code/BasicBlocker.java +++ b/dx/src/com/android/dx/cf/code/BasicBlocker.java @@ -29,33 +29,37 @@ import java.util.ArrayList; * Utility that identifies basic blocks in bytecode. */ public final class BasicBlocker implements BytecodeArray.Visitor { - /** non-null; method being converted */ + /** {@code non-null;} method being converted */ private final ConcreteMethod method; - /** non-null; work set; bits indicate offsets in need of examination */ + /** + * {@code non-null;} work set; bits indicate offsets in need of + * examination + */ private final int[] workSet; /** - * non-null; live set; bits indicate potentially-live opcodes; contrawise, - * a bit that isn't on is either in the middle of an instruction or is - * a definitely-dead opcode + * {@code non-null;} live set; bits indicate potentially-live + * opcodes; contrawise, a bit that isn't on is either in the + * middle of an instruction or is a definitely-dead opcode */ private final int[] liveSet; /** - * non-null; block start set; bits indicate the starts of basic blocks, - * including the opcodes that start blocks of definitely-dead code + * {@code non-null;} block start set; bits indicate the starts of + * basic blocks, including the opcodes that start blocks of + * definitely-dead code */ private final int[] blockSet; /** - * non-null, sparse; for each instruction offset to a branch of + * {@code non-null, sparse;} for each instruction offset to a branch of * some sort, the list of targets for that instruction */ private final IntList[] targetLists; /** - * non-null, sparse; for each instruction offset to a throwing + * {@code non-null, sparse;} for each instruction offset to a throwing * instruction, the list of exception handlers for that instruction */ private final ByteCatchList[] catchLists; @@ -68,8 +72,8 @@ public final class BasicBlocker implements BytecodeArray.Visitor { * returning a list of them. The returned list notably omits any * definitely-dead code that is identified in the process. * - * @param method non-null; method to convert - * @return non-null; list of basic blocks + * @param method {@code non-null;} method to convert + * @return {@code non-null;} list of basic blocks */ public static ByteBlockList identifyBlocks(ConcreteMethod method) { BasicBlocker bb = new BasicBlocker(method); @@ -82,7 +86,7 @@ public final class BasicBlocker implements BytecodeArray.Visitor { * Constructs an instance. This class is not publicly instantiable; use * {@link #identifyBlocks}. * - * @param method non-null; method to convert + * @param method {@code non-null;} method to convert */ private BasicBlocker(ConcreteMethod method) { if (method == null) { @@ -262,10 +266,9 @@ public final class BasicBlocker implements BytecodeArray.Visitor { /** * Extracts the list of basic blocks from the bit sets. * - * @return non-null; the list of basic blocks + * @return {@code non-null;} the list of basic blocks */ private ByteBlockList getBlockList() { - ByteCatchList catches = method.getCatches(); BytecodeArray bytes = method.getCode(); ByteBlock[] bbs = new ByteBlock[bytes.size()]; int count = 0; @@ -366,7 +369,7 @@ public final class BasicBlocker implements BytecodeArray.Visitor { * isn't yet known to be possibly-live. * * @param offset offset to the instruction in question - * @param blockStart true iff this instruction starts a + * @param blockStart {@code true} iff this instruction starts a * basic block */ private void addWorkIfNecessary(int offset, boolean blockStart) { @@ -384,7 +387,7 @@ public final class BasicBlocker implements BytecodeArray.Visitor { * * @param offset offset to the instruction * @param length length of the instruction, in bytes - * @param nextIsLive true iff the instruction after + * @param nextIsLive {@code true} iff the instruction after * the indicated one is possibly-live (because this one isn't an * unconditional branch, a return, or a switch) */ @@ -417,7 +420,7 @@ public final class BasicBlocker implements BytecodeArray.Visitor { * * @param offset offset to the instruction * @param length length of the instruction, in bytes - * @param nextIsLive true iff the instruction after + * @param nextIsLive {@code true} iff the instruction after * the indicated one is possibly-live (because this one isn't an * unconditional throw) */ diff --git a/dx/src/com/android/dx/cf/code/ByteBlock.java b/dx/src/com/android/dx/cf/code/ByteBlock.java index 065c5221c..40b91c38d 100644 --- a/dx/src/com/android/dx/cf/code/ByteBlock.java +++ b/dx/src/com/android/dx/cf/code/ByteBlock.java @@ -24,32 +24,32 @@ import com.android.dx.util.LabeledItem; * Representation of a basic block in a bytecode array. */ public final class ByteBlock implements LabeledItem { - /** >= 0; label for this block */ + /** {@code >= 0;} label for this block */ private final int label; - /** >= 0; bytecode offset (inclusive) of the start of the block */ + /** {@code >= 0;} bytecode offset (inclusive) of the start of the block */ private final int start; - /** > start; bytecode offset (exclusive) of the end of the block */ + /** {@code > start;} bytecode offset (exclusive) of the end of the block */ private final int end; - /** non-null; list of successors that this block may branch to */ + /** {@code non-null;} list of successors that this block may branch to */ private final IntList successors; - /** non-null; list of exceptions caught and their handler targets */ + /** {@code non-null;} list of exceptions caught and their handler targets */ private final ByteCatchList catches; /** * Constructs an instance. * - * @param label >= 0; target label for this block - * @param start >= 0; bytecode offset (inclusive) of the start + * @param label {@code >= 0;} target label for this block + * @param start {@code >= 0;} bytecode offset (inclusive) of the start * of the block - * @param end > start; bytecode offset (exclusive) of the end + * @param end {@code > start;} bytecode offset (exclusive) of the end * of the block - * @param successors non-null; list of successors that this block may + * @param successors {@code non-null;} list of successors that this block may * branch to - * @param catches non-null; list of exceptions caught and their + * @param catches {@code non-null;} list of exceptions caught and their * handler targets */ public ByteBlock(int label, int start, int end, IntList successors, @@ -100,7 +100,7 @@ public final class ByteBlock implements LabeledItem { /** * Gets the label of this block. * - * @return >= 0; the label + * @return {@code >= 0;} the label */ public int getLabel() { return label; @@ -109,7 +109,7 @@ public final class ByteBlock implements LabeledItem { /** * Gets the bytecode offset (inclusive) of the start of this block. * - * @return >= 0; the start offset + * @return {@code >= 0;} the start offset */ public int getStart() { return start; @@ -118,7 +118,7 @@ public final class ByteBlock implements LabeledItem { /** * Gets the bytecode offset (exclusive) of the end of this block. * - * @return > getStart(); the end offset + * @return {@code > getStart();} the end offset */ public int getEnd() { return end; @@ -128,7 +128,7 @@ public final class ByteBlock implements LabeledItem { * Gets the list of successors that this block may branch to * non-exceptionally. * - * @return non-null; the successor list + * @return {@code non-null;} the successor list */ public IntList getSuccessors() { return successors; @@ -137,7 +137,7 @@ public final class ByteBlock implements LabeledItem { /** * Gets the list of exceptions caught and their handler targets. * - * @return non-null; the catch list + * @return {@code non-null;} the catch list */ public ByteCatchList getCatches() { return catches; diff --git a/dx/src/com/android/dx/cf/code/ByteBlockList.java b/dx/src/com/android/dx/cf/code/ByteBlockList.java index 9d27b7fd9..412dfc3f7 100644 --- a/dx/src/com/android/dx/cf/code/ByteBlockList.java +++ b/dx/src/com/android/dx/cf/code/ByteBlockList.java @@ -28,7 +28,7 @@ public final class ByteBlockList extends LabeledList { /** * Constructs an instance. * - * @param size >= 0; the number of elements to be in the list + * @param size {@code >= 0;} the number of elements to be in the list */ public ByteBlockList(int size) { super(size); @@ -37,10 +37,10 @@ public final class ByteBlockList extends LabeledList { /** * 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 NullPointerException. + * will throw {@code NullPointerException}. * - * @param n >= 0, < size(); which element - * @return non-null; the indicated element + * @param n {@code >= 0, < size();} which element + * @return {@code non-null;} the indicated element */ public ByteBlock get(int n) { return (ByteBlock) get0(n); @@ -50,7 +50,7 @@ public final class ByteBlockList extends LabeledList { * Gets the block with the given label. * * @param label the label to look for - * @return non-null; the block with the given label + * @return {@code non-null;} the block with the given label */ public ByteBlock labelToBlock(int label) { int idx = indexOfLabel(label); @@ -66,8 +66,8 @@ public final class ByteBlockList extends LabeledList { /** * Sets the element at the given index. * - * @param n >= 0, < size(); which element - * @param bb null-ok; the value to store + * @param n {@code >= 0, < size();} which element + * @param bb {@code null-ok;} the value to store */ public void set(int n, ByteBlock bb) { super.set(n, bb); diff --git a/dx/src/com/android/dx/cf/code/ByteCatchList.java b/dx/src/com/android/dx/cf/code/ByteCatchList.java index 375831e55..aab2087fa 100644 --- a/dx/src/com/android/dx/cf/code/ByteCatchList.java +++ b/dx/src/com/android/dx/cf/code/ByteCatchList.java @@ -24,10 +24,10 @@ import com.android.dx.util.IntList; /** * List of catch entries, that is, the elements of an "exception table," - * which is part of a standard Code attribute. + * which is part of a standard {@code Code} attribute. */ public final class ByteCatchList extends FixedSizeList { - /** non-null; convenient zero-entry instance */ + /** {@code non-null;} convenient zero-entry instance */ public static final ByteCatchList EMPTY = new ByteCatchList(0); /** @@ -41,10 +41,10 @@ public final class ByteCatchList extends FixedSizeList { /** * Gets the total length of this structure in bytes, when included in - * a Code attribute. The returned value includes the - * two bytes for exception_table_length. + * a {@code Code} attribute. The returned value includes the + * two bytes for {@code exception_table_length}. * - * @return >= 2; the total length, in bytes + * @return {@code >= 2;} the total length, in bytes */ public int byteLength() { return 2 + size() * 8; @@ -53,8 +53,8 @@ public final class ByteCatchList extends FixedSizeList { /** * Gets the indicated item. * - * @param n >= 0; which item - * @return null-ok; the indicated item + * @param n {@code >= 0;} which item + * @return {@code null-ok;} the indicated item */ public Item get(int n) { return (Item) get0(n); @@ -63,8 +63,8 @@ public final class ByteCatchList extends FixedSizeList { /** * Sets the item at the given index. * - * @param n >= 0, < size(); which entry to set - * @param item non-null; the item + * @param n {@code >= 0, < size();} which entry to set + * @param item {@code non-null;} the item */ public void set(int n, Item item) { if (item == null) { @@ -77,13 +77,13 @@ public final class ByteCatchList extends FixedSizeList { /** * Sets the item at the given index. * - * @param n >= 0, < size(); which entry to set - * @param startPc >= 0; the start pc (inclusive) of the handler's range - * @param endPc >= startPc; the end pc (exclusive) of the + * @param n {@code >= 0, < size();} which entry to set + * @param startPc {@code >= 0;} the start pc (inclusive) of the handler's range + * @param endPc {@code >= startPc;} the end pc (exclusive) of the * handler's range - * @param handlerPc >= 0; the pc of the exception handler - * @param exceptionClass null-ok; the exception class or - * null to catch all exceptions with this handler + * @param handlerPc {@code >= 0;} the pc of the exception handler + * @param exceptionClass {@code null-ok;} the exception class or + * {@code null} to catch all exceptions with this handler */ public void set(int n, int startPc, int endPc, int handlerPc, CstType exceptionClass) { @@ -95,8 +95,8 @@ public final class ByteCatchList extends FixedSizeList { * automatically made immutable. * * @param pc which address - * @return non-null; list of exception handlers active at - * pc + * @return {@code non-null;} list of exception handlers active at + * {@code pc} */ public ByteCatchList listFor(int pc) { int sz = size(); @@ -128,12 +128,12 @@ public final class ByteCatchList extends FixedSizeList { * Helper method for {@link #listFor}, which tells whether a match * is not found for the exception type of the given item in * the given array. A match is considered to be either an exact type - * match or the class Object which represents a catch-all. + * match or the class {@code Object} which represents a catch-all. * - * @param item non-null; item with the exception type to look for - * @param arr non-null; array to search in - * @param count non-null; maximum number of elements in the array to check - * @return true iff the exception type is not found + * @param item {@code non-null;} item with the exception type to look for + * @param arr {@code non-null;} array to search in + * @param count {@code non-null;} maximum number of elements in the array to check + * @return {@code true} iff the exception type is not found */ private static boolean typeNotFound(Item item, Item[] arr, int count) { CstType type = item.getExceptionClass(); @@ -151,13 +151,13 @@ public final class ByteCatchList extends FixedSizeList { /** * Returns a target list corresponding to this instance. The result * is a list of all the exception handler addresses, with the given - * noException address appended if appropriate. The + * {@code noException} address appended if appropriate. The * result is automatically made immutable. * - * @param noException >= -1; the no-exception address to append, or - * -1 not to append anything - * @return non-null; list of exception targets, with - * noException appended if necessary + * @param noException {@code >= -1;} the no-exception address to append, or + * {@code -1} not to append anything + * @return {@code non-null;} list of exception targets, with + * {@code noException} appended if necessary */ public IntList toTargetList(int noException) { if (noException < -1) { @@ -199,7 +199,7 @@ public final class ByteCatchList extends FixedSizeList { /** * Returns a rop-style catches list equivalent to this one. * - * @return non-null; the converted instance + * @return {@code non-null;} the converted instance */ public TypeList toRopCatchList() { int sz = size(); @@ -221,29 +221,29 @@ public final class ByteCatchList extends FixedSizeList { * Item in an exception handler list. */ public static class Item { - /** >= 0; the start pc (inclusive) of the handler's range */ + /** {@code >= 0;} the start pc (inclusive) of the handler's range */ private final int startPc; - /** >= startPc; the end pc (exclusive) of the handler's range */ + /** {@code >= startPc;} the end pc (exclusive) of the handler's range */ private final int endPc; - /** >= 0; the pc of the exception handler */ + /** {@code >= 0;} the pc of the exception handler */ private final int handlerPc; - /** null-ok; the exception class or null to catch all + /** {@code null-ok;} the exception class or {@code null} to catch all * exceptions with this handler */ private final CstType exceptionClass; /** * Constructs an instance. * - * @param startPc >= 0; the start pc (inclusive) of the + * @param startPc {@code >= 0;} the start pc (inclusive) of the * handler's range - * @param endPc >= startPc; the end pc (exclusive) of the + * @param endPc {@code >= startPc;} the end pc (exclusive) of the * handler's range - * @param handlerPc >= 0; the pc of the exception handler - * @param exceptionClass null-ok; the exception class or - * null to catch all exceptions with this handler + * @param handlerPc {@code >= 0;} the pc of the exception handler + * @param exceptionClass {@code null-ok;} the exception class or + * {@code null} to catch all exceptions with this handler */ public Item(int startPc, int endPc, int handlerPc, CstType exceptionClass) { @@ -268,7 +268,7 @@ public final class ByteCatchList extends FixedSizeList { /** * Gets the start pc (inclusive) of the handler's range. * - * @return >= 0; the start pc (inclusive) of the handler's range. + * @return {@code >= 0;} the start pc (inclusive) of the handler's range. */ public int getStartPc() { return startPc; @@ -277,7 +277,7 @@ public final class ByteCatchList extends FixedSizeList { /** * Gets the end pc (exclusive) of the handler's range. * - * @return >= startPc; the end pc (exclusive) of the + * @return {@code >= startPc;} the end pc (exclusive) of the * handler's range. */ public int getEndPc() { @@ -287,7 +287,7 @@ public final class ByteCatchList extends FixedSizeList { /** * Gets the pc of the exception handler. * - * @return >= 0; the pc of the exception handler + * @return {@code >= 0;} the pc of the exception handler */ public int getHandlerPc() { return handlerPc; @@ -296,7 +296,7 @@ public final class ByteCatchList extends FixedSizeList { /** * Gets the class of exception handled. * - * @return non-null; the exception class; {@link CstType#OBJECT} + * @return {@code non-null;} the exception class; {@link CstType#OBJECT} * if this entry handles all possible exceptions */ public CstType getExceptionClass() { @@ -308,7 +308,7 @@ public final class ByteCatchList extends FixedSizeList { * Returns whether the given address is in the range of this item. * * @param pc the address - * @return true iff this item covers pc + * @return {@code true} iff this item covers {@code pc} */ public boolean covers(int pc) { return (pc >= startPc) && (pc < endPc); diff --git a/dx/src/com/android/dx/cf/code/ByteOps.java b/dx/src/com/android/dx/cf/code/ByteOps.java index 4c420f6f6..ea7b514f6 100644 --- a/dx/src/com/android/dx/cf/code/ByteOps.java +++ b/dx/src/com/android/dx/cf/code/ByteOps.java @@ -242,114 +242,114 @@ public class ByteOps { /** invalid */ public static final int FMT_INVALID = 0; - /** "-": op */ + /** "-": {@code op} */ public static final int FMT_NO_ARGS = 1; - /** "0": op; implies max_locals >= 1 */ + /** "0": {@code op}; implies {@code max_locals >= 1} */ public static final int FMT_NO_ARGS_LOCALS_1 = 2; - /** "1": op; implies max_locals >= 2 */ + /** "1": {@code op}; implies {@code max_locals >= 2} */ public static final int FMT_NO_ARGS_LOCALS_2 = 3; - /** "2": op; implies max_locals >= 3 */ + /** "2": {@code op}; implies {@code max_locals >= 3} */ public static final int FMT_NO_ARGS_LOCALS_3 = 4; - /** "3": op; implies max_locals >= 4 */ + /** "3": {@code op}; implies {@code max_locals >= 4} */ public static final int FMT_NO_ARGS_LOCALS_4 = 5; - /** "4": op; implies max_locals >= 5 */ + /** "4": {@code op}; implies {@code max_locals >= 5} */ public static final int FMT_NO_ARGS_LOCALS_5 = 6; - /** "b": op target target */ + /** "b": {@code op target target} */ public static final int FMT_BRANCH = 7; - /** "c": op target target target target */ + /** "c": {@code op target target target target} */ public static final int FMT_WIDE_BRANCH = 8; - /** "p": op #cpi #cpi; constant restricted as specified */ + /** "p": {@code op #cpi #cpi}; constant restricted as specified */ public static final int FMT_CPI = 9; /** - * "l": op local; category-1 local; implies - * max_locals is at least two more than the given + * "l": {@code op local}; category-1 local; implies + * {@code max_locals} is at least two more than the given * local number */ public static final int FMT_LOCAL_1 = 10; /** - * "m": op local; category-2 local; implies - * max_locals is at least two more than the given + * "m": {@code op local}; category-2 local; implies + * {@code max_locals} is at least two more than the given * local number */ public static final int FMT_LOCAL_2 = 11; /** - * "y": op #byte (bipush and - * newarray) + * "y": {@code op #byte} ({@code bipush} and + * {@code newarray}) */ public static final int FMT_LITERAL_BYTE = 12; - /** "I": invokeinterface cpi cpi count 0 */ + /** "I": {@code invokeinterface cpi cpi count 0} */ public static final int FMT_INVOKEINTERFACE = 13; - /** "L": ldc #cpi; constant restricted as specified */ + /** "L": {@code ldc #cpi}; constant restricted as specified */ public static final int FMT_LDC = 14; - /** "S": sipush #byte #byte */ + /** "S": {@code sipush #byte #byte} */ public static final int FMT_SIPUSH = 15; - /** "T": tableswitch ... */ + /** "T": {@code tableswitch ...} */ public static final int FMT_TABLESWITCH = 16; - /** "U": lookupswitch ... */ + /** "U": {@code lookupswitch ...} */ public static final int FMT_LOOKUPSWITCH = 17; - /** "M": multianewarray cpi cpi dims */ + /** "M": {@code multianewarray cpi cpi dims} */ public static final int FMT_MULTIANEWARRAY = 18; - /** "W": wide ... */ + /** "W": {@code wide ...} */ public static final int FMT_WIDE = 19; /** mask for the bits representing the opcode format */ public static final int FMT_MASK = 0x1f; - /** "I": flag bit for valid cp type for Integer */ + /** "I": flag bit for valid cp type for {@code Integer} */ public static final int CPOK_Integer = 0x20; - /** "F": flag bit for valid cp type for Float */ + /** "F": flag bit for valid cp type for {@code Float} */ public static final int CPOK_Float = 0x40; - /** "J": flag bit for valid cp type for Long */ + /** "J": flag bit for valid cp type for {@code Long} */ public static final int CPOK_Long = 0x80; - /** "D": flag bit for valid cp type for Double */ + /** "D": flag bit for valid cp type for {@code Double} */ public static final int CPOK_Double = 0x100; - /** "c": flag bit for valid cp type for Class */ + /** "c": flag bit for valid cp type for {@code Class} */ public static final int CPOK_Class = 0x200; - /** "s": flag bit for valid cp type for String */ + /** "s": flag bit for valid cp type for {@code String} */ public static final int CPOK_String = 0x400; - /** "f": flag bit for valid cp type for Fieldref */ + /** "f": flag bit for valid cp type for {@code Fieldref} */ public static final int CPOK_Fieldref = 0x800; - /** "m": flag bit for valid cp type for Methodref */ + /** "m": flag bit for valid cp type for {@code Methodref} */ public static final int CPOK_Methodref = 0x1000; - /** "i": flag bit for valid cp type for InterfaceMethodref */ + /** "i": flag bit for valid cp type for {@code InterfaceMethodref} */ public static final int CPOK_InterfaceMethodref = 0x2000; /** - * non-null; map from opcodes to format or'ed with allowed constant + * {@code non-null;} map from opcodes to format or'ed with allowed constant * pool types */ private static final int[] OPCODE_INFO = new int[256]; - /** non-null; map from opcodes to their names */ + /** {@code non-null;} map from opcodes to their names */ private static final String[] OPCODE_NAMES = new String[256]; - /** non-null; bigass string describing all the opcodes */ + /** {@code non-null;} bigass string describing all the opcodes */ private static final String OPCODE_DETAILS = "00 - nop;" + "01 - aconst_null;" + @@ -623,8 +623,8 @@ public class ByteOps { /** * Gets the name of the given opcode. * - * @param opcode >= 0, <= 255; the opcode - * @return non-null; its name + * @param opcode {@code >= 0, <= 255;} the opcode + * @return {@code non-null;} its name */ public static String opName(int opcode) { String result = OPCODE_NAMES[opcode]; @@ -640,7 +640,7 @@ public class ByteOps { /** * Gets the format and allowed cp types of the given opcode. * - * @param opcode >= 0, <= 255; the opcode + * @param opcode {@code >= 0, <= 255;} the opcode * @return its format and allowed cp types */ public static int opInfo(int opcode) { diff --git a/dx/src/com/android/dx/cf/code/BytecodeArray.java b/dx/src/com/android/dx/cf/code/BytecodeArray.java index 71ba029f6..60f0cee07 100644 --- a/dx/src/com/android/dx/cf/code/BytecodeArray.java +++ b/dx/src/com/android/dx/cf/code/BytecodeArray.java @@ -32,23 +32,23 @@ import com.android.dx.util.Hex; import java.util.ArrayList; /** - * Bytecode array, which is part of a standard Code attribute. + * Bytecode array, which is part of a standard {@code Code} attribute. */ public final class BytecodeArray { /** convenient no-op implementation of {@link Visitor} */ public static final Visitor EMPTY_VISITOR = new BaseVisitor(); - /** non-null; underlying bytes */ + /** {@code non-null;} underlying bytes */ private final ByteArray bytes; - /** non-null; constant pool to use when resolving constant pool indices */ + /** {@code non-null;} constant pool to use when resolving constant pool indices */ private final ConstantPool pool; /** * Constructs an instance. * - * @param bytes non-null; underlying bytes - * @param pool non-null; constant pool to use when resolving constant + * @param bytes {@code non-null;} underlying bytes + * @param pool {@code non-null;} constant pool to use when resolving constant * pool indices */ public BytecodeArray(ByteArray bytes, ConstantPool pool) { @@ -67,7 +67,7 @@ public final class BytecodeArray { /** * Gets the underlying byte array. * - * @return non-null; the byte array + * @return {@code non-null;} the byte array */ public ByteArray getBytes() { return bytes; @@ -76,7 +76,7 @@ public final class BytecodeArray { /** * Gets the size of the bytecode array, per se. * - * @return >= 0; the length of the bytecode array + * @return {@code >= 0;} the length of the bytecode array */ public int size() { return bytes.size(); @@ -84,10 +84,10 @@ public final class BytecodeArray { /** * Gets the total length of this structure in bytes, when included in - * a Code attribute. The returned value includes the - * array size plus four bytes for code_length. + * a {@code Code} attribute. The returned value includes the + * array size plus four bytes for {@code code_length}. * - * @return >= 4; the total length, in bytes + * @return {@code >= 4;} the total length, in bytes */ public int byteLength() { return 4 + bytes.size(); @@ -96,7 +96,7 @@ public final class BytecodeArray { /** * Parses each instruction in the array, in order. * - * @param visitor null-ok; visitor to call back to for each instruction + * @param visitor {@code null-ok;} visitor to call back to for each instruction */ public void forEach(Visitor visitor) { int sz = bytes.size(); @@ -116,7 +116,7 @@ public final class BytecodeArray { * result is a bit set with the offset of each opcode-per-se flipped on. * * @see Bits - * @return non-null; appropriately constructed bit set + * @return {@code non-null;} appropriately constructed bit set */ public int[] getInstructionOffsets() { int sz = bytes.size(); @@ -139,8 +139,8 @@ public final class BytecodeArray { * work set is empty. It is expected that the visitor will regularly * set new bits in the work set during the process. * - * @param workSet non-null; the work set to process - * @param visitor non-null; visitor to call back to for each instruction + * @param workSet {@code non-null;} the work set to process + * @param visitor {@code non-null;} visitor to call back to for each instruction */ public void processWorkSet(int[] workSet, Visitor visitor) { if (visitor == null) { @@ -170,42 +170,42 @@ public final class BytecodeArray { * *
      *
    • The opcodes to push literal constants of primitive types all become - * ldc. - * E.g., fconst_0, sipush, and - * lconst_0 qualify for this treatment.
    • - *
    • aconst_null becomes ldc of a + * {@code ldc}. + * E.g., {@code fconst_0}, {@code sipush}, and + * {@code lconst_0} qualify for this treatment.
    • + *
    • {@code aconst_null} becomes {@code ldc} of a * "known null."
    • *
    • Shorthand local variable accessors become the corresponding - * longhand. E.g. aload_2 becomes aload.
    • - *
    • goto_w and jsr_w become goto - * and jsr (respectively).
    • - *
    • ldc_w becomes ldc.
    • - *
    • tableswitch becomes lookupswitch. + * longhand. E.g. {@code aload_2} becomes {@code aload}.
    • + *
    • {@code goto_w} and {@code jsr_w} become {@code goto} + * and {@code jsr} (respectively).
    • + *
    • {@code ldc_w} becomes {@code ldc}.
    • + *
    • {@code tableswitch} becomes {@code lookupswitch}. *
    • Arithmetic, array, and value-returning ops are collapsed - * to the int variant opcode, with the type + * to the {@code int} variant opcode, with the {@code type} * argument set to indicate the actual type. E.g., - * fadd becomes iadd, but - * type is passed as Type.FLOAT in that - * case. Similarly, areturn becomes - * ireturn. (However, return remains + * {@code fadd} becomes {@code iadd}, but + * {@code type} is passed as {@code Type.FLOAT} in that + * case. Similarly, {@code areturn} becomes + * {@code ireturn}. (However, {@code return} remains * unchanged.
    • - *
    • Local variable access ops are collapsed to the int - * variant opcode, with the type argument set to indicate - * the actual type. E.g., aload becomes iload, - * but type is passed as Type.OBJECT in + *
    • Local variable access ops are collapsed to the {@code int} + * variant opcode, with the {@code type} argument set to indicate + * the actual type. E.g., {@code aload} becomes {@code iload}, + * but {@code type} is passed as {@code Type.OBJECT} in * that case.
    • - *
    • Numeric conversion ops (i2l, etc.) are left alone - * to avoid too much confustion, but their type is - * the pushed type. E.g., i2b gets type - * Type.INT, and f2d gets type - * Type.DOUBLE. Other unaltered opcodes also get - * their pushed type. E.g., arraylength gets type - * Type.INT.
    • + *
    • Numeric conversion ops ({@code i2l}, etc.) are left alone + * to avoid too much confustion, but their {@code type} is + * the pushed type. E.g., {@code i2b} gets type + * {@code Type.INT}, and {@code f2d} gets type + * {@code Type.DOUBLE}. Other unaltered opcodes also get + * their pushed type. E.g., {@code arraylength} gets type + * {@code Type.INT}.
    • *
    * - * @param offset >= 0, < bytes.size(); offset to the start of the + * @param offset {@code >= 0, < bytes.size();} offset to the start of the * instruction - * @param visitor null-ok; visitor to call back to + * @param visitor {@code null-ok;} visitor to call back to * @return the length of the instruction, in bytes */ public int parseInstruction(int offset, Visitor visitor) { @@ -797,10 +797,10 @@ public final class BytecodeArray { } /** - * Helper to deal with tableswitch. + * Helper to deal with {@code tableswitch}. * - * @param offset the offset to the tableswitch opcode itself - * @param visitor non-null; visitor to use + * @param offset the offset to the {@code tableswitch} opcode itself + * @param visitor {@code non-null;} visitor to use * @return instruction length, in bytes */ private int parseTableswitch(int offset, Visitor visitor) { @@ -840,10 +840,10 @@ public final class BytecodeArray { } /** - * Helper to deal with lookupswitch. + * Helper to deal with {@code lookupswitch}. * - * @param offset the offset to the lookupswitch opcode itself - * @param visitor non-null; visitor to use + * @param offset the offset to the {@code lookupswitch} opcode itself + * @param visitor {@code non-null;} visitor to use * @return instruction length, in bytes */ private int parseLookupswitch(int offset, Visitor visitor) { @@ -878,10 +878,10 @@ public final class BytecodeArray { } /** - * Helper to deal with newarray. + * Helper to deal with {@code newarray}. * - * @param offset the offset to the newarray opcode itself - * @param visitor non-null; visitor to use + * @param offset the offset to the {@code newarray} opcode itself + * @param visitor {@code non-null;} visitor to use * @return instruction length, in bytes */ private int parseNewarray(int offset, Visitor visitor) { @@ -1061,10 +1061,10 @@ public final class BytecodeArray { /** - * Helper to deal with wide. + * Helper to deal with {@code wide}. * - * @param offset the offset to the wide opcode itself - * @param visitor non-null; visitor to use + * @param offset the offset to the {@code wide} opcode itself + * @param visitor {@code non-null;} visitor to use * @return instruction length, in bytes */ private int parseWide(int offset, Visitor visitor) { @@ -1159,7 +1159,7 @@ public final class BytecodeArray { * @param opcode the opcode * @param offset offset to the instruction * @param length length of the instruction, in bytes - * @param type non-null; type the instruction operates on + * @param type {@code non-null;} type the instruction operates on */ public void visitNoArgs(int opcode, int offset, int length, Type type); @@ -1171,9 +1171,9 @@ public final class BytecodeArray { * @param offset offset to the instruction * @param length length of the instruction, in bytes * @param idx the local variable index - * @param type non-null; the type of the accessed value + * @param type {@code non-null;} the type of the accessed value * @param value additional literal integer argument, if salient (i.e., - * for iinc) + * for {@code iinc}) */ public void visitLocal(int opcode, int offset, int length, int idx, Type type, int value); @@ -1182,23 +1182,23 @@ public final class BytecodeArray { * Visits an instruction which has a (possibly synthetic) * constant argument, and possibly also an * additional literal integer argument. In the case of - * multianewarray, the argument is the count of - * dimensions. In the case of invokeinterface, + * {@code multianewarray}, the argument is the count of + * dimensions. In the case of {@code invokeinterface}, * the argument is the parameter count or'ed with the * should-be-zero value left-shifted by 8. In the case of entries - * of type int, the value field always + * of type {@code int}, the {@code value} field always * holds the raw value (for convenience of clients). * *

    Note: In order to avoid giving it a barely-useful - * visitor all its own, newarray also uses this - * form, passing value as the array type code and - * cst as a {@link CstType} instance + * visitor all its own, {@code newarray} also uses this + * form, passing {@code value} as the array type code and + * {@code cst} as a {@link CstType} instance * corresponding to the array type.

    * * @param opcode the opcode * @param offset offset to the instruction * @param length length of the instruction, in bytes - * @param cst non-null; the constant + * @param cst {@code non-null;} the constant * @param value additional literal integer argument, if salient * (ignore if not) */ @@ -1222,7 +1222,7 @@ public final class BytecodeArray { * @param opcode the opcode * @param offset offset to the instruction * @param length length of the instruction, in bytes - * @param cases non-null; list of (value, target) pairs, plus the + * @param cases {@code non-null;} list of (value, target) pairs, plus the * default target * @param padding the bytes found in the padding area (if any), * packed @@ -1235,8 +1235,8 @@ public final class BytecodeArray { * * @param offset offset to the instruction * @param length length of the instruction, in bytes - * @param cst non-null; the type of the array - * @param initVals non-null; list of bytecode offsets for init values + * @param cst {@code non-null;} the type of the array + * @param initVals {@code non-null;} list of bytecode offsets for init values */ public void visitNewarray(int offset, int length, CstType type, ArrayList initVals); diff --git a/dx/src/com/android/dx/cf/code/ConcreteMethod.java b/dx/src/com/android/dx/cf/code/ConcreteMethod.java index 47f698dc2..70b6b452f 100644 --- a/dx/src/com/android/dx/cf/code/ConcreteMethod.java +++ b/dx/src/com/android/dx/cf/code/ConcreteMethod.java @@ -35,38 +35,38 @@ import com.android.dx.rop.type.Prototype; * Container for all the giblets that make up a concrete Java bytecode method. * It implements {@link Method}, so it provides all the original access * (by delegation), but it also constructs and keeps useful versions of - * stuff extracted from the method's Code attribute. + * stuff extracted from the method's {@code Code} attribute. */ public final class ConcreteMethod implements Method { - /** non-null; method being wrapped */ + /** {@code non-null;} method being wrapped */ private final Method method; /** - * null-ok; the class's SourceFile attribute value, + * {@code null-ok;} the class's {@code SourceFile} attribute value, * if any */ private final CstUtf8 sourceFile; /** * whether the class that this method is part of is defined with - * ACC_SUPER + * {@code ACC_SUPER} */ private final boolean accSuper; - /** non-null; the code attribute */ + /** {@code non-null;} the code attribute */ private final AttCode attCode; - /** non-null; line number list */ + /** {@code non-null;} line number list */ private final LineNumberList lineNumbers; - /** non-null; local variable list */ + /** {@code non-null;} local variable list */ private final LocalVariableList localVariables; /** * Constructs an instance. * - * @param method non-null; the method to be based on - * @param cf non-null; the class file that contains this method + * @param method {@code non-null;} the method to be based on + * @param cf {@code non-null;} the class file that contains this method * @param keepLines whether to keep the line number information * (if any) * @param keepLocals whether to keep the local variable @@ -178,9 +178,9 @@ public final class ConcreteMethod implements Method { /** * Gets whether the class that this method is part of is defined with - * ACC_SUPER. + * {@code ACC_SUPER}. * - * @return the ACC_SUPER value + * @return the {@code ACC_SUPER} value */ public boolean getAccSuper() { return accSuper; @@ -189,7 +189,7 @@ public final class ConcreteMethod implements Method { /** * Gets the maximum stack size. * - * @return >= 0; the maximum stack size + * @return {@code >= 0;} the maximum stack size */ public int getMaxStack() { return attCode.getMaxStack(); @@ -198,7 +198,7 @@ public final class ConcreteMethod implements Method { /** * Gets the number of locals. * - * @return >= 0; the number of locals + * @return {@code >= 0;} the number of locals */ public int getMaxLocals() { return attCode.getMaxLocals(); @@ -207,7 +207,7 @@ public final class ConcreteMethod implements Method { /** * Gets the bytecode array. * - * @return non-null; the bytecode array + * @return {@code non-null;} the bytecode array */ public BytecodeArray getCode() { return attCode.getCode(); @@ -216,7 +216,7 @@ public final class ConcreteMethod implements Method { /** * Gets the exception table. * - * @return non-null; the exception table + * @return {@code non-null;} the exception table */ public ByteCatchList getCatches() { return attCode.getCatches(); @@ -225,7 +225,7 @@ public final class ConcreteMethod implements Method { /** * Gets the line number list. * - * @return non-null; the line number list + * @return {@code non-null;} the line number list */ public LineNumberList getLineNumbers() { return lineNumbers; @@ -234,7 +234,7 @@ public final class ConcreteMethod implements Method { /** * Gets the local variable list. * - * @return non-null; the local variable list + * @return {@code non-null;} the local variable list */ public LocalVariableList getLocalVariables() { return localVariables; @@ -244,8 +244,8 @@ public final class ConcreteMethod implements Method { * Returns a {@link SourcePosition} instance corresponding to the * given bytecode offset. * - * @param offset >= 0; the bytecode offset - * @return non-null; an appropriate instance + * @param offset {@code >= 0;} the bytecode offset + * @return {@code non-null;} an appropriate instance */ public SourcePosition makeSourcePosistion(int offset) { return new SourcePosition(sourceFile, offset, diff --git a/dx/src/com/android/dx/cf/code/ExecutionStack.java b/dx/src/com/android/dx/cf/code/ExecutionStack.java index 1a2b565dd..15a9e6c12 100644 --- a/dx/src/com/android/dx/cf/code/ExecutionStack.java +++ b/dx/src/com/android/dx/cf/code/ExecutionStack.java @@ -30,11 +30,11 @@ import com.android.dx.util.MutabilityControl; * TypeBearer}.

    */ public final class ExecutionStack extends MutabilityControl { - /** non-null; array of stack contents */ + /** {@code non-null;} array of stack contents */ private final TypeBearer[] stack; /** - * >= 0; stack pointer (points one past the end) / current stack + * {@code >= 0;} stack pointer (points one past the end) / current stack * size */ private int stackPtr; @@ -42,7 +42,7 @@ public final class ExecutionStack extends MutabilityControl { /** * Constructs an instance. * - * @param maxStack >= 0; the maximum size of the stack for this + * @param maxStack {@code >= 0;} the maximum size of the stack for this * instance */ public ExecutionStack(int maxStack) { @@ -54,7 +54,7 @@ public final class ExecutionStack extends MutabilityControl { /** * Makes and returns a mutable copy of this instance. * - * @return non-null; the copy + * @return {@code non-null;} the copy */ public ExecutionStack copy() { ExecutionStack result = new ExecutionStack(stack.length); @@ -69,7 +69,7 @@ public final class ExecutionStack extends MutabilityControl { * Annotates (adds context to) the given exception with information * about this instance. * - * @param ex non-null; the exception to annotate + * @param ex {@code non-null;} the exception to annotate */ public void annotate(ExceptionWithContext ex) { int limit = stackPtr - 1; @@ -86,7 +86,7 @@ public final class ExecutionStack extends MutabilityControl { * Replaces all the occurrences of the given uninitialized type in * this stack with its initialized equivalent. * - * @param type non-null; type to replace + * @param type {@code non-null;} type to replace */ public void makeInitialized(Type type) { if (stackPtr == 0) { @@ -108,7 +108,7 @@ public final class ExecutionStack extends MutabilityControl { /** * Gets the maximum stack size for this instance. * - * @return >= 0; the max stack size + * @return {@code >= 0;} the max stack size */ public int getMaxStack() { return stack.length; @@ -117,7 +117,7 @@ public final class ExecutionStack extends MutabilityControl { /** * Gets the current stack size. * - * @return >= 0, < getMaxStack(); the current stack size + * @return {@code >= 0, < getMaxStack();} the current stack size */ public int size() { return stackPtr; @@ -139,7 +139,7 @@ public final class ExecutionStack extends MutabilityControl { /** * Pushes a value of the given type onto the stack. * - * @param type non-null; type of the value + * @param type {@code non-null;} type of the value * @throws SimException thrown if there is insufficient room on the * stack for the value */ @@ -171,14 +171,14 @@ public final class ExecutionStack extends MutabilityControl { } /** - * Peeks at the nth element down from the top of the stack. - * n == 0 means to peek at the top of the stack. Note that - * this will return null if the indicated element is the + * Peeks at the {@code n}th element down from the top of the stack. + * {@code n == 0} means to peek at the top of the stack. Note that + * this will return {@code null} if the indicated element is the * deeper half of a category-2 value. * - * @param n >= 0; which element to peek at - * @return null-ok; the type of value stored at that element - * @throws SimException thrown if n >= size() + * @param n {@code >= 0;} which element to peek at + * @return {@code null-ok;} the type of value stored at that element + * @throws SimException thrown if {@code n >= size()} */ public TypeBearer peek(int n) { if (n < 0) { @@ -193,10 +193,10 @@ public final class ExecutionStack extends MutabilityControl { } /** - * Peeks at the nth element down from the top of the + * Peeks at the {@code n}th element down from the top of the * stack, returning the type per se, as opposed to the * type-bearer. This method is just a convenient shorthand - * for peek(n).getType(). + * for {@code peek(n).getType()}. * * @see #peek */ @@ -207,7 +207,7 @@ public final class ExecutionStack extends MutabilityControl { /** * Pops the top element off of the stack. * - * @return non-null; the type formerly on the top of the stack + * @return {@code non-null;} the type formerly on the top of the stack * @throws SimException thrown if the stack is empty */ public TypeBearer pop() { @@ -227,10 +227,10 @@ public final class ExecutionStack extends MutabilityControl { * the following restriction on its behavior: You may only replace * values with other values of the same category. * - * @param n >= 0; which element to change, where 0 is + * @param n {@code >= 0;} which element to change, where {@code 0} is * the top element of the stack - * @param type non-null; type of the new value - * @throws SimException thrown if n >= size() or + * @param type {@code non-null;} type of the new value + * @throws SimException thrown if {@code n >= size()} or * the action is otherwise prohibited */ public void change(int n, TypeBearer type) { @@ -262,8 +262,8 @@ public final class ExecutionStack extends MutabilityControl { * returned. See {@link Merger#mergeStack(ExecutionStack,ExecutionStack) * Merger.mergeStack()} * - * @param other non-null; a stack to merge with - * @return non-null; the result of the merge + * @param other {@code non-null;} a stack to merge with + * @return {@code non-null;} the result of the merge */ public ExecutionStack merge(ExecutionStack other) { try { @@ -279,11 +279,11 @@ public final class ExecutionStack extends MutabilityControl { /** * Gets the string form for a stack element. This is the same as - * toString() except that null is converted - * to "<invalid>". + * {@code toString()} except that {@code null} is converted + * to {@code ""}. * - * @param type null-ok; the stack element - * @return non-null; the string form + * @param type {@code null-ok;} the stack element + * @return {@code non-null;} the string form */ private static String stackElementString(TypeBearer type) { if (type == null) { @@ -296,7 +296,7 @@ public final class ExecutionStack extends MutabilityControl { /** * Throws a properly-formatted exception. * - * @param msg non-null; useful message + * @param msg {@code non-null;} useful message * @return never (keeps compiler happy) */ private static TypeBearer throwSimException(String msg) { diff --git a/dx/src/com/android/dx/cf/code/Frame.java b/dx/src/com/android/dx/cf/code/Frame.java index a74d142ee..f34533530 100644 --- a/dx/src/com/android/dx/cf/code/Frame.java +++ b/dx/src/com/android/dx/cf/code/Frame.java @@ -29,20 +29,20 @@ import com.android.dx.util.IntList; * results" area. */ public final class Frame { - /** non-null; the locals */ + /** {@code non-null;} the locals */ private final LocalsArray locals; - /** non-null; the stack */ + /** {@code non-null;} the stack */ private final ExecutionStack stack; - /** null-ok; stack of labels of subroutines that this block is nested in */ + /** {@code null-ok;} stack of labels of subroutines that this block is nested in */ private final IntList subroutines; /** * Constructs an instance. * - * @param locals non-null; the locals array to use - * @param stack non-null; the execution stack to use + * @param locals {@code non-null;} the locals array to use + * @param stack {@code non-null;} the execution stack to use */ private Frame(LocalsArray locals, ExecutionStack stack) { this(locals, stack, IntList.EMPTY); @@ -51,9 +51,9 @@ public final class Frame { /** * Constructs an instance. * - * @param locals non-null; the locals array to use - * @param stack non-null; the execution stack to use - * @param subroutines non-null; list of subroutine start labels for + * @param locals {@code non-null;} the locals array to use + * @param stack {@code non-null;} the execution stack to use + * @param subroutines {@code non-null;} list of subroutine start labels for * subroutines this frame is nested in */ private Frame(LocalsArray locals, @@ -75,12 +75,12 @@ public final class Frame { /** * Constructs an instance. The locals array initially consists of - * all-uninitialized values (represented as nulls) and + * all-uninitialized values (represented as {@code null}s) and * the stack starts out empty. * - * @param maxLocals >= 0; the maximum number of locals this instance + * @param maxLocals {@code >= 0;} the maximum number of locals this instance * can refer to - * @param maxStack >= 0; the maximum size of the stack for this + * @param maxStack {@code >= 0;} the maximum size of the stack for this * instance */ public Frame(int maxLocals, int maxStack) { @@ -92,7 +92,7 @@ public final class Frame { * contains copies of the locals and stack (that is, it doesn't * share them with the original). * - * @return non-null; the copy + * @return {@code non-null;} the copy */ public Frame copy() { return new Frame(locals.copy(), stack.copy(), subroutines); @@ -111,7 +111,7 @@ public final class Frame { * Replaces all the occurrences of the given uninitialized type in * this frame with its initialized equivalent. * - * @param type non-null; type to replace + * @param type {@code non-null;} type to replace */ public void makeInitialized(Type type) { locals.makeInitialized(type); @@ -121,7 +121,7 @@ public final class Frame { /** * Gets the locals array for this instance. * - * @return non-null; the locals array + * @return {@code non-null;} the locals array */ public LocalsArray getLocals() { return locals; @@ -130,7 +130,7 @@ public final class Frame { /** * Gets the execution stack for this instance. * - * @return non-null; the execution stack + * @return {@code non-null;} the execution stack */ public ExecutionStack getStack() { return stack; @@ -143,7 +143,7 @@ public final class Frame { * list is ordered such that the deepest nesting (the actual subroutine * this block is in) is the last label in the list. * - * @return non-null; list as noted above + * @return {@code non-null;} list as noted above */ public IntList getSubroutines() { return subroutines; @@ -171,10 +171,10 @@ public final class Frame { * be used when returning from a subroutine. The stack state of all * subroutine invocations is identical, but the locals state may differ. * - * @param startLabel >=0; The label of the returning subroutine's + * @param startLabel {@code >=0;} The label of the returning subroutine's * start block - * @param subLabel >=0; A calling label of a subroutine - * @return null-ok; an appropriatly-constructed instance, or null + * @param subLabel {@code >=0;} A calling label of a subroutine + * @return {@code null-ok;} an appropriatly-constructed instance, or null * if label is not in the set */ public Frame subFrameForLabel(int startLabel, int subLabel) { @@ -206,8 +206,8 @@ public final class Frame { * Merges two frames. If the merged result is the same as this frame, * then this instance is returned. * - * @param other non-null; another frame - * @return non-null; the result of merging the two frames + * @param other {@code non-null;} another frame + * @return {@code non-null;} the result of merging the two frames */ public Frame mergeWith(Frame other) { LocalsArray resultLocals; @@ -237,7 +237,7 @@ public final class Frame { * * @param otherSubroutines label list of subroutine start blocks, from * least-nested to most-nested. - * @return non-null; merged subroutine nest list as described above + * @return {@code non-null;} merged subroutine nest list as described above */ private IntList mergeSubroutineLists(IntList otherSubroutines) { if (subroutines.equals(otherSubroutines)) { @@ -265,10 +265,10 @@ public final class Frame { * need to be trimmed of all OneLocalsArray elements that relevent to * the subroutine that is returning. * - * @param locals non-null; LocalsArray from before a merge - * @param subroutines non-null; a label list of subroutine start blocks + * @param locals {@code non-null;} LocalsArray from before a merge + * @param subroutines {@code non-null;} a label list of subroutine start blocks * representing the subroutine nesting of the block being merged into. - * @return non-null; locals set appropriate for merge + * @return {@code non-null;} locals set appropriate for merge */ private static LocalsArray adjustLocalsForSubroutines( LocalsArray locals, IntList subroutines) { @@ -301,13 +301,13 @@ public final class Frame { /** * Merges this frame with the frame of a subroutine caller at - * predLabel. Only called on the frame at the first + * {@code predLabel}. Only called on the frame at the first * block of a subroutine. * - * @param other non-null; another frame + * @param other {@code non-null;} another frame * @param subLabel label of subroutine start block * @param predLabel label of calling block - * @return non-null; the result of merging the two frames + * @return {@code non-null;} the result of merging the two frames */ public Frame mergeWithSubroutineCaller(Frame other, int subLabel, int predLabel) { @@ -374,7 +374,7 @@ public final class Frame { * subroutine calls return. * * @param subLabel label of subroutine start block - * @param callerLabel >=0 label of the caller block where this frame + * @param callerLabel {@code >=0;} label of the caller block where this frame * came from. * @return a new instance to begin a called subroutine. */ @@ -406,7 +406,7 @@ public final class Frame { * Annotates (adds context to) the given exception with information * about this frame. * - * @param ex non-null; the exception to annotate + * @param ex {@code non-null;} the exception to annotate */ public void annotate(ExceptionWithContext ex) { locals.annotate(ex); diff --git a/dx/src/com/android/dx/cf/code/LineNumberList.java b/dx/src/com/android/dx/cf/code/LineNumberList.java index 35875b0a7..7af3f4e6b 100644 --- a/dx/src/com/android/dx/cf/code/LineNumberList.java +++ b/dx/src/com/android/dx/cf/code/LineNumberList.java @@ -20,19 +20,19 @@ import com.android.dx.util.FixedSizeList; /** * List of "line number" entries, which are the contents of - * LineNumberTable attributes. + * {@code LineNumberTable} attributes. */ public final class LineNumberList extends FixedSizeList { - /** non-null; zero-size instance */ + /** {@code non-null;} zero-size instance */ public static final LineNumberList EMPTY = new LineNumberList(0); /** * Returns an instance which is the concatenation of the two given * instances. * - * @param list1 non-null; first instance - * @param list2 non-null; second instance - * @return non-null; combined instance + * @param list1 {@code non-null;} first instance + * @param list2 {@code non-null;} second instance + * @return {@code non-null;} combined instance */ public static LineNumberList concat(LineNumberList list1, LineNumberList list2) { @@ -68,8 +68,8 @@ public final class LineNumberList extends FixedSizeList { /** * Gets the indicated item. * - * @param n >= 0; which item - * @return null-ok; the indicated item + * @param n {@code >= 0;} which item + * @return {@code null-ok;} the indicated item */ public Item get(int n) { return (Item) get0(n); @@ -78,8 +78,8 @@ public final class LineNumberList extends FixedSizeList { /** * Sets the item at the given index. * - * @param n >= 0, < size(); which element - * @param item non-null; the item + * @param n {@code >= 0, < size();} which element + * @param item {@code non-null;} the item */ public void set(int n, Item item) { if (item == null) { @@ -92,9 +92,9 @@ public final class LineNumberList extends FixedSizeList { /** * Sets the item at the given index. * - * @param n >= 0, < size(); which element - * @param startPc >= 0; start pc of this item - * @param lineNumber >= 0; corresponding line number + * @param n {@code >= 0, < size();} which element + * @param startPc {@code >= 0;} start pc of this item + * @param lineNumber {@code >= 0;} corresponding line number */ public void set(int n, int startPc, int lineNumber) { set0(n, new Item(startPc, lineNumber)); @@ -103,8 +103,8 @@ public final class LineNumberList extends FixedSizeList { /** * Gets the line number associated with the given address. * - * @param pc >= 0; the address to look up - * @return >= -1; the associated line number, or -1 if + * @param pc {@code >= 0;} the address to look up + * @return {@code >= -1;} the associated line number, or {@code -1} if * none is known */ public int pcToLine(int pc) { @@ -138,17 +138,17 @@ public final class LineNumberList extends FixedSizeList { * Item in a line number table. */ public static class Item { - /** >= 0; start pc of this item */ + /** {@code >= 0;} start pc of this item */ private final int startPc; - /** >= 0; corresponding line number */ + /** {@code >= 0;} corresponding line number */ private final int lineNumber; /** * Constructs an instance. * - * @param startPc >= 0; start pc of this item - * @param lineNumber >= 0; corresponding line number + * @param startPc {@code >= 0;} start pc of this item + * @param lineNumber {@code >= 0;} corresponding line number */ public Item(int startPc, int lineNumber) { if (startPc < 0) { diff --git a/dx/src/com/android/dx/cf/code/LocalVariableList.java b/dx/src/com/android/dx/cf/code/LocalVariableList.java index 10876036b..8f49a335f 100644 --- a/dx/src/com/android/dx/cf/code/LocalVariableList.java +++ b/dx/src/com/android/dx/cf/code/LocalVariableList.java @@ -23,20 +23,20 @@ import com.android.dx.util.FixedSizeList; /** * List of "local variable" entries, which are the contents of - * LocalVariableTable and LocalVariableTypeTable + * {@code LocalVariableTable} and {@code LocalVariableTypeTable} * attributes, as well as combinations of the two. */ public final class LocalVariableList extends FixedSizeList { - /** non-null; zero-size instance */ + /** {@code non-null;} zero-size instance */ public static final LocalVariableList EMPTY = new LocalVariableList(0); /** * Returns an instance which is the concatenation of the two given * instances. The result is immutable. * - * @param list1 non-null; first instance - * @param list2 non-null; second instance - * @return non-null; combined instance + * @param list1 {@code non-null;} first instance + * @param list2 {@code non-null;} second instance + * @return {@code non-null;} combined instance */ public static LocalVariableList concat(LocalVariableList list1, LocalVariableList list2) { @@ -70,14 +70,13 @@ public final class LocalVariableList extends FixedSizeList { * element in the signature list gets augmented with the * corresponding signature. The result is immutable. * - * @param descriptorList non-null; list with descriptors - * @param signatureList non-null; list with signatures - * @return non-null; the merged result + * @param descriptorList {@code non-null;} list with descriptors + * @param signatureList {@code non-null;} list with signatures + * @return {@code non-null;} the merged result */ public static LocalVariableList mergeDescriptorsAndSignatures( LocalVariableList descriptorList, LocalVariableList signatureList) { - int signatureSize = signatureList.size(); int descriptorSize = descriptorList.size(); LocalVariableList result = new LocalVariableList(descriptorSize); @@ -107,8 +106,8 @@ public final class LocalVariableList extends FixedSizeList { /** * Gets the indicated item. * - * @param n >= 0; which item - * @return null-ok; the indicated item + * @param n {@code >= 0;} which item + * @return {@code null-ok;} the indicated item */ public Item get(int n) { return (Item) get0(n); @@ -117,8 +116,8 @@ public final class LocalVariableList extends FixedSizeList { /** * Sets the item at the given index. * - * @param n >= 0, < size(); which element - * @param item non-null; the item + * @param n {@code >= 0, < size();} which element + * @param item {@code non-null;} the item */ public void set(int n, Item item) { if (item == null) { @@ -131,17 +130,17 @@ public final class LocalVariableList extends FixedSizeList { /** * Sets the item at the given index. * - *

    Note: At least one of descriptor or - * signature must be passed as non-null.

    + *

    Note: At least one of {@code descriptor} or + * {@code signature} must be passed as non-null.

    * - * @param n >= 0, < size(); which element - * @param startPc >= 0; the start pc of this variable's scope - * @param length >= 0; the length (in bytecodes) of this variable's + * @param n {@code >= 0, < size();} which element + * @param startPc {@code >= 0;} the start pc of this variable's scope + * @param length {@code >= 0;} the length (in bytecodes) of this variable's * scope - * @param name non-null; the variable's name - * @param descriptor null-ok; the variable's type descriptor - * @param signature null-ok; the variable's type signature - * @param index >= 0; the variable's local index + * @param name {@code non-null;} the variable's name + * @param descriptor {@code null-ok;} the variable's type descriptor + * @param signature {@code null-ok;} the variable's type signature + * @param index {@code >= 0;} the variable's local index */ public void set(int n, int startPc, int length, CstUtf8 name, CstUtf8 descriptor, CstUtf8 signature, int index) { @@ -153,9 +152,9 @@ public final class LocalVariableList extends FixedSizeList { * the given {@link com.android.dx.cf.code.LocalVariableList.Item} * in all respects but the type descriptor and signature, if any. * - * @param item non-null; local variable information to match - * @return null-ok; the corresponding local variable information stored - * in this instance, or null if there is no matching + * @param item {@code non-null;} local variable information to match + * @return {@code null-ok;} the corresponding local variable information stored + * in this instance, or {@code null} if there is no matching * information */ public Item itemToLocal(Item item) { @@ -178,10 +177,10 @@ public final class LocalVariableList extends FixedSizeList { * variable's start point is listed as the address of the instruction * just past the one that sets the variable. * - * @param pc >= 0; the address to look up - * @param index >= 0; the local variable index - * @return null-ok; the associated local variable information, or - * null if none is known + * @param pc {@code >= 0;} the address to look up + * @param index {@code >= 0;} the local variable index + * @return {@code null-ok;} the associated local variable information, or + * {@code null} if none is known */ public Item pcAndIndexToLocal(int pc, int index) { int sz = size(); @@ -201,37 +200,37 @@ public final class LocalVariableList extends FixedSizeList { * Item in a local variable table. */ public static class Item { - /** >= 0; the start pc of this variable's scope */ + /** {@code >= 0;} the start pc of this variable's scope */ private final int startPc; - /** >= 0; the length (in bytecodes) of this variable's scope */ + /** {@code >= 0;} the length (in bytecodes) of this variable's scope */ private final int length; - /** non-null; the variable's name */ + /** {@code non-null;} the variable's name */ private final CstUtf8 name; - /** null-ok; the variable's type descriptor */ + /** {@code null-ok;} the variable's type descriptor */ private final CstUtf8 descriptor; - /** null-ok; the variable's type signature */ + /** {@code null-ok;} the variable's type signature */ private final CstUtf8 signature; - /** >= 0; the variable's local index */ + /** {@code >= 0;} the variable's local index */ private final int index; /** * Constructs an instance. * - *

    Note: At least one of descriptor or - * signature must be passed as non-null.

    + *

    Note: At least one of {@code descriptor} or + * {@code signature} must be passed as non-null.

    * - * @param startPc >= 0; the start pc of this variable's scope - * @param length >= 0; the length (in bytecodes) of this variable's + * @param startPc {@code >= 0;} the start pc of this variable's scope + * @param length {@code >= 0;} the length (in bytecodes) of this variable's * scope - * @param name non-null; the variable's name - * @param descriptor null-ok; the variable's type descriptor - * @param signature null-ok; the variable's type signature - * @param index >= 0; the variable's local index + * @param name {@code non-null;} the variable's name + * @param descriptor {@code null-ok;} the variable's type descriptor + * @param signature {@code null-ok;} the variable's type signature + * @param index {@code >= 0;} the variable's local index */ public Item(int startPc, int length, CstUtf8 name, CstUtf8 descriptor, CstUtf8 signature, int index) { @@ -267,7 +266,7 @@ public final class LocalVariableList extends FixedSizeList { /** * Gets the start pc of this variable's scope. * - * @return >= 0; the start pc of this variable's scope + * @return {@code >= 0;} the start pc of this variable's scope */ public int getStartPc() { return startPc; @@ -276,7 +275,7 @@ public final class LocalVariableList extends FixedSizeList { /** * Gets the length (in bytecodes) of this variable's scope. * - * @return >= 0; the length (in bytecodes) of this variable's scope + * @return {@code >= 0;} the length (in bytecodes) of this variable's scope */ public int getLength() { return length; @@ -285,7 +284,7 @@ public final class LocalVariableList extends FixedSizeList { /** * Gets the variable's type descriptor. * - * @return null-ok; the variable's type descriptor + * @return {@code null-ok;} the variable's type descriptor */ public CstUtf8 getDescriptor() { return descriptor; @@ -294,7 +293,7 @@ public final class LocalVariableList extends FixedSizeList { /** * Gets the variable's LocalItem, a (name, signature) tuple * - * @return null-ok; the variable's type descriptor + * @return {@code null-ok;} the variable's type descriptor */ public LocalItem getLocalItem() { return LocalItem.make(name, signature); @@ -304,7 +303,7 @@ public final class LocalVariableList extends FixedSizeList { * Gets the variable's type signature. Private because if you need this, * you want getLocalItem() instead. * - * @return null-ok; the variable's type signature + * @return {@code null-ok;} the variable's type signature */ private CstUtf8 getSignature() { return signature; @@ -313,7 +312,7 @@ public final class LocalVariableList extends FixedSizeList { /** * Gets the variable's local index. * - * @return >= 0; the variable's local index + * @return {@code >= 0;} the variable's local index */ public int getIndex() { return index; @@ -321,9 +320,9 @@ public final class LocalVariableList extends FixedSizeList { /** * Gets the variable's type descriptor. This is a convenient shorthand - * for Type.intern(getDescriptor().getString()). + * for {@code Type.intern(getDescriptor().getString())}. * - * @return non-null; the variable's type + * @return {@code non-null;} the variable's type */ public Type getType() { return Type.intern(descriptor.getString()); @@ -333,8 +332,8 @@ public final class LocalVariableList extends FixedSizeList { * Constructs and returns an instance which is identical to this * one, except that the signature is changed to the given value. * - * @param newSignature non-null; the new signature - * @return non-null; an appropriately-constructed instance + * @param newSignature {@code non-null;} the new signature + * @return {@code non-null;} an appropriately-constructed instance */ public Item withSignature(CstUtf8 newSignature) { return new Item(startPc, length, name, descriptor, newSignature, @@ -345,10 +344,10 @@ public final class LocalVariableList extends FixedSizeList { * Gets whether this instance matches (describes) the given * address and index. * - * @param pc >= 0; the address in question - * @param index >= 0; the local variable index in question - * @return true iff this instance matches pc - * and index + * @param pc {@code >= 0;} the address in question + * @param index {@code >= 0;} the local variable index in question + * @return {@code true} iff this instance matches {@code pc} + * and {@code index} */ public boolean matchesPcAndIndex(int pc, int index) { return (index == this.index) && @@ -361,8 +360,8 @@ public final class LocalVariableList extends FixedSizeList { * other instance exactly in all fields except type descriptor and * type signature. * - * @param other non-null; the instance to compare to - * @return true iff this instance matches + * @param other {@code non-null;} the instance to compare to + * @return {@code true} iff this instance matches */ public boolean matchesAllButType(Item other) { return (startPc == other.startPc) diff --git a/dx/src/com/android/dx/cf/code/LocalsArray.java b/dx/src/com/android/dx/cf/code/LocalsArray.java index 1c324ca07..b2c268980 100644 --- a/dx/src/com/android/dx/cf/code/LocalsArray.java +++ b/dx/src/com/android/dx/cf/code/LocalsArray.java @@ -36,7 +36,7 @@ public abstract class LocalsArray extends MutabilityControl implements ToHuman { /** * Constructs an instance, explicitly indicating the mutability. * - * @param mutable true if this instance is mutable + * @param mutable {@code true} if this instance is mutable */ protected LocalsArray(boolean mutable) { super(mutable); @@ -45,7 +45,7 @@ public abstract class LocalsArray extends MutabilityControl implements ToHuman { /** * Makes and returns a mutable copy of this instance. * - * @return non-null; the copy + * @return {@code non-null;} the copy */ public abstract LocalsArray copy(); @@ -53,7 +53,7 @@ public abstract class LocalsArray extends MutabilityControl implements ToHuman { * Annotates (adds context to) the given exception with information * about this instance. * - * @param ex non-null; the exception to annotate + * @param ex {@code non-null;} the exception to annotate */ public abstract void annotate(ExceptionWithContext ex); @@ -61,7 +61,7 @@ public abstract class LocalsArray extends MutabilityControl implements ToHuman { * Replaces all the occurrences of the given uninitialized type in * this array with its initialized equivalent. * - * @param type non-null; type to replace + * @param type {@code non-null;} type to replace */ public abstract void makeInitialized(Type type); @@ -71,16 +71,17 @@ public abstract class LocalsArray extends MutabilityControl implements ToHuman { * @return the max locals */ public abstract int getMaxLocals(); + /** * Sets the type stored at the given local index. If the given type * is category-2, then (a) the index must be at least two less than - * getMaxLocals() and (b) the next index gets invalidated + * {@link #getMaxLocals} and (b) the next index gets invalidated * by the operation. In case of either category, if the previous * local contains a category-2 value, then it too is invalidated by * this operation. * - * @param idx >= 0, < getMaxLocals(); which local - * @param type non-null; new type for the local at idx + * @param idx {@code >= 0, < getMaxLocals();} which local + * @param type {@code non-null;} new type for the local at {@code idx} */ public abstract void set(int idx, TypeBearer type); @@ -88,25 +89,25 @@ public abstract class LocalsArray extends MutabilityControl implements ToHuman { * Sets the type for the local indicated by the given register spec * to that register spec (which includes type and optional name * information). This is identical to calling - * set(spec.getReg(), spec). + * {@code set(spec.getReg(), spec)}. * - * @param spec non-null; register spec to use as the basis for the update + * @param spec {@code non-null;} register spec to use as the basis for the update */ public abstract void set(RegisterSpec spec); /** * Invalidates the local at the given index. * - * @param idx >= 0, < getMaxLocals(); which local + * @param idx {@code >= 0, < getMaxLocals();} which local */ public abstract void invalidate(int idx); /** - * Gets the type stored at the given local index, or null + * Gets the type stored at the given local index, or {@code null} * if the given local is uninitialized / invalid. * - * @param idx >= 0, < getMaxLocals(); which local - * @return null-ok; the type of value stored in that local + * @param idx {@code >= 0, < getMaxLocals();} which local + * @return {@code null-ok;} the type of value stored in that local */ public abstract TypeBearer getOrNull(int idx); @@ -115,9 +116,9 @@ public abstract class LocalsArray extends MutabilityControl implements ToHuman { * the given local contains a valid type (though it is allowed to * be an uninitialized instance). * - * @param idx >= 0, < getMaxLocals(); which local - * @return non-null; the type of value stored in that local - * @throws SimException thrown if idx is valid, but + * @param idx {@code >= 0, < getMaxLocals();} which local + * @return {@code non-null;} the type of value stored in that local + * @throws SimException thrown if {@code idx} is valid, but * the contents are invalid */ public abstract TypeBearer get(int idx); @@ -126,9 +127,9 @@ public abstract class LocalsArray extends MutabilityControl implements ToHuman { * Gets the type stored at the given local index, which is expected * to be an initialized category-1 value. * - * @param idx >= 0, < getMaxLocals(); which local - * @return non-null; the type of value stored in that local - * @throws SimException thrown if idx is valid, but + * @param idx {@code >= 0, < getMaxLocals();} which local + * @return {@code non-null;} the type of value stored in that local + * @throws SimException thrown if {@code idx} is valid, but * one of the following holds: (a) the local is invalid; (b) the local * contains an uninitialized instance; (c) the local contains a * category-2 value @@ -139,39 +140,39 @@ public abstract class LocalsArray extends MutabilityControl implements ToHuman { * Gets the type stored at the given local index, which is expected * to be a category-2 value. * - * @param idx >= 0, < getMaxLocals(); which local - * @return non-null; the type of value stored in that local - * @throws SimException thrown if idx is valid, but + * @param idx {@code >= 0, < getMaxLocals();} which local + * @return {@code non-null;} the type of value stored in that local + * @throws SimException thrown if {@code idx} is valid, but * one of the following holds: (a) the local is invalid; (b) the local * contains a category-1 value */ public abstract TypeBearer getCategory2(int idx); /** - * Merges this instance with other. If the merged result is + * Merges this instance with {@code other}. If the merged result is * the same as this instance, then this is returned (not a copy). * - * @param other non-null; another LocalsArray - * @return non-null; the merge result, a new instance or this + * @param other {@code non-null;} another LocalsArray + * @return {@code non-null;} the merge result, a new instance or this */ public abstract LocalsArray merge(LocalsArray other); /** - * Merges this instance with a LocalsSet from a subroutine + * Merges this instance with a {@code LocalsSet} from a subroutine * caller. To be used when merging in the first block of a subroutine. * - * @param other other non-null; another LocalsArray. The final locals + * @param other {@code other non-null;} another LocalsArray. The final locals * state of a subroutine caller. * @param predLabel the label of the subroutine caller block. - * @return non-null; the merge result, a new instance or this + * @return {@code non-null;} the merge result, a new instance or this */ public abstract LocalsArraySet mergeWithSubroutineCaller (LocalsArray other, int predLabel); /** * Gets the locals set appropriate for the current execution context. - * That is, if this is a OneLocalsArray instance, then return - * this, otherwise return LocalsArraySet's + * That is, if this is a {@code OneLocalsArray} instance, then return + * {@code this}, otherwise return {@code LocalsArraySet}'s * primary. * * @return locals for this execution context. diff --git a/dx/src/com/android/dx/cf/code/LocalsArraySet.java b/dx/src/com/android/dx/cf/code/LocalsArraySet.java index 9e24da9b9..fa2acbe30 100644 --- a/dx/src/com/android/dx/cf/code/LocalsArraySet.java +++ b/dx/src/com/android/dx/cf/code/LocalsArraySet.java @@ -51,9 +51,9 @@ public class LocalsArraySet extends LocalsArray { /** * Constructs an instance. The locals array initially consists of - * all-uninitialized values (represented as nulls). + * all-uninitialized values (represented as {@code null}s). * - * @param maxLocals >= 0; the maximum number of locals this instance + * @param maxLocals {@code >= 0;} the maximum number of locals this instance * can refer to */ public LocalsArraySet(int maxLocals) { @@ -65,8 +65,8 @@ public class LocalsArraySet extends LocalsArray { /** * Constructs an instance with the specified primary and secondaries set. * - * @param primary non-null; primary locals to use - * @param secondaries non-null; secondaries set, indexed by subroutine + * @param primary {@code non-null;} primary locals to use + * @param secondaries {@code non-null;} secondaries set, indexed by subroutine * caller label. */ public LocalsArraySet(OneLocalsArray primary, @@ -80,7 +80,7 @@ public class LocalsArraySet extends LocalsArray { /** * Constructs an instance which is a copy of another. * - * @param toCopy non-null; instance to copy. + * @param toCopy {@code non-null;} instance to copy. */ private LocalsArraySet(LocalsArraySet toCopy) { super(toCopy.getMaxLocals() > 0); @@ -89,7 +89,7 @@ public class LocalsArraySet extends LocalsArray { secondaries = new ArrayList(toCopy.secondaries.size()); int sz = toCopy.secondaries.size(); - for(int i = 0; i < sz; i++) { + for (int i = 0; i < sz; i++) { LocalsArray la = toCopy.secondaries.get(i); if (la == null) { @@ -106,7 +106,7 @@ public class LocalsArraySet extends LocalsArray { public void setImmutable() { primary.setImmutable(); - for (LocalsArray la: secondaries) { + for (LocalsArray la : secondaries) { if (la != null) { la.setImmutable(); } @@ -127,7 +127,7 @@ public class LocalsArraySet extends LocalsArray { primary.annotate(ex); int sz = secondaries.size(); - for(int label = 0; label < sz; label++) { + for (int label = 0; label < sz; label++) { LocalsArray la = secondaries.get(label); if (la != null) { @@ -149,7 +149,7 @@ public class LocalsArraySet extends LocalsArray { sb.append('\n'); int sz = secondaries.size(); - for(int label = 0; label < sz; label++) { + for (int label = 0; label < sz; label++) { LocalsArray la = secondaries.get(label); if (la != null) { @@ -178,7 +178,7 @@ public class LocalsArraySet extends LocalsArray { primary.makeInitialized(type); - for (LocalsArray la: secondaries) { + for (LocalsArray la : secondaries) { if (la != null) { la.makeInitialized(type); } @@ -198,7 +198,7 @@ public class LocalsArraySet extends LocalsArray { primary.set(idx, type); - for (LocalsArray la: secondaries) { + for (LocalsArray la : secondaries) { if (la != null) { la.set(idx, type); } @@ -218,7 +218,7 @@ public class LocalsArraySet extends LocalsArray { primary.invalidate(idx); - for (LocalsArray la: secondaries) { + for (LocalsArray la : secondaries) { if (la != null) { la.invalidate(idx); } @@ -250,10 +250,10 @@ public class LocalsArraySet extends LocalsArray { } /** - * Merges this set with another LocalsArraySet instance. + * Merges this set with another {@code LocalsArraySet} instance. * - * @param other non-null; to merge - * @return non-null; this instance if merge was a no-op, or + * @param other {@code non-null;} to merge + * @return {@code non-null;} this instance if merge was a no-op, or * new merged instance. */ private LocalsArraySet mergeWithSet(LocalsArraySet other) { @@ -301,10 +301,10 @@ public class LocalsArraySet extends LocalsArray { } /** - * Merges this set with a OneLocalsArray instance. + * Merges this set with a {@code OneLocalsArray} instance. * - * @param other non-null; to merge - * @return non-null; this instance if merge was a no-op, or + * @param other {@code non-null;} to merge + * @return {@code non-null;} this instance if merge was a no-op, or * new merged instance. */ private LocalsArraySet mergeWithOne(OneLocalsArray other) { @@ -365,11 +365,11 @@ public class LocalsArraySet extends LocalsArray { } /** - * Gets the LocalsArray instance for a specified subroutine + * Gets the {@code LocalsArray} instance for a specified subroutine * caller label, or null if label has no locals associated with it. * - * @param label >=0 subroutine caller label - * @return null-ok; locals if available. + * @param label {@code >= 0;} subroutine caller label + * @return {@code null-ok;} locals if available. */ private LocalsArray getSecondaryForLabel(int label) { if (label >= secondaries.size()) { @@ -445,8 +445,8 @@ public class LocalsArraySet extends LocalsArray { * Returns a LocalsArray instance representing the locals state that should * be used when returning to a subroutine caller. * - * @param subLabel >= 0; A calling label of a subroutine - * @return null-ok; an instance for this subroutine, or null if subroutine + * @param subLabel {@code >= 0;} A calling label of a subroutine + * @return {@code null-ok;} an instance for this subroutine, or null if subroutine * is not in this set. */ public LocalsArray subArrayForLabel(int subLabel) { diff --git a/dx/src/com/android/dx/cf/code/Machine.java b/dx/src/com/android/dx/cf/code/Machine.java index 517a10d6a..aff50b2f3 100644 --- a/dx/src/com/android/dx/cf/code/Machine.java +++ b/dx/src/com/android/dx/cf/code/Machine.java @@ -32,9 +32,9 @@ public interface Machine { /** * Gets the effective prototype of the method that this instance is * being used for. The effective prototype includes an initial - * this argument for instance methods. + * {@code this} argument for instance methods. * - * @return non-null; the method prototype + * @return {@code non-null;} the method prototype */ public Prototype getPrototype(); @@ -48,21 +48,21 @@ public interface Machine { * and store them in the arguments area, indicating that there are now * that many arguments. Also, clear the auxiliary arguments. * - * @param frame non-null; frame to operate on - * @param count >= 0; number of values to pop + * @param frame {@code non-null;} frame to operate on + * @param count {@code >= 0;} number of values to pop */ public void popArgs(Frame frame, int count); /** * Pops values from the stack of the types indicated by the given - * Prototype (popped in reverse of the argument + * {@code Prototype} (popped in reverse of the argument * order, so the first prototype argument type is for the deepest * element of the stack), and store them in the arguments area, * indicating that there are now that many arguments. Also, clear * the auxiliary arguments. * - * @param frame non-null; frame to operate on - * @param prototype non-null; prototype indicating arguments to pop + * @param frame {@code non-null;} frame to operate on + * @param prototype {@code non-null;} prototype indicating arguments to pop */ public void popArgs(Frame frame, Prototype prototype); @@ -71,8 +71,8 @@ public interface Machine { * in the arguments area, indicating that there are now that many * arguments. Also, clear the auxiliary arguments. * - * @param frame non-null; frame to operate on - * @param type non-null; type of the argument + * @param frame {@code non-null;} frame to operate on + * @param type {@code non-null;} type of the argument */ public void popArgs(Frame frame, Type type); @@ -83,9 +83,9 @@ public interface Machine { * area, indicating that there are now that many arguments. Also, * clear the auxiliary arguments. * - * @param frame non-null; frame to operate on - * @param type1 non-null; type of the first argument - * @param type2 non-null; type of the second argument + * @param frame {@code non-null;} frame to operate on + * @param type1 {@code non-null;} type of the first argument + * @param type2 {@code non-null;} type of the second argument */ public void popArgs(Frame frame, Type type1, Type type2); @@ -96,10 +96,10 @@ public interface Machine { * area, indicating that there are now that many arguments. Also, * clear the auxiliary arguments. * - * @param frame non-null; frame to operate on - * @param type1 non-null; type of the first argument - * @param type2 non-null; type of the second argument - * @param type3 non-null; type of the third argument + * @param frame {@code non-null;} frame to operate on + * @param type1 {@code non-null;} type of the first argument + * @param type2 {@code non-null;} type of the second argument + * @param type3 {@code non-null;} type of the third argument */ public void popArgs(Frame frame, Type type1, Type type2, Type type3); @@ -107,8 +107,8 @@ public interface Machine { * Loads the local variable with the given index as the sole argument in * the arguments area. Also, clear the auxiliary arguments. * - * @param frame non-null; frame to operate on - * @param idx >= 0; the local variable index + * @param frame {@code non-null;} frame to operate on + * @param idx {@code >= 0;} the local variable index */ public void localArg(Frame frame, int idx); @@ -116,28 +116,28 @@ public interface Machine { * Indicates that the salient type of this operation is as * given. This differentiates between, for example, the various * arithmetic opcodes, which, by the time they hit a - * Machine are collapsed to the int + * {@code Machine} are collapsed to the {@code int} * variant. (See {@link BytecodeArray#parseInstruction} for * details.) * - * @param type non-null; the salient type of the upcoming operation + * @param type {@code non-null;} the salient type of the upcoming operation */ public void auxType(Type type); /** * Indicates that there is an auxiliary (inline, not stack) - * argument of type int, with the given value. + * argument of type {@code int}, with the given value. * *

    Note: Perhaps unintuitively, the stack manipulation - * ops (e.g., dup and swap) use this to + * ops (e.g., {@code dup} and {@code swap}) use this to * indicate the result stack pattern with a straightforward hex * encoding of the push order starting with least-significant * nibbles getting pushed first). For example, an all-category-1 - * dup2_x1 sets this to 0x12312, and the + * {@code dup2_x1} sets this to {@code 0x12312}, and the * other form of that op sets this to - * 0x121.

    + * {@code 0x121}.

    * - *

    Also Note: For switch* instructions, this is + *

    Also Note: For {@code switch*} instructions, this is * used to indicate the padding value (which is only useful for * verification).

    * @@ -149,10 +149,10 @@ public interface Machine { * Indicates that there is an auxiliary (inline, not stack) object * argument, with the value based on the given constant. * - *

    Note: Some opcodes use both int and + *

    Note: Some opcodes use both {@code int} and * constant auxiliary arguments.

    * - * @param cst non-null; the constant containing / referencing + * @param cst {@code non-null;} the constant containing / referencing * the value */ public void auxCstArg(Constant cst); @@ -167,12 +167,12 @@ public interface Machine { /** * Indicates that there is an auxiliary (inline, not stack) argument - * consisting of a switch* table. + * consisting of a {@code switch*} table. * *

    Note: This is generally used in conjunction with * {@link #auxIntArg} (which holds the padding).

    * - * @param cases non-null; the list of key-target pairs, plus the default + * @param cases {@code non-null;} the list of key-target pairs, plus the default * target */ public void auxSwitchArg(SwitchList cases); @@ -181,7 +181,7 @@ public interface Machine { * Indicates that there is an auxiliary (inline, not stack) argument * consisting of a list of initial values for a newly created array. * - * @param initValues non-null; the list of constant values to initialize + * @param initValues {@code non-null;} the list of constant values to initialize * the array */ public void auxInitValues(ArrayList initValues); @@ -189,9 +189,9 @@ public interface Machine { /** * Indicates that the target of this operation is the given local. * - * @param idx >= 0; the local variable index - * @param type non-null; the type of the local - * @param local null-ok; the name and signature of the local, if known + * @param idx {@code >= 0;} the local variable index + * @param type {@code non-null;} the type of the local + * @param local {@code null-ok;} the name and signature of the local, if known */ public void localTarget(int idx, Type type, LocalItem local); @@ -199,10 +199,10 @@ public interface Machine { * "Runs" the indicated opcode in an appropriate way, using the arguments * area as appropriate, and modifying the given frame in response. * - * @param frame non-null; frame to operate on - * @param offset >= 0; byte offset in the method to the opcode being + * @param frame {@code non-null;} frame to operate on + * @param offset {@code >= 0;} byte offset in the method to the opcode being * run - * @param opcode >= 0; the opcode to run + * @param opcode {@code >= 0;} the opcode to run */ public void run(Frame frame, int offset, int opcode); } diff --git a/dx/src/com/android/dx/cf/code/Merger.java b/dx/src/com/android/dx/cf/code/Merger.java index adeaab225..8da9a1841 100644 --- a/dx/src/com/android/dx/cf/code/Merger.java +++ b/dx/src/com/android/dx/cf/code/Merger.java @@ -35,9 +35,9 @@ public final class Merger { * Merges two locals arrays. If the merged result is the same as the first * argument, then return the first argument (not a copy). * - * @param locals1 non-null; a locals array - * @param locals2 non-null; another locals array - * @return non-null; the result of merging the two locals arrays + * @param locals1 {@code non-null;} a locals array + * @param locals2 {@code non-null;} another locals array + * @return {@code non-null;} the result of merging the two locals arrays */ public static OneLocalsArray mergeLocals(OneLocalsArray locals1, OneLocalsArray locals2) { @@ -87,9 +87,9 @@ public final class Merger { * Merges two stacks. If the merged result is the same as the first * argument, then return the first argument (not a copy). * - * @param stack1 non-null; a stack - * @param stack2 non-null; another stack - * @return non-null; the result of merging the two stacks + * @param stack1 {@code non-null;} a stack + * @param stack2 {@code non-null;} another stack + * @return {@code non-null;} the result of merging the two stacks */ public static ExecutionStack mergeStack(ExecutionStack stack1, ExecutionStack stack2) { @@ -144,9 +144,9 @@ public final class Merger { /** * Merges two frame types. * - * @param ft1 non-null; a frame type - * @param ft2 non-null; another frame type - * @return non-null; the result of merging the two types + * @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 */ public static TypeBearer mergeType(TypeBearer ft1, TypeBearer ft2) { if ((ft1 == null) || ft1.equals(ft2)) { @@ -209,12 +209,12 @@ public final class Merger { * the given subtype. This takes into account primitiveness, * int-likeness, known-nullness, and array dimensions, but does * not assume anything about class hierarchy other than that the - * type Object is the supertype of all reference + * type {@code Object} is the supertype of all reference * types and all arrays are assignable to - * Serializable and Cloneable. + * {@code Serializable} and {@code Cloneable}. * - * @param supertypeBearer non-null; the supertype - * @param subtypeBearer non-null; the subtype + * @param supertypeBearer {@code non-null;} the supertype + * @param subtypeBearer {@code non-null;} the subtype */ public static boolean isPossiblyAssignableFrom(TypeBearer supertypeBearer, TypeBearer subtypeBearer) { diff --git a/dx/src/com/android/dx/cf/code/OneLocalsArray.java b/dx/src/com/android/dx/cf/code/OneLocalsArray.java index 3a590a105..cafd177d3 100644 --- a/dx/src/com/android/dx/cf/code/OneLocalsArray.java +++ b/dx/src/com/android/dx/cf/code/OneLocalsArray.java @@ -31,14 +31,14 @@ import com.android.dx.util.MutabilityControl; * com.android.dx.rop.type.TypeBearer}.

    */ public class OneLocalsArray extends LocalsArray { - /** non-null; actual array */ + /** {@code non-null;} actual array */ private final TypeBearer[] locals; /** * Constructs an instance. The locals array initially consists of - * all-uninitialized values (represented as nulls). + * all-uninitialized values (represented as {@code null}s). * - * @param maxLocals >= 0; the maximum number of locals this instance + * @param maxLocals {@code >= 0;} the maximum number of locals this instance * can refer to */ public OneLocalsArray(int maxLocals) { @@ -237,7 +237,7 @@ public class OneLocalsArray extends LocalsArray { * Throws a properly-formatted exception. * * @param idx the salient local index - * @param msg non-null; useful message + * @param msg {@code non-null;} useful message * @return never (keeps compiler happy) */ private static TypeBearer throwSimException(int idx, String msg) { diff --git a/dx/src/com/android/dx/cf/code/ReturnAddress.java b/dx/src/com/android/dx/cf/code/ReturnAddress.java index c69253c95..47c6071ca 100644 --- a/dx/src/com/android/dx/cf/code/ReturnAddress.java +++ b/dx/src/com/android/dx/cf/code/ReturnAddress.java @@ -28,13 +28,13 @@ import com.android.dx.util.Hex; * what instances of this class hang onto. */ public final class ReturnAddress implements TypeBearer { - /** >= 0; the start address of the subroutine being returned from */ + /** {@code >= 0;} the start address of the subroutine being returned from */ private final int subroutineAddress; /** * Constructs an instance. * - * @param subroutineAddress >= 0; the start address of the + * @param subroutineAddress {@code >= 0;} the start address of the * subroutine being returned from */ public ReturnAddress(int subroutineAddress) { @@ -100,7 +100,7 @@ public final class ReturnAddress implements TypeBearer { /** * Gets the subroutine address. * - * @return >= 0; the subroutine address + * @return {@code >= 0;} the subroutine address */ public int getSubroutineAddress() { return subroutineAddress; diff --git a/dx/src/com/android/dx/cf/code/Ropper.java b/dx/src/com/android/dx/cf/code/Ropper.java index f3eecab53..6e8c32893 100644 --- a/dx/src/com/android/dx/cf/code/Ropper.java +++ b/dx/src/com/android/dx/cf/code/Ropper.java @@ -66,10 +66,10 @@ public final class Ropper { /** number of special label offsets */ private static final int SPECIAL_LABEL_COUNT = 7; - /** non-null; method being converted */ + /** {@code non-null;} method being converted */ private final ConcreteMethod method; - /** non-null; original block list */ + /** {@code non-null;} original block list */ private final ByteBlockList blocks; /** max locals of the method */ @@ -78,30 +78,30 @@ public final class Ropper { /** max label (exclusive) of any original bytecode block */ private final int maxLabel; - /** non-null; simulation machine to use */ + /** {@code non-null;} simulation machine to use */ private final RopperMachine machine; - /** non-null; simulator to use */ + /** {@code non-null;} simulator to use */ private final Simulator sim; /** - * non-null; sparse array mapping block labels to initial frame contents, + * {@code non-null;} sparse array mapping block labels to initial frame contents, * if known */ private final Frame[] startFrames; - /** non-null; output block list in-progress */ + /** {@code non-null;} output block list in-progress */ private final ArrayList result; /** - * non-null; list of subroutine-nest labels + * {@code non-null;} list of subroutine-nest labels * (See {@link Frame#getSubroutines} associated with each result block. * Parallel to {@link Ropper#result}. */ private final ArrayList resultSubroutines; /** - * non-null; for each block (by label) that is used as an exception + * {@code non-null;} for each block (by label) that is used as an exception * handler, the type of exception it catches */ private final Type[] catchTypes; @@ -112,10 +112,10 @@ public final class Ropper { */ private boolean synchNeedsExceptionHandler; - /** non-null; list of subroutines indexed by label of start address */ + /** {@code non-null;} list of subroutines indexed by label of start address */ private final Subroutine subroutines[]; - /** true if subroutines is non-empty */ + /** true if {@code subroutines} is non-empty */ private boolean hasSubroutines; /** @@ -155,7 +155,7 @@ public final class Ropper { } /** - * @return >= 0; the label of the subroutine's start block. + * @return {@code >= 0;} the label of the subroutine's start block. */ int getStartBlock() { return startBlock; @@ -194,13 +194,13 @@ public final class Ropper { IntList successors = new IntList(callerBlocks.size()); /* - * For each subroutine caller, get it's target. If the target is us, - * add the ret target (subroutine successor) to our list + * For each subroutine caller, get it's target. If the + * target is us, add the ret target (subroutine successor) + * to our list */ - for(int label = callerBlocks.nextSetBit(0); label >= 0 - ; label = callerBlocks.nextSetBit(label+1)) { - + for (int label = callerBlocks.nextSetBit(0); label >= 0; + label = callerBlocks.nextSetBit(label+1)) { BasicBlock subCaller = labelToBlock(label); successors.add(subCaller.getSuccessors().get(0)); } @@ -212,18 +212,15 @@ public final class Ropper { /** * Merges the specified frame into this subroutine's successors, - * setting workSet as appropriate. To be called with + * setting {@code workSet} as appropriate. To be called with * the frame of a subroutine ret block. * - * @param frame non-null; frame from ret block to merge - * @param workSet non-null; workset to update + * @param frame {@code non-null;} frame from ret block to merge + * @param workSet {@code non-null;} workset to update */ void mergeToSuccessors(Frame frame, int[] workSet) { - int sz = callerBlocks.size(); - - for(int label = callerBlocks.nextSetBit(0); label >= 0 - ; label = callerBlocks.nextSetBit(label+1)) { - + for (int label = callerBlocks.nextSetBit(0); label >= 0; + label = callerBlocks.nextSetBit(label+1)) { BasicBlock subCaller = labelToBlock(label); int succLabel = subCaller.getSuccessors().get(0); @@ -242,9 +239,9 @@ public final class Ropper { /** * Converts a {@link ConcreteMethod} to a {@link RopMethod}. * - * @param method non-null; method to convert - * @param advice non-null; translation advice to use - * @return non-null; the converted instance + * @param method {@code non-null;} method to convert + * @param advice {@code non-null;} translation advice to use + * @return {@code non-null;} the converted instance */ public static RopMethod convert(ConcreteMethod method, TranslationAdvice advice) { @@ -263,8 +260,8 @@ public final class Ropper { * Constructs an instance. This class is not publicly instantiable; use * {@link #convert}. * - * @param method non-null; method to convert - * @param advice non-null; translation advice to use + * @param method {@code non-null;} method to convert + * @param advice {@code non-null;} translation advice to use */ private Ropper(ConcreteMethod method, TranslationAdvice advice) { if (method == null) { @@ -307,7 +304,7 @@ public final class Ropper { * Gets the first (lowest) register number to use as the temporary * area when unwinding stack manipulation ops. * - * @return >= 0; the first register to use + * @return {@code >= 0;} the first register to use */ /*package*/ int getFirstTempStackReg() { /* @@ -326,8 +323,8 @@ public final class Ropper { * Gets the label for the exception handler setup block corresponding * to the given label. * - * @param label >= 0; the original label - * @return >= 0; the corresponding exception handler setup label + * @param label {@code >= 0;} the original label + * @return {@code >= 0;} the corresponding exception handler setup label */ private int getExceptionSetupLabel(int label) { return maxLabel + label; @@ -337,8 +334,8 @@ public final class Ropper { * Gets the label for the given special-purpose block. The given label * should be one of the static constants defined by this class. * - * @param label < 0; the special label constant - * @return >= 0; the actual label value to use + * @param label {@code < 0;} the special label constant + * @return {@code >= 0;} the actual label value to use */ private int getSpecialLabel(int label) { /* @@ -356,7 +353,7 @@ public final class Ropper { /** * Gets the minimum label for unreserved use. * - * @return >= 0; the minimum label + * @return {@code >= 0;} the minimum label */ private int getMinimumUnreservedLabel() { /* @@ -370,7 +367,7 @@ public final class Ropper { /** * Gets an arbitrary unreserved and available label. * - * @return >= 0; the label + * @return {@code >= 0;} the label */ private int getAvailableLabel() { int candidate = getMinimumUnreservedLabel(); @@ -409,7 +406,7 @@ public final class Ropper { * Gets the total number of registers used for "normal" purposes (i.e., * for the straightforward translation from the original Java). * - * @return >= 0; the total number of registers used + * @return {@code >= 0;} the total number of registers used */ private int getNormalRegCount() { return maxLocals + method.getMaxStack(); @@ -419,7 +416,7 @@ public final class Ropper { * Gets the register spec to use to hold the object to synchronize on, * for a synchronized method. * - * @return non-null; the register spec + * @return {@code non-null;} the register spec */ private RegisterSpec getSynchReg() { /* @@ -433,11 +430,11 @@ public final class Ropper { /** * Searches {@link #result} for a block with the given label. Return its - * index if found, or return -1 if there is no such block. + * index if found, or return {@code -1} if there is no such block. * * @param label the label to look for - * @return >= -1; the index for the block with the given label or - * -1 if there is no such block + * @return {@code >= -1;} the index for the block with the given label or + * {@code -1} if there is no such block */ private int labelToResultIndex(int label) { int sz = result.size(); @@ -456,7 +453,7 @@ public final class Ropper { * found, or throw an exception if there is no such block. * * @param label the label to look for - * @return non-null; the block with the given label + * @return {@code non-null;} the block with the given label */ private BasicBlock labelToBlock(int label) { int idx = labelToResultIndex(label); @@ -472,8 +469,8 @@ public final class Ropper { /** * Adds a block to the output result. * - * @param block non-null; the block to add - * @param subroutines non-null; subroutine label list as described in + * @param block {@code non-null;} the block to add + * @param subroutines {@code non-null;} subroutine label list as described in * {@link Frame#getSubroutines} */ private void addBlock(BasicBlock block, IntList subroutines) { @@ -491,11 +488,11 @@ public final class Ropper { * replacement, then any extra blocks that got added with the * original get removed as a result of calling this method. * - * @param block non-null; the block to add or replace - * @param subroutines non-null; subroutine label list as described in + * @param block {@code non-null;} the block to add or replace + * @param subroutines {@code non-null;} subroutine label list as described in * {@link Frame#getSubroutines} - * @return true if the block was replaced or - * false if it was added for the first time + * @return {@code true} if the block was replaced or + * {@code false} if it was added for the first time */ private boolean addOrReplaceBlock(BasicBlock block, IntList subroutines) { if (block == null) { @@ -529,11 +526,11 @@ public final class Ropper { * Adds or replaces a block in the output result. Do not delete * any successors. * - * @param block non-null; the block to add or replace - * @param subroutines non-null; subroutine label list as described in + * @param block {@code non-null;} the block to add or replace + * @param subroutines {@code non-null;} subroutine label list as described in * {@link Frame#getSubroutines} - * @return true if the block was replaced or - * false if it was added for the first time + * @return {@code true} if the block was replaced or + * {@code false} if it was added for the first time */ private boolean addOrReplaceBlockNoDelete(BasicBlock block, IntList subroutines) { @@ -564,7 +561,7 @@ public final class Ropper { * successors of it whose labels indicate that they are not in the * normally-translated range. * - * @param idx non-null; block to remove (etc.) + * @param idx {@code non-null;} block to remove (etc.) */ private void removeBlockAndSpecialSuccessors(int idx) { int minLabel = getMinimumUnreservedLabel(); @@ -591,7 +588,7 @@ public final class Ropper { /** * Extracts the resulting {@link RopMethod} from the instance. * - * @return non-null; the method object + * @return {@code non-null;} the method object */ private RopMethod getRopMethod() { @@ -663,9 +660,9 @@ public final class Ropper { /** * Processes the given block. * - * @param block non-null; block to process - * @param frame non-null; start frame for the block - * @param workSet non-null; bits representing work to do, which this + * @param block {@code non-null;} block to process + * @param frame {@code non-null;} start frame for the block + * @param workSet {@code non-null;} bits representing work to do, which this * method may add to */ private void processBlock(ByteBlock block, Frame frame, int[] workSet) { @@ -950,14 +947,14 @@ public final class Ropper { * Helper for {@link #processBlock}, which merges frames and * adds to the work set, as necessary. * - * @param label >= 0; label to work on - * @param pred predecessor label. Must be >= 0 when - * label is a subroutine start block and calledSubroutine + * @param label {@code >= 0;} label to work on + * @param pred predecessor label; must be {@code >= 0} when + * {@code label} is a subroutine start block and calledSubroutine * is non-null. Otherwise, may be -1. - * @param calledSubroutine null-ok; a Subroutine instance if - * label is the first block in a subroutine. - * @param frame non-null; new frame for the labelled block - * @param workSet non-null; bits representing work to do, which this + * @param calledSubroutine {@code null-ok;} a Subroutine instance if + * {@code label} is the first block in a subroutine. + * @param frame {@code non-null;} new frame for the labelled block + * @param workSet {@code non-null;} bits representing work to do, which this * method may add to */ private void mergeAndWorkAsNecessary(int label, int pred, @@ -1078,7 +1075,7 @@ public final class Ropper { /** * Constructs and adds the return block, if necessary. The return - * block merely contains an appropriate return + * block merely contains an appropriate {@code return} * instruction. */ private void addReturnBlock() { @@ -1217,7 +1214,7 @@ public final class Ropper { /** * Checks to see if the basic block is a subroutine caller block. * - * @param bb non-null; the basic block in question + * @param bb {@code non-null;} the basic block in question * @return true if this block calls a subroutine */ private boolean isSubroutineCaller(BasicBlock bb) { @@ -1340,7 +1337,7 @@ public final class Ropper { /** * Inlines a subroutine. Start by calling - * inlineSubroutineCalledFrom. + * {@code inlineSubroutineCalledFrom}. */ private class SubroutineInliner { /** @@ -1399,9 +1396,8 @@ public final class Ropper { */ int newSubStartLabel = mapOrAllocateLabel(subroutineStart); - for(int label = workList.nextSetBit(0); label >= 0 - ; label = workList.nextSetBit(0)) { - + for (int label = workList.nextSetBit(0); label >= 0; + label = workList.nextSetBit(0)) { workList.clear(label); int newLabel = origLabelToCopiedLabel.get(label); @@ -1421,7 +1417,8 @@ public final class Ropper { addOrReplaceBlockNoDelete( new BasicBlock(b.getLabel(), b.getInsns(), IntList.makeImmutable (newSubStartLabel), - newSubStartLabel), labelToSubroutines.get(b.getLabel())); + newSubStartLabel), + labelToSubroutines.get(b.getLabel())); } /** @@ -1502,8 +1499,8 @@ public final class Ropper { * Checks to see if a specified label is involved in a specified * subroutine. * - * @param label >=0 a basic block label - * @param subroutineStart >=0 a subroutine as identified by the + * @param label {@code >= 0;} a basic block label + * @param subroutineStart {@code >= 0;} a subroutine as identified by the * label of its start block. * @return true if the block is dominated by the subroutine call. */ @@ -1554,10 +1551,10 @@ public final class Ropper { } /** - * Finds a Subroutine that is returned from by a ret in + * Finds a {@code Subroutine} that is returned from by a ret in * a given block. * @param label A block that originally contained a ret instruction - * @return null-ok; Subroutine or null if none was found. + * @return {@code null-ok;} Subroutine or null if none was found. */ private Subroutine subroutineFromRetBlock(int label) { for (int i = subroutines.length - 1 ; i >= 0 ; i--) { diff --git a/dx/src/com/android/dx/cf/code/RopperMachine.java b/dx/src/com/android/dx/cf/code/RopperMachine.java index 6d05b382e..dd7fcd46b 100644 --- a/dx/src/com/android/dx/cf/code/RopperMachine.java +++ b/dx/src/com/android/dx/cf/code/RopperMachine.java @@ -47,13 +47,13 @@ import java.util.ArrayList; * Machine implementation for use by {@link Ropper}. */ /*package*/ final class RopperMachine extends ValueAwareMachine { - /** non-null; array reflection class */ + /** {@code non-null;} array reflection class */ private static final CstType ARRAY_REFLECT_TYPE = new CstType(Type.internClassName("java/lang/reflect/Array")); /** - * non-null; method constant for use in converting - * multianewarray instructions + * {@code non-null;} method constant for use in converting + * {@code multianewarray} instructions */ private static final CstMethodRef MULTIANEWARRAY_METHOD = new CstMethodRef(ARRAY_REFLECT_TYPE, @@ -61,34 +61,34 @@ import java.util.ArrayList; new CstUtf8("(Ljava/lang/Class;[I)" + "Ljava/lang/Object;"))); - /** non-null; {@link Ropper} controlling this instance */ + /** {@code non-null;} {@link Ropper} controlling this instance */ private final Ropper ropper; - /** non-null; method being converted */ + /** {@code non-null;} method being converted */ private final ConcreteMethod method; - /** non-null; translation advice */ + /** {@code non-null;} translation advice */ private final TranslationAdvice advice; /** max locals of the method */ private final int maxLocals; - /** non-null; instructions for the rop basic block in-progress */ + /** {@code non-null;} instructions for the rop basic block in-progress */ private final ArrayList insns; - /** non-null; catches for the block currently being processed */ + /** {@code non-null;} catches for the block currently being processed */ private TypeList catches; /** whether the catches have been used in an instruction */ private boolean catchesUsed; - /** whether the block contains a return */ + /** whether the block contains a {@code return} */ private boolean returns; /** primary successor index */ private int primarySuccessorIndex; - /** >= 0; number of extra basic blocks required */ + /** {@code >= 0;} number of extra basic blocks required */ private int extraBlockCount; /** true if last processed block ends with a jsr or jsr_W*/ @@ -105,13 +105,13 @@ import java.util.ArrayList; private ReturnAddress returnAddress; /** - * null-ok; the appropriate return op or null + * {@code null-ok;} the appropriate {@code return} op or {@code null} * if it is not yet known */ private Rop returnOp; /** - * null-ok; the source position for the return block or null + * {@code null-ok;} the source position for the return block or {@code null} * if it is not yet known */ private SourcePosition returnPosition; @@ -119,9 +119,9 @@ import java.util.ArrayList; /** * Constructs an instance. * - * @param ropper non-null; ropper controlling this instance - * @param method non-null; method being converted - * @param advice non-null; translation advice to use + * @param ropper {@code non-null;} ropper controlling this instance + * @param method {@code non-null;} method being converted + * @param advice {@code non-null;} translation advice to use */ public RopperMachine(Ropper ropper, ConcreteMethod method, TranslationAdvice advice) { @@ -154,7 +154,7 @@ import java.util.ArrayList; * Gets the instructions array. It is shared and gets modified by * subsequent calls to this instance. * - * @return non-null; the instructions array + * @return {@code non-null;} the instructions array */ public ArrayList getInsns() { return insns; @@ -163,7 +163,7 @@ import java.util.ArrayList; /** * Gets the return opcode encountered, if any. * - * @return null-ok; the return opcode + * @return {@code null-ok;} the return opcode */ public Rop getReturnOp() { return returnOp; @@ -172,7 +172,7 @@ import java.util.ArrayList; /** * Gets the return position, if known. * - * @return null-ok; the return position + * @return {@code null-ok;} the return position */ public SourcePosition getReturnPosition() { return returnPosition; @@ -182,7 +182,7 @@ import java.util.ArrayList; * Gets ready to start working on a new block. This will clear the * {@link #insns} list, set {@link #catches}, reset whether it has * been used, reset whether the block contains a - * return, and reset {@link #primarySuccessorIndex}. + * {@code return}, and reset {@link #primarySuccessorIndex}. */ public void startBlock(TypeList catches) { this.catches = catches; @@ -201,7 +201,7 @@ import java.util.ArrayList; * Gets whether {@link #catches} was used. This indicates that the * last instruction in the block is one of the ones that can throw. * - * @return whether catches has been used + * @return whether {@code catches} has been used */ public boolean wereCatchesUsed() { return catchesUsed; @@ -209,7 +209,7 @@ import java.util.ArrayList; /** * Gets whether the block just processed ended with a - * return. + * {@code return}. * * @return whether the block returns */ @@ -220,12 +220,12 @@ import java.util.ArrayList; /** * Gets the primary successor index. This is the index into the * successors list where the primary may be found or - * -1 if there are successors but no primary + * {@code -1} if there are successors but no primary * successor. This may return something other than - * -1 in the case of an instruction with no + * {@code -1} in the case of an instruction with no * successors at all (primary or otherwise). * - * @return >= -1; the primary successor index + * @return {@code >= -1;} the primary successor index */ public int getPrimarySuccessorIndex() { return primarySuccessorIndex; @@ -236,7 +236,7 @@ import java.util.ArrayList; * block currently being translated. Each extra block should consist * of one instruction from the end of the original block. * - * @return >= 0; the number of extra blocks needed + * @return {@code >= 0;} the number of extra blocks needed */ public int getExtraBlockCount() { return extraBlockCount; @@ -259,16 +259,17 @@ import java.util.ArrayList; } /** - * @return true if a RET has ben encountered since the last call to - * startBlock() + * @return {@code true} if a {@code ret} has ben encountered since + * the last call to {@code startBlock()} */ public boolean hasRet() { return returnAddress != null; } /** - * @return null-ok; return address of a ret instruction if encountered - * since last call to startBlock(). null if no ret instruction encountered. + * @return {@code null-ok;} return address of a {@code ret} + * instruction if encountered since last call to startBlock(). + * {@code null} if no ret instruction encountered. */ public ReturnAddress getReturnAddress() { return returnAddress; @@ -444,7 +445,7 @@ import java.util.ArrayList; catches, MULTIANEWARRAY_METHOD); insns.add(insn); - // Add a move-result + // Add a move-result. rop = Rops.opMoveResult(MULTIANEWARRAY_METHOD.getPrototype() .getReturnType()); insn = new PlainInsn(rop, pos, objectReg, RegisterSpecList.EMPTY); @@ -457,7 +458,6 @@ import java.util.ArrayList; opcode = ByteOps.CHECKCAST; sources = RegisterSpecList.make(objectReg); - } else if (opcode == ByteOps.JSR) { // JSR has no Rop instruction hasJsr = true; @@ -474,12 +474,14 @@ import java.util.ArrayList; } ropOpcode = jopToRopOpcode(opcode, cst); - rop = Rops.ropFor(ropOpcode, destType, sources, cst); Insn moveResult = null; if (dest != null && rop.isCallLike()) { - // We're going to want to have a move-result in the next basic block + /* + * We're going to want to have a move-result in the next + * basic block. + */ extraBlockCount++; moveResult = new PlainInsn( @@ -488,8 +490,10 @@ import java.util.ArrayList; dest = null; } else if (dest != null && rop.canThrow()) { - // We're going to want to have a move-result-pseudo - // in the next basic block + /* + * We're going to want to have a move-result-pseudo in the + * next basic block. + */ extraBlockCount++; moveResult = new PlainInsn( @@ -599,11 +603,12 @@ import java.util.ArrayList; } /* - * If initValues is non-null, it means that the parser has seen a group - * of compatible constant initialization bytecodes that are applied to - * the current newarray. The action we take here is to convert these - * initialization bytecodes into a single fill-array-data ROP which lays - * out all the constant values in a table. + * If initValues is non-null, it means that the parser has + * seen a group of compatible constant initialization + * bytecodes that are applied to the current newarray. The + * action we take here is to convert these initialization + * bytecodes into a single fill-array-data ROP which lays out + * all the constant values in a table. */ if (initValues != null) { extraBlockCount++; @@ -619,9 +624,9 @@ import java.util.ArrayList; * instruction. * * @param opcode the opcode being translated - * @param stackPointer >= 0; the stack pointer after the instruction's - * arguments have been popped - * @return non-null; the sources + * @param stackPointer {@code >= 0;} the stack pointer after the + * instruction's arguments have been popped + * @return {@code non-null;} the sources */ private RegisterSpecList getSources(int opcode, int stackPointer) { int count = argCount(); @@ -692,8 +697,8 @@ import java.util.ArrayList; /** * Sets or updates the information about the return block. * - * @param op non-null; the opcode to use - * @param pos non-null; the position to use + * @param op {@code non-null;} the opcode to use + * @param pos {@code non-null;} the position to use */ private void updateReturnOp(Rop op, SourcePosition pos) { if (op == null) { @@ -723,9 +728,9 @@ import java.util.ArrayList; /** * Gets the register opcode for the given Java opcode. * - * @param jop >= 0; the Java opcode - * @param cst null-ok; the constant argument, if any - * @return >= 0; the corresponding register opcode + * @param jop {@code >= 0;} the Java opcode + * @param cst {@code null-ok;} the constant argument, if any + * @return {@code >= 0;} the corresponding register opcode */ private int jopToRopOpcode(int jop, Constant cst) { switch (jop) { diff --git a/dx/src/com/android/dx/cf/code/Simulator.java b/dx/src/com/android/dx/cf/code/Simulator.java index 3c90ee593..408e1261d 100644 --- a/dx/src/com/android/dx/cf/code/Simulator.java +++ b/dx/src/com/android/dx/cf/code/Simulator.java @@ -35,34 +35,37 @@ import java.util.ArrayList; /** * Class which knows how to simulate the effects of executing bytecode. - * + * *

    Note: This class is not thread-safe. If multiple threads * need to use a single instance, they must synchronize access explicitly * between themselves.

    */ public class Simulator { - /** non-null; canned error message for local variable table mismatches */ - private static final String LOCAL_MISMATCH_ERROR = + /** + * {@code non-null;} canned error message for local variable + * table mismatches + */ + private static final String LOCAL_MISMATCH_ERROR = "This is symptomatic of .class transformation tools that ignore " + "local variable information."; - /** non-null; machine to use when simulating */ + /** {@code non-null;} machine to use when simulating */ private final Machine machine; - /** non-null; array of bytecode */ + /** {@code non-null;} array of bytecode */ private final BytecodeArray code; - /** non-null; local variable information */ + /** {@code non-null;} local variable information */ private final LocalVariableList localVariables; - /** non-null; visitor instance to use */ + /** {@code non-null;} visitor instance to use */ private final SimVisitor visitor; /** * Constructs an instance. * - * @param machine non-null; machine to use when simulating - * @param method non-null; method data to use + * @param machine {@code non-null;} machine to use when simulating + * @param method {@code non-null;} method data to use */ public Simulator(Machine machine, ConcreteMethod method) { if (machine == null) { @@ -83,8 +86,8 @@ public class Simulator { * Simulates the effect of executing the given basic block. This modifies * the passed-in frame to represent the end result. * - * @param bb non-null; the basic block - * @param frame non-null; frame to operate on + * @param bb {@code non-null;} the basic block + * @param frame {@code non-null;} frame to operate on */ public void simulate(ByteBlock bb, Frame frame) { int end = bb.getEnd(); @@ -107,8 +110,8 @@ public class Simulator { * Simulates the effect of the instruction at the given offset, by * making appropriate calls on the given frame. * - * @param offset >= 0; offset of the instruction to simulate - * @param frame non-null; frame to operate on + * @param offset {@code >= 0;} offset of the instruction to simulate + * @param frame {@code non-null;} frame to operate on * @return the length of the instruction, in bytes */ public int simulate(int offset, Frame frame) { @@ -130,13 +133,13 @@ public class Simulator { */ private class SimVisitor implements BytecodeArray.Visitor { /** - * non-null; machine instance to use (just to avoid excessive - * cross-object field access) + * {@code non-null;} machine instance to use (just to avoid excessive + * cross-object field access) */ private final Machine machine; /** - * null-ok; frame to use; set with each call to + * {@code null-ok;} frame to use; set with each call to * {@link Simulator#simulate} */ private Frame frame; @@ -155,7 +158,7 @@ public class Simulator { /** * Sets the frame to act on. * - * @param frame non-null; the frame + * @param frame {@code non-null;} the frame */ public void setFrame(Frame frame) { if (frame == null) { @@ -255,25 +258,21 @@ public class Simulator { /* * Change the type (which is to be pushed) to * reflect the actual component type of the array - * being popped. + * being popped, unless it turns out to be a + * known-null, in which case we just use the type + * implied by the original instruction. */ - Type requireType = type.getArrayType(); - type = frame.getStack().peekType(1); - if (type == Type.KNOWN_NULL) { - /* - * The type is a known-null: Just treat the - * popped type as whatever is expected. In - * reality, unless this frame is revisited - * (due to a branch merge), execution will - * result in the throwing of a - * NullPointerException, but claiming the - * expected type at here should be good enough - * for the purposes at this level. - */ - type = requireType; + Type foundArrayType = frame.getStack().peekType(1); + Type requireArrayType; + + if (foundArrayType != Type.KNOWN_NULL) { + requireArrayType = foundArrayType; + type = foundArrayType.getComponentType(); + } else { + requireArrayType = type.getArrayType(); } - type = type.getComponentType(); - machine.popArgs(frame, requireType, Type.INT); + + machine.popArgs(frame, requireArrayType, Type.INT); break; } case ByteOps.IADD: @@ -292,7 +291,7 @@ public class Simulator { case ByteOps.IUSHR: { machine.popArgs(frame, type, Type.INT); break; - } + } case ByteOps.LCMP: { machine.popArgs(frame, Type.LONG, Type.LONG); break; @@ -308,8 +307,28 @@ public class Simulator { break; } case ByteOps.IASTORE: { - Type arrayType = type.getArrayType(); - machine.popArgs(frame, arrayType, Type.INT, type); + /* + * Change the type (which is the type of the + * element) to reflect the actual component type + * of the array being popped, unless it turns out + * to be a known-null, in which case we just use + * the type implied by the original instruction. + * The category 1 vs. 2 thing here is that, if the + * element type is category 2, we have to skip over + * one extra stack slot to find the array. + */ + Type foundArrayType = + frame.getStack().peekType(type.isCategory1() ? 2 : 3); + Type requireArrayType; + + if (foundArrayType != Type.KNOWN_NULL) { + requireArrayType = foundArrayType; + type = foundArrayType.getComponentType(); + } else { + requireArrayType = type.getArrayType(); + } + + machine.popArgs(frame, requireArrayType, Type.INT, type); break; } case ByteOps.POP2: @@ -456,7 +475,7 @@ public class Simulator { * Checks whether the prototype is compatible with returning the * given type, and throws if not. * - * @param encountered non-null; the encountered return type + * @param encountered {@code non-null;} the encountered return type */ private void checkReturnType(Type encountered) { Type returnType = machine.getPrototype().getReturnType(); diff --git a/dx/src/com/android/dx/cf/code/SwitchList.java b/dx/src/com/android/dx/cf/code/SwitchList.java index dc04137fb..fdd159614 100644 --- a/dx/src/com/android/dx/cf/code/SwitchList.java +++ b/dx/src/com/android/dx/cf/code/SwitchList.java @@ -21,15 +21,15 @@ import com.android.dx.util.MutabilityControl; /** * List of (value, target) mappings representing the choices of a - * tableswitch or lookupswitch instruction. It + * {@code tableswitch} or {@code lookupswitch} instruction. It * also holds the default target for the switch. */ public final class SwitchList extends MutabilityControl { - /** non-null; list of test values */ + /** {@code non-null;} list of test values */ private final IntList values; /** - * non-null; list of targets corresponding to the test values; there + * {@code non-null;} list of targets corresponding to the test values; there * is always one extra element in the target list, to hold the * default target */ @@ -41,7 +41,7 @@ public final class SwitchList extends MutabilityControl { /** * Constructs an instance. * - * @param size >= 0; the number of elements to be in the table + * @param size {@code >= 0;} the number of elements to be in the table */ public SwitchList(int size) { super(true); @@ -61,7 +61,7 @@ public final class SwitchList extends MutabilityControl { /** * Gets the size of the list. * - * @return >= 0; the list size + * @return {@code >= 0;} the list size */ public int size() { return size; @@ -70,7 +70,7 @@ public final class SwitchList extends MutabilityControl { /** * Gets the indicated test value. * - * @param n >= 0;, < size(); which index + * @param n {@code >= 0;}, < size(); which index * @return the test value */ public int getValue(int n) { @@ -78,11 +78,11 @@ public final class SwitchList extends MutabilityControl { } /** - * Gets the indicated target. Asking for the target at size() + * Gets the indicated target. Asking for the target at {@code size()} * returns the default target. * - * @param n >= 0, <= size(); which index - * @return >= 0; the target + * @param n {@code >= 0, <= size();} which index + * @return {@code >= 0;} the target */ public int getTarget(int n) { return targets.get(n); @@ -90,9 +90,9 @@ public final class SwitchList extends MutabilityControl { /** * Gets the default target. This is just a shorthand for - * getTarget(size()). + * {@code getTarget(size())}. * - * @return >= 0; the default target + * @return {@code >= 0;} the default target */ public int getDefaultTarget() { return targets.get(size); @@ -102,7 +102,7 @@ public final class SwitchList extends MutabilityControl { * Gets the list of all targets. This includes one extra element at the * end of the list, which holds the default target. * - * @return non-null; the target list + * @return {@code non-null;} the target list */ public IntList getTargets() { return targets; @@ -111,7 +111,7 @@ public final class SwitchList extends MutabilityControl { /** * Gets the list of all case values. * - * @return non-null; the case value list + * @return {@code non-null;} the case value list */ public IntList getValues() { return values; @@ -121,7 +121,7 @@ public final class SwitchList extends MutabilityControl { * Sets the default target. It is only valid to call this method * when all the non-default elements have been set. * - * @param target >= 0; the absolute (not relative) default target + * @param target {@code >= 0;} the absolute (not relative) default target * address */ public void setDefaultTarget(int target) { @@ -142,7 +142,7 @@ public final class SwitchList extends MutabilityControl { * Adds the given item. * * @param value the test value - * @param target >= 0; the absolute (not relative) target address + * @param target {@code >= 0;} the absolute (not relative) target address */ public void add(int value, int target) { throwIfImmutable(); diff --git a/dx/src/com/android/dx/cf/code/ValueAwareMachine.java b/dx/src/com/android/dx/cf/code/ValueAwareMachine.java index 4062c3b94..43aab8a67 100644 --- a/dx/src/com/android/dx/cf/code/ValueAwareMachine.java +++ b/dx/src/com/android/dx/cf/code/ValueAwareMachine.java @@ -30,7 +30,8 @@ public class ValueAwareMachine extends BaseMachine { /** * Constructs an instance. * - * @param prototype non-null; the prototype for the associated method + * @param prototype {@code non-null;} the prototype for the associated + * method */ public ValueAwareMachine(Prototype prototype) { super(prototype); diff --git a/dx/src/com/android/dx/cf/cst/ConstantPoolParser.java b/dx/src/com/android/dx/cf/cst/ConstantPoolParser.java index 953981c90..7cd9c9be4 100644 --- a/dx/src/com/android/dx/cf/cst/ConstantPoolParser.java +++ b/dx/src/com/android/dx/cf/cst/ConstantPoolParser.java @@ -41,29 +41,29 @@ import static com.android.dx.cf.cst.ConstantTags.*; * Parser for a constant pool embedded in a class file. */ public final class ConstantPoolParser { - /** non-null; the bytes of the constant pool */ + /** {@code non-null;} the bytes of the constant pool */ private final ByteArray bytes; - /** non-null; actual parsed constant pool contents */ + /** {@code non-null;} actual parsed constant pool contents */ private final StdConstantPool pool; - /** non-null; byte offsets to each cst */ + /** {@code non-null;} byte offsets to each cst */ private final int[] offsets; /** * -1 || >= 10; the end offset of this constant pool in the - * byte[] which it came from or -1 if not + * {@code byte[]} which it came from or {@code -1} if not * yet parsed */ private int endOffset; - /** null-ok; parse observer, if any */ + /** {@code null-ok;} parse observer, if any */ private ParseObserver observer; /** * Constructs an instance. * - * @param bytes non-null; the bytes of the file + * @param bytes {@code non-null;} the bytes of the file */ public ConstantPoolParser(ByteArray bytes) { int size = bytes.getUnsignedShort(8); // constant_pool_count @@ -77,17 +77,17 @@ public final class ConstantPoolParser { /** * Sets the parse observer for this instance. * - * @param observer null-ok; the observer + * @param observer {@code null-ok;} the observer */ public void setObserver(ParseObserver observer) { this.observer = observer; } /** - * Gets the end offset of this constant pool in the byte[] + * Gets the end offset of this constant pool in the {@code byte[]} * which it came from. * - * @return >= 10; the end offset + * @return {@code >= 10;} the end offset */ public int getEndOffset() { parseIfNecessary(); @@ -97,7 +97,7 @@ public final class ConstantPoolParser { /** * Gets the actual constant pool. * - * @return non-null; the constant pool + * @return {@code non-null;} the constant pool */ public StdConstantPool getPool() { parseIfNecessary(); @@ -215,7 +215,7 @@ public final class ConstantPoolParser { * depends on. * * @param idx which constant - * @return non-null; the parsed constant + * @return {@code non-null;} the parsed constant */ private Constant parse0(int idx) { Constant cst = pool.getOrNull(idx); @@ -316,7 +316,7 @@ public final class ConstantPoolParser { * Parses a utf8 constant. * * @param at offset to the start of the constant (where the tag byte is) - * @return non-null; the parsed value + * @return {@code non-null;} the parsed value */ private CstUtf8 parseUtf8(int at) { int length = bytes.getUnsignedShort(at + 1); diff --git a/dx/src/com/android/dx/cf/cst/ConstantTags.java b/dx/src/com/android/dx/cf/cst/ConstantTags.java index 64bc8d874..9febbdf5e 100644 --- a/dx/src/com/android/dx/cf/cst/ConstantTags.java +++ b/dx/src/com/android/dx/cf/cst/ConstantTags.java @@ -20,36 +20,36 @@ package com.android.dx.cf.cst; * Tags for constant pool constants. */ public interface ConstantTags { - /** tag for a CONSTANT_Utf8_info */ + /** tag for a {@code CONSTANT_Utf8_info} */ int CONSTANT_Utf8 = 1; - /** tag for a CONSTANT_Integer_info */ + /** tag for a {@code CONSTANT_Integer_info} */ int CONSTANT_Integer = 3; - /** tag for a CONSTANT_Float_info */ + /** tag for a {@code CONSTANT_Float_info} */ int CONSTANT_Float = 4; - /** tag for a CONSTANT_Long_info */ + /** tag for a {@code CONSTANT_Long_info} */ int CONSTANT_Long = 5; - /** tag for a CONSTANT_Double_info */ + /** tag for a {@code CONSTANT_Double_info} */ int CONSTANT_Double = 6; - /** tag for a CONSTANT_Class_info */ + /** tag for a {@code CONSTANT_Class_info} */ int CONSTANT_Class = 7; - /** tag for a CONSTANT_String_info */ + /** tag for a {@code CONSTANT_String_info} */ int CONSTANT_String = 8; - /** tag for a CONSTANT_Fieldref_info */ + /** tag for a {@code CONSTANT_Fieldref_info} */ int CONSTANT_Fieldref = 9; - /** tag for a CONSTANT_Methodref_info */ + /** tag for a {@code CONSTANT_Methodref_info} */ int CONSTANT_Methodref = 10; - /** tag for a CONSTANT_InterfaceMethodref_info */ + /** tag for a {@code CONSTANT_InterfaceMethodref_info} */ int CONSTANT_InterfaceMethodref = 11; - /** tag for a CONSTANT_NameAndType_info */ + /** tag for a {@code CONSTANT_NameAndType_info} */ int CONSTANT_NameAndType = 12; } diff --git a/dx/src/com/android/dx/cf/direct/AnnotationParser.java b/dx/src/com/android/dx/cf/direct/AnnotationParser.java index 5d800869b..88e4cd289 100644 --- a/dx/src/com/android/dx/cf/direct/AnnotationParser.java +++ b/dx/src/com/android/dx/cf/direct/AnnotationParser.java @@ -51,23 +51,23 @@ import java.io.IOException; * Parser for annotations. */ public final class AnnotationParser { - /** non-null; class file being parsed */ + /** {@code non-null;} class file being parsed */ private final DirectClassFile cf; - /** non-null; constant pool to use */ + /** {@code non-null;} constant pool to use */ private final ConstantPool pool; - /** non-null; bytes of the attribute data */ + /** {@code non-null;} bytes of the attribute data */ private final ByteArray bytes; - /** null-ok; parse observer, if any */ + /** {@code null-ok;} parse observer, if any */ private final ParseObserver observer; - /** non-null; input stream to parse from */ + /** {@code non-null;} input stream to parse from */ private final ByteArray.MyDataInputStream input; /** - * non-null; cursor for use when informing the observer of what + * {@code non-null;} cursor for use when informing the observer of what * was parsed */ private int parseCursor; @@ -75,10 +75,10 @@ public final class AnnotationParser { /** * Constructs an instance. * - * @param cf non-null; class file to parse from - * @param offset >= 0; offset into the class file data to parse at - * @param length >= 0; number of bytes left in the attribute data - * @param observer null-ok; parse observer to notify, if any + * @param cf {@code non-null;} class file to parse from + * @param offset {@code >= 0;} offset into the class file data to parse at + * @param length {@code >= 0;} number of bytes left in the attribute data + * @param observer {@code null-ok;} parse observer to notify, if any */ public AnnotationParser(DirectClassFile cf, int offset, int length, ParseObserver observer) { @@ -95,9 +95,9 @@ public final class AnnotationParser { } /** - * Parses an annotation value (element_value) attribute. + * Parses an annotation value ({@code element_value}) attribute. * - * @return non-null; the parsed constant value + * @return {@code non-null;} the parsed constant value */ public Constant parseValueAttribute() { Constant result; @@ -119,8 +119,8 @@ public final class AnnotationParser { /** * Parses a parameter annotation attribute. * - * @param visibility non-null; visibility of the parsed annotations - * @return non-null; the parsed list of lists of annotations + * @param visibility {@code non-null;} visibility of the parsed annotations + * @return {@code non-null;} the parsed list of lists of annotations */ public AnnotationsList parseParameterAttribute( AnnotationVisibility visibility) { @@ -143,8 +143,8 @@ public final class AnnotationParser { /** * Parses an annotation attribute, per se. * - * @param visibility non-null; visibility of the parsed annotations - * @return non-null; the list of annotations read from the attribute + * @param visibility {@code non-null;} visibility of the parsed annotations + * @return {@code non-null;} the list of annotations read from the attribute * data */ public Annotations parseAnnotationAttribute( @@ -168,8 +168,8 @@ public final class AnnotationParser { /** * Parses a list of annotation lists. * - * @param visibility non-null; visibility of the parsed annotations - * @return non-null; the list of annotation lists read from the attribute + * @param visibility {@code non-null;} visibility of the parsed annotations + * @return {@code non-null;} the list of annotation lists read from the attribute * data */ private AnnotationsList parseAnnotationsList( @@ -203,8 +203,8 @@ public final class AnnotationParser { /** * Parses an annotation list. * - * @param visibility non-null; visibility of the parsed annotations - * @return non-null; the list of annotations read from the attribute + * @param visibility {@code non-null;} visibility of the parsed annotations + * @return {@code non-null;} the list of annotations read from the attribute * data */ private Annotations parseAnnotations(AnnotationVisibility visibility) @@ -238,8 +238,8 @@ public final class AnnotationParser { /** * Parses a single annotation. * - * @param visibility non-null; visibility of the parsed annotation - * @return non-null; the parsed annotation + * @param visibility {@code non-null;} visibility of the parsed annotation + * @return {@code non-null;} the parsed annotation */ private Annotation parseAnnotation(AnnotationVisibility visibility) throws IOException { @@ -278,7 +278,7 @@ public final class AnnotationParser { /** * Parses a {@link NameValuePair}. * - * @return non-null; the parsed element + * @return {@code non-null;} the parsed element */ private NameValuePair parseElement() throws IOException { requireLength(5); @@ -304,7 +304,7 @@ public final class AnnotationParser { /** * Parses an annotation value. * - * @return non-null; the parsed value + * @return {@code non-null;} the parsed value */ private Constant parseValue() throws IOException { int tag = input.readUnsignedByte(); @@ -421,7 +421,7 @@ public final class AnnotationParser { * Helper for {@link #parseValue}, which parses a constant reference * and returns the referred-to constant value. * - * @return non-null; the parsed value + * @return {@code non-null;} the parsed value */ private Constant parseConstant() throws IOException { int constValueIndex = input.readUnsignedShort(); @@ -454,8 +454,8 @@ public final class AnnotationParser { * only be used (for efficiency sake) if the parse is known to be * observed. * - * @param length >= 0; number of bytes parsed - * @param message non-null; associated message + * @param length {@code >= 0;} number of bytes parsed + * @param message {@code non-null;} associated message */ private void parsed(int length, String message) { observer.parsed(bytes, parseCursor, length, message); @@ -464,7 +464,7 @@ public final class AnnotationParser { /** * Convenience wrapper that simply calls through to - * observer.changeIndent(). + * {@code observer.changeIndent()}. * * @param indent the amount to change the indent by */ diff --git a/dx/src/com/android/dx/cf/direct/AttributeFactory.java b/dx/src/com/android/dx/cf/direct/AttributeFactory.java index 420d74175..d00a85961 100644 --- a/dx/src/com/android/dx/cf/direct/AttributeFactory.java +++ b/dx/src/com/android/dx/cf/direct/AttributeFactory.java @@ -58,13 +58,13 @@ public class AttributeFactory { * the name, and then does all the setup to call on to {@link #parse0}, * which does the actual construction. * - * @param cf non-null; class file to parse from - * @param context context to parse in; one of the CTX_* + * @param cf {@code non-null;} class file to parse from + * @param context context to parse in; one of the {@code CTX_*} * constants - * @param offset offset into dcf's bytes + * @param offset offset into {@code dcf}'s {@code bytes} * to start parsing at - * @param observer null-ok; parse observer to report to, if any - * @return non-null; an appropriately-constructed {@link Attribute} + * @param observer {@code null-ok;} parse observer to report to, if any + * @return {@code non-null;} an appropriately-constructed {@link Attribute} */ public final Attribute parse(DirectClassFile cf, int context, int offset, ParseObserver observer) { @@ -108,15 +108,15 @@ public class AttributeFactory { * an instance of {@link RawAttribute}. Subclasses are expected to * override this to do something better in most cases. * - * @param cf non-null; class file to parse from - * @param context context to parse in; one of the CTX_* + * @param cf {@code non-null;} class file to parse from + * @param context context to parse in; one of the {@code CTX_*} * constants - * @param name non-null; the attribute name - * @param offset offset into bytes to start parsing at; this + * @param name {@code non-null;} the attribute name + * @param offset offset into {@code bytes} to start parsing at; this * is the offset to the start of attribute data, not to the header * @param length the length of the attribute data - * @param observer null-ok; parse observer to report to, if any - * @return non-null; an appropriately-constructed {@link Attribute} + * @param observer {@code null-ok;} parse observer to report to, if any + * @return {@code non-null;} an appropriately-constructed {@link Attribute} */ protected Attribute parse0(DirectClassFile cf, int context, String name, int offset, int length, diff --git a/dx/src/com/android/dx/cf/direct/AttributeListParser.java b/dx/src/com/android/dx/cf/direct/AttributeListParser.java index 765226565..2715e6a94 100644 --- a/dx/src/com/android/dx/cf/direct/AttributeListParser.java +++ b/dx/src/com/android/dx/cf/direct/AttributeListParser.java @@ -27,7 +27,7 @@ import com.android.dx.util.Hex; * Parser for lists of attributes. */ final /*package*/ class AttributeListParser { - /** non-null; the class file to parse from */ + /** {@code non-null;} the class file to parse from */ private final DirectClassFile cf; /** attribute parsing context */ @@ -36,26 +36,26 @@ final /*package*/ class AttributeListParser { /** offset in the byte array of the classfile to the start of the list */ private final int offset; - /** non-null; attribute factory to use */ + /** {@code non-null;} attribute factory to use */ private final AttributeFactory attributeFactory; - /** non-null; list of parsed attributes */ + /** {@code non-null;} list of parsed attributes */ private final StdAttributeList list; - /** >= -1; the end offset of this list in the byte array of the - * classfile, or -1 if not yet parsed */ + /** {@code >= -1;} the end offset of this list in the byte array of the + * classfile, or {@code -1} if not yet parsed */ private int endOffset; - /** null-ok; parse observer, if any */ + /** {@code null-ok;} parse observer, if any */ private ParseObserver observer; /** * Constructs an instance. * - * @param cf non-null; class file to parse from + * @param cf {@code non-null;} class file to parse from * @param context attribute parsing context (see {@link AttributeFactory}) - * @param offset offset in bytes to the start of the list - * @param attributeFactory non-null; attribute factory to use + * @param offset offset in {@code bytes} to the start of the list + * @param attributeFactory {@code non-null;} attribute factory to use */ public AttributeListParser(DirectClassFile cf, int context, int offset, AttributeFactory attributeFactory) { @@ -80,17 +80,17 @@ final /*package*/ class AttributeListParser { /** * Sets the parse observer for this instance. * - * @param observer null-ok; the observer + * @param observer {@code null-ok;} the observer */ public void setObserver(ParseObserver observer) { this.observer = observer; } /** - * Gets the end offset of this constant pool in the byte[] + * Gets the end offset of this constant pool in the {@code byte[]} * which it came from. * - * @return >= 0; the end offset + * @return {@code >= 0;} the end offset */ public int getEndOffset() { parseIfNecessary(); @@ -100,7 +100,7 @@ final /*package*/ class AttributeListParser { /** * Gets the parsed list. * - * @return non-null; the list + * @return {@code non-null;} the list */ public StdAttributeList getList() { parseIfNecessary(); diff --git a/dx/src/com/android/dx/cf/direct/ClassPathOpener.java b/dx/src/com/android/dx/cf/direct/ClassPathOpener.java index d30234990..927e9bd68 100644 --- a/dx/src/com/android/dx/cf/direct/ClassPathOpener.java +++ b/dx/src/com/android/dx/cf/direct/ClassPathOpener.java @@ -36,9 +36,9 @@ import java.util.Collections; */ public class ClassPathOpener { - /** non-null; pathname to start with */ + /** {@code non-null;} pathname to start with */ private final String pathname; - /** non-null; callback interface */ + /** {@code non-null;} callback interface */ private final Consumer consumer; /** * If true, sort such that classes appear before their inner @@ -48,20 +48,20 @@ public class ClassPathOpener { private final boolean sort; /** - * Callback interface for ClassOpener. + * Callback interface for {@code ClassOpener}. */ public interface Consumer { /** * Provides the file name and byte array for a class path element. * - * @param name non-null; filename of element. May not be a valid + * @param name {@code non-null;} filename of element. May not be a valid * filesystem path. * - * @param bytes non-null; file data + * @param bytes {@code non-null;} file data * @return true on success. Result is or'd with all other results - * from processFileBytes and returned to the caller - * of process(). + * from {@code processFileBytes} and returned to the caller + * of {@code process()}. */ boolean processFileBytes(String name, byte[] bytes); @@ -69,14 +69,14 @@ public class ClassPathOpener { * Informs consumer that an exception occurred while processing * this path element. Processing will continue if possible. * - * @param ex non-null; exception + * @param ex {@code non-null;} exception */ void onException(Exception ex); /** * Informs consumer that processing of an archive file has begun. * - * @param file non-null; archive file being processed + * @param file {@code non-null;} archive file being processed */ void onProcessArchiveStart(File file); } @@ -84,11 +84,11 @@ public class ClassPathOpener { /** * Constructs an instance. * - * @param pathname non-null; path element to process + * @param pathname {@code non-null;} path element to process * @param sort if true, sort such that classes appear before their inner * classes and "package-info" occurs before all other classes in that * package. - * @param consumer non-null; callback interface + * @param consumer {@code non-null;} callback interface */ public ClassPathOpener(String pathname, boolean sort, Consumer consumer) { this.pathname = pathname; @@ -100,7 +100,7 @@ public class ClassPathOpener { * Processes a path element. * * @return the OR of all return values - * from Consumer.processFileBytes(). + * from {@code Consumer.processFileBytes()}. */ public boolean process() { File file = new File(pathname); @@ -111,7 +111,7 @@ public class ClassPathOpener { /** * Processes one file. * - * @param file non-null; the file to process + * @param file {@code non-null;} the file to process * @param topLevel whether this is a top-level file (that is, * specified directly on the commandline) * @return whether any processing actually happened @@ -142,9 +142,9 @@ public class ClassPathOpener { * Sorts java class names such that outer classes preceed their inner * classes and "package-info" preceeds all other classes in its package. * - * @param a non-null; first class name - * @param b non-null; second class name - * @return compareTo()-style result + * @param a {@code non-null;} first class name + * @param b {@code non-null;} second class name + * @return {@code compareTo()}-style result */ private static int compareClassNames(String a, String b) { // Ensure inner classes sort second @@ -164,7 +164,7 @@ public class ClassPathOpener { /** * Processes a directory recursively. * - * @param dir non-null; file representing the directory + * @param dir {@code non-null;} file representing the directory * @param topLevel whether this is a top-level directory (that is, * specified directly on the commandline) * @return whether any processing actually happened @@ -194,10 +194,10 @@ public class ClassPathOpener { } /** - * Processes the contents of an archive (.zip, - * .jar, or .apk). + * Processes the contents of an archive ({@code .zip}, + * {@code .jar}, or {@code .apk}). * - * @param file non-null; archive file to process + * @param file {@code non-null;} archive file to process * @return whether any processing actually happened * @throws IOException on i/o problem */ @@ -220,8 +220,7 @@ public class ClassPathOpener { consumer.onProcessArchiveStart(file); - for (ZipEntry one: entriesList) { - + for (ZipEntry one : entriesList) { if (one.isDirectory()) { continue; } diff --git a/dx/src/com/android/dx/cf/direct/CodeObserver.java b/dx/src/com/android/dx/cf/direct/CodeObserver.java index 950147fdb..952f1bc5e 100644 --- a/dx/src/com/android/dx/cf/direct/CodeObserver.java +++ b/dx/src/com/android/dx/cf/direct/CodeObserver.java @@ -39,17 +39,17 @@ import java.util.ArrayList; * Bytecode visitor to use when "observing" bytecode getting parsed. */ public class CodeObserver implements BytecodeArray.Visitor { - /** non-null; actual array of bytecode */ + /** {@code non-null;} actual array of bytecode */ private final ByteArray bytes; - /** non-null; observer to inform of parsing */ + /** {@code non-null;} observer to inform of parsing */ private final ParseObserver observer; /** * Constructs an instance. * - * @param bytes non-null; actual array of bytecode - * @param observer non-null; observer to inform of parsing + * @param bytes {@code non-null;} actual array of bytecode + * @param observer {@code non-null;} observer to inform of parsing */ public CodeObserver(ByteArray bytes, ParseObserver observer) { if (bytes == null) { @@ -218,8 +218,8 @@ public class CodeObserver implements BytecodeArray.Visitor { } /** - * Helper for {code #visitConstant} where the constant is an - * int. + * Helper for {@link #visitConstant} where the constant is an + * {@code int}. * * @param opcode the opcode * @param offset offset to the instruction @@ -245,8 +245,8 @@ public class CodeObserver implements BytecodeArray.Visitor { } /** - * Helper for {code #visitConstant} where the constant is a - * long. + * Helper for {@link #visitConstant} where the constant is a + * {@code long}. * * @param opcode the opcode * @param offset offset to the instruction @@ -269,8 +269,8 @@ public class CodeObserver implements BytecodeArray.Visitor { } /** - * Helper for {code #visitConstant} where the constant is a - * float. + * Helper for {@link #visitConstant} where the constant is a + * {@code float}. * * @param opcode the opcode * @param offset offset to the instruction @@ -287,8 +287,8 @@ public class CodeObserver implements BytecodeArray.Visitor { } /** - * Helper for {code #visitConstant} where the constant is a - * double. + * Helper for {@link #visitConstant} where the constant is a + * {@code double}. * * @param opcode the opcode * @param offset offset to the instruction diff --git a/dx/src/com/android/dx/cf/direct/DirectClassFile.java b/dx/src/com/android/dx/cf/direct/DirectClassFile.java index e4751a4cc..ac227fadb 100644 --- a/dx/src/com/android/dx/cf/direct/DirectClassFile.java +++ b/dx/src/com/android/dx/cf/direct/DirectClassFile.java @@ -38,14 +38,14 @@ import com.android.dx.util.ByteArray; import com.android.dx.util.Hex; /** - * Class file with info taken from a byte[] or slice thereof. + * Class file with info taken from a {@code byte[]} or slice thereof. */ public class DirectClassFile implements ClassFile { /** the expected value of the ClassFile.magic field */ private static final int CLASS_FILE_MAGIC = 0xcafebabe; /** - * minimum .class file major version + * minimum {@code .class} file major version * * The class file definition (vmspec/2nd-edition) says: * @@ -64,92 +64,92 @@ public class DirectClassFile implements ClassFile { */ private static final int CLASS_FILE_MIN_MAJOR_VERSION = 45; - /** maximum .class file major version */ + /** maximum {@code .class} file major version */ private static final int CLASS_FILE_MAX_MAJOR_VERSION = 50; - /** maximum .class file minor version */ + /** maximum {@code .class} file minor version */ private static final int CLASS_FILE_MAX_MINOR_VERSION = 0; /** - * non-null; the file path for the class, excluding any base directory + * {@code non-null;} the file path for the class, excluding any base directory * specification */ private final String filePath; - /** non-null; the bytes of the file */ + /** {@code non-null;} the bytes of the file */ private final ByteArray bytes; /** * whether to be strict about parsing; if - * false, this avoids doing checks that only exist + * {@code false}, this avoids doing checks that only exist * for purposes of verification (such as magic number matching and * path-package consistency checking) */ private final boolean strictParse; /** - * null-ok; the constant pool; only ever null + * {@code null-ok;} the constant pool; only ever {@code null} * before the constant pool is successfully parsed */ private StdConstantPool pool; /** - * the class file field access_flags; will be -1 + * the class file field {@code access_flags}; will be {@code -1} * before the file is successfully parsed */ private int accessFlags; /** - * null-ok; the class file field this_class, - * interpreted as a type constant; only ever null + * {@code null-ok;} the class file field {@code this_class}, + * interpreted as a type constant; only ever {@code null} * before the file is successfully parsed */ private CstType thisClass; /** - * null-ok; the class file field super_class, interpreted + * {@code null-ok;} the class file field {@code super_class}, interpreted * as a type constant if non-zero */ private CstType superClass; /** - * null-ok; the class file field interfaces; only - * ever null before the file is successfully + * {@code null-ok;} the class file field {@code interfaces}; only + * ever {@code null} before the file is successfully * parsed */ private TypeList interfaces; /** - * null-ok; the class file field fields; only ever - * null before the file is successfully parsed + * {@code null-ok;} the class file field {@code fields}; only ever + * {@code null} before the file is successfully parsed */ private FieldList fields; /** - * null-ok; the class file field methods; only ever - * null before the file is successfully parsed + * {@code null-ok;} the class file field {@code methods}; only ever + * {@code null} before the file is successfully parsed */ private MethodList methods; /** - * null-ok; the class file field attributes; only - * ever null before the file is successfully + * {@code null-ok;} the class file field {@code attributes}; only + * ever {@code null} before the file is successfully * parsed */ private StdAttributeList attributes; - /** null-ok; attribute factory, if any */ + /** {@code null-ok;} attribute factory, if any */ private AttributeFactory attributeFactory; - /** null-ok; parse observer, if any */ + /** {@code null-ok;} parse observer, if any */ private ParseObserver observer; /** - * Returns the string form of an object or "(none)" - * (rather than "null") for null. + * Returns the string form of an object or {@code "(none)"} + * (rather than {@code "null"}) for {@code null}. * - * @param obj null-ok; the object to stringify - * @return non-null; the appropriate string form + * @param obj {@code null-ok;} the object to stringify + * @return {@code non-null;} the appropriate string form */ public static String stringOrNone(Object obj) { if (obj == null) { @@ -162,11 +162,11 @@ public class DirectClassFile implements ClassFile { /** * Constructs an instance. * - * @param bytes non-null; the bytes of the file - * @param filePath non-null; the file path for the class, + * @param bytes {@code non-null;} the bytes of the file + * @param filePath {@code non-null;} the file path for the class, * excluding any base directory specification * @param strictParse whether to be strict about parsing; if - * false, this avoids doing checks that only exist + * {@code false}, this avoids doing checks that only exist * for purposes of verification (such as magic number matching and * path-package consistency checking) */ @@ -189,11 +189,11 @@ public class DirectClassFile implements ClassFile { /** * Constructs an instance. * - * @param bytes non-null; the bytes of the file - * @param filePath non-null; the file path for the class, + * @param bytes {@code non-null;} the bytes of the file + * @param filePath {@code non-null;} the file path for the class, * excluding any base directory specification * @param strictParse whether to be strict about parsing; if - * false, this avoids doing checks that only exist + * {@code false}, this avoids doing checks that only exist * for purposes of verification (such as magic number matching and * path-package consistency checking) */ @@ -205,7 +205,7 @@ public class DirectClassFile implements ClassFile { /** * Sets the parse observer for this instance. * - * @param observer null-ok; the observer + * @param observer {@code null-ok;} the observer */ public void setObserver(ParseObserver observer) { this.observer = observer; @@ -214,7 +214,7 @@ public class DirectClassFile implements ClassFile { /** * Sets the attribute factory to use. * - * @param attributeFactory non-null; the attribute factory + * @param attributeFactory {@code non-null;} the attribute factory */ public void setAttributeFactory(AttributeFactory attributeFactory) { if (attributeFactory == null) { @@ -227,7 +227,7 @@ public class DirectClassFile implements ClassFile { /** * Gets the {@link ByteArray} that this instance's data comes from. * - * @return non-null; the bytes + * @return {@code non-null;} the bytes */ public ByteArray getBytes() { return bytes; @@ -317,12 +317,12 @@ public class DirectClassFile implements ClassFile { * list of constant pool indices for classes, which are in turn * translated to type constants. Instance construction will fail * if any of the (alleged) indices turn out not to refer to - * constant pool entries of type Class. + * constant pool entries of type {@code Class}. * * @param offset offset into {@link #bytes} for the start of the * data * @param size number of elements in the list (not number of bytes) - * @return non-null; an appropriately-constructed class list + * @return {@code non-null;} an appropriately-constructed class list */ public TypeList makeTypeList(int offset, int size) { if (size == 0) { @@ -337,7 +337,7 @@ public class DirectClassFile implements ClassFile { } /** - * Gets the class file field magic, but without doing any + * Gets the class file field {@code magic}, but without doing any * checks or parsing first. * * @return the magic value @@ -347,7 +347,7 @@ public class DirectClassFile implements ClassFile { } /** - * Gets the class file field minor_version, but + * Gets the class file field {@code minor_version}, but * without doing any checks or parsing first. * * @return the minor version @@ -357,7 +357,7 @@ public class DirectClassFile implements ClassFile { } /** - * Gets the class file field major_version, but + * Gets the class file field {@code major_version}, but * without doing any checks or parsing first. * * @return the major version @@ -554,27 +554,27 @@ public class DirectClassFile implements ClassFile { * which are in turn returned as type constants. Instance * construction will fail if any of the (alleged) indices turn out * not to refer to constant pool entries of type - * Class. + * {@code Class}. */ private static class DcfTypeList implements TypeList { - /** non-null; array containing the data */ + /** {@code non-null;} array containing the data */ private final ByteArray bytes; /** number of elements in the list (not number of bytes) */ private final int size; - /** non-null; the constant pool */ + /** {@code non-null;} the constant pool */ private final StdConstantPool pool; /** * Constructs an instance. * - * @param bytes non-null; original classfile's bytes + * @param bytes {@code non-null;} original classfile's bytes * @param offset offset into {@link #bytes} for the start of the * data * @param size number of elements in the list (not number of bytes) - * @param pool non-null; the constant pool to use - * @param observer null-ok; parse observer to use, if any + * @param pool {@code non-null;} the constant pool to use + * @param observer {@code null-ok;} parse observer to use, if any */ public DcfTypeList(ByteArray bytes, int offset, int size, StdConstantPool pool, ParseObserver observer) { diff --git a/dx/src/com/android/dx/cf/direct/FieldListParser.java b/dx/src/com/android/dx/cf/direct/FieldListParser.java index 06dcc4127..24ba7e0e3 100644 --- a/dx/src/com/android/dx/cf/direct/FieldListParser.java +++ b/dx/src/com/android/dx/cf/direct/FieldListParser.java @@ -28,16 +28,16 @@ import com.android.dx.rop.cst.CstType; * Parser for lists of fields in a class file. */ final /*package*/ class FieldListParser extends MemberListParser { - /** non-null; list in progress */ + /** {@code non-null;} list in progress */ private final StdFieldList fields; /** * Constructs an instance. * - * @param cf non-null; the class file to parse from - * @param definer non-null; class being defined - * @param offset offset in bytes to the start of the list - * @param attributeFactory non-null; attribute factory to use + * @param cf {@code non-null;} the class file to parse from + * @param definer {@code non-null;} class being defined + * @param offset offset in {@code bytes} to the start of the list + * @param attributeFactory {@code non-null;} attribute factory to use */ public FieldListParser(DirectClassFile cf, CstType definer, int offset, AttributeFactory attributeFactory) { @@ -48,7 +48,7 @@ final /*package*/ class FieldListParser extends MemberListParser { /** * Gets the parsed list. * - * @return non-null; the parsed list + * @return {@code non-null;} the parsed list */ public StdFieldList getList() { parseIfNecessary(); diff --git a/dx/src/com/android/dx/cf/direct/MemberListParser.java b/dx/src/com/android/dx/cf/direct/MemberListParser.java index 3c0bfa837..a0023c658 100644 --- a/dx/src/com/android/dx/cf/direct/MemberListParser.java +++ b/dx/src/com/android/dx/cf/direct/MemberListParser.java @@ -32,32 +32,32 @@ import com.android.dx.util.Hex; * Parser for lists of class file members (that is, fields and methods). */ abstract /*package*/ class MemberListParser { - /** non-null; the class file to parse from */ + /** {@code non-null;} the class file to parse from */ private final DirectClassFile cf; - /** non-null; class being defined */ + /** {@code non-null;} class being defined */ private final CstType definer; /** offset in the byte array of the classfile to the start of the list */ private final int offset; - /** non-null; attribute factory to use */ + /** {@code non-null;} attribute factory to use */ private final AttributeFactory attributeFactory; - /** >= -1; the end offset of this list in the byte array of the - * classfile, or -1 if not yet parsed */ + /** {@code >= -1;} the end offset of this list in the byte array of the + * classfile, or {@code -1} if not yet parsed */ private int endOffset; - /** null-ok; parse observer, if any */ + /** {@code null-ok;} parse observer, if any */ private ParseObserver observer; /** * Constructs an instance. * - * @param cf non-null; the class file to parse from - * @param definer non-null; class being defined - * @param offset offset in bytes to the start of the list - * @param attributeFactory non-null; attribute factory to use + * @param cf {@code non-null;} the class file to parse from + * @param definer {@code non-null;} class being defined + * @param offset offset in {@code bytes} to the start of the list + * @param attributeFactory {@code non-null;} attribute factory to use */ public MemberListParser(DirectClassFile cf, CstType definer, int offset, AttributeFactory attributeFactory) { @@ -81,10 +81,10 @@ abstract /*package*/ class MemberListParser { } /** - * Gets the end offset of this constant pool in the byte[] + * Gets the end offset of this constant pool in the {@code byte[]} * which it came from. * - * @return >= 0; the end offset + * @return {@code >= 0;} the end offset */ public int getEndOffset() { parseIfNecessary(); @@ -94,7 +94,7 @@ abstract /*package*/ class MemberListParser { /** * Sets the parse observer for this instance. * - * @param observer null-ok; the observer + * @param observer {@code null-ok;} the observer */ public final void setObserver(ParseObserver observer) { this.observer = observer; @@ -122,7 +122,7 @@ abstract /*package*/ class MemberListParser { /** * Gets the class file being defined. * - * @return non-null; the class + * @return {@code non-null;} the class */ protected final CstType getDefiner() { return definer; @@ -132,7 +132,7 @@ abstract /*package*/ class MemberListParser { * Gets the human-oriented name for what this instance is parsing. * Subclasses must override this method. * - * @return non-null; the human oriented name + * @return {@code non-null;} the human oriented name */ protected abstract String humanName(); @@ -141,15 +141,15 @@ abstract /*package*/ class MemberListParser { * Subclasses must override this method. * * @param accessFlags the flags - * @return non-null; the string form + * @return {@code non-null;} the string form */ protected abstract String humanAccessFlags(int accessFlags); /** - * Gets the CTX_* constant to use when parsing attributes. + * Gets the {@code CTX_*} constant to use when parsing attributes. * Subclasses must override this method. * - * @return non-null; the human oriented name + * @return {@code non-null;} the human oriented name */ protected abstract int getAttributeContext(); @@ -157,11 +157,11 @@ abstract /*package*/ class MemberListParser { * Sets an element in the list. Subclasses must override this method. * * @param n which element - * @param accessFlags the access_flags + * @param accessFlags the {@code access_flags} * @param nat the interpreted name and type (based on the two - * *_index fields) + * {@code *_index} fields) * @param attributes list of parsed attributes - * @return non-null; the constructed member + * @return {@code non-null;} the constructed member */ protected abstract Member set(int n, int accessFlags, CstNat nat, AttributeList attributes); diff --git a/dx/src/com/android/dx/cf/direct/MethodListParser.java b/dx/src/com/android/dx/cf/direct/MethodListParser.java index 9ca8ba6b8..6ab1abaad 100644 --- a/dx/src/com/android/dx/cf/direct/MethodListParser.java +++ b/dx/src/com/android/dx/cf/direct/MethodListParser.java @@ -28,16 +28,16 @@ import com.android.dx.rop.cst.CstType; * Parser for lists of methods in a class file. */ final /*package*/ class MethodListParser extends MemberListParser { - /** non-null; list in progress */ + /** {@code non-null;} list in progress */ final private StdMethodList methods; /** * Constructs an instance. * - * @param cf non-null; the class file to parse from - * @param definer non-null; class being defined - * @param offset offset in bytes to the start of the list - * @param attributeFactory non-null; attribute factory to use + * @param cf {@code non-null;} the class file to parse from + * @param definer {@code non-null;} class being defined + * @param offset offset in {@code bytes} to the start of the list + * @param attributeFactory {@code non-null;} attribute factory to use */ public MethodListParser(DirectClassFile cf, CstType definer, int offset, AttributeFactory attributeFactory) { @@ -48,7 +48,7 @@ final /*package*/ class MethodListParser extends MemberListParser { /** * Gets the parsed list. * - * @return non-null; the parsed list + * @return {@code non-null;} the parsed list */ public StdMethodList getList() { parseIfNecessary(); diff --git a/dx/src/com/android/dx/cf/direct/StdAttributeFactory.java b/dx/src/com/android/dx/cf/direct/StdAttributeFactory.java index ab0e2f7fd..da12a4e5a 100644 --- a/dx/src/com/android/dx/cf/direct/StdAttributeFactory.java +++ b/dx/src/com/android/dx/cf/direct/StdAttributeFactory.java @@ -65,7 +65,7 @@ import java.io.IOException; */ public class StdAttributeFactory extends AttributeFactory { - /** non-null; shared instance of this class */ + /** {@code non-null;} shared instance of this class */ public static final StdAttributeFactory THE_ONE = new StdAttributeFactory(); @@ -191,7 +191,7 @@ public class StdAttributeFactory } /** - * Parses an AnnotationDefault attribute. + * Parses an {@code AnnotationDefault} attribute. */ private Attribute annotationDefault(DirectClassFile cf, int offset, int length, ParseObserver observer) { @@ -207,7 +207,7 @@ public class StdAttributeFactory } /** - * Parses a Code attribute. + * Parses a {@code Code} attribute. */ private Attribute code(DirectClassFile cf, int offset, int length, ParseObserver observer) { @@ -311,7 +311,7 @@ public class StdAttributeFactory } /** - * Parses a ConstantValue attribute. + * Parses a {@code ConstantValue} attribute. */ private Attribute constantValue(DirectClassFile cf, int offset, int length, ParseObserver observer) { @@ -333,7 +333,7 @@ public class StdAttributeFactory } /** - * Parses a Deprecated attribute. + * Parses a {@code Deprecated} attribute. */ private Attribute deprecated(DirectClassFile cf, int offset, int length, ParseObserver observer) { @@ -345,7 +345,7 @@ public class StdAttributeFactory } /** - * Parses an EnclosingMethod attribute. + * Parses an {@code EnclosingMethod} attribute. */ private Attribute enclosingMethod(DirectClassFile cf, int offset, int length, ParseObserver observer) { @@ -374,7 +374,7 @@ public class StdAttributeFactory } /** - * Parses an Exceptions attribute. + * Parses an {@code Exceptions} attribute. */ private Attribute exceptions(DirectClassFile cf, int offset, int length, ParseObserver observer) { @@ -402,7 +402,7 @@ public class StdAttributeFactory } /** - * Parses an InnerClasses attribute. + * Parses an {@code InnerClasses} attribute. */ private Attribute innerClasses(DirectClassFile cf, int offset, int length, ParseObserver observer) { @@ -459,7 +459,7 @@ public class StdAttributeFactory } /** - * Parses a LineNumberTable attribute. + * Parses a {@code LineNumberTable} attribute. */ private Attribute lineNumberTable(DirectClassFile cf, int offset, int length, ParseObserver observer) { @@ -500,7 +500,7 @@ public class StdAttributeFactory } /** - * Parses a LocalVariableTable attribute. + * Parses a {@code LocalVariableTable} attribute. */ private Attribute localVariableTable(DirectClassFile cf, int offset, int length, ParseObserver observer) { @@ -523,7 +523,7 @@ public class StdAttributeFactory } /** - * Parses a LocalVariableTypeTable attribute. + * Parses a {@code LocalVariableTypeTable} attribute. */ private Attribute localVariableTypeTable(DirectClassFile cf, int offset, int length, ParseObserver observer) { @@ -546,15 +546,15 @@ public class StdAttributeFactory } /** - * Parse the table part of either a LocalVariableTable - * or a LocalVariableTypeTable. + * Parse the table part of either a {@code LocalVariableTable} + * or a {@code LocalVariableTypeTable}. * - * @param bytes non-null; bytes to parse, which should only + * @param bytes {@code non-null;} bytes to parse, which should only * contain the table data (no header) - * @param pool non-null; constant pool to use - * @param count >= 0; the number of entries - * @param typeTable true iff this is for a type table - * @return non-null; the constructed list + * @param pool {@code non-null;} constant pool to use + * @param count {@code >= 0;} the number of entries + * @param typeTable {@code true} iff this is for a type table + * @return {@code non-null;} the constructed list */ private LocalVariableList parseLocalVariables(ByteArray bytes, ConstantPool pool, ParseObserver observer, int count, @@ -604,7 +604,7 @@ public class StdAttributeFactory } /** - * Parses a RuntimeInvisibleAnnotations attribute. + * Parses a {@code RuntimeInvisibleAnnotations} attribute. */ private Attribute runtimeInvisibleAnnotations(DirectClassFile cf, int offset, int length, ParseObserver observer) { @@ -621,7 +621,7 @@ public class StdAttributeFactory } /** - * Parses a RuntimeVisibleAnnotations attribute. + * Parses a {@code RuntimeVisibleAnnotations} attribute. */ private Attribute runtimeVisibleAnnotations(DirectClassFile cf, int offset, int length, ParseObserver observer) { @@ -638,7 +638,7 @@ public class StdAttributeFactory } /** - * Parses a RuntimeInvisibleParameterAnnotations attribute. + * Parses a {@code RuntimeInvisibleParameterAnnotations} attribute. */ private Attribute runtimeInvisibleParameterAnnotations(DirectClassFile cf, int offset, int length, ParseObserver observer) { @@ -655,7 +655,7 @@ public class StdAttributeFactory } /** - * Parses a RuntimeVisibleParameterAnnotations attribute. + * Parses a {@code RuntimeVisibleParameterAnnotations} attribute. */ private Attribute runtimeVisibleParameterAnnotations(DirectClassFile cf, int offset, int length, ParseObserver observer) { @@ -672,7 +672,7 @@ public class StdAttributeFactory } /** - * Parses a Signature attribute. + * Parses a {@code Signature} attribute. */ private Attribute signature(DirectClassFile cf, int offset, int length, ParseObserver observer) { @@ -694,7 +694,7 @@ public class StdAttributeFactory } /** - * Parses a SourceFile attribute. + * Parses a {@code SourceFile} attribute. */ private Attribute sourceFile(DirectClassFile cf, int offset, int length, ParseObserver observer) { @@ -716,7 +716,7 @@ public class StdAttributeFactory } /** - * Parses a Synthetic attribute. + * Parses a {@code Synthetic} attribute. */ private Attribute synthetic(DirectClassFile cf, int offset, int length, ParseObserver observer) { diff --git a/dx/src/com/android/dx/cf/iface/Attribute.java b/dx/src/com/android/dx/cf/iface/Attribute.java index f28f51e24..b075251db 100644 --- a/dx/src/com/android/dx/cf/iface/Attribute.java +++ b/dx/src/com/android/dx/cf/iface/Attribute.java @@ -23,16 +23,16 @@ public interface Attribute { /** * Get the name of the attribute. * - * @return non-null; the name + * @return {@code non-null;} the name */ public String getName(); /** * Get the total length of the attribute in bytes, including the * header. Since the header is always six bytes, the result of - * this method is always at least 6. + * this method is always at least {@code 6}. * - * @return >= 6; the total length, in bytes + * @return {@code >= 6;} the total length, in bytes */ public int byteLength(); } diff --git a/dx/src/com/android/dx/cf/iface/AttributeList.java b/dx/src/com/android/dx/cf/iface/AttributeList.java index a72965abd..f7a1d2705 100644 --- a/dx/src/com/android/dx/cf/iface/AttributeList.java +++ b/dx/src/com/android/dx/cf/iface/AttributeList.java @@ -22,11 +22,11 @@ package com.android.dx.cf.iface; public interface AttributeList { /** * Get whether this instance is mutable. Note that the - * AttributeList interface itself doesn't provide any means + * {@code AttributeList} interface itself doesn't provide any means * of mutation, but that doesn't mean that there isn't a non-interface * way of mutating an instance. * - * @return true iff this instance is somehow mutable + * @return {@code true} iff this instance is somehow mutable */ public boolean isMutable(); @@ -38,28 +38,28 @@ public interface AttributeList { public int size(); /** - * Get the nth attribute. + * Get the {@code n}th attribute. * - * @param n n >= 0, n < size(); which attribute - * @return non-null; the attribute in question + * @param n {@code n >= 0, n < size();} which attribute + * @return {@code non-null;} the attribute in question */ public Attribute get(int n); /** * Get the total length of this list in bytes, when part of a * class file. The returned value includes the two bytes for the - * attributes_count length indicator. + * {@code attributes_count} length indicator. * - * @return >= 2; the total length, in bytes + * @return {@code >= 2;} the total length, in bytes */ public int byteLength(); /** * Get the first attribute in the list with the given name, if any. * - * @param name non-null; attribute name - * @return null-ok; first attribute in the list with the given name, - * or null if there is none + * @param name {@code non-null;} attribute name + * @return {@code null-ok;} first attribute in the list with the given name, + * or {@code null} if there is none */ public Attribute findFirst(String name); @@ -67,9 +67,9 @@ public interface AttributeList { * Get the next attribute in the list after the given one, with the same * name, if any. * - * @param attrib non-null; attribute to start looking after - * @return null-ok; next attribute after attrib with the - * same name as attrib + * @param attrib {@code non-null;} attribute to start looking after + * @return {@code null-ok;} next attribute after {@code attrib} with the + * same name as {@code attrib} */ public Attribute findNext(Attribute attrib); } diff --git a/dx/src/com/android/dx/cf/iface/ClassFile.java b/dx/src/com/android/dx/cf/iface/ClassFile.java index 36a5f7415..e37fec03a 100644 --- a/dx/src/com/android/dx/cf/iface/ClassFile.java +++ b/dx/src/com/android/dx/cf/iface/ClassFile.java @@ -26,98 +26,98 @@ import com.android.dx.rop.type.TypeList; * facsimiles thereof. * *

    Note: The fields referred to in this documentation are of the - * ClassFile structure defined in vmspec-2 sec4.1. + * {@code ClassFile} structure defined in vmspec-2 sec4.1. */ public interface ClassFile { /** - * Gets the field magic. + * Gets the field {@code magic}. * * @return the value in question */ public int getMagic(); /** - * Gets the field minor_version. + * Gets the field {@code minor_version}. * * @return the value in question */ public int getMinorVersion(); /** - * Gets the field major_version. + * Gets the field {@code major_version}. * * @return the value in question */ public int getMajorVersion(); /** - * Gets the field access_flags. + * Gets the field {@code access_flags}. * * @return the value in question */ public int getAccessFlags(); /** - * Gets the field this_class, interpreted as a type constant. + * Gets the field {@code this_class}, interpreted as a type constant. * - * @return non-null; the value in question + * @return {@code non-null;} the value in question */ public CstType getThisClass(); /** - * Gets the field super_class, interpreted as a type constant + * Gets the field {@code super_class}, interpreted as a type constant * if non-zero. * - * @return null-ok; the value in question + * @return {@code null-ok;} the value in question */ public CstType getSuperclass(); /** - * Gets the field constant_pool (along with - * constant_pool_count). + * Gets the field {@code constant_pool} (along with + * {@code constant_pool_count}). * - * @return non-null; the constant pool + * @return {@code non-null;} the constant pool */ public ConstantPool getConstantPool(); /** - * Gets the field interfaces (along with - * interfaces_count). + * Gets the field {@code interfaces} (along with + * {@code interfaces_count}). * - * @return non-null; the list of interfaces + * @return {@code non-null;} the list of interfaces */ public TypeList getInterfaces(); /** - * Gets the field fields (along with - * fields_count). + * Gets the field {@code fields} (along with + * {@code fields_count}). * - * @return non-null; the list of fields + * @return {@code non-null;} the list of fields */ public FieldList getFields(); /** - * Gets the field methods (along with - * methods_count). + * Gets the field {@code methods} (along with + * {@code methods_count}). * - * @return non-null; the list of fields + * @return {@code non-null;} the list of fields */ public MethodList getMethods(); /** - * Gets the field attributes (along with - * attributes_count). + * Gets the field {@code attributes} (along with + * {@code attributes_count}). * - * @return non-null; the list of attributes + * @return {@code non-null;} the list of attributes */ public AttributeList getAttributes(); /** - * Gets the name out of the SourceFile attribute of this + * Gets the name out of the {@code SourceFile} attribute of this * file, if any. This is a convenient shorthand for scrounging around * the class's attributes. * - * @return non-null; the constant pool + * @return {@code non-null;} the constant pool */ public CstUtf8 getSourceFile(); } diff --git a/dx/src/com/android/dx/cf/iface/Field.java b/dx/src/com/android/dx/cf/iface/Field.java index d1694fc32..e3002bcd0 100644 --- a/dx/src/com/android/dx/cf/iface/Field.java +++ b/dx/src/com/android/dx/cf/iface/Field.java @@ -25,10 +25,10 @@ public interface Field extends Member { /** * Get the constant value for this field, if any. This only returns - * non-null for a static final field which - * includes a ConstantValue attribute. + * non-{@code null} for a {@code static final} field which + * includes a {@code ConstantValue} attribute. * - * @return null-ok; the constant value, or null if this + * @return {@code null-ok;} the constant value, or {@code null} if this * field isn't a constant */ public TypedConstant getConstantValue(); diff --git a/dx/src/com/android/dx/cf/iface/FieldList.java b/dx/src/com/android/dx/cf/iface/FieldList.java index 80a794fdb..9cd27a311 100644 --- a/dx/src/com/android/dx/cf/iface/FieldList.java +++ b/dx/src/com/android/dx/cf/iface/FieldList.java @@ -23,11 +23,11 @@ public interface FieldList { /** * Get whether this instance is mutable. Note that the - * FieldList interface itself doesn't provide any means + * {@code FieldList} interface itself doesn't provide any means * of mutation, but that doesn't mean that there isn't a non-interface * way of mutating an instance. * - * @return true iff this instance is somehow mutable + * @return {@code true} iff this instance is somehow mutable */ public boolean isMutable(); @@ -39,10 +39,10 @@ public interface FieldList public int size(); /** - * Get the nth field. + * Get the {@code n}th field. * - * @param n n >= 0, n < size(); which field - * @return non-null; the field in question + * @param n {@code n >= 0, n < size();} which field + * @return {@code non-null;} the field in question */ public Field get(int n); } diff --git a/dx/src/com/android/dx/cf/iface/Member.java b/dx/src/com/android/dx/cf/iface/Member.java index b305e09b6..0453a6f60 100644 --- a/dx/src/com/android/dx/cf/iface/Member.java +++ b/dx/src/com/android/dx/cf/iface/Member.java @@ -27,48 +27,48 @@ public interface Member { /** * Get the defining class. * - * @return non-null; the defining class + * @return {@code non-null;} the defining class */ public CstType getDefiningClass(); /** - * Get the field access_flags. + * Get the field {@code access_flags}. * * @return the access flags */ public int getAccessFlags(); /** - * Get the field name_index of the member. This is - * just a convenient shorthand for getNat().getName(). + * Get the field {@code name_index} of the member. This is + * just a convenient shorthand for {@code getNat().getName()}. * - * @return non-null; the name + * @return {@code non-null;} the name */ public CstUtf8 getName(); /** - * Get the field descriptor_index of the member. This is - * just a convenient shorthand for getNat().getDescriptor(). + * Get the field {@code descriptor_index} of the member. This is + * just a convenient shorthand for {@code getNat().getDescriptor()}. * - * @return non-null; the descriptor + * @return {@code non-null;} the descriptor */ public CstUtf8 getDescriptor(); /** * Get the name and type associated with this member. This is a - * combination of the fields name_index and - * descriptor_index in the original classfile, interpreted + * combination of the fields {@code name_index} and + * {@code descriptor_index} in the original classfile, interpreted * via the constant pool. * - * @return non-null; the name and type + * @return {@code non-null;} the name and type */ public CstNat getNat(); /** - * Get the field attributes (along with - * attributes_count). + * Get the field {@code attributes} (along with + * {@code attributes_count}). * - * @return non-null; the constant pool + * @return {@code non-null;} the constant pool */ public AttributeList getAttributes(); } diff --git a/dx/src/com/android/dx/cf/iface/Method.java b/dx/src/com/android/dx/cf/iface/Method.java index 424400a2b..18b9af64f 100644 --- a/dx/src/com/android/dx/cf/iface/Method.java +++ b/dx/src/com/android/dx/cf/iface/Method.java @@ -26,9 +26,9 @@ public interface Method { /** * Get the effective method descriptor, which includes, if - * necessary, a first this parameter. + * necessary, a first {@code this} parameter. * - * @return non-null; the effective method descriptor + * @return {@code non-null;} the effective method descriptor */ public Prototype getEffectiveDescriptor(); } diff --git a/dx/src/com/android/dx/cf/iface/MethodList.java b/dx/src/com/android/dx/cf/iface/MethodList.java index a7a395cbe..dfa6528a6 100644 --- a/dx/src/com/android/dx/cf/iface/MethodList.java +++ b/dx/src/com/android/dx/cf/iface/MethodList.java @@ -22,11 +22,11 @@ package com.android.dx.cf.iface; public interface MethodList { /** * Get whether this instance is mutable. Note that the - * MethodList interface itself doesn't provide any means + * {@code MethodList} interface itself doesn't provide any means * of mutation, but that doesn't mean that there isn't a non-interface * way of mutating an instance. * - * @return true iff this instance is somehow mutable + * @return {@code true} iff this instance is somehow mutable */ public boolean isMutable(); @@ -38,10 +38,10 @@ public interface MethodList { public int size(); /** - * Get the nth method. + * Get the {@code n}th method. * - * @param n n >= 0, n < size(); which method - * @return non-null; the method in question + * @param n {@code n >= 0, n < size();} which method + * @return {@code non-null;} the method in question */ public Method get(int n); } diff --git a/dx/src/com/android/dx/cf/iface/ParseObserver.java b/dx/src/com/android/dx/cf/iface/ParseObserver.java index 2ad3493c6..98d5a75c2 100644 --- a/dx/src/com/android/dx/cf/iface/ParseObserver.java +++ b/dx/src/com/android/dx/cf/iface/ParseObserver.java @@ -34,11 +34,11 @@ public interface ParseObserver { /** * Indicate that a particular member is now being parsed. * - * @param bytes non-null; the source that is being parsed - * @param offset offset into bytes for the start of the + * @param bytes {@code non-null;} the source that is being parsed + * @param offset offset into {@code bytes} for the start of the * member - * @param name non-null; name of the member - * @param descriptor non-null; descriptor of the member + * @param name {@code non-null;} name of the member + * @param descriptor {@code non-null;} descriptor of the member */ public void startParsingMember(ByteArray bytes, int offset, String name, String descriptor); @@ -46,12 +46,12 @@ public interface ParseObserver { /** * Indicate that a particular member is no longer being parsed. * - * @param bytes non-null; the source that was parsed - * @param offset offset into bytes for the end of the + * @param bytes {@code non-null;} the source that was parsed + * @param offset offset into {@code bytes} for the end of the * member - * @param name non-null; name of the member - * @param descriptor non-null; descriptor of the member - * @param member non-null; the actual member that was parsed + * @param name {@code non-null;} name of the member + * @param descriptor {@code non-null;} descriptor of the member + * @param member {@code non-null;} the actual member that was parsed */ public void endParsingMember(ByteArray bytes, int offset, String name, String descriptor, Member member); @@ -59,10 +59,10 @@ public interface ParseObserver { /** * Indicate that some parsing happened. * - * @param bytes non-null; the source that was parsed - * @param offset offset into bytes for what was parsed + * @param bytes {@code non-null;} the source that was parsed + * @param offset offset into {@code bytes} for what was parsed * @param len number of bytes parsed - * @param human non-null; human form for what was parsed + * @param human {@code non-null;} human form for what was parsed */ public void parsed(ByteArray bytes, int offset, int len, String human); } diff --git a/dx/src/com/android/dx/cf/iface/StdAttributeList.java b/dx/src/com/android/dx/cf/iface/StdAttributeList.java index c29bb08d0..dd5dfd7a8 100644 --- a/dx/src/com/android/dx/cf/iface/StdAttributeList.java +++ b/dx/src/com/android/dx/cf/iface/StdAttributeList.java @@ -25,7 +25,7 @@ import com.android.dx.util.FixedSizeList; public final class StdAttributeList extends FixedSizeList implements AttributeList { /** - * Constructs an instance. All indices initially contain null. + * Constructs an instance. All indices initially contain {@code null}. * * @param size the size of the list */ @@ -95,8 +95,8 @@ public final class StdAttributeList extends FixedSizeList /** * Sets the attribute at the given index. * - * @param n >= 0, < size(); which attribute - * @param attribute null-ok; the attribute object + * @param n {@code >= 0, < size();} which attribute + * @param attribute {@code null-ok;} the attribute object */ public void set(int n, Attribute attribute) { set0(n, attribute); diff --git a/dx/src/com/android/dx/cf/iface/StdField.java b/dx/src/com/android/dx/cf/iface/StdField.java index 3551aee69..c3a4da63a 100644 --- a/dx/src/com/android/dx/cf/iface/StdField.java +++ b/dx/src/com/android/dx/cf/iface/StdField.java @@ -29,10 +29,10 @@ public final class StdField extends StdMember implements Field { /** * Constructs an instance. * - * @param definingClass non-null; the defining class + * @param definingClass {@code non-null;} the defining class * @param accessFlags access flags - * @param nat non-null; member name and type (descriptor) - * @param attributes non-null; list of associated attributes + * @param nat {@code non-null;} member name and type (descriptor) + * @param attributes {@code non-null;} list of associated attributes */ public StdField(CstType definingClass, int accessFlags, CstNat nat, AttributeList attributes) { diff --git a/dx/src/com/android/dx/cf/iface/StdFieldList.java b/dx/src/com/android/dx/cf/iface/StdFieldList.java index 0f8654b20..044d6b78d 100644 --- a/dx/src/com/android/dx/cf/iface/StdFieldList.java +++ b/dx/src/com/android/dx/cf/iface/StdFieldList.java @@ -24,7 +24,7 @@ import com.android.dx.util.FixedSizeList; */ public final class StdFieldList extends FixedSizeList implements FieldList { /** - * Constructs an instance. All indices initially contain null. + * Constructs an instance. All indices initially contain {@code null}. * * @param size the size of the list */ @@ -40,8 +40,8 @@ public final class StdFieldList extends FixedSizeList implements FieldList { /** * Sets the field at the given index. * - * @param n >= 0, < size(); which field - * @param field null-ok; the field object + * @param n {@code >= 0, < size();} which field + * @param field {@code null-ok;} the field object */ public void set(int n, Field field) { set0(n, field); diff --git a/dx/src/com/android/dx/cf/iface/StdMember.java b/dx/src/com/android/dx/cf/iface/StdMember.java index eaf949ef0..dfe45c329 100644 --- a/dx/src/com/android/dx/cf/iface/StdMember.java +++ b/dx/src/com/android/dx/cf/iface/StdMember.java @@ -25,25 +25,25 @@ import com.android.dx.rop.cst.CstUtf8; * all the associated data. */ public abstract class StdMember implements Member { - /** non-null; the defining class */ + /** {@code non-null;} the defining class */ private final CstType definingClass; /** access flags */ private final int accessFlags; - /** non-null; member name and type */ + /** {@code non-null;} member name and type */ private final CstNat nat; - /** non-null; list of associated attributes */ + /** {@code non-null;} list of associated attributes */ private final AttributeList attributes; /** * Constructs an instance. * - * @param definingClass non-null; the defining class + * @param definingClass {@code non-null;} the defining class * @param accessFlags access flags - * @param nat non-null; member name and type (descriptor) - * @param attributes non-null; list of associated attributes + * @param nat {@code non-null;} member name and type (descriptor) + * @param attributes {@code non-null;} list of associated attributes */ public StdMember(CstType definingClass, int accessFlags, CstNat nat, AttributeList attributes) { diff --git a/dx/src/com/android/dx/cf/iface/StdMethod.java b/dx/src/com/android/dx/cf/iface/StdMethod.java index a4acbaa7f..15fd6e1fc 100644 --- a/dx/src/com/android/dx/cf/iface/StdMethod.java +++ b/dx/src/com/android/dx/cf/iface/StdMethod.java @@ -26,16 +26,16 @@ import com.android.dx.rop.type.Prototype; * all the associated data. */ public final class StdMethod extends StdMember implements Method { - /** non-null; the effective method descriptor */ + /** {@code non-null;} the effective method descriptor */ private final Prototype effectiveDescriptor; /** * Constructs an instance. * - * @param definingClass non-null; the defining class + * @param definingClass {@code non-null;} the defining class * @param accessFlags access flags - * @param nat non-null; member name and type (descriptor) - * @param attributes non-null; list of associated attributes + * @param nat {@code non-null;} member name and type (descriptor) + * @param attributes {@code non-null;} list of associated attributes */ public StdMethod(CstType definingClass, int accessFlags, CstNat nat, AttributeList attributes) { diff --git a/dx/src/com/android/dx/cf/iface/StdMethodList.java b/dx/src/com/android/dx/cf/iface/StdMethodList.java index ef0ff3153..521021ec0 100644 --- a/dx/src/com/android/dx/cf/iface/StdMethodList.java +++ b/dx/src/com/android/dx/cf/iface/StdMethodList.java @@ -24,7 +24,7 @@ import com.android.dx.util.FixedSizeList; */ public final class StdMethodList extends FixedSizeList implements MethodList { /** - * Constructs an instance. All indices initially contain null. + * Constructs an instance. All indices initially contain {@code null}. * * @param size the size of the list */ @@ -40,8 +40,8 @@ public final class StdMethodList extends FixedSizeList implements MethodList { /** * Sets the method at the given index. * - * @param n >= 0, < size(); which method - * @param method null-ok; the method object + * @param n {@code >= 0, < size();} which method + * @param method {@code null-ok;} the method object */ public void set(int n, Method method) { set0(n, method); diff --git a/dx/src/com/android/dx/command/DxConsole.java b/dx/src/com/android/dx/command/DxConsole.java index 982e6593c..9ce9836ef 100644 --- a/dx/src/com/android/dx/command/DxConsole.java +++ b/dx/src/com/android/dx/command/DxConsole.java @@ -20,18 +20,18 @@ import java.io.PrintStream; /** * Provides standard and error PrintStream object to output information.
    - * By default the PrintStream objects link to System.out and - * System.err but they can be changed to link to other + * By default the PrintStream objects link to {@code System.out} and + * {@code System.err} but they can be changed to link to other * PrintStream. */ public class DxConsole { /** - * Standard output stream. Links to System.out by default. + * Standard output stream. Links to {@code System.out} by default. */ public static PrintStream out = System.out; /** - * Error output stream. Links to System.err by default. + * Error output stream. Links to {@code System.err} by default. */ public static PrintStream err = System.err; } diff --git a/dx/src/com/android/dx/command/Main.java b/dx/src/com/android/dx/command/Main.java index 17f5db386..281a83ee3 100644 --- a/dx/src/com/android/dx/command/Main.java +++ b/dx/src/com/android/dx/command/Main.java @@ -43,10 +43,12 @@ public class Main { " options: none, important, lines.\n" + " dx --annotool --annotation= [--element=]\n" + " [--print=]\n" + - " dx --dump [--debug] [--strict] [--bytes] [--basic-blocks | " + - "--rop-blocks]\n" + + " dx --dump [--debug] [--strict] [--bytes] [--optimize]\n" + + " [--basic-blocks | --rop-blocks | --ssa-blocks | --dot] " + + "[--ssa-step=]\n" + " [--width=] [.class | .txt] ...\n" + - " Dump classfiles in a human-oriented format.\n" + + " Dump classfiles, or transformations thereof, in a " + + "human-oriented format.\n" + " dx --junit [-wait] \n" + " Run the indicated unit test.\n" + " dx -J
    instance. + * by the {@code Main.Arguments} instance. */ class AnnotationLister { - /** * The string name of the pseudo-class that * contains package-wide annotations @@ -59,7 +58,7 @@ class AnnotationLister { /** Processes based on configuration specified in constructor. */ void process() { - for (String path: args.files) { + for (String path : args.files) { ClassPathOpener opener; opener = new ClassPathOpener(path, true, @@ -137,8 +136,8 @@ class AnnotationLister { /** * Inspects a class annotation. * - * @param cf non-null; class file - * @param ann non-null; annotation + * @param cf {@code non-null;} class file + * @param ann {@code non-null;} annotation */ private void visitClassAnnotation(DirectClassFile cf, BaseAnnotations ann) { @@ -147,7 +146,7 @@ class AnnotationLister { return; } - for (Annotation anAnn: ann.getAnnotations().getAnnotations()) { + for (Annotation anAnn : ann.getAnnotations().getAnnotations()) { String annClassName = anAnn.getType().getClassType().getClassName(); if (args.aclass.equals(annClassName)) { @@ -159,8 +158,8 @@ class AnnotationLister { /** * Inspects a package annotation * - * @param cf non-null; class file of "package-info" pseudo-class - * @param ann non-null; annotation + * @param cf {@code non-null;} class file of "package-info" pseudo-class + * @param ann {@code non-null;} annotation */ private void visitPackageAnnotation( DirectClassFile cf, BaseAnnotations ann) { @@ -181,7 +180,7 @@ class AnnotationLister { } - for (Annotation anAnn: ann.getAnnotations().getAnnotations()) { + for (Annotation anAnn : ann.getAnnotations().getAnnotations()) { String annClassName = anAnn.getType().getClassType().getClassName(); if (args.aclass.equals(annClassName)) { @@ -195,10 +194,10 @@ class AnnotationLister { * Prints, or schedules for printing, elements related to a * matching package. * - * @param packageName non-null; name of package + * @param packageName {@code non-null;} name of package */ private void printMatchPackage(String packageName) { - for(Main.PrintType pt: args.printTypes) { + for (Main.PrintType pt : args.printTypes) { switch (pt) { case CLASS: case INNERCLASS: @@ -216,14 +215,15 @@ class AnnotationLister { * Prints, or schedules for printing, elements related to a matching * class. * - * @param cf non-null; matching class + * @param cf {@code non-null;} matching class */ private void printMatch(DirectClassFile cf) { - for(Main.PrintType pt: args.printTypes) { + for (Main.PrintType pt : args.printTypes) { switch (pt) { case CLASS: String classname; - classname = cf.getThisClass().getClassType().getClassName(); + classname = + cf.getThisClass().getClassType().getClassName(); classname = classname.replace('/','.'); System.out.println(classname); break; @@ -244,7 +244,7 @@ class AnnotationLister { * Checks to see if a specified class name should be considered a match * due to previous matches. * - * @param s non-null; class name + * @param s {@code non-null;} class name * @return true if this class should be considered a match */ private boolean isMatchingInnerClass(String s) { @@ -264,7 +264,7 @@ class AnnotationLister { * Checks to see if a specified package should be considered a match due * to previous matches. * - * @param s non-null; package name + * @param s {@code non-null;} package name * @return true if this package should be considered a match */ private boolean isMatchingPackage(String s) { diff --git a/dx/src/com/android/dx/command/annotool/Main.java b/dx/src/com/android/dx/command/annotool/Main.java index 4f1d9a40b..9fd1ac57a 100644 --- a/dx/src/com/android/dx/command/annotool/Main.java +++ b/dx/src/com/android/dx/command/annotool/Main.java @@ -87,17 +87,18 @@ public class Main { String argParam = arg.substring(arg.indexOf('=') + 1); try { - for (String p: argParam.split(",")) { + for (String p : argParam.split(",")) { eTypes.add(ElementType.valueOf(p.toUpperCase())); } } catch (IllegalArgumentException ex) { - throw new InvalidArgumentException("invalid --element"); + throw new InvalidArgumentException( + "invalid --element"); } } else if (arg.startsWith("--print=")) { String argParam = arg.substring(arg.indexOf('=') + 1); try { - for (String p: argParam.split(",")) { + for (String p : argParam.split(",")) { printTypes.add(PrintType.valueOf(p.toUpperCase())); } } catch (IllegalArgumentException ex) { diff --git a/dx/src/com/android/dx/command/dexer/Main.java b/dx/src/com/android/dx/command/dexer/Main.java index 19f67b443..5a9f41702 100644 --- a/dx/src/com/android/dx/command/dexer/Main.java +++ b/dx/src/com/android/dx/command/dexer/Main.java @@ -55,26 +55,26 @@ import java.util.jar.Manifest; */ public class Main { /** - * non-null; name for the .dex file that goes into - * .jar files + * {@code non-null;} name for the {@code .dex} file that goes into + * {@code .jar} files */ private static final String DEX_IN_JAR_NAME = "classes.dex"; /** - * non-null; name of the standard manifest file in .jar + * {@code non-null;} name of the standard manifest file in {@code .jar} * files */ private static final String MANIFEST_NAME = "META-INF/MANIFEST.MF"; /** - * non-null; attribute name for the (quasi-standard?) - * Created-By attribute + * {@code non-null;} attribute name for the (quasi-standard?) + * {@code Created-By} attribute */ private static final Attributes.Name CREATED_BY = new Attributes.Name("Created-By"); /** - * non-null; list of javax subpackages that are considered + * {@code non-null;} list of {@code javax} subpackages that are considered * to be "core". Note:: This list must be sorted, since it * is binary-searched. */ @@ -90,15 +90,15 @@ public class Main { /** number of errors during processing */ private static int errors = 0; - /** non-null; parsed command-line arguments */ + /** {@code non-null;} parsed command-line arguments */ private static Arguments args; - /** non-null; output file in-progress */ + /** {@code non-null;} output file in-progress */ private static DexFile outputDex; /** - * null-ok; map of resources to include in the output, or - * null if resources are being ignored + * {@code null-ok;} map of resources to include in the output, or + * {@code null} if resources are being ignored */ private static TreeMap outputResources; @@ -215,8 +215,9 @@ public class Main { /** * Processes one pathname element. * - * @param pathname non-null; the pathname to process. May be the path of - * a class file, a jar file, or a directory containing class files. + * @param pathname {@code non-null;} the pathname to process. May + * be the path of a class file, a jar file, or a directory + * containing class files. * @return whether any processing actually happened */ private static boolean processOne(String pathname) { @@ -237,7 +238,8 @@ public class Main { } public void onProcessArchiveStart(File file) { if (args.verbose) { - DxConsole.out.println("processing archive " + file + "..."); + DxConsole.out.println("processing archive " + file + + "..."); } } }); @@ -248,8 +250,8 @@ public class Main { /** * Processes one file, which may be either a class or a resource. * - * @param name non-null; name of the file - * @param bytes non-null; contents of the file + * @param name {@code non-null;} name of the file + * @param bytes {@code non-null;} contents of the file * @return whether processing was successful */ private static boolean processFileBytes(String name, byte[] bytes) { @@ -283,9 +285,9 @@ public class Main { /** * Processes one classfile. * - * @param name non-null; name of the file, clipped such that it + * @param name {@code non-null;} name of the file, clipped such that it * should correspond to the name of the class it contains - * @param bytes non-null; contents of the file + * @param bytes {@code non-null;} contents of the file * @return whether processing was successful */ private static boolean processClass(String name, byte[] bytes) { @@ -316,7 +318,8 @@ public class Main { * class. If there is a problem, this updates the error count and * throws an exception to stop processing. * - * @param name non-null; the fully-qualified internal-form class name + * @param name {@code non-null;} the fully-qualified internal-form + * class name */ private static void checkClassName(String name) { boolean bogus = false; @@ -346,35 +349,60 @@ public class Main { DxConsole.err.println("\ntrouble processing \"" + name + "\":"); DxConsole.err.println("\n" + - "Attempt to include a core VM class in something other " + - "than a core library.\n" + - "It is likely that you have attempted to include the " + - "core library from a desktop\n" + - "virtual machine into an application, which will most " + - "assuredly not work. If\n" + - "you really intend to build a core library -- which is "+ - "only appropriate as\n" + - "part of creating a full virtual machine binary, as " + - "opposed to compiling an\n" + - "application -- then use the \"--core-library\" option " + - "to suppress this error\n" + - "message. If you go ahead and use \"--core-library\" " + - "but are in fact building\n" + - "an application, then please be aware that your build " + - "will still fail at some\n" + - "point; you will simply be denied the pleasure of " + - "reading this helpful error\n" + - "message."); + "Attempt to include a core class (java.* or javax.*) in " + + "something other\n" + + "than a core library. It is likely that you have " + + "attempted to include\n" + + "in an application the core library (or a part thereof) " + + "from a desktop\n" + + "virtual machine. This will most assuredly not work. " + + "At a minimum, it\n" + + "jeopardizes the compatibility of your app with future " + + "versions of the\n" + + "platform. It is also often of questionable legality.\n" + + "\n" + + "If you really intend to build a core library -- which is " + + "only\n" + + "appropriate as part of creating a full virtual machine " + + "distribution,\n" + + "as opposed to compiling an application -- then use the\n" + + "\"--core-library\" option to suppress this error message.\n" + + "\n" + + "If you go ahead and use \"--core-library\" but are in " + + "fact building an\n" + + "application, then be forewarned that your application " + + "will still fail\n" + + "to build or run, at some point. Please be prepared for " + + "angry customers\n" + + "who find, for example, that your application ceases to " + + "function once\n" + + "they upgrade their operating system. You will be to " + + "blame for this\n" + + "problem.\n" + + "\n" + + "If you are legitimately using some code that happens to " + + "be in a core\n" + + "package, then the easiest safe alternative you have is " + + "to repackage\n" + + "that code. That is, move the classes in question into " + + "your own package\n" + + "namespace. This means that they will never be in " + + "conflict with core\n" + + "system classes. If you find that you cannot do this, " + + "then that is an\n" + + "indication that the path you are on will ultimately lead " + + "to pain,\n" + + "suffering, grief, and lamentation.\n"); errors++; throw new StopProcessing(); } /** - * Converts {@link #outputDex} into a byte[], write + * Converts {@link #outputDex} into a {@code byte[]}, write * it out to the proper file (if any), and also do whatever human-oriented * dumping is required. * - * @return null-ok; the converted byte[] or null + * @return {@code null-ok;} the converted {@code byte[]} or {@code null} * if there was a problem */ private static byte[] writeDex() { @@ -438,8 +466,9 @@ public class Main { /** * Creates a jar file from the resources and given dex file array. * - * @param fileName non-null; name of the file - * @param dexArray non-null; array containing the dex file to include + * @param fileName {@code non-null;} name of the file + * @param dexArray {@code non-null;} array containing the dex file + * to include * @return whether the creation was successful */ private static boolean createJar(String fileName, byte[] dexArray) { @@ -496,7 +525,7 @@ public class Main { * Creates and returns the manifest to use for the output. This may * modify {@link #outputResources} (removing the pre-existing manifest). * - * @return non-null; the manifest + * @return {@code non-null;} the manifest */ private static Manifest makeManifest() throws IOException { byte[] manifestBytes = outputResources.get(MANIFEST_NAME); @@ -531,8 +560,8 @@ public class Main { /** * Opens and returns the named file for writing, treating "-" specially. * - * @param name non-null; the file name - * @return non-null; the opened file + * @param name {@code non-null;} the file name + * @return {@code non-null;} the opened file */ private static OutputStream openOutput(String name) throws IOException { if (name.equals("-") || @@ -547,9 +576,9 @@ public class Main { * Flushes and closes the given output stream, except if it happens to be * {@link System#out} in which case this method does the flush but not * the close. This method will also silently do nothing if given a - * null argument. + * {@code null} argument. * - * @param stream null-ok; what to close + * @param stream {@code null-ok;} what to close */ private static void closeOutput(OutputStream stream) throws IOException { if (stream == null) { @@ -565,18 +594,18 @@ public class Main { /** * Returns the "fixed" version of a given file path, suitable for - * use as a path within a .jar file and for checking + * use as a path within a {@code .jar} file and for checking * against a classfile-internal "this class" name. This looks for - * the last instance of the substring "/./" within + * the last instance of the substring {@code "/./"} within * the path, and if it finds it, it takes the portion after to be * the fixed path. If that isn't found but the path starts with - * "./", then that prefix is removed and the rest is + * {@code "./"}, then that prefix is removed and the rest is * return. If neither of these is the case, this method returns * its argument. * - * @param path non-null; the path to "fix" - * @return non-null; the fixed version (which might be the same as - * the given path) + * @param path {@code non-null;} the path to "fix" + * @return {@code non-null;} the fixed version (which might be the same as + * the given {@code path}) */ private static String fixPath(String path) { /* @@ -603,9 +632,10 @@ public class Main { /** * Dumps any method with the given name in the given file. * - * @param dex non-null; the dex file - * @param fqName non-null; the fully-qualified name of the method(s) - * @param out non-null; where to dump to + * @param dex {@code non-null;} the dex file + * @param fqName {@code non-null;} the fully-qualified name of the + * method(s) + * @param out {@code non-null;} where to dump to */ private static void dumpMethod(DexFile dex, String fqName, OutputStreamWriter out) { @@ -719,36 +749,36 @@ public class Main { /** whether we are constructing a core library */ public boolean coreLibrary = false; - /** null-ok; particular method to dump */ + /** {@code null-ok;} particular method to dump */ public String methodToDump = null; /** max width for columnar output */ public int dumpWidth = 0; - /** null-ok; output file name for binary file */ + /** {@code null-ok;} output file name for binary file */ public String outName = null; - /** null-ok; output file name for human-oriented dump */ + /** {@code null-ok;} output file name for human-oriented dump */ public String humanOutName = null; /** whether strict file-name-vs-class-name checking should be done */ public boolean strictNameCheck = true; /** - * whether it is okay for there to be no .class files + * whether it is okay for there to be no {@code .class} files * to process */ public boolean emptyOk = false; /** - * whether the binary output is to be a .jar file - * instead of a plain .dex + * whether the binary output is to be a {@code .jar} file + * instead of a plain {@code .dex} */ public boolean jarOutput = false; /** - * when writing a .jar file, whether to still - * keep the .class files + * when writing a {@code .jar} file, whether to still + * keep the {@code .class} files */ public boolean keepClassesInJar = false; @@ -758,7 +788,7 @@ public class Main { /** whether to keep local variable information */ public boolean localInfo = true; - /** non-null after {@link #parse}; file name arguments */ + /** {@code non-null after {@link #parse};} file name arguments */ public String[] fileNames; /** whether to do SSA/register optimization */ @@ -779,7 +809,7 @@ public class Main { /** * Parses the given command-line arguments. * - * @param args non-null; the arguments + * @param args {@code non-null;} the arguments */ public void parse(String[] args) { int at = 0; @@ -866,8 +896,6 @@ public class Main { } int fileCount = args.length - at; - fileNames = new String[fileCount]; - System.arraycopy(args, at, fileNames, 0, fileCount); if (fileCount == 0) { if (!emptyOk) { @@ -876,9 +904,13 @@ public class Main { } } else if (emptyOk) { System.out.println("ignoring input files"); - at = args.length; + at = 0; + fileCount = 0; } + fileNames = new String[fileCount]; + System.arraycopy(args, at, fileNames, 0, fileCount); + if ((humanOutName == null) && (methodToDump != null)) { humanOutName = "-"; } diff --git a/dx/src/com/android/dx/command/dump/BaseDumper.java b/dx/src/com/android/dx/command/dump/BaseDumper.java index f4a8dee1d..d2c1e133a 100644 --- a/dx/src/com/android/dx/command/dump/BaseDumper.java +++ b/dx/src/com/android/dx/command/dump/BaseDumper.java @@ -34,21 +34,21 @@ import java.io.StringWriter; */ public abstract class BaseDumper implements ParseObserver { - /** non-null; array of data being dumped */ + /** {@code non-null;} array of data being dumped */ private final byte[] bytes; /** whether or not to include the raw bytes (in a column on the left) */ private final boolean rawBytes; - /** non-null; where to dump to */ + /** {@code non-null;} where to dump to */ private final PrintStream out; /** width of the output in columns */ private final int width; /** - * non-null; the file path for the class, excluding any base directory - * specification + * {@code non-null;} the file path for the class, excluding any base + * directory specification */ private final String filePath; @@ -61,7 +61,7 @@ public abstract class BaseDumper /** the current level of indentation */ private int indent; - /** non-null; the current column separator string */ + /** {@code non-null;} the current column separator string */ private String separator; /** the offset of the next byte to dump */ @@ -73,10 +73,9 @@ public abstract class BaseDumper /** * Constructs an instance. * - * @param bytes non-null; bytes of the (alleged) class file + * @param bytes {@code non-null;} bytes of the (alleged) class file * on the left) - * @param out non-null; where to dump to - * passed in as <= 0 + * @param out {@code non-null;} where to dump to * @param filePath the file path for the class, excluding any base * directory specification */ @@ -109,7 +108,8 @@ public abstract class BaseDumper * @return width in register-units */ static int computeParamWidth(ConcreteMethod meth, boolean isStatic) { - return meth.getEffectiveDescriptor().getParameterTypes().getWordCount(); + return meth.getEffectiveDescriptor().getParameterTypes(). + getWordCount(); } /** {@inheritDoc} */ @@ -158,7 +158,7 @@ public abstract class BaseDumper * Gets the current dump cursor (that is, the offset of the expected * next byte to dump). * - * @return >= 0; the dump cursor + * @return {@code >= 0;} the dump cursor */ protected final int getAt() { return at; @@ -167,17 +167,17 @@ public abstract class BaseDumper /** * Sets the dump cursor to the indicated offset in the given array. * - * @param arr non-null; array in question - * @param offset >= 0; offset into the array + * @param arr {@code non-null;} array in question + * @param offset {@code >= 0;} offset into the array */ protected final void setAt(ByteArray arr, int offset) { at = arr.underlyingOffset(offset, bytes); } /** - * Gets the array of bytes to process. + * Gets the array of {@code byte}s to process. * - * @return non-null; the bytes + * @return {@code non-null;} the bytes */ protected final byte[] getBytes() { return bytes; @@ -186,7 +186,7 @@ public abstract class BaseDumper /** * Gets the filesystem/jar path of the file being dumped. * - * @return non-null; the path + * @return {@code non-null;} the path */ protected final String getFilePath() { return filePath; @@ -204,7 +204,7 @@ public abstract class BaseDumper /** * Prints the given string to this instance's output stream. * - * @param s null-ok; string to print + * @param s {@code null-ok;} string to print */ protected final void print(String s) { out.print(s); @@ -214,7 +214,7 @@ public abstract class BaseDumper * Prints the given string to this instance's output stream, followed * by a newline. * - * @param s null-ok; string to print + * @param s {@code null-ok;} string to print */ protected final void println(String s) { out.println(s); @@ -230,10 +230,10 @@ public abstract class BaseDumper } /** - * Gets the width of the first column of output. This is 0 + * Gets the width of the first column of output. This is {@code 0} * unless raw bytes are being included in the output. * - * @return >= 0; the width of the first column + * @return {@code >= 0;} the width of the first column */ protected final int getWidth1() { if (rawBytes) { @@ -246,7 +246,7 @@ public abstract class BaseDumper /** * Gets the width of the second column of output. * - * @return >= 0; the width of the second column + * @return {@code >= 0;} the width of the second column */ protected final int getWidth2() { int w1 = rawBytes ? (getWidth1() + 1) : 0; @@ -258,7 +258,7 @@ public abstract class BaseDumper * * @param offset offset to start dumping at * @param len length to dump - * @return non-null; the dump + * @return {@code non-null;} the dump */ protected final String hexDump(int offset, int len) { return Hex.dump(bytes, offset, len, offset, hexCols, 4); @@ -268,9 +268,9 @@ public abstract class BaseDumper * Combines a pair of strings as two columns, or if this is one-column * output, format the otherwise-second column. * - * @param s1 non-null; the first column's string - * @param s2 non-null; the second column's string - * @return non-null; the combined output + * @param s1 {@code non-null;} the first column's string + * @param s2 {@code non-null;} the second column's string + * @return {@code non-null;} the combined output */ protected final String twoColumns(String s1, String s2) { int w1 = getWidth1(); diff --git a/dx/src/com/android/dx/command/dump/BlockDumper.java b/dx/src/com/android/dx/command/dump/BlockDumper.java index 0be2fb402..0858eabba 100644 --- a/dx/src/com/android/dx/command/dump/BlockDumper.java +++ b/dx/src/com/android/dx/command/dump/BlockDumper.java @@ -54,14 +54,11 @@ public class BlockDumper private boolean rop; /** - * null-ok; the class file object being constructed; becomes non-null - * during {@link #dump} + * {@code null-ok;} the class file object being constructed; + * becomes non-null during {@link #dump} */ protected DirectClassFile classFile; - /** null-ok; most recently parsed code attribute */ - private AttCode codeAtt; - /** whether or not to suppress dumping */ protected boolean suppressDump; @@ -75,9 +72,8 @@ public class BlockDumper * Dumps the given array, interpreting it as a class file and dumping * methods with indications of block-level stuff. * - * @param bytes non-null; bytes of the (alleged) class file - * @param out non-null; where to dump to - * passed in as <= 0 + * @param bytes {@code non-null;} bytes of the (alleged) class file + * @param out {@code non-null;} where to dump to * @param filePath the file path for the class, excluding any base * directory specification * @param rop whether or not to registerize (make rop blocks) @@ -102,7 +98,6 @@ public class BlockDumper this.rop = rop; this.classFile = null; - this.codeAtt = null; this.suppressDump = true; this.first = true; this.optimize = args.optimize; @@ -208,7 +203,7 @@ public class BlockDumper /** * Does a regular basic block dump. * - * @param meth non-null; method data to dump + * @param meth {@code non-null;} method data to dump */ private void regularDump(ConcreteMethod meth) { BytecodeArray code = meth.getCode(); @@ -283,7 +278,7 @@ public class BlockDumper /** * Does a registerizing dump. * - * @param meth non-null; method data to dump + * @param meth {@code non-null;} method data to dump */ private void ropDump(ConcreteMethod meth) { BytecodeArray code = meth.getCode(); diff --git a/dx/src/com/android/dx/command/dump/ClassDumper.java b/dx/src/com/android/dx/command/dump/ClassDumper.java index 10dacf374..e98b6d6ef 100644 --- a/dx/src/com/android/dx/command/dump/ClassDumper.java +++ b/dx/src/com/android/dx/command/dump/ClassDumper.java @@ -30,8 +30,8 @@ public final class ClassDumper /** * Dumps the given array, interpreting it as a class file. * - * @param bytes non-null; bytes of the (alleged) class file - * @param out non-null; where to dump to + * @param bytes {@code non-null;} bytes of the (alleged) class file + * @param out {@code non-null;} where to dump to * passed in as <= 0 * @param filePath the file path for the class, excluding any base * directory specification diff --git a/dx/src/com/android/dx/command/dump/DotDumper.java b/dx/src/com/android/dx/command/dump/DotDumper.java index 87c529816..9de48fc82 100644 --- a/dx/src/com/android/dx/command/dump/DotDumper.java +++ b/dx/src/com/android/dx/command/dump/DotDumper.java @@ -39,16 +39,15 @@ import com.android.dx.util.IntList; * with the popular graph utility "dot". */ public class DotDumper implements ParseObserver { + private DirectClassFile classFile; - DirectClassFile classFile; + private final byte[] bytes; + private final String filePath; + private final boolean strictParse; + private final boolean optimize; + private final Args args; - byte[] bytes; - String filePath; - boolean strictParse; - boolean optimize; - Args args; - - static void dump (byte[] bytes, String filePath, Args args) { + static void dump(byte[] bytes, String filePath, Args args) { new DotDumper(bytes, filePath, args).run(); } @@ -60,7 +59,6 @@ public class DotDumper implements ParseObserver { this.args = args; } - private void run() { ByteArray ba = new ByteArray(bytes); @@ -89,17 +87,17 @@ public class DotDumper implements ParseObserver { } public void changeIndent(int indentDelta) { - + // This space intentionally left blank. } public void parsed(ByteArray bytes, int offset, int len, String human) { - + // This space intentionally left blank. } /** {@inheritDoc} */ public void startParsingMember(ByteArray bytes, int offset, String name, String descriptor) { - + // This space intentionally left blank. } public void endParsingMember(ByteArray bytes, int offset, String name, @@ -165,6 +163,5 @@ public class DotDumper implements ParseObserver { } System.out.println("}"); - } } diff --git a/dx/src/com/android/dx/command/dump/Main.java b/dx/src/com/android/dx/command/dump/Main.java index 1ea26bc94..d6ba3740a 100644 --- a/dx/src/com/android/dx/command/dump/Main.java +++ b/dx/src/com/android/dx/command/dump/Main.java @@ -110,8 +110,8 @@ public class Main { /** * Processes one file. * - * @param name non-null; name of the file - * @param bytes non-null; contents of the file + * @param name {@code non-null;} name of the file + * @param bytes {@code non-null;} contents of the file */ private static void processOne(String name, byte[] bytes) { if (parsedArgs.dotDump) { diff --git a/dx/src/com/android/dx/command/dump/SsaDumper.java b/dx/src/com/android/dx/command/dump/SsaDumper.java index 95442a174..e5e9d97db 100644 --- a/dx/src/com/android/dx/command/dump/SsaDumper.java +++ b/dx/src/com/android/dx/command/dump/SsaDumper.java @@ -40,29 +40,48 @@ import com.android.dx.util.Hex; import com.android.dx.util.IntList; import java.io.PrintStream; +import java.util.ArrayList; import java.util.BitSet; +import java.util.Collections; import java.util.EnumSet; +/** + * Dumper for the SSA-translated blocks of a method. + */ public class SsaDumper extends BlockDumper { - + /** + * Does the dump. + * + * @param bytes {@code non-null;} bytes of the original class file + * @param out {@code non-null;} where to dump to + * @param filePath the file path for the class, excluding any base + * directory specification + * @param args commandline parsedArgs + */ public static void dump(byte[] bytes, PrintStream out, String filePath, Args args) { - - SsaDumper sd = - new SsaDumper(bytes, out, filePath, args); + SsaDumper sd = new SsaDumper(bytes, out, filePath, args); sd.dump(); } - SsaDumper(byte[] bytes, PrintStream out, String filePath, Args args) { - + /** + * Constructs an instance. + * + * @param bytes {@code non-null;} bytes of the original class file + * @param out {@code non-null;} where to dump to + * @param filePath the file path for the class, excluding any base + * directory specification + * @param args commandline parsedArgs + */ + private SsaDumper(byte[] bytes, PrintStream out, String filePath, + Args args) { super(bytes, out, filePath, true, args); - } /** {@inheritDoc} */ @Override public void endParsingMember(ByteArray bytes, int offset, String name, - String descriptor, Member member) { + String descriptor, Member member) { if (!(member instanceof Method)) { return; } @@ -71,17 +90,14 @@ public class SsaDumper extends BlockDumper { return; } - ConcreteMethod meth = new ConcreteMethod((Method) member, classFile, - true, true); - + ConcreteMethod meth = + new ConcreteMethod((Method) member, classFile, true, true); TranslationAdvice advice = DexTranslationAdvice.THE_ONE; - RopMethod rmeth = Ropper.convert(meth, advice); - SsaMethod ssaMeth = null; - boolean isStatic = AccessFlags.isStatic(meth.getAccessFlags()); int paramWidth = computeParamWidth(meth, isStatic); + if (args.ssaStep == null) { ssaMeth = Optimizer.debugNoRegisterAllocation(rmeth, paramWidth, isStatic, true, advice, @@ -106,30 +122,36 @@ public class SsaDumper extends BlockDumper { sb.append(Hex.u2( ssaMeth.blockIndexToRopLabel(ssaMeth.getEntryBlockIndex()))); sb.append('\n'); + + ArrayList blocks = ssaMeth.getBlocks(); + ArrayList sortedBlocks = + (ArrayList) blocks.clone(); + Collections.sort(sortedBlocks, SsaBasicBlock.LABEL_COMPARATOR); - for (SsaBasicBlock block : ssaMeth.getBlocks()) { + for (SsaBasicBlock block : sortedBlocks) { sb.append("block ") .append(Hex.u2(block.getRopLabel())).append('\n'); BitSet preds = block.getPredecessors(); - for(int i=preds.nextSetBit(0); i>=0; i=preds.nextSetBit(i+1)) { - sb.append (" pred "); - sb.append (Hex.u2(ssaMeth.blockIndexToRopLabel(i))); + for (int i = preds.nextSetBit(0); i >= 0; + i = preds.nextSetBit(i+1)) { + sb.append(" pred "); + sb.append(Hex.u2(ssaMeth.blockIndexToRopLabel(i))); sb.append('\n'); } - sb.append (" live in:" + block.getLiveInRegs()); - sb.append ("\n"); + sb.append(" live in:" + block.getLiveInRegs()); + sb.append("\n"); - for (SsaInsn insn: block.getInsns()) { + for (SsaInsn insn : block.getInsns()) { sb.append(" "); sb.append(insn.toHuman()); sb.append('\n'); } if (block.getSuccessors().cardinality() == 0) { - sb.append (" returns\n"); + sb.append(" returns\n"); } else { int primary = block.getPrimarySuccessorRopLabel(); @@ -138,18 +160,18 @@ public class SsaDumper extends BlockDumper { int szSuccLabels = succLabelList.size(); for (int i = 0; i < szSuccLabels; i++) { - sb.append (" next "); - sb.append (Hex.u2(succLabelList.get(i))); + sb.append(" next "); + sb.append(Hex.u2(succLabelList.get(i))); if (szSuccLabels != 1 && primary == succLabelList.get(i)) { - sb.append (" *"); + sb.append(" *"); } sb.append('\n'); } } - sb.append (" live out:" + block.getLiveOutRegs()); - sb.append ("\n"); + sb.append(" live out:" + block.getLiveOutRegs()); + sb.append("\n"); } suppressDump = false; diff --git a/dx/src/com/android/dx/dex/cf/AttributeTranslator.java b/dx/src/com/android/dx/dex/cf/AttributeTranslator.java index dab15c9a0..cdd29d73d 100644 --- a/dx/src/com/android/dx/dex/cf/AttributeTranslator.java +++ b/dx/src/com/android/dx/dex/cf/AttributeTranslator.java @@ -51,7 +51,7 @@ import java.util.ArrayList; /** * Utility methods that translate various classfile attributes - * into forms suitable for use in creating dex files. + * into forms suitable for use in creating {@code dex} files. */ /*package*/ class AttributeTranslator { /** @@ -64,8 +64,8 @@ import java.util.ArrayList; /** * Gets the list of thrown exceptions for a given method. * - * @param method non-null; the method in question - * @return non-null; the list of thrown exceptions + * @param method {@code non-null;} the method in question + * @return {@code non-null;} the list of thrown exceptions */ public static TypeList getExceptions(Method method) { AttributeList attribs = method.getAttributes(); @@ -83,10 +83,10 @@ import java.util.ArrayList; * Gets the annotations out of a given {@link AttributeList}. This * combines both visible and invisible annotations into a single * result set and also adds in a system annotation for the - * Signature attribute if present. + * {@code Signature} attribute if present. * - * @param attribs non-null; the attributes list to search in - * @return non-null; the set of annotations, which may be empty + * @param attribs {@code non-null;} the attributes list to search in + * @return {@code non-null;} the set of annotations, which may be empty */ public static Annotations getAnnotations(AttributeList attribs) { Annotations result = getAnnotations0(attribs); @@ -102,15 +102,15 @@ import java.util.ArrayList; /** * Gets the annotations out of a given class, similar to {@link * #getAnnotations}, also including annotations for translations - * of class-level attributes EnclosingMethod and - * InnerClasses, if present. Additionally, if the + * of class-level attributes {@code EnclosingMethod} and + * {@code InnerClasses}, if present. Additionally, if the * class is an annotation class, then this also includes a - * representation of all the AnnotationDefault + * representation of all the {@code AnnotationDefault} * values. * - * @param cf non-null; the class in question - * @param args non-null; the high-level options - * @return non-null; the set of annotations, which may be empty + * @param cf {@code non-null;} the class in question + * @param args {@code non-null;} the high-level options + * @return {@code non-null;} the set of annotations, which may be empty */ public static Annotations getClassAnnotations(DirectClassFile cf, CfOptions args) { @@ -148,10 +148,10 @@ import java.util.ArrayList; /** * Gets the annotations out of a given method, similar to {@link * #getAnnotations}, also including an annotation for the translation - * of the method-specific attribute Exceptions. + * of the method-specific attribute {@code Exceptions}. * - * @param method non-null; the method in question - * @return non-null; the set of annotations, which may be empty + * @param method {@code non-null;} the method in question + * @return {@code non-null;} the set of annotations, which may be empty */ public static Annotations getMethodAnnotations(Method method) { Annotations result = getAnnotations(method.getAttributes()); @@ -170,8 +170,8 @@ import java.util.ArrayList; * Helper method for {@link #getAnnotations} which just gets the * existing annotations, per se. * - * @param attribs non-null; the attributes list to search in - * @return non-null; the set of annotations, which may be empty + * @param attribs {@code non-null;} the attributes list to search in + * @return {@code non-null;} the set of annotations, which may be empty */ private static Annotations getAnnotations0(AttributeList attribs) { AttRuntimeVisibleAnnotations visible = @@ -199,11 +199,11 @@ import java.util.ArrayList; } /** - * Gets the Signature attribute out of a given + * Gets the {@code Signature} attribute out of a given * {@link AttributeList}, if any, translating it to an annotation. * - * @param attribs non-null; the attributes list to search in - * @return null-ok; the converted Signature annotation, + * @param attribs {@code non-null;} the attributes list to search in + * @return {@code null-ok;} the converted {@code Signature} annotation, * if there was an attribute to translate */ private static Annotation getSignature(AttributeList attribs) { @@ -218,15 +218,15 @@ import java.util.ArrayList; } /** - * Gets the EnclosingMethod attribute out of a given + * Gets the {@code EnclosingMethod} attribute out of a given * {@link AttributeList}, if any, translating it to an annotation. * If the class really has an enclosing method, this returns an - * EnclosingMethod annotation; if not, this returns - * an EnclosingClass annotation. + * {@code EnclosingMethod} annotation; if not, this returns + * an {@code EnclosingClass} annotation. * - * @param attribs non-null; the attributes list to search in - * @return null-ok; the converted EnclosingMethod or - * EnclosingClass annotation, if there was an + * @param attribs {@code non-null;} the attributes list to search in + * @return {@code null-ok;} the converted {@code EnclosingMethod} or + * {@code EnclosingClass} annotation, if there was an * attribute to translate */ private static Annotation translateEnclosingMethod(AttributeList attribs) { @@ -256,16 +256,16 @@ import java.util.ArrayList; } /** - * Gets the InnerClasses attribute out of a given + * Gets the {@code InnerClasses} attribute out of a given * {@link AttributeList}, if any, translating it to one or more of an - * InnerClass, EnclosingClass, or - * MemberClasses annotation. + * {@code InnerClass}, {@code EnclosingClass}, or + * {@code MemberClasses} annotation. * - * @param thisClass non-null; type representing the class being processed - * @param attribs non-null; the attributes list to search in + * @param thisClass {@code non-null;} type representing the class being processed + * @param attribs {@code non-null;} the attributes list to search in * @param needEnclosingClass whether to include an - * EnclosingClass annotation - * @return null-ok; the converted list of annotations, if there + * {@code EnclosingClass} annotation + * @return {@code null-ok;} the converted list of annotations, if there * was an attribute to translate */ private static Annotations translateInnerClasses(CstType thisClass, @@ -342,8 +342,8 @@ import java.util.ArrayList; * combines both visible and invisible annotations into a single * result set. * - * @param method non-null; the method in question - * @return non-null; the list of annotation sets, which may be empty + * @param method {@code non-null;} the method in question + * @return {@code non-null;} the list of annotation sets, which may be empty */ public static AnnotationsList getParameterAnnotations(Method method) { AttributeList attribs = method.getAttributes(); @@ -374,14 +374,14 @@ import java.util.ArrayList; } /** - * Gets the AnnotationDefault attributes out of a + * Gets the {@code AnnotationDefault} attributes out of a * given class, if any, reforming them as an - * AnnotationDefault annotation. + * {@code AnnotationDefault} annotation. * - * @param cf non-null; the class in question - * @return null-ok; an appropriately-constructed - * AnnotationDefault annotation, if there were any - * annotation defaults in the class, or null if not + * @param cf {@code non-null;} the class in question + * @return {@code null-ok;} an appropriately-constructed + * {@code AnnotationDefault} annotation, if there were any + * annotation defaults in the class, or {@code null} if not */ private static Annotation translateAnnotationDefaults(DirectClassFile cf) { CstType thisClass = cf.getThisClass(); diff --git a/dx/src/com/android/dx/dex/cf/CfTranslator.java b/dx/src/com/android/dx/dex/cf/CfTranslator.java index c48be53e3..8210e907b 100644 --- a/dx/src/com/android/dx/dex/cf/CfTranslator.java +++ b/dx/src/com/android/dx/dex/cf/CfTranslator.java @@ -55,11 +55,11 @@ import com.android.dx.ssa.Optimizer; import com.android.dx.util.ExceptionWithContext; /** - * Static method that turns byte[]s containing Java + * Static method that turns {@code byte[]}s containing Java * classfiles into {@link ClassDefItem} instances. */ public class CfTranslator { - /** set to true to enable development-time debugging code */ + /** set to {@code true} to enable development-time debugging code */ private static final boolean DEBUG = false; /** @@ -70,14 +70,14 @@ public class CfTranslator { } /** - * Takes a byte[], interprets it as a Java classfile, and + * Takes a {@code byte[]}, interprets it as a Java classfile, and * translates it into a {@link ClassDefItem}. * - * @param filePath non-null; the file path for the class, + * @param filePath {@code non-null;} the file path for the class, * excluding any base directory specification - * @param bytes non-null; contents of the file + * @param bytes {@code non-null;} contents of the file * @param args command-line arguments - * @return non-null; the translated class + * @return {@code non-null;} the translated class */ public static ClassDefItem translate(String filePath, byte[] bytes, CfOptions args) { @@ -94,11 +94,11 @@ public class CfTranslator { * from {@link #translate} just to keep things a bit simpler in * terms of exception handling. * - * @param filePath non-null; the file path for the class, + * @param filePath {@code non-null;} the file path for the class, * excluding any base directory specification - * @param bytes non-null; contents of the file + * @param bytes {@code non-null;} contents of the file * @param args command-line arguments - * @return non-null; the translated class + * @return {@code non-null;} the translated class */ private static ClassDefItem translate0(String filePath, byte[] bytes, CfOptions args) { @@ -136,8 +136,8 @@ public class CfTranslator { /** * Processes the fields of the given class. * - * @param cf non-null; class being translated - * @param out non-null; output class + * @param cf {@code non-null;} class being translated + * @param out {@code non-null;} output class */ private static void processFields(DirectClassFile cf, ClassDefItem out) { CstType thisClass = cf.getThisClass(); @@ -179,8 +179,8 @@ public class CfTranslator { * Helper for {@link #processFields}, which translates constants into * more specific types if necessary. * - * @param constant non-null; the constant in question - * @param type non-null; the desired type + * @param constant {@code non-null;} the constant in question + * @param type {@code non-null;} the desired type */ private static TypedConstant coerceConstant(TypedConstant constant, Type type) { @@ -213,9 +213,9 @@ public class CfTranslator { /** * Processes the methods of the given class. * - * @param cf non-null; class being translated - * @param args non-null; command-line args - * @param out non-null; output class + * @param cf {@code non-null;} class being translated + * @param args {@code non-null;} command-line args + * @param out {@code non-null;} output class */ private static void processMethods(DirectClassFile cf, CfOptions args, ClassDefItem out) { diff --git a/dx/src/com/android/dx/dex/cf/CodeStatistics.java b/dx/src/com/android/dx/dex/cf/CodeStatistics.java index fa83100bd..b692d77eb 100644 --- a/dx/src/com/android/dx/dex/cf/CodeStatistics.java +++ b/dx/src/com/android/dx/dex/cf/CodeStatistics.java @@ -26,7 +26,7 @@ import java.io.PrintStream; * code. */ public final class CodeStatistics { - /** set to true to enable development-time debugging code */ + /** set to {@code true} to enable development-time debugging code */ private static final boolean DEBUG = false; /** @@ -76,7 +76,7 @@ public final class CodeStatistics { /** * Updates the number of original bytecode bytes processed. * - * @param count >= 0; the number of bytes to add + * @param count {@code >= 0;} the number of bytes to add */ public static void updateOriginalByteCount(int count) { runningOriginalBytes += count; @@ -146,7 +146,7 @@ public final class CodeStatistics { /** * Prints out the collected statistics. * - * @param out non-null; where to output to + * @param out {@code non-null;} where to output to */ public static void dumpStatistics(PrintStream out) { out.printf("Optimizer Delta Rop Insns: %d total: %d " diff --git a/dx/src/com/android/dx/dex/cf/OptimizerOptions.java b/dx/src/com/android/dx/dex/cf/OptimizerOptions.java index 1dc3f76a4..fa606e34b 100644 --- a/dx/src/com/android/dx/dex/cf/OptimizerOptions.java +++ b/dx/src/com/android/dx/dex/cf/OptimizerOptions.java @@ -31,13 +31,14 @@ import java.util.HashSet; */ public class OptimizerOptions { /** - * null-ok; hash set of class name + method names that should be optimized. - * null if this constraint was not specified on the command line + * {@code null-ok;} hash set of class name + method names that + * should be optimized. {@code null} if this constraint was not + * specified on the command line */ private static HashSet optimizeList; /** - * null-ok; hash set of class name + method names that should NOT + * {@code null-ok;} hash set of class name + method names that should NOT * be optimized. null if this constraint was not specified on the * command line */ @@ -97,7 +98,6 @@ public class OptimizerOptions { try { FileReader fr = new FileReader(filename); - BufferedReader bfr = new BufferedReader(fr); String line; @@ -105,6 +105,8 @@ public class OptimizerOptions { while (null != (line = bfr.readLine())) { result.add(line); } + + fr.close(); } catch (IOException ex) { // Let the exception percolate up as a RuntimeException. throw new RuntimeException("Error with optimize list: " + @@ -118,12 +120,12 @@ public class OptimizerOptions { * Compares the output of the optimizer run normally with a run skipping * some optional steps. Results are printed to stderr. * - * @param nonOptRmeth non-null; origional rop method - * @param paramSize >= 0 parameter size of method + * @param nonOptRmeth {@code non-null;} origional rop method + * @param paramSize {@code >= 0;} parameter size of method * @param isStatic true if this method has no 'this' pointer argument. - * @param args non-null; translator arguments - * @param advice non-null; translation advice - * @param rmeth non-null; method with all optimization steps run. + * @param args {@code non-null;} translator arguments + * @param advice {@code non-null;} translation advice + * @param rmeth {@code non-null;} method with all optimization steps run. */ public static void compareOptimizerStep(RopMethod nonOptRmeth, int paramSize, boolean isStatic, CfOptions args, diff --git a/dx/src/com/android/dx/dex/code/ArrayData.java b/dx/src/com/android/dx/dex/code/ArrayData.java index 8476a0361..7698de1ad 100644 --- a/dx/src/com/android/dx/dex/code/ArrayData.java +++ b/dx/src/com/android/dx/dex/code/ArrayData.java @@ -29,12 +29,12 @@ import java.util.ArrayList; */ public final class ArrayData extends VariableSizeInsn { /** - * non-null; address representing the instruction that uses this + * {@code non-null;} address representing the instruction that uses this * instance */ private final CodeAddress user; - /** non-null; initial values to be filled into an array */ + /** {@code non-null;} initial values to be filled into an array */ private final ArrayList values; /** non-null: type of constant that initializes the array */ @@ -48,12 +48,12 @@ public final class ArrayData extends VariableSizeInsn { /** * Constructs an instance. The output address of this instance is initially - * unknown (-1). + * unknown ({@code -1}). * - * @param position non-null; source position - * @param user non-null; address representing the instruction that + * @param position {@code non-null;} source position + * @param user {@code non-null;} address representing the instruction that * uses this instance - * @param values non-null; initial values to be filled into an array + * @param values {@code non-null;} initial values to be filled into an array */ public ArrayData(SourcePosition position, CodeAddress user, ArrayList values, @@ -107,7 +107,6 @@ public final class ArrayData extends VariableSizeInsn { /** {@inheritDoc} */ @Override public void writeTo(AnnotatedOutput out) { - int baseAddress = user.getAddress(); int sz = values.size(); out.writeShort(0x300 | DalvOps.NOP); diff --git a/dx/src/com/android/dx/dex/code/BlockAddresses.java b/dx/src/com/android/dx/dex/code/BlockAddresses.java index 9fea66c62..e55f89379 100644 --- a/dx/src/com/android/dx/dex/code/BlockAddresses.java +++ b/dx/src/com/android/dx/dex/code/BlockAddresses.java @@ -28,15 +28,15 @@ import com.android.dx.rop.code.SourcePosition; * start address, end address, and last instruction address. */ public final class BlockAddresses { - /** non-null; array containing addresses for the start of each basic + /** {@code non-null;} array containing addresses for the start of each basic * block (indexed by basic block label) */ private final CodeAddress[] starts; - /** non-null; array containing addresses for the final instruction + /** {@code non-null;} array containing addresses for the final instruction * of each basic block (indexed by basic block label) */ private final CodeAddress[] lasts; - /** non-null; array containing addresses for the end (just past the + /** {@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; @@ -44,7 +44,7 @@ public final class BlockAddresses { /** * Constructs an instance. * - * @param method non-null; the method to have block addresses for + * @param method {@code non-null;} the method to have block addresses for */ public BlockAddresses(RopMethod method) { BasicBlockList blocks = method.getBlocks(); @@ -60,8 +60,8 @@ public final class BlockAddresses { /** * Gets the instance for the start of the given block. * - * @param block non-null; the block in question - * @return non-null; the appropriate instance + * @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()]; @@ -70,8 +70,8 @@ public final class BlockAddresses { /** * Gets the instance for the start of the block with the given label. * - * @param label non-null; the label of the block in question - * @return non-null; the appropriate instance + * @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]; @@ -80,8 +80,8 @@ public final class BlockAddresses { /** * Gets the instance for the final instruction of the given block. * - * @param block non-null; the block in question - * @return non-null; the appropriate instance + * @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()]; @@ -91,8 +91,8 @@ public final class BlockAddresses { * Gets the instance for the final instruction of the block with * the given label. * - * @param label non-null; the label of the block in question - * @return non-null; the appropriate instance + * @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]; @@ -102,8 +102,8 @@ public final class BlockAddresses { * Gets the instance for the end (address after the final instruction) * of the given block. * - * @param block non-null; the block in question - * @return non-null; the appropriate instance + * @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()]; @@ -113,8 +113,8 @@ public final class BlockAddresses { * Gets the instance for the end (address after the final instruction) * of the block with the given label. * - * @param label non-null; the label of the block in question - * @return non-null; the appropriate instance + * @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]; diff --git a/dx/src/com/android/dx/dex/code/CatchBuilder.java b/dx/src/com/android/dx/dex/code/CatchBuilder.java index 8bc963b5b..d2ec3d74f 100644 --- a/dx/src/com/android/dx/dex/code/CatchBuilder.java +++ b/dx/src/com/android/dx/dex/code/CatchBuilder.java @@ -27,7 +27,7 @@ public interface CatchBuilder { /** * Builds and returns the catch table for this instance. * - * @return non-null; the constructed table + * @return {@code non-null;} the constructed table */ public CatchTable build(); @@ -42,7 +42,7 @@ public interface CatchBuilder { /** * Gets the set of catch types associated with this instance. * - * @return non-null; the set of catch types + * @return {@code non-null;} the set of catch types */ public HashSet getCatchTypes(); } diff --git a/dx/src/com/android/dx/dex/code/CatchHandlerList.java b/dx/src/com/android/dx/dex/code/CatchHandlerList.java index 862586c8b..a8a97be20 100644 --- a/dx/src/com/android/dx/dex/code/CatchHandlerList.java +++ b/dx/src/com/android/dx/dex/code/CatchHandlerList.java @@ -25,13 +25,13 @@ import com.android.dx.util.Hex; */ public final class CatchHandlerList extends FixedSizeList implements Comparable { - /** non-null; empty instance */ + /** {@code non-null;} empty instance */ public static final CatchHandlerList EMPTY = new CatchHandlerList(0); /** - * Constructs an instance. All indices initially contain null. + * Constructs an instance. All indices initially contain {@code null}. * - * @param size >= 0; the size of the list + * @param size {@code >= 0;} the size of the list */ public CatchHandlerList(int size) { super(size); @@ -40,10 +40,10 @@ public final class CatchHandlerList extends FixedSizeList /** * 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 NullPointerException. + * do that, this will throw {@code NullPointerException}. * - * @param n >= 0, < size(); which index - * @return non-null; element at that index + * @param n {@code >= 0, < size();} which index + * @return {@code non-null;} element at that index */ public Entry get(int n) { return (Entry) get0(n); @@ -58,10 +58,10 @@ public final class CatchHandlerList extends FixedSizeList * Get the human form of this instance, prefixed on each line * with the string. * - * @param prefix non-null; the prefix for every line - * @param header non-null; the header for the first line (after the + * @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 non-null; the human form + * @return {@code non-null;} the human form */ public String toHuman(String prefix, String header) { StringBuilder sb = new StringBuilder(100); @@ -97,8 +97,8 @@ public final class CatchHandlerList extends FixedSizeList * Returns whether or not this instance ends with a "catch-all" * handler. * - * @return true if this instance ends with a "catch-all" - * handler or false if not + * @return {@code true} if this instance ends with a "catch-all" + * handler or {@code false} if not */ public boolean catchesAll() { int size = size(); @@ -114,9 +114,9 @@ public final class CatchHandlerList extends FixedSizeList /** * Sets the entry at the given index. * - * @param n >= 0, < size(); which index - * @param exceptionType non-null; type of exception handled - * @param handler >= 0; exception handler address + * @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)); @@ -125,8 +125,8 @@ public final class CatchHandlerList extends FixedSizeList /** * Sets the entry at the given index. * - * @param n >= 0, < size(); which index - * @param entry non-null; the entry to set at n + * @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); @@ -165,17 +165,17 @@ public final class CatchHandlerList extends FixedSizeList * Entry in the list. */ public static class Entry implements Comparable { - /** non-null; type of exception handled */ + /** {@code non-null;} type of exception handled */ private final CstType exceptionType; - /** >= 0; exception handler address */ + /** {@code >= 0;} exception handler address */ private final int handler; /** * Constructs an instance. * - * @param exceptionType non-null; type of exception handled - * @param handler >= 0; exception handler address + * @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) { @@ -220,7 +220,7 @@ public final class CatchHandlerList extends FixedSizeList /** * Gets the exception type handled. * - * @return non-null; the exception type + * @return {@code non-null;} the exception type */ public CstType getExceptionType() { return exceptionType; @@ -229,7 +229,7 @@ public final class CatchHandlerList extends FixedSizeList /** * Gets the handler address. * - * @return >= 0; the handler address + * @return {@code >= 0;} the handler address */ public int getHandler() { return handler; diff --git a/dx/src/com/android/dx/dex/code/CatchTable.java b/dx/src/com/android/dx/dex/code/CatchTable.java index 86f9c588b..fd8e3a7fa 100644 --- a/dx/src/com/android/dx/dex/code/CatchTable.java +++ b/dx/src/com/android/dx/dex/code/CatchTable.java @@ -26,13 +26,13 @@ import com.android.dx.util.FixedSizeList; */ public final class CatchTable extends FixedSizeList implements Comparable { - /** non-null; empty instance */ + /** {@code non-null;} empty instance */ public static final CatchTable EMPTY = new CatchTable(0); /** - * Constructs an instance. All indices initially contain null. + * Constructs an instance. All indices initially contain {@code null}. * - * @param size >= 0; the size of the table + * @param size {@code >= 0;} the size of the table */ public CatchTable(int size) { super(size); @@ -41,10 +41,10 @@ public final class CatchTable extends FixedSizeList /** * 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 NullPointerException. + * do that, this will throw {@code NullPointerException}. * - * @param n >= 0, < size(); which index - * @return non-null; element at that index + * @param n {@code >= 0, < size();} which index + * @return {@code non-null;} element at that index */ public Entry get(int n) { return (Entry) get0(n); @@ -53,8 +53,8 @@ public final class CatchTable extends FixedSizeList /** * Sets the entry at the given index. * - * @param n >= 0, < size(); which index - * @param entry non-null; the entry to set at n + * @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); @@ -93,21 +93,21 @@ public final class CatchTable extends FixedSizeList * Entry in a catch list. */ public static class Entry implements Comparable { - /** >= 0; start address */ + /** {@code >= 0;} start address */ private final int start; - /** > start; end address (exclusive) */ + /** {@code > start;} end address (exclusive) */ private final int end; - /** non-null; list of catch handlers */ + /** {@code non-null;} list of catch handlers */ private final CatchHandlerList handlers; /** * Constructs an instance. * - * @param start >= 0; start address - * @param end > start; end address (exclusive) - * @param handlers non-null; list of catch handlers + * @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) { @@ -165,7 +165,7 @@ public final class CatchTable extends FixedSizeList /** * Gets the start address. * - * @return >= 0; the start address + * @return {@code >= 0;} the start address */ public int getStart() { return start; @@ -174,7 +174,7 @@ public final class CatchTable extends FixedSizeList /** * Gets the end address (exclusive). * - * @return > start; the end address (exclusive) + * @return {@code > start;} the end address (exclusive) */ public int getEnd() { return end; @@ -183,7 +183,7 @@ public final class CatchTable extends FixedSizeList /** * Gets the handlers. * - * @return non-null; the handlers + * @return {@code non-null;} the handlers */ public CatchHandlerList getHandlers() { return handlers; diff --git a/dx/src/com/android/dx/dex/code/CodeAddress.java b/dx/src/com/android/dx/dex/code/CodeAddress.java index 97309137b..f25718e14 100644 --- a/dx/src/com/android/dx/dex/code/CodeAddress.java +++ b/dx/src/com/android/dx/dex/code/CodeAddress.java @@ -29,9 +29,9 @@ import com.android.dx.rop.code.SourcePosition; public final class CodeAddress extends ZeroSizeInsn { /** * Constructs an instance. The output address of this instance is initially - * unknown (-1). + * unknown ({@code -1}). * - * @param position non-null; source position + * @param position {@code non-null;} source position */ public CodeAddress(SourcePosition position) { super(position); diff --git a/dx/src/com/android/dx/dex/code/CstInsn.java b/dx/src/com/android/dx/dex/code/CstInsn.java index 1bc9021ac..a579c5ec8 100644 --- a/dx/src/com/android/dx/dex/code/CstInsn.java +++ b/dx/src/com/android/dx/dex/code/CstInsn.java @@ -25,31 +25,31 @@ import com.android.dx.rop.cst.Constant; * to all the normal instruction information. */ public final class CstInsn extends FixedSizeInsn { - /** non-null; the constant argument for this instruction */ + /** {@code non-null;} the constant argument for this instruction */ private final Constant constant; /** - * >= -1; the constant pool index for {@link #constant}, or - * -1 if not yet set + * {@code >= -1;} the constant pool index for {@link #constant}, or + * {@code -1} if not yet set */ private int index; /** - * >= -1; the constant pool index for the class reference in - * {@link #constant} if any, or -1 if not yet set + * {@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 (-1) as is the constant pool index. + * initially unknown ({@code -1}) as is the constant pool index. * * @param opcode the opcode; one of the constants from {@link Dops} - * @param position non-null; source position - * @param registers non-null; register list, including a + * @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 non-null; constant argument + * @param constant {@code non-null;} constant argument */ public CstInsn(Dop opcode, SourcePosition position, RegisterSpecList registers, Constant constant) { @@ -101,7 +101,7 @@ public final class CstInsn extends FixedSizeInsn { /** * Gets the constant argument. * - * @return non-null; the constant argument + * @return {@code non-null;} the constant argument */ public Constant getConstant() { return constant; @@ -111,7 +111,7 @@ public final class CstInsn extends FixedSizeInsn { * Gets the constant's index. It is only valid to call this after * {@link #setIndex} has been called. * - * @return >= 0; the constant pool index + * @return {@code >= 0;} the constant pool index */ public int getIndex() { if (index < 0) { @@ -126,7 +126,7 @@ public final class CstInsn extends FixedSizeInsn { * * @see #setIndex * - * @return true iff the index has been set + * @return {@code true} iff the index has been set */ public boolean hasIndex() { return (index >= 0); @@ -136,7 +136,7 @@ public final class CstInsn extends FixedSizeInsn { * Sets the constant's index. It is only valid to call this method once * per instance. * - * @param index >= 0; the constant pool index + * @param index {@code >= 0;} the constant pool index */ public void setIndex(int index) { if (index < 0) { @@ -154,7 +154,7 @@ public final class CstInsn extends FixedSizeInsn { * Gets the constant's class index. It is only valid to call this after * {@link #setClassIndex} has been called. * - * @return >= 0; the constant's class's constant pool index + * @return {@code >= 0;} the constant's class's constant pool index */ public int getClassIndex() { if (classIndex < 0) { @@ -170,7 +170,7 @@ public final class CstInsn extends FixedSizeInsn { * * @see #setClassIndex * - * @return true iff the index has been set + * @return {@code true} iff the index has been set */ public boolean hasClassIndex() { return (classIndex >= 0); @@ -183,7 +183,7 @@ public final class CstInsn extends FixedSizeInsn { * with reference constants that this method should ever be * called. It is only valid to call this method once per instance. * - * @param index >= 0; the constant's class's constant pool index + * @param index {@code >= 0;} the constant's class's constant pool index */ public void setClassIndex(int index) { if (index < 0) { diff --git a/dx/src/com/android/dx/dex/code/DalvCode.java b/dx/src/com/android/dx/dex/code/DalvCode.java index cf6af099b..f91191291 100644 --- a/dx/src/com/android/dx/dex/code/DalvCode.java +++ b/dx/src/com/android/dx/dex/code/DalvCode.java @@ -23,7 +23,7 @@ import java.util.HashSet; /** * Container for all the pieces of a concrete method. Each instance - * corresponds to a code structure in a .dex file. + * corresponds to a {@code code} structure in a {@code .dex} file. */ public final class DalvCode { /** @@ -33,37 +33,37 @@ public final class DalvCode { private final int positionInfo; /** - * null-ok; the instruction list, ready for final processing; + * {@code null-ok;} the instruction list, ready for final processing; * nulled out in {@link #finishProcessingIfNecessary} */ private OutputFinisher unprocessedInsns; /** - * non-null; unprocessed catch table; + * {@code non-null;} unprocessed catch table; * nulled out in {@link #finishProcessingIfNecessary} */ private CatchBuilder unprocessedCatches; /** - * null-ok; catch table; set in + * {@code null-ok;} catch table; set in * {@link #finishProcessingIfNecessary} */ private CatchTable catches; /** - * null-ok; source positions list; set in + * {@code null-ok;} source positions list; set in * {@link #finishProcessingIfNecessary} */ private PositionList positions; /** - * null-ok; local variable list; set in + * {@code null-ok;} local variable list; set in * {@link #finishProcessingIfNecessary} */ private LocalList locals; /** - * null-ok; the processed instruction list; set in + * {@code null-ok;} the processed instruction list; set in * {@link #finishProcessingIfNecessary} */ private DalvInsnList insns; @@ -73,9 +73,9 @@ public final class DalvCode { * * @param positionInfo how much position info to preserve; one of the * static constants in {@link PositionList} - * @param unprocessedInsns non-null; the instruction list, ready + * @param unprocessedInsns {@code non-null;} the instruction list, ready * for final processing - * @param unprocessedCatches non-null; unprocessed catch + * @param unprocessedCatches {@code non-null;} unprocessed catch * (exception handler) table */ public DalvCode(int positionInfo, OutputFinisher unprocessedInsns, @@ -120,7 +120,7 @@ public final class DalvCode { * given callback to perform lookups. This must be called before * {@link #getInsns}. * - * @param callback non-null; callback object + * @param callback {@code non-null;} callback object */ public void assignIndices(AssignIndicesCallback callback) { unprocessedInsns.assignIndices(callback); @@ -129,7 +129,7 @@ public final class DalvCode { /** * Gets whether this instance has any position data to represent. * - * @return true iff this instance has any position + * @return {@code true} iff this instance has any position * data to represent */ public boolean hasPositions() { @@ -140,7 +140,7 @@ public final class DalvCode { /** * Gets whether this instance has any local variable data to represent. * - * @return true iff this instance has any local variable + * @return {@code true} iff this instance has any local variable * data to represent */ public boolean hasLocals() { @@ -160,7 +160,7 @@ public final class DalvCode { /** * Gets the set of catch types handled anywhere in the code. * - * @return non-null; the set of catch types + * @return {@code non-null;} the set of catch types */ public HashSet getCatchTypes() { return unprocessedCatches.getCatchTypes(); @@ -170,7 +170,7 @@ public final class DalvCode { * Gets the set of all constants referred to by instructions in * the code. * - * @return non-null; the set of constants + * @return {@code non-null;} the set of constants */ public HashSet getInsnConstants() { return unprocessedInsns.getAllConstants(); @@ -179,7 +179,7 @@ public final class DalvCode { /** * Gets the list of instructions. * - * @return non-null; the instruction list + * @return {@code non-null;} the instruction list */ public DalvInsnList getInsns() { finishProcessingIfNecessary(); @@ -189,7 +189,7 @@ public final class DalvCode { /** * Gets the catch (exception handler) table. * - * @return non-null; the catch table + * @return {@code non-null;} the catch table */ public CatchTable getCatches() { finishProcessingIfNecessary(); @@ -199,7 +199,7 @@ public final class DalvCode { /** * Gets the source positions list. * - * @return non-null; the source positions list + * @return {@code non-null;} the source positions list */ public PositionList getPositions() { finishProcessingIfNecessary(); @@ -209,7 +209,7 @@ public final class DalvCode { /** * Gets the source positions list. * - * @return non-null; the source positions list + * @return {@code non-null;} the source positions list */ public LocalList getLocals() { finishProcessingIfNecessary(); @@ -223,8 +223,8 @@ public final class DalvCode { /** * Gets the index for the given constant. * - * @param cst non-null; the constant - * @return >= -1; the index or -1 if the constant + * @param cst {@code non-null;} the constant + * @return {@code >= -1;} the index or {@code -1} if the constant * shouldn't actually be reified with an index */ public int getIndex(Constant cst); diff --git a/dx/src/com/android/dx/dex/code/DalvInsn.java b/dx/src/com/android/dx/dex/code/DalvInsn.java index d92219341..11ee55d5d 100644 --- a/dx/src/com/android/dx/dex/code/DalvInsn.java +++ b/dx/src/com/android/dx/dex/code/DalvInsn.java @@ -29,26 +29,26 @@ import com.android.dx.util.TwoColumnOutput; public abstract class DalvInsn { /** * the actual output address of this instance, if known, or - * -1 if not + * {@code -1} if not */ private int address; /** the opcode; one of the constants from {@link Dops} */ private final Dop opcode; - /** non-null; source position */ + /** {@code non-null;} source position */ private final SourcePosition position; - /** non-null; list of register arguments */ + /** {@code non-null;} list of register arguments */ private final RegisterSpecList registers; /** * Makes a move instruction, appropriate and ideal for the given arguments. * - * @param position non-null; source position information - * @param dest non-null; destination register - * @param src non-null; source register - * @return non-null; an appropriately-constructed instance + * @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) { @@ -75,17 +75,17 @@ public abstract class DalvInsn { /** * Constructs an instance. The output address of this instance is initially - * unknown (-1). + * unknown ({@code -1}). * *

    Note: In the unlikely event that an instruction takes - * absolutely no registers (e.g., a nop or a + * 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}.

    * * @param opcode the opcode; one of the constants from {@link Dops} - * @param position non-null; source position - * @param registers non-null; register list, including a + * @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) */ @@ -151,11 +151,11 @@ public abstract class DalvInsn { /** * Gets the output address of this instruction, if it is known. This throws - * a RuntimeException if it has not yet been set. + * a {@code RuntimeException} if it has not yet been set. * * @see #setAddress * - * @return >= 0; the output address + * @return {@code >= 0;} the output address */ public final int getAddress() { if (address < 0) { @@ -168,7 +168,7 @@ public abstract class DalvInsn { /** * Gets the opcode. * - * @return non-null; the opcode + * @return {@code non-null;} the opcode */ public final Dop getOpcode() { return opcode; @@ -177,7 +177,7 @@ public abstract class DalvInsn { /** * Gets the source position. * - * @return non-null; the source position + * @return {@code non-null;} the source position */ public final SourcePosition getPosition() { return position; @@ -186,7 +186,7 @@ public abstract class DalvInsn { /** * Gets the register list for this instruction. * - * @return non-null; the registers + * @return {@code non-null;} the registers */ public final RegisterSpecList getRegisters() { return registers; @@ -195,9 +195,9 @@ public abstract class DalvInsn { /** * Returns whether this instance's opcode uses a result register. * This method is a convenient shorthand for - * getOpcode().hasResult(). + * {@code getOpcode().hasResult()}. * - * @return true iff this opcode uses a result register + * @return {@code true} iff this opcode uses a result register */ public final boolean hasResult() { return opcode.hasResult(); @@ -210,7 +210,7 @@ public abstract class DalvInsn { * (to be explicit here) category-2 values take up two consecutive * registers. * - * @return >= 0; the minimum distinct register requirement + * @return {@code >= 0;} the minimum distinct register requirement */ public final int getMinimumRegisterRequirement() { boolean hasResult = hasResult(); @@ -231,7 +231,7 @@ public abstract class DalvInsn { * * @see #hrVersion * - * @return null-ok; the prefix, if any + * @return {@code null-ok;} the prefix, if any */ public DalvInsn hrPrefix() { RegisterSpecList regs = registers; @@ -255,7 +255,7 @@ public abstract class DalvInsn { * * @see #hrVersion * - * @return null-ok; the suffix, if any + * @return {@code null-ok;} the suffix, if any */ public DalvInsn hrSuffix() { if (hasResult()) { @@ -268,8 +268,8 @@ public abstract class DalvInsn { /** * Gets the instruction that is equivalent to this one, except that - * uses sequential registers starting at 0 (storing - * the result, if any, in register 0 as well). The + * uses sequential registers starting at {@code 0} (storing + * the result, if any, in register {@code 0} as well). The * sequence of instructions from {@link #hrPrefix} and {@link * #hrSuffix} (if non-null) surrounding the result of a call to * this method are the high register transformation of this @@ -277,7 +277,7 @@ public abstract class DalvInsn { * used will be the number returned by {@link * #getMinimumRegisterRequirement}. * - * @return non-null; the replacement + * @return {@code non-null;} the replacement */ public DalvInsn hrVersion() { RegisterSpecList regs = @@ -289,7 +289,7 @@ public abstract class DalvInsn { * Gets the short identifier for this instruction. This is its * address, if assigned, or its identity hashcode if not. * - * @return non-null; the identifier + * @return {@code non-null;} the identifier */ public final String identifierString() { if (address != -1) { @@ -301,16 +301,16 @@ public abstract class DalvInsn { /** * Returns the string form of this instance suitable for inclusion in - * a human-oriented listing dump. This method will return null + * a human-oriented listing dump. This method will return {@code null} * if this instance should not appear in a listing. * - * @param prefix non-null; prefix before the address; each follow-on + * @param prefix {@code non-null;} prefix before the address; each follow-on * line will be indented to match as well - * @param width >= 0; the width of the output or 0 for + * @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 null-ok; the string form or null if this + * @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, @@ -331,7 +331,7 @@ public abstract class DalvInsn { /** * Sets the output address. * - * @param address >= 0; the output address + * @param address {@code >= 0;} the output address */ public final void setAddress(int address) { if (address < 0) { @@ -347,7 +347,7 @@ public abstract class DalvInsn { * to the address plus the length of the instruction format of this * instance's opcode. * - * @return >= 0; the next address + * @return {@code >= 0;} the next address */ public final int getNextAddress() { return getAddress() + codeSize(); @@ -356,7 +356,7 @@ public abstract class DalvInsn { /** * Gets the size of this instruction, in 16-bit code units. * - * @return >= 0; the code size of this instruction + * @return {@code >= 0;} the code size of this instruction */ public abstract int codeSize(); @@ -364,7 +364,7 @@ public abstract class DalvInsn { * Writes this instance to the given output. This method should * never annotate the output. * - * @param out non-null; where to write to + * @param out {@code non-null;} where to write to */ public abstract void writeTo(AnnotatedOutput out); @@ -372,8 +372,8 @@ public abstract class DalvInsn { * Returns an instance that is just like this one, except that its * opcode is replaced by the one given, and its address is reset. * - * @param opcode non-null; the new opcode - * @return non-null; an appropriately-constructed instance + * @param opcode {@code non-null;} the new opcode + * @return {@code non-null;} an appropriately-constructed instance */ public abstract DalvInsn withOpcode(Dop opcode); @@ -383,7 +383,7 @@ public abstract class DalvInsn { * address is reset. * * @param delta the amount to offset register references by - * @return non-null; an appropriately-constructed instance + * @return {@code non-null;} an appropriately-constructed instance */ public abstract DalvInsn withRegisterOffset(int delta); @@ -392,8 +392,8 @@ public abstract class DalvInsn { * register list is replaced by the given one, and its address is * reset. * - * @param registers non-null; new register list - * @return non-null; an appropriately-constructed instance + * @param registers {@code non-null;} new register list + * @return {@code non-null;} an appropriately-constructed instance */ public abstract DalvInsn withRegisters(RegisterSpecList registers); @@ -401,8 +401,8 @@ public abstract class DalvInsn { * Gets the string form for any arguments to this instance. Subclasses * must override this. * - * @return null-ok; the string version of any arguments or - * null if there are none + * @return {@code null-ok;} the string version of any arguments or + * {@code null} if there are none */ protected abstract String argString(); @@ -411,12 +411,12 @@ public abstract class DalvInsn { * 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 null if this instance should + * 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 null-ok; the listing string + * @return {@code null-ok;} the listing string */ protected abstract String listingString0(boolean noteIndices); } diff --git a/dx/src/com/android/dx/dex/code/DalvInsnList.java b/dx/src/com/android/dx/dex/code/DalvInsnList.java index 2cd15c6dd..5cf22f250 100644 --- a/dx/src/com/android/dx/dex/code/DalvInsnList.java +++ b/dx/src/com/android/dx/dex/code/DalvInsnList.java @@ -46,10 +46,10 @@ public final class DalvInsnList extends FixedSizeList { * Constructs and returns an immutable instance whose elements are * identical to the ones in the given list, in the same order. * - * @param list non-null; the list to use for elements + * @param 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 non-null; an appropriately-constructed instance of this + * @return {@code non-null;} an appropriately-constructed instance of this * class */ public static DalvInsnList makeImmutable(ArrayList list, @@ -66,7 +66,7 @@ public final class DalvInsnList extends FixedSizeList { } /** - * Constructs an instance. All indices initially contain null. + * Constructs an instance. All indices initially contain {@code null}. * * @param size the size of the list */ @@ -78,10 +78,10 @@ public final class DalvInsnList extends FixedSizeList { /** * 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 NullPointerException. + * do that, this will throw {@code NullPointerException}. * - * @param n >= 0, < size(); which index - * @return non-null; element at that index + * @param n {@code >= 0, < size();} which index + * @return {@code non-null;} element at that index */ public DalvInsn get(int n) { return (DalvInsn) get0(n); @@ -90,8 +90,8 @@ public final class DalvInsnList extends FixedSizeList { /** * Sets the instruction at the given index. * - * @param n >= 0, < size(); which index - * @param insn non-null; the instruction to set at n + * @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); @@ -102,7 +102,7 @@ public final class DalvInsnList extends FixedSizeList { * return a meaningful result if the instructions in this instance all * have valid addresses. * - * @return >= 0; the size + * @return {@code >= 0;} the size */ public int codeSize() { int sz = size(); @@ -119,7 +119,7 @@ public final class DalvInsnList extends FixedSizeList { * Writes all the instructions in this instance to the given output * destination. * - * @param out non-null; where to write to + * @param out {@code non-null;} where to write to */ public void writeTo(AnnotatedOutput out) { int startCursor = out.getCursor(); @@ -170,7 +170,7 @@ public final class DalvInsnList extends FixedSizeList { * 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 >= 0; the required registers size + * @return {@code >= 0;} the required registers size */ public int getRegistersSize() { return regCount; @@ -181,7 +181,7 @@ public final class DalvInsnList extends FixedSizeList { * method. This is equal to the largest argument word count of any * method referred to by this instance. * - * @return >= 0; the required outgoing arguments size + * @return {@code >= 0;} the required outgoing arguments size */ public int getOutsSize() { int sz = size(); @@ -216,8 +216,8 @@ public final class DalvInsnList extends FixedSizeList { /** * Does a human-friendly dump of this instance. * - * @param out non-null; where to dump - * @param prefix non-null; prefix to attach to each line of output + * @param 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 */ @@ -250,8 +250,8 @@ public final class DalvInsnList extends FixedSizeList { /** * Does a human-friendly dump of this instance. * - * @param out non-null; where to dump - * @param prefix non-null; prefix to attach to each line of output + * @param 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 */ diff --git a/dx/src/com/android/dx/dex/code/Dop.java b/dx/src/com/android/dx/dex/code/Dop.java index 6914fcae6..d1f92bfb7 100644 --- a/dx/src/com/android/dx/dex/code/Dop.java +++ b/dx/src/com/android/dx/dex/code/Dop.java @@ -26,25 +26,25 @@ public final class Dop { /** DalvOps.MIN_VALUE..DalvOps.MAX_VALUE; the opcode family */ private final int family; - /** non-null; the instruction format */ + /** {@code non-null;} the instruction format */ private final InsnFormat format; /** whether this opcode uses a result register */ private final boolean hasResult; - /** non-null; the name */ + /** {@code non-null;} the name */ private final String name; /** * Constructs an instance. * - * @param opcode DalvOps.MIN_VALUE..DalvOps.MAX_VALUE; the opcode + * @param opcode {@code DalvOps.MIN_VALUE..DalvOps.MAX_VALUE;} the opcode * value itself - * @param family DalvOps.MIN_VALUE..DalvOps.MAX_VALUE; the opcode family - * @param format non-null; the instruction format + * @param family {@code DalvOps.MIN_VALUE..DalvOps.MAX_VALUE;} the opcode family + * @param format {@code non-null;} the instruction format * @param hasResult whether the opcode has a result register; if so it * is always the first register - * @param name non-null; the name + * @param name {@code non-null;} the name */ public Dop(int opcode, int family, InsnFormat format, boolean hasResult, String name) { @@ -80,7 +80,7 @@ public final class Dop { /** * Gets the opcode value. * - * @return DalvOps.MIN_VALUE..DalvOps.MAX_VALUE; the opcode value + * @return {@code DalvOps.MIN_VALUE..DalvOps.MAX_VALUE;} the opcode value */ public int getOpcode() { return opcode; @@ -90,7 +90,7 @@ public final class Dop { * Gets the opcode family. The opcode family is the unmarked (no * "/...") opcode that has equivalent semantics to this one. * - * @return DalvOps.MIN_VALUE..DalvOps.MAX_VALUE; the opcode family + * @return {@code DalvOps.MIN_VALUE..DalvOps.MAX_VALUE;} the opcode family */ public int getFamily() { return family; @@ -99,7 +99,7 @@ public final class Dop { /** * Gets the instruction format. * - * @return non-null; the instruction format + * @return {@code non-null;} the instruction format */ public InsnFormat getFormat() { return format; @@ -108,7 +108,7 @@ public final class Dop { /** * Returns whether this opcode uses a result register. * - * @return true iff this opcode uses a result register + * @return {@code true} iff this opcode uses a result register */ public boolean hasResult() { return hasResult; @@ -117,7 +117,7 @@ public final class Dop { /** * Gets the opcode name. * - * @return non-null; the opcode name + * @return {@code non-null;} the opcode name */ public String getName() { return name; @@ -127,7 +127,7 @@ public final class Dop { * Gets the opcode for the opposite test of this instance. This is only * valid for opcodes which are in fact tests. * - * @return non-null; the opposite test + * @return {@code non-null;} the opposite test */ public Dop getOppositeTest() { switch (opcode) { diff --git a/dx/src/com/android/dx/dex/code/Dops.java b/dx/src/com/android/dx/dex/code/Dops.java index 8923c59bf..dfdaa7378 100644 --- a/dx/src/com/android/dx/dex/code/Dops.java +++ b/dx/src/com/android/dx/dex/code/Dops.java @@ -47,7 +47,7 @@ import com.android.dx.dex.code.form.SpecialFormat; * them. */ public final class Dops { - /** non-null; array containing all the standard instances */ + /** {@code non-null;} array containing all the standard instances */ private static final Dop[] DOPS; /** @@ -1172,8 +1172,8 @@ public final class Dops { /** * Gets the {@link Dop} for the given opcode value. * - * @param opcode DalvOps.MIN_VALUE..DalvOps.MAX_VALUE; the opcode value - * @return non-null; the associated opcode instance + * @param opcode {@code DalvOps.MIN_VALUE..DalvOps.MAX_VALUE;} the opcode value + * @return {@code non-null;} the associated opcode instance */ public static Dop get(int opcode) { int idx = opcode - DalvOps.MIN_VALUE; @@ -1194,9 +1194,9 @@ public final class Dops { * Gets the {@link Dop} with the given family/format combination, if * any. * - * @param family DalvOps.MIN_VALUE..DalvOps.MAX_VALUE; the opcode family - * @param format non-null; the opcode's instruction format - * @return null-ok; the corresponding opcode, or null if + * @param family {@code DalvOps.MIN_VALUE..DalvOps.MAX_VALUE;} the opcode family + * @param format {@code non-null;} the opcode's instruction format + * @return {@code null-ok;} the corresponding opcode, or {@code null} if * there is none */ public static Dop getOrNull(int family, InsnFormat format) { @@ -1222,7 +1222,7 @@ public final class Dops { /** * Puts the given opcode into the table of all ops. * - * @param opcode non-null; the opcode + * @param opcode {@code non-null;} the opcode */ private static void set(Dop opcode) { int idx = opcode.getOpcode() - DalvOps.MIN_VALUE; diff --git a/dx/src/com/android/dx/dex/code/FixedSizeInsn.java b/dx/src/com/android/dx/dex/code/FixedSizeInsn.java index 63c9d2407..147937f9e 100644 --- a/dx/src/com/android/dx/dex/code/FixedSizeInsn.java +++ b/dx/src/com/android/dx/dex/code/FixedSizeInsn.java @@ -28,17 +28,17 @@ import com.android.dx.util.AnnotatedOutput; public abstract class FixedSizeInsn extends DalvInsn { /** * Constructs an instance. The output address of this instance is initially - * unknown (-1). + * unknown ({@code -1}). * *

    Note: In the unlikely event that an instruction takes - * absolutely no registers (e.g., a nop or a + * 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}.

    * * @param opcode the opcode; one of the constants from {@link Dops} - * @param position non-null; source position - * @param registers non-null; register list, including a + * @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) */ diff --git a/dx/src/com/android/dx/dex/code/HighRegisterPrefix.java b/dx/src/com/android/dx/dex/code/HighRegisterPrefix.java index 458bc893d..915536795 100644 --- a/dx/src/com/android/dx/dex/code/HighRegisterPrefix.java +++ b/dx/src/com/android/dx/dex/code/HighRegisterPrefix.java @@ -24,21 +24,21 @@ import com.android.dx.util.AnnotatedOutput; /** * Combination instruction which turns into a variable number of - * move* instructions to move a set of registers into - * registers starting at 0 sequentially. This is used + * {@code move*} instructions to move a set of registers into + * registers starting at {@code 0} sequentially. This is used * in translating an instruction whose register requirements cannot * be met using a straightforward choice of a single opcode. */ public final class HighRegisterPrefix extends VariableSizeInsn { - /** null-ok; cached instructions, if constructed */ + /** {@code null-ok;} cached instructions, if constructed */ private SimpleInsn[] insns; /** * Constructs an instance. The output address of this instance is initially - * unknown (-1). + * unknown ({@code -1}). * - * @param position non-null; source position - * @param registers non-null; source registers + * @param position {@code non-null;} source position + * @param registers {@code non-null;} source registers */ public HighRegisterPrefix(SourcePosition position, RegisterSpecList registers) { @@ -135,9 +135,9 @@ public final class HighRegisterPrefix extends VariableSizeInsn { * Returns the proper move instruction for the given source spec * and destination index. * - * @param src non-null; the source register spec - * @param destIndex >= 0; the destination register index - * @return non-null; the appropriate move instruction + * @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, diff --git a/dx/src/com/android/dx/dex/code/InsnFormat.java b/dx/src/com/android/dx/dex/code/InsnFormat.java index ed4137b3f..ca6688bd3 100644 --- a/dx/src/com/android/dx/dex/code/InsnFormat.java +++ b/dx/src/com/android/dx/dex/code/InsnFormat.java @@ -37,10 +37,10 @@ public abstract class InsnFormat { * dump, of the given instruction. The instruction must be of this * instance's format for proper operation. * - * @param insn non-null; the instruction + * @param insn {@code non-null;} the instruction * @param noteIndices whether to include an explicit notation of * constant pool indices - * @return non-null; the string form + * @return {@code non-null;} the string form */ public final String listingString(DalvInsn insn, boolean noteIndices) { String op = insn.getOpcode().getName(); @@ -66,28 +66,28 @@ public abstract class InsnFormat { /** * 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 "", not - * null. + * has no arguments, then the result should be {@code ""}, not + * {@code null}. * *

    Subclasses must override this method.

    * - * @param insn non-null; the instruction - * @return non-null; the string form + * @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 "", not - * null. + * has no comment, then the result should be {@code ""}, not + * {@code null}. * *

    Subclasses must override this method.

    * - * @param insn non-null; the instruction + * @param insn {@code non-null;} the instruction * @param noteIndices whether to include an explicit notation of * constant pool indices - * @return non-null; the string form + * @return {@code non-null;} the string form */ public abstract String insnCommentString(DalvInsn insn, boolean noteIndices); @@ -97,7 +97,7 @@ public abstract class InsnFormat { * size is a number of 16-bit code units, not bytes. This should * throw an exception if this format is of variable size. * - * @return >= 0; the instruction length in 16-bit code units + * @return {@code >= 0;} the instruction length in 16-bit code units */ public abstract int codeSize(); @@ -112,24 +112,24 @@ public abstract class InsnFormat { * *

    Subclasses must override this method.

    * - * @param insn non-null; the instruction to check - * @return true iff the instruction's arguments are - * appropriate for this instance, or false if not + * @param insn {@code non-null;} the instruction to check + * @return {@code true} iff the instruction's arguments are + * appropriate for this instance, or {@code false} if not */ public abstract boolean isCompatible(DalvInsn insn); /** * Returns whether or not the given instruction's branch offset will - * fit in this instance's format. This always returns false + * fit in this instance's format. This always returns {@code false} * for formats that don't include a branch offset. * *

    The default implementation of this method always returns - * false. Subclasses must override this method if they + * {@code false}. Subclasses must override this method if they * include branch offsets.

    * - * @param insn non-null; the instruction to check - * @return true iff the instruction's branch offset is - * appropriate for this instance, or false if not + * @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; @@ -141,7 +141,7 @@ public abstract class InsnFormat { * *

    Subclasses must override this method.

    * - * @return null-ok; the next format to try, or null if + * @return {@code null-ok;} the next format to try, or {@code null} if * there are no suitable alternatives */ public abstract InsnFormat nextUp(); @@ -152,16 +152,16 @@ public abstract class InsnFormat { * *

    Subclasses must override this method.

    * - * @param out non-null; the output destination to write to - * @param insn non-null; the instruction to write + * @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 non-null; the list of registers - * @return non-null; the string form + * @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(); @@ -185,7 +185,7 @@ public abstract class InsnFormat { * Helper method to return a literal bits argument string. * * @param value the value - * @return non-null; the string form + * @return {@code non-null;} the string form */ protected static String literalBitsString(CstLiteralBits value) { StringBuffer sb = new StringBuffer(100); @@ -208,8 +208,8 @@ public abstract class InsnFormat { * * @param value the value * @param width the width of the constant, in bits (used for displaying - * the uninterpreted bits; one of: 4 8 16 32 64 - * @return non-null; the comment + * 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) { @@ -242,8 +242,8 @@ public abstract class InsnFormat { /** * Helper method to return a branch address string. * - * @param insn non-null; the instruction in question - * @return non-null; the string form of the instruction's branch target + * @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; @@ -255,8 +255,8 @@ public abstract class InsnFormat { /** * Helper method to return the comment for a branch. * - * @param insn non-null; the instruction in question - * @return non-null; the comment + * @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; @@ -268,8 +268,8 @@ public abstract class InsnFormat { /** * Helper method to return a constant string. * - * @param insn non-null; a constant-bearing instruction - * @return non-null; the string form of the contained constant + * @param insn {@code non-null;} a constant-bearing instruction + * @return {@code non-null;} the string form of the contained constant */ protected static String cstString(DalvInsn insn) { CstInsn ci = (CstInsn) insn; @@ -281,8 +281,8 @@ public abstract class InsnFormat { /** * Helper method to return an instruction comment for a constant. * - * @param insn non-null; a constant-bearing instruction - * @return non-null; comment string representing the constant + * @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; @@ -310,7 +310,7 @@ public abstract class InsnFormat { * Helper method to determine if a signed int value fits in a nibble. * * @param value the value in question - * @return true iff it's in the range -8..+7 + * @return {@code true} iff it's in the range -8..+7 */ protected static boolean signedFitsInNibble(int value) { return (value >= -8) && (value <= 7); @@ -320,7 +320,7 @@ public abstract class InsnFormat { * Helper method to determine if an unsigned int value fits in a nibble. * * @param value the value in question - * @return true iff it's in the range 0..0xf + * @return {@code true} iff it's in the range 0..0xf */ protected static boolean unsignedFitsInNibble(int value) { return value == (value & 0xf); @@ -330,7 +330,7 @@ public abstract class InsnFormat { * Helper method to determine if a signed int value fits in a byte. * * @param value the value in question - * @return true iff it's in the range -0x80..+0x7f + * @return {@code true} iff it's in the range -0x80..+0x7f */ protected static boolean signedFitsInByte(int value) { return (byte) value == value; @@ -340,7 +340,7 @@ public abstract class InsnFormat { * Helper method to determine if an unsigned int value fits in a byte. * * @param value the value in question - * @return true iff it's in the range 0..0xff + * @return {@code true} iff it's in the range 0..0xff */ protected static boolean unsignedFitsInByte(int value) { return value == (value & 0xff); @@ -350,7 +350,7 @@ public abstract class InsnFormat { * Helper method to determine if a signed int value fits in a short. * * @param value the value in question - * @return true iff it's in the range -0x8000..+0x7fff + * @return {@code true} iff it's in the range -0x8000..+0x7fff */ protected static boolean signedFitsInShort(int value) { return (short) value == value; @@ -360,7 +360,7 @@ public abstract class InsnFormat { * Helper method to determine if an unsigned int value fits in a short. * * @param value the value in question - * @return true iff it's in the range 0..0xffff + * @return {@code true} iff it's in the range 0..0xffff */ protected static boolean unsignedFitsInShort(int value) { return value == (value & 0xffff); @@ -370,7 +370,7 @@ public abstract class InsnFormat { * Helper method to determine if a signed int value fits in three bytes. * * @param value the value in question - * @return true iff it's in the range -0x800000..+0x7fffff + * @return {@code true} iff it's in the range -0x800000..+0x7fffff */ protected static boolean signedFitsIn3Bytes(int value) { return value == ((value << 8) >> 8); @@ -380,8 +380,8 @@ public abstract class InsnFormat { * Helper method to extract the callout-argument index from an * appropriate instruction. * - * @param insn non-null; the instruction - * @return >= 0; the callout argument index + * @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(); @@ -397,8 +397,8 @@ public abstract class InsnFormat { * Helper method to combine an opcode and a second byte of data into * the appropriate form for emitting into a code buffer. * - * @param insn non-null; the instruction containing the opcode - * @param arg 0..255; arbitrary other byte value + * @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) { @@ -418,8 +418,8 @@ public abstract class InsnFormat { /** * Helper method to combine two bytes into a code unit. * - * @param low 0..255; low byte - * @param high 0..255; high byte + * @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) { @@ -437,10 +437,10 @@ public abstract class InsnFormat { /** * Helper method to combine four nibbles into a code unit. * - * @param n0 0..15; low nibble - * @param n1 0..15; medium-low nibble - * @param n2 0..15; medium-high nibble - * @param n3 0..15; high nibble + * @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) { @@ -466,9 +466,9 @@ public abstract class InsnFormat { /** * Helper method to combine two nibbles into a byte. * - * @param low 0..15; low nibble - * @param high 0..15; high nibble - * @return 0..255; combined value + * @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) { @@ -485,7 +485,7 @@ public abstract class InsnFormat { /** * Writes one code unit to the given output destination. * - * @param out non-null; where to write to + * @param out {@code non-null;} where to write to * @param c0 code unit to write */ protected static void write(AnnotatedOutput out, short c0) { @@ -495,7 +495,7 @@ public abstract class InsnFormat { /** * Writes two code units to the given output destination. * - * @param out non-null; where to write to + * @param out {@code non-null;} where to write to * @param c0 code unit to write * @param c1 code unit to write */ @@ -507,7 +507,7 @@ public abstract class InsnFormat { /** * Writes three code units to the given output destination. * - * @param out non-null; where to write to + * @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 @@ -522,7 +522,7 @@ public abstract class InsnFormat { /** * Writes four code units to the given output destination. * - * @param out non-null; where to write to + * @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 @@ -539,7 +539,7 @@ public abstract class InsnFormat { /** * Writes five code units to the given output destination. * - * @param out non-null; where to write to + * @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 @@ -558,7 +558,7 @@ public abstract class InsnFormat { /** * Writes six code units to the given output destination. * - * @param out non-null; where to write to + * @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 diff --git a/dx/src/com/android/dx/dex/code/LocalEnd.java b/dx/src/com/android/dx/dex/code/LocalEnd.java index c19a8dcec..360a55cde 100644 --- a/dx/src/com/android/dx/dex/code/LocalEnd.java +++ b/dx/src/com/android/dx/dex/code/LocalEnd.java @@ -28,7 +28,7 @@ import com.android.dx.rop.code.SourcePosition; */ public final class LocalEnd extends ZeroSizeInsn { /** - * non-null; register spec representing the local variable ended + * {@code non-null;} register spec representing the local variable ended * by this instance. Note: 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 @@ -38,10 +38,10 @@ public final class LocalEnd extends ZeroSizeInsn { /** * Constructs an instance. The output address of this instance is initially - * unknown (-1). + * unknown ({@code -1}). * - * @param position non-null; source position - * @param local non-null; register spec representing the local + * @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) { @@ -70,7 +70,7 @@ public final class LocalEnd extends ZeroSizeInsn { * Gets the register spec representing the local variable ended * by this instance. * - * @return non-null; the register spec + * @return {@code non-null;} the register spec */ public RegisterSpec getLocal() { return local; diff --git a/dx/src/com/android/dx/dex/code/LocalList.java b/dx/src/com/android/dx/dex/code/LocalList.java index 4614fc41e..93e7c3f61 100644 --- a/dx/src/com/android/dx/dex/code/LocalList.java +++ b/dx/src/com/android/dx/dex/code/LocalList.java @@ -33,16 +33,16 @@ import java.util.Arrays; * and a type. */ public final class LocalList extends FixedSizeList { - /** non-null; empty instance */ + /** {@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 null. - * - * @param size >= 0; the size of the list + * Constructs an instance. All indices initially contain {@code null}. + * + * @param size {@code >= 0;} the size of the list */ public LocalList(int size) { super(size); @@ -51,10 +51,10 @@ public final class LocalList extends FixedSizeList { /** * 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 NullPointerException. - * - * @param n >= 0, < size(); which index - * @return non-null; element at that index + * 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); @@ -62,9 +62,9 @@ public final class LocalList extends FixedSizeList { /** * Sets the entry at the given index. - * - * @param n >= 0, < size(); which index - * @param entry non-null; the entry to set at n + * + * @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); @@ -72,9 +72,9 @@ public final class LocalList extends FixedSizeList { /** * Does a human-friendly dump of this instance. - * - * @param out non-null; where to dump - * @param prefix non-null; prefix to attach to each line of output + * + * @param 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(); @@ -90,7 +90,7 @@ public final class LocalList extends FixedSizeList { */ public static enum Disposition { /** local started (introduced) */ - START, + START, /** local ended without being replaced */ END_SIMPLY, @@ -118,24 +118,25 @@ public final class LocalList extends FixedSizeList { * Entry in a local list. */ public static class Entry implements Comparable { - /** >= 0; address */ + /** {@code >= 0;} address */ private final int address; - /** non-null; disposition of the local */ + /** {@code non-null;} disposition of the local */ private final Disposition disposition; - /** non-null; register spec representing the variable */ + /** {@code non-null;} register spec representing the variable */ private final RegisterSpec spec; - /** non-null; variable type (derived from {@code spec}) */ + /** {@code non-null;} variable type (derived from {@code spec}) */ private final CstType type; - + /** * Constructs an instance. - * - * @param address >= 0; address - * @param disposition non-null; disposition of the local - * @param spec non-null; register spec representing the variable + * + * @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) { @@ -181,9 +182,9 @@ public final class LocalList extends FixedSizeList { * Compares by (in priority order) address, end then start * disposition (variants of end are all consistered * equivalent), and spec. - * - * @param other non-null; entry to compare to - * @return {@code -1..1}; standard result of comparison + * + * @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) { @@ -194,7 +195,7 @@ public final class LocalList extends FixedSizeList { boolean thisIsStart = isStart(); boolean otherIsStart = other.isStart(); - + if (thisIsStart != otherIsStart) { return thisIsStart ? 1 : -1; } @@ -204,8 +205,8 @@ public final class LocalList extends FixedSizeList { /** * Gets the address. - * - * @return >= 0; the address + * + * @return {@code >= 0;} the address */ public int getAddress() { return address; @@ -213,8 +214,8 @@ public final class LocalList extends FixedSizeList { /** * Gets the disposition. - * - * @return non-null; the disposition + * + * @return {@code non-null;} the disposition */ public Disposition getDisposition() { return disposition; @@ -223,7 +224,7 @@ public final class LocalList extends FixedSizeList { /** * 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() { @@ -232,8 +233,8 @@ public final class LocalList extends FixedSizeList { /** * Gets the variable name. - * - * @return null-ok; the variable name + * + * @return {@code null-ok;} the variable name */ public CstUtf8 getName() { return spec.getLocalItem().getName(); @@ -242,7 +243,7 @@ public final class LocalList extends FixedSizeList { /** * Gets the variable signature. * - * @return null-ok; the variable signature + * @return {@code null-ok;} the variable signature */ public CstUtf8 getSignature() { return spec.getLocalItem().getSignature(); @@ -250,8 +251,8 @@ public final class LocalList extends FixedSizeList { /** * Gets the variable's type. - * - * @return non-null; the type + * + * @return {@code non-null;} the type */ public CstType getType() { return type; @@ -259,8 +260,9 @@ public final class LocalList extends FixedSizeList { /** * Gets the number of the register holding the variable. - * - * @return >= 0; the number fo the register holding the variable + * + * @return {@code >= 0;} the number of the register holding + * the variable */ public int getRegister() { return spec.getReg(); @@ -269,7 +271,7 @@ public final class LocalList extends FixedSizeList { /** * Gets the RegisterSpec of the register holding the variable. * - * @return non-null; RegisterSpec of the holding register. + * @return {@code non-null;} RegisterSpec of the holding register. */ public RegisterSpec getRegisterSpec() { return spec; @@ -277,10 +279,10 @@ public final class LocalList extends FixedSizeList { /** * Returns whether or not this instance matches the given spec. - * - * @param spec non-null; the spec in question - * @return true iff this instance matches - * spec + * + * @param spec {@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); @@ -290,9 +292,9 @@ public final class LocalList extends FixedSizeList { * Returns whether or not this instance matches the spec in * the given instance. * - * @param other non-null; another entry - * @return true iff this instance's spec matches - * other + * @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); @@ -300,26 +302,26 @@ public final class LocalList extends FixedSizeList { /** * Returns an instance just like this one but with the disposition - * set as given - * - * @param disposition non-null; the new disposition - * @return non-null; an appropriately-constructed instance + * set as given. + * + * @param disposition {@code non-null;} the new disposition + * @return {@code non-null;} an appropriately-constructed instance */ public Entry withDisposition(Disposition disposition) { if (disposition == this.disposition) { return this; } - + return new Entry(address, disposition, spec); } } - + /** * Constructs an instance for the given method, based on the given * block order and intermediate local information. - * - * @param insns non-null; instructions to convert - * @return non-null; the constructed list + * + * @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(); @@ -330,8 +332,8 @@ public final class LocalList extends FixedSizeList { * 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). - * + * 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 @@ -382,9 +384,9 @@ public final class LocalList extends FixedSizeList { } throw ex; } - + } - + /** * Helper for {@link #debugVerify} which does most of the work. */ @@ -411,7 +413,7 @@ public final class LocalList extends FixedSizeList { throw new RuntimeException("redundant end at " + Integer.toHexString(e.getAddress())); } - + int addr = e.getAddress(); boolean foundStart = false; @@ -433,7 +435,7 @@ public final class LocalList extends FixedSizeList { throw new RuntimeException( "redundant end at " + Integer.toHexString(addr)); - } + } } } @@ -443,7 +445,7 @@ public final class LocalList extends FixedSizeList { "improper end replacement claim at " + Integer.toHexString(addr)); } - + active[reg] = null; } } @@ -453,30 +455,24 @@ public final class LocalList extends FixedSizeList { * Intermediate state when constructing a local list. */ public static class MakeState { - /** non-null; result being collected */ + /** {@code non-null;} result being collected */ private final ArrayList result; /** - * >= 0; running count of nulled result entries, to help with + * {@code >= 0;} running count of nulled result entries, to help with * sizing the final list */ private int nullResultCount; - /** null-ok; current register mappings */ + /** {@code null-ok;} current register mappings */ private RegisterSpecSet regs; - /** null-ok; result indices where local ends are stored */ + /** {@code null-ok;} result indices where local ends are stored */ private int[] endIndices; - /** >= 0; last address seen */ + /** {@code >= 0;} last address seen */ private int lastAddress; - /** - * >= 0; result index where the first element for the most - * recent address is stored - */ - private int startIndexForAddress; - /** * Constructs an instance. */ @@ -486,19 +482,18 @@ public final class LocalList extends FixedSizeList { regs = null; endIndices = null; lastAddress = 0; - startIndexForAddress = 0; } /** * Checks the address and other vitals as a prerequisite to * further processing. * - * @param address >= 0; address about to be processed - * @param reg >= 0; register number about to be processed + * @param address {@code >= 0;} address about to be processed + * @param reg {@code >= 0;} register number about to be processed */ private void aboutToProcess(int address, int reg) { boolean first = (endIndices == null); - + if ((address == lastAddress) && !first) { return; } @@ -534,11 +529,15 @@ public final class LocalList extends FixedSizeList { * 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 >= 0; the address - * @param specs non-null; spec set representing the locals + * + * @param address {@code >= 0;} the address + * @param specs {@code non-null;} spec set representing the locals */ public void snapshot(int address, RegisterSpecSet specs) { + if (DEBUG) { + System.err.printf("%04x snapshot %s\n", address, specs); + } + int sz = specs.getMaxSize(); aboutToProcess(address, sz - 1); @@ -557,15 +556,24 @@ public final class LocalList extends FixedSizeList { startLocal(address, newSpec); } } + + if (DEBUG) { + System.err.printf("%04x snapshot done\n", address); + } } - + /** * Starts a local at the given address. - * - * @param address >= 0; the address - * @param startedLocal non-null; spec representing the started local + * + * @param address {@code >= 0;} the address + * @param startedLocal {@code non-null;} spec representing the + * started local */ public void startLocal(int address, RegisterSpec startedLocal) { + if (DEBUG) { + System.err.printf("%04x start %s\n", address, startedLocal); + } + int regNum = startedLocal.getReg(); startedLocal = filterSpec(startedLocal); @@ -588,7 +596,7 @@ public final class LocalList extends FixedSizeList { } int endAt = endIndices[regNum]; - + if (existingLocal != null) { /* * There is an existing (but non-matching) local. @@ -633,8 +641,8 @@ public final class LocalList extends FixedSizeList { } } } - - /* + + /* * 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. @@ -672,17 +680,36 @@ public final class LocalList extends FixedSizeList { * if any (that is, if the local migrates from vX to vY, * we should note that as a local end in vX). */ - + add(address, Disposition.START, startedLocal); } /** - * Ends a local at the given address. + * Ends a local at the given address, using the disposition + * {@code END_SIMPLY}. * - * @param address >= 0; the address - * @param endedLocal non-null; spec representing the local being ended + * @param address {@code >= 0;} the address + * @param endedLocal {@code non-null;} spec representing the + * local being ended */ public void endLocal(int address, RegisterSpec endedLocal) { + endLocal(address, endedLocal, Disposition.END_SIMPLY); + } + + /** + * Ends a local at the given address. + * + * @param address {@code >= 0;} the address + * @param endedLocal {@code non-null;} spec representing the + * local being ended + * @param disposition reason for the end + */ + public void endLocal(int address, RegisterSpec endedLocal, + Disposition disposition) { + if (DEBUG) { + System.err.printf("%04x end %s\n", address, endedLocal); + } + int regNum = endedLocal.getReg(); endedLocal = filterSpec(endedLocal); @@ -703,7 +730,7 @@ public final class LocalList extends FixedSizeList { return; } - add(address, Disposition.END_SIMPLY, endedLocal); + add(address, disposition, endedLocal); } /** @@ -714,9 +741,10 @@ public final class LocalList extends FixedSizeList { * active), update the {@link #endIndices} to be accurate, and * if needed update the newly-active end to reflect an altered * disposition. - * - * @param address >= 0; the address - * @param endedLocal non-null; spec representing the local being ended + * + * @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 */ @@ -747,7 +775,7 @@ public final class LocalList extends FixedSizeList { * In fact, we found that the endedLocal had started at the * same address, so do all the requisite cleanup. */ - + regs.remove(endedLocal); result.set(at, null); nullResultCount++; @@ -773,7 +801,7 @@ public final class LocalList extends FixedSizeList { 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 @@ -793,13 +821,13 @@ public final class LocalList extends FixedSizeList { * null" type into simply {@code Object}. This method needs to * be called for any spec that is on its way into a locals * list. - * + * *

    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.

    - * - * @param orig null-ok; the original spec - * @return null-ok; an appropriately modified spec, or the + * + * @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) { @@ -814,9 +842,9 @@ public final class LocalList extends FixedSizeList { * Adds an entry to the result, updating the adjunct tables * accordingly. * - * @param address >= 0; the address - * @param disposition non-null; the disposition - * @param spec non-null; spec representing the local + * @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) { @@ -834,11 +862,13 @@ public final class LocalList extends FixedSizeList { } /** - * Adds or updates an end local (changing its disposition). - * - * @param address >= 0; the address - * @param disposition non-null; the disposition - * @param spec non-null; spec representing the local + * 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) { @@ -850,29 +880,34 @@ public final class LocalList extends FixedSizeList { 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); + regs.remove(spec); // TODO: Is this line superfluous? return; } } - - add(address, disposition, spec); + + endLocal(address, spec, disposition); } /** * Finishes processing altogether and gets the result. - * - * @return non-null; the result list + * + * @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; } @@ -909,5 +944,5 @@ public final class LocalList extends FixedSizeList { resultList.setImmutable(); return resultList; } - } + } } diff --git a/dx/src/com/android/dx/dex/code/LocalSnapshot.java b/dx/src/com/android/dx/dex/code/LocalSnapshot.java index 19a9baa35..409ad15bd 100644 --- a/dx/src/com/android/dx/dex/code/LocalSnapshot.java +++ b/dx/src/com/android/dx/dex/code/LocalSnapshot.java @@ -27,15 +27,15 @@ import com.android.dx.rop.code.SourcePosition; * the instance in an instruction array. */ public final class LocalSnapshot extends ZeroSizeInsn { - /** non-null; local state associated with this instance */ + /** {@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 (-1). + * unknown ({@code -1}). * - * @param position non-null; source position - * @param locals non-null; associated local variable state + * @param position {@code non-null;} source position + * @param locals {@code non-null;} associated local variable state */ public LocalSnapshot(SourcePosition position, RegisterSpecSet locals) { super(position); @@ -62,7 +62,7 @@ public final class LocalSnapshot extends ZeroSizeInsn { /** * Gets the local state associated with this instance. * - * @return non-null; the state + * @return {@code non-null;} the state */ public RegisterSpecSet getLocals() { return locals; diff --git a/dx/src/com/android/dx/dex/code/LocalStart.java b/dx/src/com/android/dx/dex/code/LocalStart.java index 22e20f80d..ec70e3011 100644 --- a/dx/src/com/android/dx/dex/code/LocalStart.java +++ b/dx/src/com/android/dx/dex/code/LocalStart.java @@ -28,7 +28,7 @@ import com.android.dx.rop.code.SourcePosition; */ public final class LocalStart extends ZeroSizeInsn { /** - * non-null; register spec representing the local variable introduced + * {@code non-null;} register spec representing the local variable introduced * by this instance */ private final RegisterSpec local; @@ -36,8 +36,8 @@ public final class LocalStart extends ZeroSizeInsn { /** * Returns the local variable listing string for a single register spec. * - * @param spec non-null; the spec to convert - * @return non-null; the string form + * @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() + ": " + @@ -46,10 +46,10 @@ public final class LocalStart extends ZeroSizeInsn { /** * Constructs an instance. The output address of this instance is initially - * unknown (-1). + * unknown ({@code -1}). * - * @param position non-null; source position - * @param local non-null; register spec representing the local + * @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) { @@ -78,7 +78,7 @@ public final class LocalStart extends ZeroSizeInsn { * Gets the register spec representing the local variable introduced * by this instance. * - * @return non-null; the register spec + * @return {@code non-null;} the register spec */ public RegisterSpec getLocal() { return local; diff --git a/dx/src/com/android/dx/dex/code/OddSpacer.java b/dx/src/com/android/dx/dex/code/OddSpacer.java index f99df3629..727def04b 100644 --- a/dx/src/com/android/dx/dex/code/OddSpacer.java +++ b/dx/src/com/android/dx/dex/code/OddSpacer.java @@ -21,7 +21,7 @@ import com.android.dx.rop.code.SourcePosition; import com.android.dx.util.AnnotatedOutput; /** - * Pseudo-instruction which either turns into a nop or + * Pseudo-instruction which either turns into a {@code nop} or * nothingness, in order to make the subsequent instruction have an * even address. This is used to align (subsequent) instructions that * require it. @@ -29,9 +29,9 @@ import com.android.dx.util.AnnotatedOutput; public final class OddSpacer extends VariableSizeInsn { /** * Constructs an instance. The output address of this instance is initially - * unknown (-1). + * unknown ({@code -1}). * - * @param position non-null; source position + * @param position {@code non-null;} source position */ public OddSpacer(SourcePosition position) { super(position, RegisterSpecList.EMPTY); diff --git a/dx/src/com/android/dx/dex/code/OutputCollector.java b/dx/src/com/android/dx/dex/code/OutputCollector.java index 98d8a9cd0..264337378 100644 --- a/dx/src/com/android/dx/dex/code/OutputCollector.java +++ b/dx/src/com/android/dx/dex/code/OutputCollector.java @@ -28,13 +28,13 @@ import java.util.ArrayList; */ public final class OutputCollector { /** - * non-null; the associated finisher (which holds the instruction + * {@code non-null;} the associated finisher (which holds the instruction * list in-progress) */ private final OutputFinisher finisher; /** - * null-ok; suffix for the output, or null if the 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 suffix; @@ -42,10 +42,10 @@ public final class OutputCollector { /** * Constructs an instance. * - * @param initialCapacity >= 0; initial capacity of the output list - * @param suffixInitialCapacity >= 0; initial capacity of the output + * @param initialCapacity {@code >= 0;} initial capacity of the output list + * @param suffixInitialCapacity {@code >= 0;} initial capacity of the output * suffix - * @param regCount >= 0; register count for the method + * @param regCount {@code >= 0;} register count for the method */ public OutputCollector(int initialCapacity, int suffixInitialCapacity, int regCount) { @@ -56,7 +56,7 @@ public final class OutputCollector { /** * Adds an instruction to the output. * - * @param insn non-null; the instruction to add + * @param insn {@code non-null;} the instruction to add */ public void add(DalvInsn insn) { finisher.add(insn); @@ -68,9 +68,9 @@ public final class OutputCollector { * indicated instruction really is a reversible branch. * * @param which how many instructions back to find the branch; - * 0 is the most recently added instruction, - * 1 is the instruction before that, etc. - * @param newTarget non-null; the new target for the reversed 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); @@ -79,7 +79,7 @@ public final class OutputCollector { /** * Adds an instruction to the output suffix. * - * @param insn non-null; the instruction to add + * @param insn {@code non-null;} the instruction to add */ public void addSuffix(DalvInsn insn) { suffix.add(insn); @@ -89,7 +89,7 @@ public final class OutputCollector { * Gets the results of all the calls on this instance, in the form of * an {@link OutputFinisher}. * - * @return non-null; the output finisher + * @return {@code non-null;} the output finisher * @throws UnsupportedOperationException if this method has * already been called */ diff --git a/dx/src/com/android/dx/dex/code/OutputFinisher.java b/dx/src/com/android/dx/dex/code/OutputFinisher.java index 73eecf82e..5b1d53313 100644 --- a/dx/src/com/android/dx/dex/code/OutputFinisher.java +++ b/dx/src/com/android/dx/dex/code/OutputFinisher.java @@ -37,12 +37,12 @@ import java.util.HashSet; */ public final class OutputFinisher { /** - * >= 0; register count for the method, not including any extra + * {@code >= 0;} register count for the method, not including any extra * "reserved" registers needed to translate "difficult" instructions */ private final int unreservedRegCount; - /** non-null; the list of instructions, per se */ + /** {@code non-null;} the list of instructions, per se */ private ArrayList insns; /** whether any instruction has position info */ @@ -52,7 +52,7 @@ public final class OutputFinisher { private boolean hasAnyLocalInfo; /** - * >= 0; the count of reserved registers (low-numbered + * {@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} @@ -62,8 +62,8 @@ public final class OutputFinisher { /** * Constructs an instance. It initially contains no instructions. * - * @param regCount >= 0; register count for the method - * @param initialCapacity >= 0; initial capacity of the instructions + * @param regCount {@code >= 0;} register count for the method + * @param initialCapacity {@code >= 0;} initial capacity of the instructions * list */ public OutputFinisher(int initialCapacity, int regCount) { @@ -98,8 +98,8 @@ public final class OutputFinisher { * Helper for {@link #add} which scrutinizes a single * instruction for local variable information. * - * @param insn non-null; instruction to scrutinize - * @return true iff the instruction refers to any + * @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) { @@ -125,8 +125,8 @@ public final class OutputFinisher { * Helper for {@link #hasAnyLocalInfo} which scrutinizes a single * register spec. * - * @param spec non-null; spec to scrutinize - * @return true iff the spec refers to any + * @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) { @@ -138,7 +138,7 @@ public final class OutputFinisher { * Returns the set of all constants referred to by instructions added * to this instance. * - * @return non-null; the set of constants + * @return {@code non-null;} the set of constants */ public HashSet getAllConstants() { HashSet result = new HashSet(20); @@ -154,8 +154,8 @@ public final class OutputFinisher { * Helper for {@link #getAllConstants} which adds all the info for * a single instruction. * - * @param result non-null; result set to add to - * @param insn non-null; instruction to scrutinize + * @param result {@code non-null;} result set to add to + * @param insn {@code non-null;} instruction to scrutinize */ private static void addConstants(HashSet result, DalvInsn insn) { @@ -176,10 +176,10 @@ public final class OutputFinisher { /** * Helper for {@link #getAllConstants} which adds all the info for - * a single RegisterSpec. + * a single {@code RegisterSpec}. * - * @param result non-null; result set to add to - * @param spec null-ok; register spec to add + * @param result {@code non-null;} result set to add to + * @param spec {@code null-ok;} register spec to add */ private static void addConstants(HashSet result, RegisterSpec spec) { @@ -208,7 +208,7 @@ public final class OutputFinisher { /** * Adds an instruction to the output. * - * @param insn non-null; the instruction to add + * @param insn {@code non-null;} the instruction to add */ public void add(DalvInsn insn) { insns.add(insn); @@ -218,8 +218,8 @@ public final class OutputFinisher { /** * Inserts an instruction in the output at the given offset. * - * @param at >= 0; what index to insert at - * @param insn non-null; the instruction to insert + * @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); @@ -230,7 +230,7 @@ public final class OutputFinisher { * Helper for {@link #add} and {@link #insert}, * which updates the position and local info flags. * - * @param insn non-null; an instruction that was just introduced + * @param insn {@code non-null;} an instruction that was just introduced */ private void updateInfo(DalvInsn insn) { if (! hasAnyPositionInfo) { @@ -253,9 +253,9 @@ public final class OutputFinisher { * indicated instruction really is a reversible branch. * * @param which how many instructions back to find the branch; - * 0 is the most recently added instruction, - * 1 is the instruction before that, etc. - * @param newTarget non-null; the new target for the reversed 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(); @@ -284,7 +284,7 @@ public final class OutputFinisher { * given callback to perform lookups. This should be called before * calling {@link #finishProcessingAndGetList}. * - * @param callback non-null; callback object + * @param callback {@code non-null;} callback object */ public void assignIndices(DalvCode.AssignIndicesCallback callback) { for (DalvInsn insn : insns) { @@ -298,8 +298,8 @@ public final class OutputFinisher { * Helper for {@link #assignIndices} which does assignment for one * instruction. * - * @param insn non-null; the instruction - * @param callback non-null; the callback + * @param insn {@code non-null;} the instruction + * @param callback {@code non-null;} the callback */ private static void assignIndices(CstInsn insn, DalvCode.AssignIndicesCallback callback) { @@ -336,7 +336,7 @@ public final class OutputFinisher { *

    Note: This method may only be called once per instance * of this class.

    * - * @return non-null; the output list + * @return {@code non-null;} the output list * @throws UnsupportedOperationException if this method has * already been called */ @@ -359,7 +359,7 @@ public final class OutputFinisher { * the format out of each instruction into a separate array, to be * further manipulated as things progress. * - * @return non-null; the array of formats + * @return {@code non-null;} the array of formats */ private InsnFormat[] makeFormatsArray() { int size = insns.size(); @@ -375,11 +375,11 @@ public final class OutputFinisher { /** * Helper for {@link #finishProcessingAndGetList}, which figures * out how many reserved registers are required and then reserving - * them. It also updates the given formats array so + * them. It also updates the given {@code formats} array so * as to avoid extra work when constructing the massaged * instruction list. * - * @param formats non-null; array of per-instruction format selections + * @param formats {@code non-null;} array of per-instruction format selections */ private void reserveRegisters(InsnFormat[] formats) { int oldReservedCount = (reservedCount < 0) ? 0 : reservedCount; @@ -425,11 +425,11 @@ public final class OutputFinisher { * 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 - * formats list to help avoid extra work in future + * {@code formats} list to help avoid extra work in future * register reservation passes. * - * @param formats non-null; array of per-instruction format selections - * @return >= 0; the count of reserved registers + * @param formats {@code non-null;} array of per-instruction format selections + * @return {@code >= 0;} the count of reserved registers */ private int calculateReservedCount(InsnFormat[] formats) { int size = insns.size(); @@ -470,16 +470,16 @@ public final class OutputFinisher { /** * Attempts to fit the given instruction into a format, returning - * either a format that the instruction fits into or null + * either a format that the instruction fits into or {@code null} * to indicate that the instruction will need to be expanded. This * fitting process starts with the given format as a first "best * guess" and then pessimizes from there if necessary. * - * @param insn non-null; the instruction in question - * @param format null-ok; the current guess as to the best instruction - * format to use; null means that no simple format fits - * @return null-ok; a possibly-different format, which is either a - * good fit or null to indicate that no simple format + * @param insn {@code non-null;} the instruction in question + * @param format {@code null-ok;} the current guess as to the best instruction + * format to use; {@code null} means that no simple format fits + * @return {@code null-ok;} a possibly-different format, which is either a + * good fit or {@code null} to indicate that no simple format * fits */ private InsnFormat findFormatForInsn(DalvInsn insn, InsnFormat format) { @@ -527,7 +527,7 @@ public final class OutputFinisher { * final addresses aren't known at the point that this method is * called.

    * - * @param formats non-null; array of per-instruction format selections + * @param formats {@code non-null;} array of per-instruction format selections */ private void massageInstructions(InsnFormat[] formats) { if (reservedCount == 0) { @@ -566,8 +566,8 @@ public final class OutputFinisher { * problems) is expanded into a series of instances that together * perform the proper function. * - * @param formats non-null; array of per-instruction format selections - * @return non-null; the replacement list + * @param formats {@code non-null;} array of per-instruction format selections + * @return {@code non-null;} the replacement list */ private ArrayList performExpansion(InsnFormat[] formats) { int size = insns.size(); @@ -651,9 +651,9 @@ public final class OutputFinisher { * 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 goto of some sort. In the + * 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 goto + * test is reversed in order to branch around a {@code goto} * to the original target. * * @return whether any branches had to be fixed diff --git a/dx/src/com/android/dx/dex/code/PositionList.java b/dx/src/com/android/dx/dex/code/PositionList.java index d8f76ebd4..41e36678a 100644 --- a/dx/src/com/android/dx/dex/code/PositionList.java +++ b/dx/src/com/android/dx/dex/code/PositionList.java @@ -24,7 +24,7 @@ import com.android.dx.util.FixedSizeList; * method to extract an instance out of a {@link DalvInsnList}. */ public final class PositionList extends FixedSizeList { - /** non-null; empty instance */ + /** {@code non-null;} empty instance */ public static final PositionList EMPTY = new PositionList(0); /** @@ -50,10 +50,10 @@ public final class PositionList extends FixedSizeList { * Extracts and returns the source position information out of an * instruction list. * - * @param insns non-null; instructions to convert + * @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 non-null; the positions list + * @return {@code non-null;} the positions list */ public static PositionList make(DalvInsnList insns, int howMuch) { switch (howMuch) { @@ -112,9 +112,9 @@ public final class PositionList extends FixedSizeList { } /** - * Constructs an instance. All indices initially contain null. + * Constructs an instance. All indices initially contain {@code null}. * - * @param size >= 0; the size of the list + * @param size {@code >= 0;} the size of the list */ public PositionList(int size) { super(size); @@ -123,10 +123,10 @@ public final class PositionList extends FixedSizeList { /** * 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 NullPointerException. + * do that, this will throw {@code NullPointerException}. * - * @param n >= 0, < size(); which index - * @return non-null; element at that index + * @param n {@code >= 0, < size();} which index + * @return {@code non-null;} element at that index */ public Entry get(int n) { return (Entry) get0(n); @@ -135,8 +135,8 @@ public final class PositionList extends FixedSizeList { /** * Sets the entry at the given index. * - * @param n >= 0, < size(); which index - * @param entry non-null; the entry to set at n + * @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); @@ -146,17 +146,17 @@ public final class PositionList extends FixedSizeList { * Entry in a position list. */ public static class Entry { - /** >= 0; address of this entry */ + /** {@code >= 0;} address of this entry */ private final int address; - /** non-null; corresponding source position information */ + /** {@code non-null;} corresponding source position information */ private final SourcePosition position; /** * Constructs an instance. * - * @param address >= 0; address of this entry - * @param position non-null; corresponding source position information + * @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) { @@ -174,7 +174,7 @@ public final class PositionList extends FixedSizeList { /** * Gets the address. * - * @return >= 0; the address + * @return {@code >= 0;} the address */ public int getAddress() { return address; @@ -183,7 +183,7 @@ public final class PositionList extends FixedSizeList { /** * Gets the source position information. * - * @return non-null; the position information + * @return {@code non-null;} the position information */ public SourcePosition getPosition() { return position; diff --git a/dx/src/com/android/dx/dex/code/RopToDop.java b/dx/src/com/android/dx/dex/code/RopToDop.java index 8ad0e64a8..0385467e4 100644 --- a/dx/src/com/android/dx/dex/code/RopToDop.java +++ b/dx/src/com/android/dx/dex/code/RopToDop.java @@ -35,7 +35,7 @@ import java.util.HashMap; * {@link Dop} instances. */ public final class RopToDop { - /** non-null; map from all the common rops to dalvik opcodes */ + /** {@code non-null;} map from all the common rops to dalvik opcodes */ private static final HashMap MAP; /** @@ -278,7 +278,7 @@ public final class RopToDop { * Returns the dalvik opcode appropriate for the given register-based * instruction. * - * @param insn non-null; the original instruction + * @param insn {@code non-null;} the original instruction * @return the corresponding dalvik opcode; one of the constants in * {@link Dops} */ diff --git a/dx/src/com/android/dx/dex/code/RopTranslator.java b/dx/src/com/android/dx/dex/code/RopTranslator.java index f3dfe0b19..9f47b13be 100644 --- a/dx/src/com/android/dx/dex/code/RopTranslator.java +++ b/dx/src/com/android/dx/dex/code/RopTranslator.java @@ -46,7 +46,7 @@ import java.util.ArrayList; * #translate} method is the thing to call on this class. */ public final class RopTranslator { - /** non-null; method to translate */ + /** {@code non-null;} method to translate */ private final RopMethod method; /** @@ -55,22 +55,22 @@ public final class RopTranslator { */ private final int positionInfo; - /** null-ok; local variable info to use */ + /** {@code null-ok;} local variable info to use */ private final LocalVariableInfo locals; - /** non-null; container for all the address objects for the method */ + /** {@code non-null;} container for all the address objects for the method */ private final BlockAddresses addresses; - /** non-null; list of output instructions in-progress */ + /** {@code non-null;} list of output instructions in-progress */ private final OutputCollector output; - /** non-null; visitor to use during translation */ + /** {@code non-null;} visitor to use during translation */ private final TranslationVisitor translationVisitor; - /** >= 0; register count for the method */ + /** {@code >= 0;} register count for the method */ private final int regCount; - /** null-ok; block output order; becomes non-null in {@link #pickOrder} */ + /** {@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 */ @@ -86,13 +86,13 @@ public final class RopTranslator { * Translates a {@link RopMethod}. This may modify the given * input. * - * @param method non-null; the original method + * @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 null-ok; local variable information to use + * @param locals {@code null-ok;} local variable information to use * @param paramSize size, in register units, of all the parameters to * this method - * @return non-null; the translated version + * @return {@code non-null;} the translated version */ public static DalvCode translate(RopMethod method, int positionInfo, LocalVariableInfo locals, int paramSize) { @@ -105,10 +105,10 @@ public final class RopTranslator { /** * Constructs an instance. This method is private. Use {@link #translate}. * - * @param method non-null; the original method + * @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 null-ok; local variable information to use + * @param locals {@code null-ok;} local variable information to use * @param paramSize size, in register units, of all the parameters to * this method */ @@ -177,7 +177,7 @@ public final class RopTranslator { /* * We almost could just check the first block here, but the - * cf layer will put in a second move-param in a + * {@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() { @@ -199,7 +199,7 @@ public final class RopTranslator { /** * Does the translation and returns the result. * - * @return non-null; the result + * @return {@code non-null;} the result */ private DalvCode translateAndGetResult() { pickOrder(); @@ -232,9 +232,9 @@ public final class RopTranslator { * Helper for {@link #outputInstructions}, which does the processing * and output of one block. * - * @param block non-null; the block to process and output - * @param nextLabel >= -1; the next block that will be processed, or - * -1 if there is no next 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. @@ -440,8 +440,8 @@ public final class RopTranslator { * two register sources, and have a source equal to the result, * place that source first. * - * @param insn non-null; instruction in question - * @return non-null; the instruction's complete register list + * @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()); @@ -453,9 +453,9 @@ public final class RopTranslator { * two register sources, and have a source equal to the result, * place that source first. * - * @param insn non-null; instruction in question - * @param resultReg null-ok; the real result to use (ignore the insn's) - * @return non-null; the instruction's complete register list + * @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) { @@ -486,14 +486,14 @@ public final class RopTranslator { * Instruction visitor class for doing the instruction translation per se. */ private class TranslationVisitor implements Insn.Visitor { - /** non-null; list of output instructions in-progress */ + /** {@code non-null;} list of output instructions in-progress */ private final OutputCollector output; - /** non-null; basic block being worked on */ + /** {@code non-null;} basic block being worked on */ private BasicBlock block; /** - * null-ok; code address for the salient last instruction of the + * {@code null-ok;} code address for the salient last instruction of the * block (used before switches and throwing instructions) */ private CodeAddress lastAddress; @@ -501,7 +501,7 @@ public final class RopTranslator { /** * Constructs an instance. * - * @param output non-null; destination for instruction output + * @param output {@code non-null;} destination for instruction output */ public TranslationVisitor(OutputCollector output) { this.output = output; @@ -510,8 +510,8 @@ public final class RopTranslator { /** * Sets the block currently being worked on. * - * @param block non-null; the block - * @param lastAddress non-null; code address for the salient + * @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) { @@ -654,7 +654,7 @@ public final class RopTranslator { * the RegisterSpec of the result of the move-result-pseudo at the * top of that block or null if none. * - * @return null-ok; result of move-result-pseudo at the beginning of + * @return {@code null-ok;} result of move-result-pseudo at the beginning of * primary successor */ private RegisterSpec getNextMoveResultPseudo() @@ -783,7 +783,7 @@ public final class RopTranslator { /** * Adds to the output. * - * @param insn non-null; instruction to add + * @param insn {@code non-null;} instruction to add */ protected void addOutput(DalvInsn insn) { output.add(insn); @@ -792,7 +792,7 @@ public final class RopTranslator { /** * Adds to the output suffix. * - * @param insn non-null; instruction to add + * @param insn {@code non-null;} instruction to add */ protected void addOutputSuffix(DalvInsn insn) { output.addSuffix(insn); @@ -805,14 +805,14 @@ public final class RopTranslator { */ private class LocalVariableAwareTranslationVisitor extends TranslationVisitor { - /** non-null; local variable info */ + /** {@code non-null;} local variable info */ private LocalVariableInfo locals; /** * Constructs an instance. * - * @param output non-null; destination for instruction output - * @param locals non-null; the local variable info + * @param output {@code non-null;} destination for instruction output + * @param locals {@code non-null;} the local variable info */ public LocalVariableAwareTranslationVisitor(OutputCollector output, LocalVariableInfo locals) { @@ -859,7 +859,7 @@ public final class RopTranslator { * Adds a {@link LocalStart} to the output if the given * instruction in fact introduces a local variable. * - * @param insn non-null; instruction in question + * @param insn {@code non-null;} instruction in question */ public void addIntroductionIfNecessary(Insn insn) { RegisterSpec spec = locals.getAssignment(insn); diff --git a/dx/src/com/android/dx/dex/code/SimpleInsn.java b/dx/src/com/android/dx/dex/code/SimpleInsn.java index ba204090b..5e7b2591a 100644 --- a/dx/src/com/android/dx/dex/code/SimpleInsn.java +++ b/dx/src/com/android/dx/dex/code/SimpleInsn.java @@ -26,11 +26,11 @@ import com.android.dx.rop.code.SourcePosition; public final class SimpleInsn extends FixedSizeInsn { /** * Constructs an instance. The output address of this instance is initially - * unknown (-1). + * unknown ({@code -1}). * * @param opcode the opcode; one of the constants from {@link Dops} - * @param position non-null; source position - * @param registers non-null; register list, including a + * @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) */ diff --git a/dx/src/com/android/dx/dex/code/StdCatchBuilder.java b/dx/src/com/android/dx/dex/code/StdCatchBuilder.java index 1240f3f7d..6e3a169b4 100644 --- a/dx/src/com/android/dx/dex/code/StdCatchBuilder.java +++ b/dx/src/com/android/dx/dex/code/StdCatchBuilder.java @@ -35,22 +35,22 @@ 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; - /** non-null; method to build the list for */ + /** {@code non-null;} method to build the list for */ private final RopMethod method; - /** non-null; block output order */ + /** {@code non-null;} block output order */ private final int[] order; - /** non-null; address objects for each block */ + /** {@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 non-null; method to build the list for - * @param order non-null; block output order - * @param addresses non-null; address objects for each block + * @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) { @@ -78,7 +78,6 @@ public final class StdCatchBuilder implements CatchBuilder { /** {@inheritDoc} */ public boolean hasAnyCatches() { - HashSet result = new HashSet(20); BasicBlockList blocks = method.getBlocks(); int size = blocks.size(); @@ -115,10 +114,10 @@ public final class StdCatchBuilder implements CatchBuilder { /** * Builds and returns the catch table for a given method. * - * @param method non-null; method to build the list for - * @param order non-null; block output order - * @param addresses non-null; address objects for each block - * @return non-null; the constructed table + * @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) { @@ -210,9 +209,9 @@ public final class StdCatchBuilder implements CatchBuilder { /** * Makes the {@link CatchHandlerList} for the given basic block. * - * @param block non-null; block to get entries for - * @param addresses non-null; address objects for each block - * @return non-null; array of entries + * @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) { @@ -267,10 +266,10 @@ public final class StdCatchBuilder implements CatchBuilder { * Makes a {@link CatchTable#Entry} for the given block range and * handlers. * - * @param start non-null; the start block for the range (inclusive) - * @param end non-null; the start block for the range (also inclusive) - * @param handlers non-null; the handlers for the range - * @param addresses non-null; address objects for each block + * @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, @@ -293,10 +292,10 @@ public final class StdCatchBuilder implements CatchBuilder { * for a catch handler. This is true as long as the covered range is * under 65536 code units. * - * @param start non-null; the start block for the range (inclusive) - * @param end non-null; the start block for the range (also inclusive) - * @param addresses non-null; address objects for each block - * @return true if the range is valid as a catch range + * @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) { diff --git a/dx/src/com/android/dx/dex/code/SwitchData.java b/dx/src/com/android/dx/dex/code/SwitchData.java index 1aaafa951..e5a8da4ac 100644 --- a/dx/src/com/android/dx/dex/code/SwitchData.java +++ b/dx/src/com/android/dx/dex/code/SwitchData.java @@ -29,16 +29,16 @@ import com.android.dx.util.IntList; */ public final class SwitchData extends VariableSizeInsn { /** - * non-null; address representing the instruction that uses this + * {@code non-null;} address representing the instruction that uses this * instance */ private final CodeAddress user; - /** non-null; sorted list of switch cases (keys) */ + /** {@code non-null;} sorted list of switch cases (keys) */ private final IntList cases; /** - * non-null; corresponding list of code addresses; the branch + * {@code non-null;} corresponding list of code addresses; the branch * target for each case */ private final CodeAddress[] targets; @@ -48,13 +48,13 @@ public final class SwitchData extends VariableSizeInsn { /** * Constructs an instance. The output address of this instance is initially - * unknown (-1). + * unknown ({@code -1}). * - * @param position non-null; source position - * @param user non-null; address representing the instruction that + * @param position {@code non-null;} source position + * @param user {@code non-null;} address representing the instruction that * uses this instance - * @param cases non-null; sorted list of switch cases (keys) - * @param targets non-null; corresponding list of code addresses; the + * @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, @@ -151,7 +151,7 @@ public final class SwitchData extends VariableSizeInsn { /** * Returns whether or not this instance's data will be output as packed. * - * @return true iff the data is to be packed + * @return {@code true} iff the data is to be packed */ public boolean isPacked() { return packed; @@ -202,8 +202,8 @@ public final class SwitchData extends VariableSizeInsn { * Gets the size of a packed table for the given cases, in 16-bit code * units. * - * @param cases non-null; sorted list of cases - * @return >= -1; the packed table size or -1 if the + * @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) { @@ -219,8 +219,8 @@ public final class SwitchData extends VariableSizeInsn { * Gets the size of a sparse table for the given cases, in 16-bit code * units. * - * @param cases non-null; sorted list of cases - * @return > 0; the sparse table size + * @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(); @@ -231,8 +231,8 @@ public final class SwitchData extends VariableSizeInsn { /** * Determines whether the given list of cases warrant being packed. * - * @param cases non-null; sorted list of cases - * @return true iff the table encoding the cases + * @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) { diff --git a/dx/src/com/android/dx/dex/code/TargetInsn.java b/dx/src/com/android/dx/dex/code/TargetInsn.java index 5620795d3..0faaadada 100644 --- a/dx/src/com/android/dx/dex/code/TargetInsn.java +++ b/dx/src/com/android/dx/dex/code/TargetInsn.java @@ -23,20 +23,20 @@ import com.android.dx.rop.code.SourcePosition; * Instruction which has a single branch target. */ public final class TargetInsn extends FixedSizeInsn { - /** non-null; the branch target */ + /** {@code non-null;} the branch target */ private CodeAddress target; /** * Constructs an instance. The output address of this instance is initially - * unknown (-1), and the target is initially - * null. + * unknown ({@code -1}), and the target is initially + * {@code null}. * * @param opcode the opcode; one of the constants from {@link Dops} - * @param position non-null; source position - * @param registers non-null; register list, including a + * @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 non-null; the branch target + * @param target {@code non-null;} the branch target */ public TargetInsn(Dop opcode, SourcePosition position, RegisterSpecList registers, CodeAddress target) { @@ -64,12 +64,12 @@ public final class TargetInsn extends FixedSizeInsn { /** * Returns an instance that is just like this one, except that its * opcode has the opposite sense (as a test; e.g. a - * lt test becomes a ge), and its branch + * {@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 non-null; the new branch target - * @return non-null; an appropriately-constructed instance + * @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(); @@ -80,7 +80,7 @@ public final class TargetInsn extends FixedSizeInsn { /** * Gets the unique branch target of this instruction. * - * @return non-null; the branch target + * @return {@code non-null;} the branch target */ public CodeAddress getTarget() { return target; @@ -90,9 +90,9 @@ public final class TargetInsn extends FixedSizeInsn { * 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 - * getTarget().getAddress(). + * {@code getTarget().getAddress()}. * - * @return >= 0; the target address + * @return {@code >= 0;} the target address */ public int getTargetAddress() { return target.getAddress(); @@ -102,7 +102,7 @@ public final class TargetInsn extends FixedSizeInsn { * 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 - * getTargetAddress() - getAddress(). + * {@code getTargetAddress() - getAddress()}. * * @return the branch offset */ @@ -113,8 +113,8 @@ public final class TargetInsn extends FixedSizeInsn { /** * Returns whether the target offset is known. * - * @return true if the target offset is known or - * false if not + * @return {@code true} if the target offset is known or + * {@code false} if not */ public boolean hasTargetOffset() { return hasAddress() && target.hasAddress(); diff --git a/dx/src/com/android/dx/dex/code/VariableSizeInsn.java b/dx/src/com/android/dx/dex/code/VariableSizeInsn.java index 788424901..889a50ced 100644 --- a/dx/src/com/android/dx/dex/code/VariableSizeInsn.java +++ b/dx/src/com/android/dx/dex/code/VariableSizeInsn.java @@ -25,10 +25,10 @@ import com.android.dx.rop.code.SourcePosition; public abstract class VariableSizeInsn extends DalvInsn { /** * Constructs an instance. The output address of this instance is initially - * unknown (-1). + * unknown ({@code -1}). * - * @param position non-null; source position - * @param registers non-null; source registers + * @param position {@code non-null;} source position + * @param registers {@code non-null;} source registers */ public VariableSizeInsn(SourcePosition position, RegisterSpecList registers) { diff --git a/dx/src/com/android/dx/dex/code/ZeroSizeInsn.java b/dx/src/com/android/dx/dex/code/ZeroSizeInsn.java index 2ddb181e0..198bebf1e 100644 --- a/dx/src/com/android/dx/dex/code/ZeroSizeInsn.java +++ b/dx/src/com/android/dx/dex/code/ZeroSizeInsn.java @@ -28,9 +28,9 @@ import com.android.dx.util.AnnotatedOutput; public abstract class ZeroSizeInsn extends DalvInsn { /** * Constructs an instance. The output address of this instance is initially - * unknown (-1). + * unknown ({@code -1}). * - * @param position non-null; source position + * @param position {@code non-null;} source position */ public ZeroSizeInsn(SourcePosition position) { super(Dops.SPECIAL_FORMAT, position, RegisterSpecList.EMPTY); diff --git a/dx/src/com/android/dx/dex/code/form/Form10t.java b/dx/src/com/android/dx/dex/code/form/Form10t.java index 8551012de..82b731d6f 100644 --- a/dx/src/com/android/dx/dex/code/form/Form10t.java +++ b/dx/src/com/android/dx/dex/code/form/Form10t.java @@ -22,11 +22,11 @@ import com.android.dx.dex.code.TargetInsn; import com.android.dx.util.AnnotatedOutput; /** - * Instruction format 10t. See the instruction format spec + * Instruction format {@code 10t}. See the instruction format spec * for details. */ public final class Form10t extends InsnFormat { - /** non-null; unique instance of this class */ + /** {@code non-null;} unique instance of this class */ public static final InsnFormat THE_ONE = new Form10t(); /** diff --git a/dx/src/com/android/dx/dex/code/form/Form10x.java b/dx/src/com/android/dx/dex/code/form/Form10x.java index 7dc7c43b8..c7a22a618 100644 --- a/dx/src/com/android/dx/dex/code/form/Form10x.java +++ b/dx/src/com/android/dx/dex/code/form/Form10x.java @@ -22,11 +22,11 @@ import com.android.dx.dex.code.SimpleInsn; import com.android.dx.util.AnnotatedOutput; /** - * Instruction format 10x. See the instruction format spec + * Instruction format {@code 10x}. See the instruction format spec * for details. */ public final class Form10x extends InsnFormat { - /** non-null; unique instance of this class */ + /** {@code non-null;} unique instance of this class */ public static final InsnFormat THE_ONE = new Form10x(); /** diff --git a/dx/src/com/android/dx/dex/code/form/Form11n.java b/dx/src/com/android/dx/dex/code/form/Form11n.java index b94038bb2..d63fc6fad 100644 --- a/dx/src/com/android/dx/dex/code/form/Form11n.java +++ b/dx/src/com/android/dx/dex/code/form/Form11n.java @@ -25,11 +25,11 @@ import com.android.dx.rop.cst.CstLiteralBits; import com.android.dx.util.AnnotatedOutput; /** - * Instruction format 11n. See the instruction format spec + * Instruction format {@code 11n}. See the instruction format spec * for details. */ public final class Form11n extends InsnFormat { - /** non-null; unique instance of this class */ + /** {@code non-null;} unique instance of this class */ public static final InsnFormat THE_ONE = new Form11n(); /** diff --git a/dx/src/com/android/dx/dex/code/form/Form11x.java b/dx/src/com/android/dx/dex/code/form/Form11x.java index d6561472d..b4acc1a35 100644 --- a/dx/src/com/android/dx/dex/code/form/Form11x.java +++ b/dx/src/com/android/dx/dex/code/form/Form11x.java @@ -23,11 +23,11 @@ import com.android.dx.rop.code.RegisterSpecList; import com.android.dx.util.AnnotatedOutput; /** - * Instruction format 11x. See the instruction format spec + * Instruction format {@code 11x}. See the instruction format spec * for details. */ public final class Form11x extends InsnFormat { - /** non-null; unique instance of this class */ + /** {@code non-null;} unique instance of this class */ public static final InsnFormat THE_ONE = new Form11x(); /** diff --git a/dx/src/com/android/dx/dex/code/form/Form12x.java b/dx/src/com/android/dx/dex/code/form/Form12x.java index 3ed8ce99c..c7754be69 100644 --- a/dx/src/com/android/dx/dex/code/form/Form12x.java +++ b/dx/src/com/android/dx/dex/code/form/Form12x.java @@ -25,11 +25,11 @@ import com.android.dx.rop.code.RegisterSpecList; import com.android.dx.util.AnnotatedOutput; /** - * Instruction format 12x. See the instruction format spec + * Instruction format {@code 12x}. See the instruction format spec * for details. */ public final class Form12x extends InsnFormat { - /** non-null; unique instance of this class */ + /** {@code non-null;} unique instance of this class */ public static final InsnFormat THE_ONE = new Form12x(); /** diff --git a/dx/src/com/android/dx/dex/code/form/Form20t.java b/dx/src/com/android/dx/dex/code/form/Form20t.java index 341bef3a0..0b5a3b26b 100644 --- a/dx/src/com/android/dx/dex/code/form/Form20t.java +++ b/dx/src/com/android/dx/dex/code/form/Form20t.java @@ -22,11 +22,11 @@ import com.android.dx.dex.code.TargetInsn; import com.android.dx.util.AnnotatedOutput; /** - * Instruction format 20t. See the instruction format spec + * Instruction format {@code 20t}. See the instruction format spec * for details. */ public final class Form20t extends InsnFormat { - /** non-null; unique instance of this class */ + /** {@code non-null;} unique instance of this class */ public static final InsnFormat THE_ONE = new Form20t(); /** diff --git a/dx/src/com/android/dx/dex/code/form/Form21c.java b/dx/src/com/android/dx/dex/code/form/Form21c.java index 5695e7a1f..ed1ec3c40 100644 --- a/dx/src/com/android/dx/dex/code/form/Form21c.java +++ b/dx/src/com/android/dx/dex/code/form/Form21c.java @@ -28,11 +28,11 @@ import com.android.dx.rop.cst.CstType; import com.android.dx.util.AnnotatedOutput; /** - * Instruction format 21c. See the instruction format spec + * Instruction format {@code 21c}. See the instruction format spec * for details. */ public final class Form21c extends InsnFormat { - /** non-null; unique instance of this class */ + /** {@code non-null;} unique instance of this class */ public static final InsnFormat THE_ONE = new Form21c(); /** diff --git a/dx/src/com/android/dx/dex/code/form/Form21h.java b/dx/src/com/android/dx/dex/code/form/Form21h.java index cf4b4714f..e0bd7515a 100644 --- a/dx/src/com/android/dx/dex/code/form/Form21h.java +++ b/dx/src/com/android/dx/dex/code/form/Form21h.java @@ -25,11 +25,11 @@ import com.android.dx.rop.cst.CstLiteralBits; import com.android.dx.util.AnnotatedOutput; /** - * Instruction format 21h. See the instruction format spec + * Instruction format {@code 21h}. See the instruction format spec * for details. */ public final class Form21h extends InsnFormat { - /** non-null; unique instance of this class */ + /** {@code non-null;} unique instance of this class */ public static final InsnFormat THE_ONE = new Form21h(); /** diff --git a/dx/src/com/android/dx/dex/code/form/Form21s.java b/dx/src/com/android/dx/dex/code/form/Form21s.java index df10f80a7..a03ee4349 100644 --- a/dx/src/com/android/dx/dex/code/form/Form21s.java +++ b/dx/src/com/android/dx/dex/code/form/Form21s.java @@ -25,11 +25,11 @@ import com.android.dx.rop.cst.CstLiteralBits; import com.android.dx.util.AnnotatedOutput; /** - * Instruction format 21s. See the instruction format spec + * Instruction format {@code 21s}. See the instruction format spec * for details. */ public final class Form21s extends InsnFormat { - /** non-null; unique instance of this class */ + /** {@code non-null;} unique instance of this class */ public static final InsnFormat THE_ONE = new Form21s(); /** diff --git a/dx/src/com/android/dx/dex/code/form/Form21t.java b/dx/src/com/android/dx/dex/code/form/Form21t.java index 03f2ddfbd..f0ce64465 100644 --- a/dx/src/com/android/dx/dex/code/form/Form21t.java +++ b/dx/src/com/android/dx/dex/code/form/Form21t.java @@ -23,11 +23,11 @@ import com.android.dx.rop.code.RegisterSpecList; import com.android.dx.util.AnnotatedOutput; /** - * Instruction format 21t. See the instruction format spec + * Instruction format {@code 21t}. See the instruction format spec * for details. */ public final class Form21t extends InsnFormat { - /** non-null; unique instance of this class */ + /** {@code non-null;} unique instance of this class */ public static final InsnFormat THE_ONE = new Form21t(); /** diff --git a/dx/src/com/android/dx/dex/code/form/Form22b.java b/dx/src/com/android/dx/dex/code/form/Form22b.java index e2a777fcc..2884fbb76 100644 --- a/dx/src/com/android/dx/dex/code/form/Form22b.java +++ b/dx/src/com/android/dx/dex/code/form/Form22b.java @@ -25,11 +25,11 @@ import com.android.dx.rop.cst.CstLiteralBits; import com.android.dx.util.AnnotatedOutput; /** - * Instruction format 22b. See the instruction format spec + * Instruction format {@code 22b}. See the instruction format spec * for details. */ public final class Form22b extends InsnFormat { - /** non-null; unique instance of this class */ + /** {@code non-null;} unique instance of this class */ public static final InsnFormat THE_ONE = new Form22b(); /** diff --git a/dx/src/com/android/dx/dex/code/form/Form22c.java b/dx/src/com/android/dx/dex/code/form/Form22c.java index 547eea858..e77677fd5 100644 --- a/dx/src/com/android/dx/dex/code/form/Form22c.java +++ b/dx/src/com/android/dx/dex/code/form/Form22c.java @@ -27,11 +27,11 @@ import com.android.dx.rop.cst.CstType; import com.android.dx.util.AnnotatedOutput; /** - * Instruction format 22c. See the instruction format spec + * Instruction format {@code 22c}. See the instruction format spec * for details. */ public final class Form22c extends InsnFormat { - /** non-null; unique instance of this class */ + /** {@code non-null;} unique instance of this class */ public static final InsnFormat THE_ONE = new Form22c(); /** diff --git a/dx/src/com/android/dx/dex/code/form/Form22s.java b/dx/src/com/android/dx/dex/code/form/Form22s.java index 3ed173f22..596421728 100644 --- a/dx/src/com/android/dx/dex/code/form/Form22s.java +++ b/dx/src/com/android/dx/dex/code/form/Form22s.java @@ -25,11 +25,11 @@ import com.android.dx.rop.cst.CstLiteralBits; import com.android.dx.util.AnnotatedOutput; /** - * Instruction format 22s. See the instruction format spec + * Instruction format {@code 22s}. See the instruction format spec * for details. */ public final class Form22s extends InsnFormat { - /** non-null; unique instance of this class */ + /** {@code non-null;} unique instance of this class */ public static final InsnFormat THE_ONE = new Form22s(); /** diff --git a/dx/src/com/android/dx/dex/code/form/Form22t.java b/dx/src/com/android/dx/dex/code/form/Form22t.java index 1034b925c..1577803d2 100644 --- a/dx/src/com/android/dx/dex/code/form/Form22t.java +++ b/dx/src/com/android/dx/dex/code/form/Form22t.java @@ -23,11 +23,11 @@ import com.android.dx.rop.code.RegisterSpecList; import com.android.dx.util.AnnotatedOutput; /** - * Instruction format 22t. See the instruction format spec + * Instruction format {@code 22t}. See the instruction format spec * for details. */ public final class Form22t extends InsnFormat { - /** non-null; unique instance of this class */ + /** {@code non-null;} unique instance of this class */ public static final InsnFormat THE_ONE = new Form22t(); /** diff --git a/dx/src/com/android/dx/dex/code/form/Form22x.java b/dx/src/com/android/dx/dex/code/form/Form22x.java index ee91e85a2..b7ce4e746 100644 --- a/dx/src/com/android/dx/dex/code/form/Form22x.java +++ b/dx/src/com/android/dx/dex/code/form/Form22x.java @@ -23,11 +23,11 @@ import com.android.dx.rop.code.RegisterSpecList; import com.android.dx.util.AnnotatedOutput; /** - * Instruction format 22x. See the instruction format spec + * Instruction format {@code 22x}. See the instruction format spec * for details. */ public final class Form22x extends InsnFormat { - /** non-null; unique instance of this class */ + /** {@code non-null;} unique instance of this class */ public static final InsnFormat THE_ONE = new Form22x(); /** diff --git a/dx/src/com/android/dx/dex/code/form/Form23x.java b/dx/src/com/android/dx/dex/code/form/Form23x.java index c0a4482e8..64dd6b051 100644 --- a/dx/src/com/android/dx/dex/code/form/Form23x.java +++ b/dx/src/com/android/dx/dex/code/form/Form23x.java @@ -23,11 +23,11 @@ import com.android.dx.rop.code.RegisterSpecList; import com.android.dx.util.AnnotatedOutput; /** - * Instruction format 23x. See the instruction format spec + * Instruction format {@code 23x}. See the instruction format spec * for details. */ public final class Form23x extends InsnFormat { - /** non-null; unique instance of this class */ + /** {@code non-null;} unique instance of this class */ public static final InsnFormat THE_ONE = new Form23x(); /** diff --git a/dx/src/com/android/dx/dex/code/form/Form30t.java b/dx/src/com/android/dx/dex/code/form/Form30t.java index 32e2efa2a..af0a699d3 100644 --- a/dx/src/com/android/dx/dex/code/form/Form30t.java +++ b/dx/src/com/android/dx/dex/code/form/Form30t.java @@ -22,11 +22,11 @@ import com.android.dx.dex.code.TargetInsn; import com.android.dx.util.AnnotatedOutput; /** - * Instruction format 30t. See the instruction format spec + * Instruction format {@code 30t}. See the instruction format spec * for details. */ public final class Form30t extends InsnFormat { - /** non-null; unique instance of this class */ + /** {@code non-null;} unique instance of this class */ public static final InsnFormat THE_ONE = new Form30t(); /** diff --git a/dx/src/com/android/dx/dex/code/form/Form31c.java b/dx/src/com/android/dx/dex/code/form/Form31c.java index 5837009e5..0c983c570 100644 --- a/dx/src/com/android/dx/dex/code/form/Form31c.java +++ b/dx/src/com/android/dx/dex/code/form/Form31c.java @@ -28,11 +28,11 @@ import com.android.dx.rop.cst.CstType; import com.android.dx.util.AnnotatedOutput; /** - * Instruction format 31c. See the instruction format spec + * Instruction format {@code 31c}. See the instruction format spec * for details. */ public final class Form31c extends InsnFormat { - /** non-null; unique instance of this class */ + /** {@code non-null;} unique instance of this class */ public static final InsnFormat THE_ONE = new Form31c(); /** diff --git a/dx/src/com/android/dx/dex/code/form/Form31i.java b/dx/src/com/android/dx/dex/code/form/Form31i.java index 2855bcb38..c893a12fc 100644 --- a/dx/src/com/android/dx/dex/code/form/Form31i.java +++ b/dx/src/com/android/dx/dex/code/form/Form31i.java @@ -25,11 +25,11 @@ import com.android.dx.rop.cst.CstLiteralBits; import com.android.dx.util.AnnotatedOutput; /** - * Instruction format 31i. See the instruction format spec + * Instruction format {@code 31i}. See the instruction format spec * for details. */ public final class Form31i extends InsnFormat { - /** non-null; unique instance of this class */ + /** {@code non-null;} unique instance of this class */ public static final InsnFormat THE_ONE = new Form31i(); /** diff --git a/dx/src/com/android/dx/dex/code/form/Form31t.java b/dx/src/com/android/dx/dex/code/form/Form31t.java index 5472687be..682408c6d 100644 --- a/dx/src/com/android/dx/dex/code/form/Form31t.java +++ b/dx/src/com/android/dx/dex/code/form/Form31t.java @@ -23,11 +23,11 @@ import com.android.dx.rop.code.RegisterSpecList; import com.android.dx.util.AnnotatedOutput; /** - * Instruction format 31t. See the instruction format spec + * Instruction format {@code 31t}. See the instruction format spec * for details. */ public final class Form31t extends InsnFormat { - /** non-null; unique instance of this class */ + /** {@code non-null;} unique instance of this class */ public static final InsnFormat THE_ONE = new Form31t(); /** diff --git a/dx/src/com/android/dx/dex/code/form/Form32x.java b/dx/src/com/android/dx/dex/code/form/Form32x.java index 9c52d9334..4a981eebe 100644 --- a/dx/src/com/android/dx/dex/code/form/Form32x.java +++ b/dx/src/com/android/dx/dex/code/form/Form32x.java @@ -23,11 +23,11 @@ import com.android.dx.rop.code.RegisterSpecList; import com.android.dx.util.AnnotatedOutput; /** - * Instruction format 32x. See the instruction format spec + * Instruction format {@code 32x}. See the instruction format spec * for details. */ public final class Form32x extends InsnFormat { - /** non-null; unique instance of this class */ + /** {@code non-null;} unique instance of this class */ public static final InsnFormat THE_ONE = new Form32x(); /** diff --git a/dx/src/com/android/dx/dex/code/form/Form35c.java b/dx/src/com/android/dx/dex/code/form/Form35c.java index 6be55fc5c..411e3c322 100644 --- a/dx/src/com/android/dx/dex/code/form/Form35c.java +++ b/dx/src/com/android/dx/dex/code/form/Form35c.java @@ -28,11 +28,11 @@ import com.android.dx.rop.type.Type; import com.android.dx.util.AnnotatedOutput; /** - * Instruction format 35c. See the instruction format spec + * Instruction format {@code 35c}. See the instruction format spec * for details. */ public final class Form35c extends InsnFormat { - /** non-null; unique instance of this class */ + /** {@code non-null;} unique instance of this class */ public static final InsnFormat THE_ONE = new Form35c(); /** Maximal number of operands */ @@ -120,12 +120,12 @@ public final class Form35c extends InsnFormat { /** * Gets the number of words required for the given register list, where - * category-2 values count as two words. Return -1 if the + * 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 non-null; the register list in question - * @return >= -1; the number of words required, or -1 + * @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) { @@ -161,8 +161,8 @@ public final class Form35c extends InsnFormat { * entries. This returns the original list if no modification is * required * - * @param orig non-null; the original list - * @return non-null; the list with the described transformation + * @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); diff --git a/dx/src/com/android/dx/dex/code/form/Form3rc.java b/dx/src/com/android/dx/dex/code/form/Form3rc.java index 0accbc226..2d185cf00 100644 --- a/dx/src/com/android/dx/dex/code/form/Form3rc.java +++ b/dx/src/com/android/dx/dex/code/form/Form3rc.java @@ -27,11 +27,11 @@ import com.android.dx.rop.cst.CstType; import com.android.dx.util.AnnotatedOutput; /** - * Instruction format 3rc. See the instruction format spec + * Instruction format {@code 3rc}. See the instruction format spec * for details. */ public final class Form3rc extends InsnFormat { - /** non-null; unique instance of this class */ + /** {@code non-null;} unique instance of this class */ public static final InsnFormat THE_ONE = new Form3rc(); /** diff --git a/dx/src/com/android/dx/dex/code/form/Form51l.java b/dx/src/com/android/dx/dex/code/form/Form51l.java index 09a32f619..9e3ab6aae 100644 --- a/dx/src/com/android/dx/dex/code/form/Form51l.java +++ b/dx/src/com/android/dx/dex/code/form/Form51l.java @@ -26,11 +26,11 @@ import com.android.dx.rop.cst.CstLiteralBits; import com.android.dx.util.AnnotatedOutput; /** - * Instruction format 51l. See the instruction format spec + * Instruction format {@code 51l}. See the instruction format spec * for details. */ public final class Form51l extends InsnFormat { - /** non-null; unique instance of this class */ + /** {@code non-null;} unique instance of this class */ public static final InsnFormat THE_ONE = new Form51l(); /** diff --git a/dx/src/com/android/dx/dex/code/form/SpecialFormat.java b/dx/src/com/android/dx/dex/code/form/SpecialFormat.java index 79efd670b..8a2e5edf6 100644 --- a/dx/src/com/android/dx/dex/code/form/SpecialFormat.java +++ b/dx/src/com/android/dx/dex/code/form/SpecialFormat.java @@ -27,10 +27,10 @@ import com.android.dx.util.AnnotatedOutput; * lists. Most of the overridden methods on this class end up throwing * exceptions, as code should know (implicitly or explicitly) to avoid * using this class. The one exception is {@link #isCompatible}, which - * always returns true. + * always returns {@code true}. */ public final class SpecialFormat extends InsnFormat { - /** non-null; unique instance of this class */ + /** {@code non-null;} unique instance of this class */ public static final InsnFormat THE_ONE = new SpecialFormat(); /** diff --git a/dx/src/com/android/dx/dex/file/AnnotationItem.java b/dx/src/com/android/dx/dex/file/AnnotationItem.java index 43ac36266..08422bc6a 100644 --- a/dx/src/com/android/dx/dex/file/AnnotationItem.java +++ b/dx/src/com/android/dx/dex/file/AnnotationItem.java @@ -46,20 +46,20 @@ public final class AnnotationItem extends OffsettedItem { /** the required alignment for instances of this class */ private static final int ALIGNMENT = 1; - /** non-null; unique instance of {@link #TypeIdSorter} */ + /** {@code non-null;} unique instance of {@link #TypeIdSorter} */ private static final TypeIdSorter TYPE_ID_SORTER = new TypeIdSorter(); - /** non-null; the annotation to represent */ + /** {@code non-null;} the annotation to represent */ private final Annotation annotation; /** - * null-ok; type reference for the annotation type; set during + * {@code null-ok;} type reference for the annotation type; set during * {@link #addContents} */ private TypeIdItem type; /** - * null-ok; encoded form, ready for writing to a file; set during + * {@code null-ok;} encoded form, ready for writing to a file; set during * {@link #place0} */ private byte[] encodedForm; @@ -88,7 +88,7 @@ public final class AnnotationItem extends OffsettedItem { * ignoring all other aspects of the elements. This is only valid * to use after type id indices are known. * - * @param array non-null; array to sort + * @param array {@code non-null;} array to sort */ public static void sortByTypeIdIndex(AnnotationItem[] array) { Arrays.sort(array, TYPE_ID_SORTER); @@ -97,7 +97,7 @@ public final class AnnotationItem extends OffsettedItem { /** * Constructs an instance. * - * @param annotation non-null; annotation to represent + * @param annotation {@code non-null;} annotation to represent */ public AnnotationItem(Annotation annotation) { /* @@ -167,8 +167,8 @@ public final class AnnotationItem extends OffsettedItem { * output, that consumes no bytes of output. This is for annotating * a reference to this instance at the point of the reference. * - * @param out non-null; where to output to - * @param prefix non-null; prefix for each line of output + * @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: " + diff --git a/dx/src/com/android/dx/dex/file/AnnotationSetItem.java b/dx/src/com/android/dx/dex/file/AnnotationSetItem.java index f03cc08d1..2ff005ac8 100644 --- a/dx/src/com/android/dx/dex/file/AnnotationSetItem.java +++ b/dx/src/com/android/dx/dex/file/AnnotationSetItem.java @@ -28,14 +28,14 @@ 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 uint */ + /** the size of an entry int the set: one {@code uint} */ private static final int ENTRY_WRITE_SIZE = 4; - /** non-null; the set of annotations */ + /** {@code non-null;} the set of annotations */ private final Annotations annotations; /** - * non-null; set of annotations as individual items in an array. + * {@code non-null;} set of annotations as individual items in an array. * Note: The contents have to get sorted by type id before * writing. */ @@ -44,7 +44,7 @@ public final class AnnotationSetItem extends OffsettedItem { /** * Constructs an instance. * - * @param annotations non-null; set of annotations + * @param annotations {@code non-null;} set of annotations */ public AnnotationSetItem(Annotations annotations) { super(ALIGNMENT, writeSize(annotations)); @@ -62,8 +62,8 @@ public final class AnnotationSetItem extends OffsettedItem { /** * Gets the write size for the given set. * - * @param annotations non-null; the set - * @return > 0; the write size + * @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. @@ -79,7 +79,7 @@ public final class AnnotationSetItem extends OffsettedItem { /** * Gets the underlying annotations of this instance * - * @return non-null; the annotations + * @return {@code non-null;} the annotations */ public Annotations getAnnotations() { return annotations; diff --git a/dx/src/com/android/dx/dex/file/AnnotationSetRefItem.java b/dx/src/com/android/dx/dex/file/AnnotationSetRefItem.java index 422e2f019..1427e6a05 100644 --- a/dx/src/com/android/dx/dex/file/AnnotationSetRefItem.java +++ b/dx/src/com/android/dx/dex/file/AnnotationSetRefItem.java @@ -29,13 +29,13 @@ public final class AnnotationSetRefItem extends OffsettedItem { /** write size of this class, in bytes */ private static final int WRITE_SIZE = 4; - /** non-null; the annotation set to refer to */ + /** {@code non-null;} the annotation set to refer to */ private AnnotationSetItem annotations; /** * Constructs an instance. * - * @param annotations non-null; the annotation set to refer to + * @param annotations {@code non-null;} the annotation set to refer to */ public AnnotationSetRefItem(AnnotationSetItem annotations) { super(ALIGNMENT, WRITE_SIZE); diff --git a/dx/src/com/android/dx/dex/file/AnnotationUtils.java b/dx/src/com/android/dx/dex/file/AnnotationUtils.java index c9d796843..8431d3511 100644 --- a/dx/src/com/android/dx/dex/file/AnnotationUtils.java +++ b/dx/src/com/android/dx/dex/file/AnnotationUtils.java @@ -38,41 +38,41 @@ import static com.android.dx.rop.annotation.AnnotationVisibility.*; * Utility class for dealing with annotations. */ public final class AnnotationUtils { - /** non-null; type for AnnotationDefault annotations */ + /** {@code non-null;} type for {@code AnnotationDefault} annotations */ private static final CstType ANNOTATION_DEFAULT_TYPE = CstType.intern(Type.intern("Ldalvik/annotation/AnnotationDefault;")); - /** non-null; type for EnclosingClass annotations */ + /** {@code non-null;} type for {@code EnclosingClass} annotations */ private static final CstType ENCLOSING_CLASS_TYPE = CstType.intern(Type.intern("Ldalvik/annotation/EnclosingClass;")); - /** non-null; type for EnclosingMethod annotations */ + /** {@code non-null;} type for {@code EnclosingMethod} annotations */ private static final CstType ENCLOSING_METHOD_TYPE = CstType.intern(Type.intern("Ldalvik/annotation/EnclosingMethod;")); - /** non-null; type for InnerClass annotations */ + /** {@code non-null;} type for {@code InnerClass} annotations */ private static final CstType INNER_CLASS_TYPE = CstType.intern(Type.intern("Ldalvik/annotation/InnerClass;")); - /** non-null; type for MemberClasses annotations */ + /** {@code non-null;} type for {@code MemberClasses} annotations */ private static final CstType MEMBER_CLASSES_TYPE = CstType.intern(Type.intern("Ldalvik/annotation/MemberClasses;")); - /** non-null; type for Signature annotations */ + /** {@code non-null;} type for {@code Signature} annotations */ private static final CstType SIGNATURE_TYPE = CstType.intern(Type.intern("Ldalvik/annotation/Signature;")); - /** non-null; type for Throws annotations */ + /** {@code non-null;} type for {@code Throws} annotations */ private static final CstType THROWS_TYPE = CstType.intern(Type.intern("Ldalvik/annotation/Throws;")); - /** non-null; the UTF-8 constant "accessFlags" */ + /** {@code non-null;} the UTF-8 constant {@code "accessFlags"} */ private static final CstUtf8 ACCESS_FLAGS_UTF = new CstUtf8("accessFlags"); - /** non-null; the UTF-8 constant "name" */ + /** {@code non-null;} the UTF-8 constant {@code "name"} */ private static final CstUtf8 NAME_UTF = new CstUtf8("name"); - /** non-null; the UTF-8 constant "value" */ + /** {@code non-null;} the UTF-8 constant {@code "value"} */ private static final CstUtf8 VALUE_UTF = new CstUtf8("value"); /** @@ -83,10 +83,10 @@ public final class AnnotationUtils { } /** - * Constructs a standard AnnotationDefault annotation. + * Constructs a standard {@code AnnotationDefault} annotation. * - * @param defaults non-null; the defaults, itself as an annotation - * @return non-null; the constructed 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); @@ -97,10 +97,10 @@ public final class AnnotationUtils { } /** - * Constructs a standard EnclosingClass annotation. + * Constructs a standard {@code EnclosingClass} annotation. * - * @param clazz non-null; the enclosing class - * @return non-null; the 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); @@ -111,10 +111,10 @@ public final class AnnotationUtils { } /** - * Constructs a standard EnclosingMethod annotation. + * Constructs a standard {@code EnclosingMethod} annotation. * - * @param method non-null; the enclosing method - * @return non-null; the 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); @@ -125,12 +125,12 @@ public final class AnnotationUtils { } /** - * Constructs a standard InnerClass annotation. + * Constructs a standard {@code InnerClass} annotation. * - * @param name null-ok; the original name of the class, or - * null to represent an anonymous class + * @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 non-null; the annotation + * @return {@code non-null;} the annotation */ public static Annotation makeInnerClass(CstUtf8 name, int accessFlags) { Annotation result = new Annotation(INNER_CLASS_TYPE, SYSTEM); @@ -145,10 +145,10 @@ public final class AnnotationUtils { } /** - * Constructs a standard MemberClasses annotation. + * Constructs a standard {@code MemberClasses} annotation. * - * @param types non-null; the list of (the types of) the member classes - * @return non-null; the 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); @@ -159,10 +159,10 @@ public final class AnnotationUtils { } /** - * Constructs a standard Signature annotation. + * Constructs a standard {@code Signature} annotation. * - * @param signature non-null; the signature string - * @return non-null; the annotation + * @param signature {@code non-null;} the signature string + * @return {@code non-null;} the annotation */ public static Annotation makeSignature(CstUtf8 signature) { Annotation result = new Annotation(SIGNATURE_TYPE, SYSTEM); @@ -221,10 +221,10 @@ public final class AnnotationUtils { } /** - * Constructs a standard Throws annotation. + * Constructs a standard {@code Throws} annotation. * - * @param types non-null; the list of thrown types - * @return non-null; the 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); @@ -237,8 +237,8 @@ public final class AnnotationUtils { /** * Converts a {@link TypeList} to a {@link CstArray}. * - * @param types non-null; the type list - * @return non-null; the corresponding array constant + * @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(); diff --git a/dx/src/com/android/dx/dex/file/AnnotationsDirectoryItem.java b/dx/src/com/android/dx/dex/file/AnnotationsDirectoryItem.java index 4521e4c5b..d55195f97 100644 --- a/dx/src/com/android/dx/dex/file/AnnotationsDirectoryItem.java +++ b/dx/src/com/android/dx/dex/file/AnnotationsDirectoryItem.java @@ -40,16 +40,16 @@ public final class AnnotationsDirectoryItem extends OffsettedItem { /** write size of a list element, in bytes */ private static final int ELEMENT_SIZE = 8; - /** null-ok; the class-level annotations, if any */ + /** {@code null-ok;} the class-level annotations, if any */ private AnnotationSetItem classAnnotations; - /** null-ok; the annotated fields, if any */ + /** {@code null-ok;} the annotated fields, if any */ private ArrayList fieldAnnotations; - /** null-ok; the annotated methods, if any */ + /** {@code null-ok;} the annotated methods, if any */ private ArrayList methodAnnotations; - /** null-ok; the annotated parameters, if any */ + /** {@code null-ok;} the annotated parameters, if any */ private ArrayList parameterAnnotations; /** @@ -73,7 +73,7 @@ public final class AnnotationsDirectoryItem extends OffsettedItem { /** * Returns whether this item is empty (has no contents). * - * @return true if this item is empty, or false + * @return {@code true} if this item is empty, or {@code false} * if not */ public boolean isEmpty() { @@ -88,8 +88,8 @@ public final class AnnotationsDirectoryItem extends OffsettedItem { * interning candidates are ones that only have a non-null * set of class annotations, with no other lists. * - * @return true if this is an interning candidate, or - * false if not + * @return {@code true} if this is an interning candidate, or + * {@code false} if not */ public boolean isInternable() { return (classAnnotations != null) && @@ -132,7 +132,7 @@ public final class AnnotationsDirectoryItem extends OffsettedItem { * made on the class, per se, as opposed to on one of its members. * It is only valid to call this method at most once per instance. * - * @param annotations non-null; annotations to set for this class + * @param annotations {@code non-null;} annotations to set for this class */ public void setClassAnnotations(Annotations annotations) { if (annotations == null) { @@ -150,8 +150,8 @@ public final class AnnotationsDirectoryItem extends OffsettedItem { /** * Adds a field annotations item to this instance. * - * @param field non-null; field in question - * @param annotations non-null; associated annotations to add + * @param field {@code non-null;} field in question + * @param annotations {@code non-null;} associated annotations to add */ public void addFieldAnnotations(CstFieldRef field, Annotations annotations) { @@ -166,8 +166,8 @@ public final class AnnotationsDirectoryItem extends OffsettedItem { /** * Adds a method annotations item to this instance. * - * @param method non-null; method in question - * @param annotations non-null; associated annotations to add + * @param method {@code non-null;} method in question + * @param annotations {@code non-null;} associated annotations to add */ public void addMethodAnnotations(CstMethodRef method, Annotations annotations) { @@ -182,8 +182,8 @@ public final class AnnotationsDirectoryItem extends OffsettedItem { /** * Adds a parameter annotations item to this instance. * - * @param method non-null; method in question - * @param list non-null; associated list of annotation sets to add + * @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) { @@ -198,8 +198,8 @@ public final class AnnotationsDirectoryItem extends OffsettedItem { * Gets the method annotations for a given method, if any. This is * meant for use by debugging / dumping code. * - * @param method non-null; the method - * @return null-ok; the method annotations, if any + * @param method {@code non-null;} the method + * @return {@code null-ok;} the method annotations, if any */ public Annotations getMethodAnnotations(CstMethodRef method) { if (methodAnnotations == null) { @@ -219,8 +219,8 @@ public final class AnnotationsDirectoryItem extends OffsettedItem { * Gets the parameter annotations for a given method, if any. This is * meant for use by debugging / dumping code. * - * @param method non-null; the method - * @return null-ok; the parameter annotations, if any + * @param method {@code non-null;} the method + * @return {@code null-ok;} the parameter annotations, if any */ public AnnotationsList getParameterAnnotations(CstMethodRef method) { if (parameterAnnotations == null) { @@ -336,11 +336,11 @@ public final class AnnotationsDirectoryItem extends OffsettedItem { } /** - * Gets the list size of the given list, or 0 if given - * null. + * Gets the list size of the given list, or {@code 0} if given + * {@code null}. * - * @param list null-ok; the list in question - * @return >= 0; its size + * @param list {@code null-ok;} the list in question + * @return {@code >= 0;} its size */ private static int listSize(ArrayList list) { if (list == null) { @@ -354,7 +354,7 @@ public final class AnnotationsDirectoryItem extends OffsettedItem { * Prints out the contents of this instance, in a debugging-friendly * way. This is meant to be called from {@link ClassDefItem#debugPrint}. * - * @param out non-null; where to output to + * @param out {@code non-null;} where to output to */ /*package*/ void debugPrint(PrintWriter out) { if (classAnnotations != null) { diff --git a/dx/src/com/android/dx/dex/file/CatchStructs.java b/dx/src/com/android/dx/dex/file/CatchStructs.java index b7abc3f53..341201538 100644 --- a/dx/src/com/android/dx/dex/file/CatchStructs.java +++ b/dx/src/com/android/dx/dex/file/CatchStructs.java @@ -32,27 +32,27 @@ import java.util.TreeMap; /** * List of exception handlers (tuples of covered range, catch type, * handler address) for a particular piece of code. Instances of this - * class correspond to a try_item[] and a - * catch_handler_item[]. + * class correspond to a {@code try_item[]} and a + * {@code catch_handler_item[]}. */ public final class CatchStructs { /** - * the size of a try_item: a uint - * and two ushorts + * the size of a {@code try_item}: a {@code uint} + * and two {@code ushort}s */ private static final int TRY_ITEM_WRITE_SIZE = 4 + (2 * 2); - /** non-null; code that contains the catches */ + /** {@code non-null;} code that contains the catches */ private final DalvCode code; /** - * null-ok; the underlying table; set in + * {@code null-ok;} the underlying table; set in * {@link #finishProcessingIfNecessary} */ private CatchTable table; /** - * null-ok; the encoded handler list, if calculated; set in + * {@code null-ok;} the encoded handler list, if calculated; set in * {@link #encode} */ private byte[] encodedHandlers; @@ -64,7 +64,7 @@ public final class CatchStructs { private int encodedHandlerHeaderSize; /** - * null-ok; map from handler lists to byte offsets, if calculated; set in + * {@code null-ok;} map from handler lists to byte offsets, if calculated; set in * {@link #encode} */ private TreeMap handlerOffsets; @@ -72,7 +72,7 @@ public final class CatchStructs { /** * Constructs an instance. * - * @param code non-null; code that contains the catches + * @param code {@code non-null;} code that contains the catches */ public CatchStructs(DalvCode code) { this.code = code; @@ -94,7 +94,7 @@ public final class CatchStructs { /** * Gets the size of the tries list, in entries. * - * @return >= 0; the tries list size + * @return {@code >= 0;} the tries list size */ public int triesSize() { finishProcessingIfNecessary(); @@ -104,8 +104,8 @@ public final class CatchStructs { /** * Does a human-friendly dump of this instance. * - * @param out non-null; where to dump - * @param prefix non-null; prefix to attach to each line of output + * @param 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); @@ -114,7 +114,7 @@ public final class CatchStructs { /** * Encodes the handler lists. * - * @param file non-null; file this instance is part of + * @param file {@code non-null;} file this instance is part of */ public void encode(DexFile file) { finishProcessingIfNecessary(); @@ -179,7 +179,7 @@ public final class CatchStructs { /** * Gets the write size of this instance, in bytes. * - * @return >= 0; the write size + * @return {@code >= 0;} the write size */ public int writeSize() { return (triesSize() * TRY_ITEM_WRITE_SIZE) + @@ -189,20 +189,17 @@ public final class CatchStructs { /** * Writes this instance to the given stream. * - * @param file non-null; file this instance is part of - * @param out non-null; where to write to + * @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(); - TypeIdsSection typeIds = file.getTypeIds(); - int tableSize = table.size(); - int handlersSize = handlerOffsets.size(); - 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(); @@ -225,12 +222,12 @@ public final class CatchStructs { /** * Helper method to annotate or simply print the exception handlers. - * Only one of printTo or annotateTo should + * Only one of {@code printTo} or {@code annotateTo} should * be non-null. * - * @param prefix non-null; prefix for each line - * @param printTo null-ok; where to print to - * @param annotateTo null-ok; where to consume bytes and annotate to + * @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) { @@ -299,12 +296,12 @@ public final class CatchStructs { * Helper for {@link #annotateEntries} to annotate a catch handler list * while consuming it. * - * @param handlers non-null; handlers to annotate - * @param offset >= 0; the offset of this handler - * @param size >= 1; the number of bytes the handlers consume - * @param prefix non-null; prefix for each line - * @param printTo null-ok; where to print to - * @param annotateTo non-null; where to annotate to + * @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, diff --git a/dx/src/com/android/dx/dex/file/ClassDataItem.java b/dx/src/com/android/dx/dex/file/ClassDataItem.java index 638daed21..4823f9f5f 100644 --- a/dx/src/com/android/dx/dex/file/ClassDataItem.java +++ b/dx/src/com/android/dx/dex/file/ClassDataItem.java @@ -38,32 +38,32 @@ import java.util.HashMap; * Representation of all the parts of a Dalvik class that are generally * "inflated" into an in-memory representation at runtime. Instances of * this class are represented in a compact streamable form in a - * dex file, as opposed to a random-access form. + * {@code dex} file, as opposed to a random-access form. */ public final class ClassDataItem extends OffsettedItem { - /** non-null; what class this data is for, just for listing generation */ + /** {@code non-null;} what class this data is for, just for listing generation */ private final CstType thisClass; - /** non-null; list of static fields */ + /** {@code non-null;} list of static fields */ private final ArrayList staticFields; - /** non-null; list of initial values for static fields */ + /** {@code non-null;} list of initial values for static fields */ private final HashMap staticValues; - /** non-null; list of instance fields */ + /** {@code non-null;} list of instance fields */ private final ArrayList instanceFields; - /** non-null; list of direct methods */ + /** {@code non-null;} list of direct methods */ private final ArrayList directMethods; - /** non-null; list of virtual methods */ + /** {@code non-null;} list of virtual methods */ private final ArrayList virtualMethods; - /** null-ok; static initializer list; set in {@link #addContents} */ + /** {@code null-ok;} static initializer list; set in {@link #addContents} */ private CstArray staticValuesConstant; /** - * null-ok; encoded form, ready for writing to a file; set during + * {@code null-ok;} encoded form, ready for writing to a file; set during * {@link #place0} */ private byte[] encodedForm; @@ -72,7 +72,7 @@ public final class ClassDataItem extends OffsettedItem { * Constructs an instance. Its sets of members are initially * empty. * - * @param thisClass non-null; what class this data is for, just + * @param thisClass {@code non-null;} what class this data is for, just * for listing generation */ public ClassDataItem(CstType thisClass) { @@ -106,8 +106,8 @@ public final class ClassDataItem extends OffsettedItem { /** * Returns whether this instance is empty. * - * @return true if this instance is empty or - * false if at least one element has been added to it + * @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() @@ -117,8 +117,8 @@ public final class ClassDataItem extends OffsettedItem { /** * Adds a static field. * - * @param field non-null; the field to add - * @param value null-ok; initial value for the field, if any + * @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) { @@ -137,7 +137,7 @@ public final class ClassDataItem extends OffsettedItem { /** * Adds an instance field. * - * @param field non-null; the field to add + * @param field {@code non-null;} the field to add */ public void addInstanceField(EncodedField field) { if (field == null) { @@ -148,9 +148,9 @@ public final class ClassDataItem extends OffsettedItem { } /** - * Adds a direct (static and/or private) method. + * Adds a direct ({@code static} and/or {@code private}) method. * - * @param method non-null; the method to add + * @param method {@code non-null;} the method to add */ public void addDirectMethod(EncodedMethod method) { if (method == null) { @@ -163,7 +163,7 @@ public final class ClassDataItem extends OffsettedItem { /** * Adds a virtual method. * - * @param method non-null; the method to add + * @param method {@code non-null;} the method to add */ public void addVirtualMethod(EncodedMethod method) { if (method == null) { @@ -178,7 +178,7 @@ public final class ClassDataItem extends OffsettedItem { * in any way to the underlying lists contained in this instance, but * the objects contained in the list are shared. * - * @return non-null; list of all methods + * @return {@code non-null;} list of all methods */ public ArrayList getMethods() { int sz = directMethods.size() + virtualMethods.size(); @@ -195,7 +195,7 @@ public final class ClassDataItem extends OffsettedItem { * Prints out the contents of this instance, in a debugging-friendly * way. * - * @param out non-null; where to output to + * @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) { @@ -258,9 +258,9 @@ public final class ClassDataItem extends OffsettedItem { /** * Gets a {@link CstArray} corresponding to {@link #staticValues} if - * it contains any non-zero non-null values. + * it contains any non-zero non-{@code null} values. * - * @return null-ok; the corresponding constant or null if + * @return {@code null-ok;} the corresponding constant or {@code null} if * there are no values to encode */ public CstArray getStaticValuesConstant() { @@ -273,9 +273,9 @@ public final class ClassDataItem extends OffsettedItem { /** * Gets a {@link CstArray} corresponding to {@link #staticValues} if - * it contains any non-zero non-null values. + * it contains any non-zero non-{@code null} values. * - * @return null-ok; the corresponding constant or null if + * @return {@code null-ok;} the corresponding constant or {@code null} if * there are no values to encode */ private CstArray makeStaticValuesConstant() { @@ -337,13 +337,11 @@ public final class ClassDataItem extends OffsettedItem { /** * Writes out the encoded form of this instance. * - * @param file non-null; file this instance is part of - * @param out non-null; where to write to + * @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(); - int svSize = (staticValuesConstant == null) ? 0 : - staticValuesConstant.getList().size(); if (annotates) { out.annotate(0, offsetString() + " class data for " + @@ -369,10 +367,10 @@ public final class ClassDataItem extends OffsettedItem { * Helper for {@link #encodeOutput}, which writes out the given * size value, annotating it as well (if annotations are enabled). * - * @param file non-null; file this instance is part of - * @param out non-null; where to write to - * @param label non-null; the label for the purposes of annotation - * @param size >= 0; the size to write + * @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) { @@ -389,10 +387,10 @@ public final class ClassDataItem extends OffsettedItem { * list. It also annotates the items (if any and if annotations * are enabled). * - * @param file non-null; file this instance is part of - * @param out non-null; where to write to - * @param label non-null; the label for the purposes of annotation - * @param list non-null; the list in question + * @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 list) { diff --git a/dx/src/com/android/dx/dex/file/ClassDefItem.java b/dx/src/com/android/dx/dex/file/ClassDefItem.java index 5a0b27c90..acd8cf515 100644 --- a/dx/src/com/android/dx/dex/file/ClassDefItem.java +++ b/dx/src/com/android/dx/dex/file/ClassDefItem.java @@ -45,47 +45,47 @@ public final class ClassDefItem extends IndexedItem { /** size of instances when written out to a file, in bytes */ public static final int WRITE_SIZE = 32; - /** non-null; type constant for this class */ + /** {@code non-null;} type constant for this class */ private final CstType thisClass; /** access flags */ private final int accessFlags; /** - * null-ok; superclass or null if this class is a/the + * {@code null-ok;} superclass or {@code null} if this class is a/the * root class */ private final CstType superclass; - /** null-ok; list of implemented interfaces */ + /** {@code null-ok;} list of implemented interfaces */ private TypeListItem interfaces; - /** null-ok; source file name or null if unknown */ + /** {@code null-ok;} source file name or {@code null} if unknown */ private final CstUtf8 sourceFile; - /** non-null; associated class data object */ + /** {@code non-null;} associated class data object */ private final ClassDataItem classData; /** - * null-ok; item wrapper for the static values, initialized + * {@code null-ok;} item wrapper for the static values, initialized * in {@link #addContents} */ private EncodedArrayItem staticValuesItem; - /** non-null; annotations directory */ + /** {@code non-null;} annotations directory */ private AnnotationsDirectoryItem annotationsDirectory; /** * Constructs an instance. Its sets of members and annotations are * initially empty. * - * @param thisClass non-null; type constant for this class + * @param thisClass {@code non-null;} type constant for this class * @param accessFlags access flags - * @param superclass null-ok; superclass or null if + * @param superclass {@code null-ok;} superclass or {@code null} if * this class is a/the root class - * @param interfaces non-null; list of implemented interfaces - * @param sourceFile null-ok; source file name or - * null if unknown + * @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, CstUtf8 sourceFile) { @@ -223,7 +223,7 @@ public final class ClassDefItem extends IndexedItem { /** * Gets the constant corresponding to this class. * - * @return non-null; the constant + * @return {@code non-null;} the constant */ public CstType getThisClass() { return thisClass; @@ -241,7 +241,7 @@ public final class ClassDefItem extends IndexedItem { /** * Gets the superclass. * - * @return null-ok; the superclass or null if + * @return {@code null-ok;} the superclass or {@code null} if * this class is a/the root class */ public CstType getSuperclass() { @@ -251,7 +251,7 @@ public final class ClassDefItem extends IndexedItem { /** * Gets the list of interfaces implemented. * - * @return non-null; the interfaces list + * @return {@code non-null;} the interfaces list */ public TypeList getInterfaces() { if (interfaces == null) { @@ -264,7 +264,7 @@ public final class ClassDefItem extends IndexedItem { /** * Gets the source file name. * - * @return null-ok; the source file name or null if unknown + * @return {@code null-ok;} the source file name or {@code null} if unknown */ public CstUtf8 getSourceFile() { return sourceFile; @@ -273,8 +273,8 @@ public final class ClassDefItem extends IndexedItem { /** * Adds a static field. * - * @param field non-null; the field to add - * @param value null-ok; initial value for the field, if any + * @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); @@ -283,16 +283,16 @@ public final class ClassDefItem extends IndexedItem { /** * Adds an instance field. * - * @param field non-null; the field to add + * @param field {@code non-null;} the field to add */ public void addInstanceField(EncodedField field) { classData.addInstanceField(field); } /** - * Adds a direct (static and/or private) method. + * Adds a direct ({@code static} and/or {@code private}) method. * - * @param method non-null; the method to add + * @param method {@code non-null;} the method to add */ public void addDirectMethod(EncodedMethod method) { classData.addDirectMethod(method); @@ -301,7 +301,7 @@ public final class ClassDefItem extends IndexedItem { /** * Adds a virtual method. * - * @param method non-null; the method to add + * @param method {@code non-null;} the method to add */ public void addVirtualMethod(EncodedMethod method) { classData.addVirtualMethod(method); @@ -312,7 +312,7 @@ public final class ClassDefItem extends IndexedItem { * in any way to the underlying lists contained in this instance, but * the objects contained in the list are shared. * - * @return non-null; list of all methods + * @return {@code non-null;} list of all methods */ public ArrayList getMethods() { return classData.getMethods(); @@ -323,7 +323,7 @@ public final class ClassDefItem extends IndexedItem { * made on the class, per se, as opposed to on one of its members. * It is only valid to call this method at most once per instance. * - * @param annotations non-null; annotations to set for this class + * @param annotations {@code non-null;} annotations to set for this class */ public void setClassAnnotations(Annotations annotations) { annotationsDirectory.setClassAnnotations(annotations); @@ -332,8 +332,8 @@ public final class ClassDefItem extends IndexedItem { /** * Adds a field annotations item to this class. * - * @param field non-null; field in question - * @param annotations non-null; associated annotations to add + * @param field {@code non-null;} field in question + * @param annotations {@code non-null;} associated annotations to add */ public void addFieldAnnotations(CstFieldRef field, Annotations annotations) { @@ -343,8 +343,8 @@ public final class ClassDefItem extends IndexedItem { /** * Adds a method annotations item to this class. * - * @param method non-null; method in question - * @param annotations non-null; associated annotations to add + * @param method {@code non-null;} method in question + * @param annotations {@code non-null;} associated annotations to add */ public void addMethodAnnotations(CstMethodRef method, Annotations annotations) { @@ -354,8 +354,8 @@ public final class ClassDefItem extends IndexedItem { /** * Adds a parameter annotations item to this class. * - * @param method non-null; method in question - * @param list non-null; associated list of annotation sets to add + * @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) { @@ -366,8 +366,8 @@ public final class ClassDefItem extends IndexedItem { * Gets the method annotations for a given method, if any. This is * meant for use by debugging / dumping code. * - * @param method non-null; the method - * @return null-ok; the method annotations, if any + * @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); @@ -377,8 +377,8 @@ public final class ClassDefItem extends IndexedItem { * Gets the parameter annotations for a given method, if any. This is * meant for use by debugging / dumping code. * - * @param method non-null; the method - * @return null-ok; the parameter annotations, if any + * @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); @@ -388,7 +388,7 @@ public final class ClassDefItem extends IndexedItem { * Prints out the contents of this instance, in a debugging-friendly * way. * - * @param out non-null; where to output to + * @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) { diff --git a/dx/src/com/android/dx/dex/file/ClassDefsSection.java b/dx/src/com/android/dx/dex/file/ClassDefsSection.java index cf61b4732..e8efd5745 100644 --- a/dx/src/com/android/dx/dex/file/ClassDefsSection.java +++ b/dx/src/com/android/dx/dex/file/ClassDefsSection.java @@ -28,22 +28,22 @@ import java.util.Collection; import java.util.TreeMap; /** - * Class definitions list section of a .dex file. + * Class definitions list section of a {@code .dex} file. */ public final class ClassDefsSection extends UniformItemSection { /** - * non-null; map from type constants for classes to {@link + * {@code non-null;} map from type constants for classes to {@link * ClassDefItem} instances that define those classes */ private final TreeMap classDefs; - /** null-ok; ordered list of classes; set in {@link #orderItems} */ + /** {@code null-ok;} ordered list of classes; set in {@link #orderItems} */ private ArrayList orderedDefs; /** * Constructs an instance. The file offset is initially unknown. * - * @param file non-null; file that this instance is part of + * @param file {@code non-null;} file that this instance is part of */ public ClassDefsSection(DexFile file) { super("class_defs", file, 4); @@ -84,7 +84,7 @@ public final class ClassDefsSection extends UniformItemSection { /** * Writes the portion of the file header that refers to this instance. * - * @param out non-null; where to write + * @param out {@code non-null;} where to write */ public void writeHeaderPart(AnnotatedOutput out) { throwIfNotPrepared(); @@ -105,7 +105,7 @@ public final class ClassDefsSection extends UniformItemSection { * Adds an element to this instance. It is illegal to attempt to add more * than one class with the same name. * - * @param clazz non-null; the class def to add + * @param clazz {@code non-null;} the class def to add */ public void add(ClassDefItem clazz) { Type type; @@ -149,11 +149,11 @@ public final class ClassDefsSection extends UniformItemSection { * Helper for {@link #orderItems}, which recursively assigns indices * to classes. * - * @param type null-ok; type ref to assign, if any - * @param idx >= 0; the next index to assign + * @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 >= 0; the next index to assign + * @return {@code >= 0;} the next index to assign */ private int orderItems0(Type type, int idx, int maxDepth) { ClassDefItem c = classDefs.get(type); diff --git a/dx/src/com/android/dx/dex/file/CodeItem.java b/dx/src/com/android/dx/dex/file/CodeItem.java index dc0bb5272..ab7abbebb 100644 --- a/dx/src/com/android/dx/dex/file/CodeItem.java +++ b/dx/src/com/android/dx/dex/file/CodeItem.java @@ -39,7 +39,7 @@ import java.util.HashSet; /** * Representation of all the parts needed for concrete methods in a - * dex file. + * {@code dex} file. */ public final class CodeItem extends OffsettedItem { /** file alignment of this class, in bytes */ @@ -48,26 +48,26 @@ public final class CodeItem extends OffsettedItem { /** write size of the header of this class, in bytes */ private static final int HEADER_SIZE = 16; - /** non-null; method that this code implements */ + /** {@code non-null;} method that this code implements */ private final CstMethodRef ref; - /** non-null; the bytecode instructions and associated data */ + /** {@code non-null;} the bytecode instructions and associated data */ private final DalvCode code; - /** null-ok; the catches, if needed; set in {@link #addContents} */ + /** {@code null-ok;} the catches, if needed; set in {@link #addContents} */ private CatchStructs catches; - /** whether this instance is for a static method */ + /** whether this instance is for a {@code static} method */ private final boolean isStatic; /** - * non-null; list of possibly-thrown exceptions; just used in + * {@code non-null;} list of possibly-thrown exceptions; just used in * generating debugging output (listings) */ private final TypeList throwsList; /** - * null-ok; the debug info or null if there is none; + * {@code null-ok;} the debug info or {@code null} if there is none; * set in {@link #addContents} */ private DebugInfoItem debugInfo; @@ -75,11 +75,11 @@ public final class CodeItem extends OffsettedItem { /** * Constructs an instance. * - * @param ref non-null; method that this code implements - * @param code non-null; the underlying code - * @param isStatic whether this instance is for a static + * @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 non-null; list of possibly-thrown exceptions, + * @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, @@ -114,7 +114,6 @@ public final class CodeItem extends OffsettedItem { /** {@inheritDoc} */ public void addContents(DexFile file) { - MixedItemSection wordData = file.getWordData(); MixedItemSection byteData = file.getByteData(); TypeIdsSection typeIds = file.getTypeIds(); @@ -150,7 +149,7 @@ public final class CodeItem extends OffsettedItem { /** * Gets the reference to the method this instance implements. * - * @return non-null; the method reference + * @return {@code non-null;} the method reference */ public CstMethodRef getRef() { return ref; @@ -159,8 +158,8 @@ public final class CodeItem extends OffsettedItem { /** * Does a human-friendly dump of this instance. * - * @param out non-null; where to dump - * @param prefix non-null; per-line prefix to use + * @param 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 */ public void debugPrint(PrintWriter out, String prefix, boolean verbose) { @@ -292,8 +291,8 @@ public final class CodeItem extends OffsettedItem { /** * Helper for {@link #writeTo0} which writes out the actual bytecode. * - * @param file non-null; file we are part of - * @param out non-null; where to write to + * @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(); diff --git a/dx/src/com/android/dx/dex/file/DebugInfoConstants.java b/dx/src/com/android/dx/dex/file/DebugInfoConstants.java index 010acb434..78b6b049d 100644 --- a/dx/src/com/android/dx/dex/file/DebugInfoConstants.java +++ b/dx/src/com/android/dx/dex/file/DebugInfoConstants.java @@ -110,8 +110,8 @@ public interface DebugInfoConstants { * next position entry that is added should be considered the end of * a method prologue (an appropriate place for a method breakpoint).

    * - * The prologue_end register is cleared by any special (>= OPCODE_BASE) - * opcode. + * The prologue_end register is cleared by any special + * ({@code >= OPCODE_BASE}) opcode. */ static final int DBG_SET_PROLOGUE_END = 0x07; @@ -121,8 +121,8 @@ public interface DebugInfoConstants { * a method epilogue (an appropriate place to suspend execution before * method exit).

    * - * The epilogue_begin register is cleared by any special (>= OPCODE_BASE) - * opcode. + * The epilogue_begin register is cleared by any special + * ({@code >= OPCODE_BASE}) opcode. */ static final int DBG_SET_EPILOGUE_BEGIN = 0x08; diff --git a/dx/src/com/android/dx/dex/file/DebugInfoDecoder.java b/dx/src/com/android/dx/dex/file/DebugInfoDecoder.java index 3ffd27696..cd2005526 100644 --- a/dx/src/com/android/dx/dex/file/DebugInfoDecoder.java +++ b/dx/src/com/android/dx/dex/file/DebugInfoDecoder.java @@ -38,7 +38,7 @@ import static com.android.dx.dex.file.DebugInfoConstants.*; /** * A decoder for the dex debug info state machine format. * This code exists mostly as a reference implementation and test for - * for the DebugInfoEncoder + * for the {@code DebugInfoEncoder} */ public class DebugInfoDecoder { /** encoded debug info */ @@ -180,7 +180,7 @@ public class DebugInfoDecoder { /** * Gets the decoded positions list. - * Valid after calling decode. + * Valid after calling {@code decode}. * * @return positions list in ascending address order. */ @@ -190,7 +190,7 @@ public class DebugInfoDecoder { /** * Gets the decoded locals list, in ascending start-address order. - * Valid after calling decode. + * Valid after calling {@code decode}. * * @return locals list in ascending address order. */ @@ -227,7 +227,7 @@ public class DebugInfoDecoder { /** * Gets the register that begins the method's parameter range (including * the 'this' parameter for non-static methods). The range continues until - * regSize + * {@code regSize} * * @return register as noted above. */ @@ -416,9 +416,9 @@ public class DebugInfoDecoder { * encoder. * * @param info encoded debug info - * @param file non-null; file to refer to during decoding - * @param ref non-null; method whose info is being decoded - * @param code non-null; original code object that was encoded + * @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, diff --git a/dx/src/com/android/dx/dex/file/DebugInfoEncoder.java b/dx/src/com/android/dx/dex/file/DebugInfoEncoder.java index 780e18d5b..08b663793 100644 --- a/dx/src/com/android/dx/dex/file/DebugInfoEncoder.java +++ b/dx/src/com/android/dx/dex/file/DebugInfoEncoder.java @@ -46,19 +46,19 @@ import static com.android.dx.dex.file.DebugInfoConstants.*; *

  • signed LEB128: initial value for line register. *
  • n instances of signed LEB128: string indicies (offset by 1) * for each method argument in left-to-right order - * with this excluded. A value of '0' indicates "no name" + * with {@code this} excluded. A value of '0' indicates "no name" *
  • A sequence of special or normal opcodes as defined in - * DebugInfoConstants. - *
  • A single terminating OP_END_SEQUENCE + * {@code DebugInfoConstants}. + *
  • A single terminating {@code OP_END_SEQUENCE} * */ public final class DebugInfoEncoder { private static final boolean DEBUG = false; - /** null-ok; positions (line numbers) to encode */ + /** {@code null-ok;} positions (line numbers) to encode */ private final PositionList positions; - /** null-ok; local variables to encode */ + /** {@code null-ok;} local variables to encode */ private final LocalList locals; private final ByteArrayAnnotatedOutput output; @@ -96,33 +96,32 @@ public final class DebugInfoEncoder { /** * Creates an instance. * - * @param pl null-ok; positions (line numbers) to encode - * @param ll null-ok; local variables to encode - * @param file null-ok; may only be null if simply using + * @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 pl, LocalList ll, + public DebugInfoEncoder(PositionList positions, LocalList locals, DexFile file, int codeSize, int regSize, boolean isStatic, CstMethodRef ref) { - this.positions = pl; - this.locals = ll; + this.positions = positions; + this.locals = locals; this.file = file; - output = new ByteArrayAnnotatedOutput(); 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 debugPrint writer + * Annotates or writes a message to the {@code debugPrint} writer * if applicable. * * @param length the number of bytes associated with this message @@ -146,8 +145,8 @@ public final class DebugInfoEncoder { * Converts this (PositionList, LocalList) pair into a state machine * sequence. * - * @return encoded byte sequence without padding and - * terminated with a '\00' + * @return {@code non-null;} encoded byte sequence without padding and + * terminated with a {@code 0x00} byte */ public byte[] convert() { try { @@ -169,15 +168,15 @@ public final class DebugInfoEncoder { /** * Converts and produces annotations on a stream. Does not write - * actual bits to the AnnotatedOutput. + * actual bits to the {@code AnnotatedOutput}. * - * @param prefix null-ok; prefix to attach to each line of output - * @param debugPrint null-ok; if specified, an alternate output for + * @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 null-ok; if specified, where annotations should go + * @param out {@code null-ok;} if specified, where annotations should go * @param consume whether to claim to have consumed output for - * out - * @return output sequence + * {@code out} + * @return {@code non-null;} encoded output */ public byte[] convertAndAnnotate(String prefix, PrintWriter debugPrint, AnnotatedOutput out, boolean consume) { @@ -190,7 +189,7 @@ public final class DebugInfoEncoder { return result; } - + private byte[] convert0() throws IOException { ArrayList sortedPositions = buildSortedPositions(); ArrayList methodArgs = extractMethodArguments(); @@ -204,21 +203,22 @@ public final class DebugInfoEncoder { annotate(1, String.format("%04x: prologue end",address)); } - int szp = sortedPositions.size(); - int szl = locals.size(); + int positionsSz = sortedPositions.size(); + int localsSz = locals.size(); // Current index in sortedPositions - int curp = 0; + int curPositionIdx = 0; // Current index in locals - int curl = 0; + int curLocalIdx = 0; for (;;) { /* * Emit any information for the current address. */ - curl = emitLocalsAtAddress(curl); - curp = emitPositionsAtAddress(curp, sortedPositions); + curLocalIdx = emitLocalsAtAddress(curLocalIdx); + curPositionIdx = + emitPositionsAtAddress(curPositionIdx, sortedPositions); /* * Figure out what the next important address is. @@ -227,12 +227,12 @@ public final class DebugInfoEncoder { int nextAddrL = Integer.MAX_VALUE; // local variable int nextAddrP = Integer.MAX_VALUE; // position (line number) - if (curl < szl) { - nextAddrL = locals.get(curl).getAddress(); + if (curLocalIdx < localsSz) { + nextAddrL = locals.get(curLocalIdx).getAddress(); } - if (curp < szp) { - nextAddrP = sortedPositions.get(curp).getAddress(); + if (curPositionIdx < positionsSz) { + nextAddrP = sortedPositions.get(curPositionIdx).getAddress(); } int next = Math.min(nextAddrP, nextAddrL); @@ -249,12 +249,12 @@ public final class DebugInfoEncoder { if (next == codeSize && nextAddrL == Integer.MAX_VALUE && nextAddrP == Integer.MAX_VALUE) { - break; + break; } if (next == nextAddrP) { // Combined advance PC + position entry - emitPosition(sortedPositions.get(curp++)); + emitPosition(sortedPositions.get(curPositionIdx++)); } else { emitAdvancePc(next - address); } @@ -271,96 +271,96 @@ public final class DebugInfoEncoder { * locals} and including all subsequent activity at the same * address. * - * @param curl Current index in locals - * @return new value for curl + * @param curLocalIdx Current index in locals + * @return new value for {@code curLocalIdx} * @throws IOException */ - private int emitLocalsAtAddress(int curl) + private int emitLocalsAtAddress(int curLocalIdx) throws IOException { int sz = locals.size(); // TODO: Don't emit ends implied by starts. - while ((curl < sz) - && (locals.get(curl).getAddress() == address)) { - LocalList.Entry lle = locals.get(curl++); - int reg = lle.getRegister(); - LocalList.Entry prevlle = lastEntryForReg[reg]; + while ((curLocalIdx < sz) + && (locals.get(curLocalIdx).getAddress() == address)) { + LocalList.Entry entry = locals.get(curLocalIdx++); + int reg = entry.getRegister(); + LocalList.Entry prevEntry = lastEntryForReg[reg]; - if (lle == prevlle) { + 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] = lle; + lastEntryForReg[reg] = entry; - if (lle.isStart()) { - if ((prevlle != null) && lle.matches(prevlle)) { + 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 (prevlle.isStart()) { + 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(lle); + emitLocalRestart(entry); } else { - emitLocalStart(lle); + 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 (lle.getDisposition() + if (entry.getDisposition() != LocalList.Disposition.END_REPLACED) { - emitLocalEnd(lle); + emitLocalEnd(entry); } } } - return curl; + return curLocalIdx; } /** - * Emits all positions that occur at the current address + * Emits all positions that occur at the current {@code address} * - * @param curp Current index in sortedPositions + * @param curPositionIdx Current index in sortedPositions * @param sortedPositions positions, sorted by ascending address - * @return new value for curp + * @return new value for {@code curPositionIdx} * @throws IOException */ - private int emitPositionsAtAddress(int curp, + private int emitPositionsAtAddress(int curPositionIdx, ArrayList sortedPositions) throws IOException { - - int szp = sortedPositions.size(); - while (curp < szp - && sortedPositions.get(curp).getAddress() == address) { - emitPosition(sortedPositions.get(curp++)); + int positionsSz = sortedPositions.size(); + while ((curPositionIdx < positionsSz) + && (sortedPositions.get(curPositionIdx).getAddress() + == address)) { + emitPosition(sortedPositions.get(curPositionIdx++)); } - return curp; + 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" @@ -392,7 +392,7 @@ public final class DebugInfoEncoder { * entry for the 'this' pointer. */ if (!isStatic) { - for (LocalList.Entry arg: methodArgs) { + for (LocalList.Entry arg : methodArgs) { if (curParam == arg.getRegister()) { lastEntryForReg[curParam] = arg; break; @@ -406,7 +406,7 @@ public final class DebugInfoEncoder { output.writeUnsignedLeb128(szParamTypes); if (annotate) { - annotate(output.getCursor() - mark, + annotate(output.getCursor() - mark, String.format("parameters_size: %04x", szParamTypes)); } @@ -420,7 +420,7 @@ public final class DebugInfoEncoder { mark = output.getCursor(); - for (LocalList.Entry arg: methodArgs) { + for (LocalList.Entry arg : methodArgs) { if (curParam == arg.getRegister()) { found = arg; @@ -507,7 +507,7 @@ public final class DebugInfoEncoder { /** * Gets the register that begins the method's parameter range (including * the 'this' parameter for non-static methods). The range continues until - * regSize + * {@code regSize} * * @return register as noted above */ @@ -521,7 +521,7 @@ public final class DebugInfoEncoder { * from the input list and sorted by ascending register in the * returned list. * - * @return list of non-this method argument locals, + * @return list of non-{@code this} method argument locals, * sorted by ascending register */ private ArrayList extractMethodArguments() { @@ -566,8 +566,8 @@ public final class DebugInfoEncoder { * Returns a string representation of this LocalList entry that is * appropriate for emitting as an annotation. * - * @param e non-null; entry - * @return non-null; annotation string + * @param e {@code non-null;} entry + * @return {@code non-null;} annotation string */ private String entryAnnotationString(LocalList.Entry e) { StringBuilder sb = new StringBuilder(); @@ -633,7 +633,7 @@ public final class DebugInfoEncoder { * null symbol is used in some cases by the parameter name list * at the beginning of the sequence. * - * @param string null-ok; string to emit + * @param string {@code null-ok;} string to emit * @throws IOException */ private void emitStringIndex(CstUtf8 string) throws IOException { @@ -654,7 +654,7 @@ public final class DebugInfoEncoder { * Emits a type index as an unsigned LEB128. The actual value written * is shifted by 1, so that the '0' value is reserved for "null". * - * @param type null-ok; type to emit + * @param type {@code null-ok;} type to emit * @throws IOException */ private void emitTypeIndex(CstType type) throws IOException { @@ -739,7 +739,7 @@ public final class DebugInfoEncoder { /** * Emits a {@link DebugInfoConstants#DBG_END_LOCAL DBG_END_LOCAL} sequence. * - * @param entry entry non-null; entry associated with end. + * @param entry {@code entry non-null;} entry associated with end. * @throws IOException */ private void emitLocalEnd(LocalList.Entry entry) @@ -823,16 +823,17 @@ public final class DebugInfoEncoder { * Essentially the same as described in "DWARF Debugging Format Version 3" * section 6.2.5.1. * - * @param deltaLines >= DBG_LINE_BASE and <= DBG_LINE_BASE + - * DBG_LINE_RANGE, the line change to encode - * @param deltaAddress >= 0; the address change to encode - * @return <= 0xff if in range, otherwise parameters are out of range + * @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"); + throw new RuntimeException("Parameter out of range"); } return (deltaLines - DBG_LINE_BASE) @@ -864,10 +865,10 @@ public final class DebugInfoEncoder { } /** - * Emits an {@link DebugInfoConstants#DBG_ADVANCE_PC DBG_ADVANCE_PC} + * Emits an {@link DebugInfoConstants#DBG_ADVANCE_PC DBG_ADVANCE_PC} * sequence. * - * @param deltaAddress >= 0 amount to change program counter by + * @param deltaAddress {@code >= 0;} amount to change program counter by * @throws IOException */ private void emitAdvancePc(int deltaAddress) throws IOException { @@ -890,8 +891,9 @@ public final class DebugInfoEncoder { /** * Emits an unsigned LEB128 value. * - * @param n >= 0 vallue to emit. Note that, although this can represent - * integers larger than Integer.MAX_VALUE, we currently don't allow that. + * @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 { @@ -901,7 +903,7 @@ public final class DebugInfoEncoder { "Signed value where unsigned required: " + n); } - output.writeSignedLeb128(n); + output.writeUnsignedLeb128(n); } /** diff --git a/dx/src/com/android/dx/dex/file/DebugInfoItem.java b/dx/src/com/android/dx/dex/file/DebugInfoItem.java index 0e4329b14..1c32bd7a0 100644 --- a/dx/src/com/android/dx/dex/file/DebugInfoItem.java +++ b/dx/src/com/android/dx/dex/file/DebugInfoItem.java @@ -34,7 +34,7 @@ public class DebugInfoItem extends OffsettedItem { private static final boolean ENABLE_ENCODER_SELF_CHECK = false; - /** non-null; the code this item represents */ + /** {@code non-null;} the code this item represents */ private final DalvCode code; private byte[] encoded; @@ -93,9 +93,9 @@ public class DebugInfoItem extends OffsettedItem { * directly after a code dump (with the real local list actually * existing elsewhere in the output). * - * @param file non-null; the file to use for referencing other sections - * @param out non-null; where to annotate to - * @param prefix null-ok; prefix to attach to each line of output + * @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); @@ -104,8 +104,8 @@ public class DebugInfoItem extends OffsettedItem { /** * Does a human-friendly dump of this instance. * - * @param out non-null; where to dump - * @param prefix non-null; prefix to attach to each line of output + * @param 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); @@ -130,14 +130,14 @@ public class DebugInfoItem extends OffsettedItem { /** * Performs debug info encoding. * - * @param file null-ok; file to refer to during encoding - * @param prefix null-ok; prefix to attach to each line of output - * @param debugPrint null-ok; if specified, an alternate output for + * @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 null-ok; if specified, where annotations should go + * @param out {@code null-ok;} if specified, where annotations should go * @param consume whether to claim to have consumed output for - * out - * @return non-null; the encoded array + * {@code out} + * @return {@code non-null;} the encoded array */ private byte[] encode(DexFile file, String prefix, PrintWriter debugPrint, AnnotatedOutput out, boolean consume) { @@ -161,14 +161,14 @@ public class DebugInfoItem extends OffsettedItem { /** * Helper for {@link #encode} to do most of the work. * - * @param file null-ok; file to refer to during encoding - * @param prefix null-ok; prefix to attach to each line of output - * @param debugPrint null-ok; if specified, an alternate output for + * @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 null-ok; if specified, where annotations should go + * @param out {@code null-ok;} if specified, where annotations should go * @param consume whether to claim to have consumed output for - * out - * @return non-null; the encoded array + * {@code out} + * @return {@code non-null;} the encoded array */ private byte[] encode0(DexFile file, String prefix, PrintWriter debugPrint, AnnotatedOutput out, boolean consume) { diff --git a/dx/src/com/android/dx/dex/file/DexFile.java b/dx/src/com/android/dx/dex/file/DexFile.java index 8a4075d67..a829fed82 100644 --- a/dx/src/com/android/dx/dex/file/DexFile.java +++ b/dx/src/com/android/dx/dex/file/DexFile.java @@ -38,67 +38,67 @@ import java.util.zip.Adler32; import static com.android.dx.dex.file.MixedItemSection.SortType; /** - * Representation of an entire .dex (Dalvik EXecutable) + * Representation of an entire {@code .dex} (Dalvik EXecutable) * file, which itself consists of a set of Dalvik classes. */ public final class DexFile { - /** non-null; word data section */ + /** {@code non-null;} word data section */ private final MixedItemSection wordData; /** - * non-null; type lists section. This is word data, but separating + * {@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; /** - * non-null; map section. The map needs to be in a section by itself + * {@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; - /** non-null; string data section */ + /** {@code non-null;} string data section */ private final MixedItemSection stringData; - /** non-null; string identifiers section */ + /** {@code non-null;} string identifiers section */ private final StringIdsSection stringIds; - /** non-null; type identifiers section */ + /** {@code non-null;} type identifiers section */ private final TypeIdsSection typeIds; - /** non-null; prototype identifiers section */ + /** {@code non-null;} prototype identifiers section */ private final ProtoIdsSection protoIds; - /** non-null; field identifiers section */ + /** {@code non-null;} field identifiers section */ private final FieldIdsSection fieldIds; - /** non-null; method identifiers section */ + /** {@code non-null;} method identifiers section */ private final MethodIdsSection methodIds; - /** non-null; class definitions section */ + /** {@code non-null;} class definitions section */ private final ClassDefsSection classDefs; - /** non-null; class data section */ + /** {@code non-null;} class data section */ private final MixedItemSection classData; - /** non-null; byte data section */ + /** {@code non-null;} byte data section */ private final MixedItemSection byteData; - /** non-null; file header */ + /** {@code non-null;} file header */ private final HeaderSection header; /** - * non-null; array of sections in the order they will appear in the + * {@code non-null;} array of sections in the order they will appear in the * final output file */ private final Section[] sections; - /** >= -1; total file size or -1 if unknown */ + /** {@code >= -1;} total file size or {@code -1} if unknown */ private int fileSize; - /** >= 40; maximum width of the file dump */ + /** {@code >= 40;} maximum width of the file dump */ private int dumpWidth; /** @@ -137,7 +137,7 @@ public final class DexFile { * Adds a class to this instance. It is illegal to attempt to add more * than one class with the same name. * - * @param clazz non-null; the class to add + * @param clazz {@code non-null;} the class to add */ public void add(ClassDefItem clazz) { classDefs.add(clazz); @@ -146,8 +146,8 @@ public final class DexFile { /** * Gets the class definition with the given name, if any. * - * @param name non-null; the class name to look for - * @return null-ok; the class with the given name, or null + * @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) { @@ -164,8 +164,8 @@ public final class DexFile { * Writes the contents of this instance as either a binary or a * human-readable form, or both. * - * @param out null-ok; where to write to - * @param humanOut null-ok; where to write human-oriented output to + * @param 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) @@ -183,12 +183,12 @@ public final class DexFile { } /** - * Returns the contents of this instance as a .dex file, - * in byte[] form. + * Returns the contents of this instance as a {@code .dex} file, + * in {@code byte[]} form. * - * @param humanOut null-ok; where to write human-oriented output to + * @param humanOut {@code null-ok;} where to write human-oriented output to * @param verbose whether to be verbose when writing human-oriented output - * @return non-null; a .dex file for this instance + * @return {@code non-null;} a {@code .dex} file for this instance */ public byte[] toDex(Writer humanOut, boolean verbose) throws IOException { @@ -205,7 +205,7 @@ public final class DexFile { /** * Sets the maximum width of the human-oriented dump of the instance. * - * @param dumpWidth >= 40; the width + * @param dumpWidth {@code >= 40;} the width */ public void setDumpWidth(int dumpWidth) { if (dumpWidth < 40) { @@ -221,7 +221,7 @@ public final class DexFile { *

    This is package-scope in order to allow * the {@link HeaderSection} to set itself up properly.

    * - * @return >= 0; the total file size + * @return {@code >= 0;} the total file size * @throws RuntimeException thrown if the file size is not yet known */ /*package*/ int getFileSize() { @@ -239,7 +239,7 @@ public final class DexFile { * the various {@link Item} instances to add items to the * instance.

    * - * @return non-null; the string data section + * @return {@code non-null;} the string data section */ /*package*/ MixedItemSection getStringData() { return stringData; @@ -252,7 +252,7 @@ public final class DexFile { * the various {@link Item} instances to add items to the * instance.

    * - * @return non-null; the word data section + * @return {@code non-null;} the word data section */ /*package*/ MixedItemSection getWordData() { return wordData; @@ -265,7 +265,7 @@ public final class DexFile { * the various {@link Item} instances to add items to the * instance.

    * - * @return non-null; the word data section + * @return {@code non-null;} the word data section */ /*package*/ MixedItemSection getTypeLists() { return typeLists; @@ -277,7 +277,7 @@ public final class DexFile { *

    This is package-scope in order to allow the header section * to query it.

    * - * @return non-null; the map section + * @return {@code non-null;} the map section */ /*package*/ MixedItemSection getMap() { return map; @@ -290,7 +290,7 @@ public final class DexFile { * the various {@link Item} instances to add items to the * instance.

    * - * @return non-null; the string identifiers section + * @return {@code non-null;} the string identifiers section */ /*package*/ StringIdsSection getStringIds() { return stringIds; @@ -303,7 +303,7 @@ public final class DexFile { * the various {@link Item} instances to add items to the * instance.

    * - * @return non-null; the class definitions section + * @return {@code non-null;} the class definitions section */ /*package*/ ClassDefsSection getClassDefs() { return classDefs; @@ -316,7 +316,7 @@ public final class DexFile { * the various {@link Item} instances to add items to the * instance.

    * - * @return non-null; the class data section + * @return {@code non-null;} the class data section */ /*package*/ MixedItemSection getClassData() { return classData; @@ -329,7 +329,7 @@ public final class DexFile { * the various {@link Item} instances to add items to the * instance.

    * - * @return non-null; the class identifiers section + * @return {@code non-null;} the class identifiers section */ /*package*/ TypeIdsSection getTypeIds() { return typeIds; @@ -342,7 +342,7 @@ public final class DexFile { * the various {@link Item} instances to add items to the * instance.

    * - * @return non-null; the prototype identifiers section + * @return {@code non-null;} the prototype identifiers section */ /*package*/ ProtoIdsSection getProtoIds() { return protoIds; @@ -355,7 +355,7 @@ public final class DexFile { * the various {@link Item} instances to add items to the * instance.

    * - * @return non-null; the field identifiers section + * @return {@code non-null;} the field identifiers section */ /*package*/ FieldIdsSection getFieldIds() { return fieldIds; @@ -368,7 +368,7 @@ public final class DexFile { * the various {@link Item} instances to add items to the * instance.

    * - * @return non-null; the method identifiers section + * @return {@code non-null;} the method identifiers section */ /*package*/ MethodIdsSection getMethodIds() { return methodIds; @@ -381,7 +381,7 @@ public final class DexFile { * the various {@link Item} instances to add items to the * instance.

    * - * @return non-null; the byte data section + * @return {@code non-null;} the byte data section */ /*package*/ MixedItemSection getByteData() { return byteData; @@ -394,7 +394,7 @@ public final class DexFile { *

    This is package-scope in order to allow the header section * to query it.

    * - * @return non-null; the section + * @return {@code non-null;} the section */ /*package*/ Section getFirstDataSection() { return wordData; @@ -407,7 +407,7 @@ public final class DexFile { *

    This is package-scope in order to allow the header section * to query it.

    * - * @return non-null; the section + * @return {@code non-null;} the section */ /*package*/ Section getLastDataSection() { return map; @@ -418,7 +418,7 @@ public final class DexFile { * instance, or do nothing if the given constant isn't the sort * that should be interned. * - * @param cst non-null; constant to possibly intern + * @param cst {@code non-null;} constant to possibly intern */ /*package*/ void internIfAppropriate(Constant cst) { if (cst instanceof CstString) { @@ -441,13 +441,13 @@ public final class DexFile { /** * Gets the {@link IndexedItem} corresponding to the given constant, * if it is a constant that has such a correspondence, or return - * null if it isn't such a constant. This will throw + * {@code null} if it isn't such a constant. This will throw * an exception if the given constant should have been found * but wasn't. * - * @param cst non-null; the constant to look up - * @return null-ok; its corresponding item, if it has a corresponding - * item, or null if it's not that sort of constant + * @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 */ /*package*/ IndexedItem findItemOrNull(Constant cst) { IndexedItem item; @@ -466,12 +466,12 @@ public final class DexFile { } /** - * Returns the contents of this instance as a .dex file, + * 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 non-null; a .dex file for this instance + * @return {@code non-null;} a {@code .dex} file for this instance */ private ByteArrayAnnotatedOutput toDex0(boolean annotate, boolean verbose) { @@ -586,7 +586,7 @@ public final class DexFile { /** * Generates and returns statistics for all the items in the file. * - * @return non-null; the statistics + * @return {@code non-null;} the statistics */ public Statistics getStatistics() { Statistics stats = new Statistics(); @@ -599,10 +599,10 @@ public final class DexFile { } /** - * Calculates the signature for the .dex file in the + * Calculates the signature for the {@code .dex} file in the * given array, and modify the array to contain it. * - * @param bytes non-null; the bytes of the file + * @param bytes {@code non-null;} the bytes of the file */ private static void calcSignature(byte[] bytes) { MessageDigest md; @@ -627,10 +627,10 @@ public final class DexFile { } /** - * Calculates the checksum for the .dex file in the + * Calculates the checksum for the {@code .dex} file in the * given array, and modify the array to contain it. * - * @param bytes non-null; the bytes of the file + * @param bytes {@code non-null;} the bytes of the file */ private static void calcChecksum(byte[] bytes) { Adler32 a32 = new Adler32(); diff --git a/dx/src/com/android/dx/dex/file/EncodedArrayItem.java b/dx/src/com/android/dx/dex/file/EncodedArrayItem.java index 9ec72fa42..c55c6f503 100644 --- a/dx/src/com/android/dx/dex/file/EncodedArrayItem.java +++ b/dx/src/com/android/dx/dex/file/EncodedArrayItem.java @@ -36,11 +36,11 @@ public final class EncodedArrayItem extends OffsettedItem { /** the required alignment for instances of this class */ private static final int ALIGNMENT = 1; - /** non-null; the array to represent */ + /** {@code non-null;} the array to represent */ private final CstArray array; /** - * null-ok; encoded form, ready for writing to a file; set during + * {@code null-ok;} encoded form, ready for writing to a file; set during * {@link #place0} */ private byte[] encodedForm; @@ -48,7 +48,7 @@ public final class EncodedArrayItem extends OffsettedItem { /** * Constructs an instance. * - * @param array non-null; array to represent + * @param array {@code non-null;} array to represent */ public EncodedArrayItem(CstArray array) { /* diff --git a/dx/src/com/android/dx/dex/file/EncodedField.java b/dx/src/com/android/dx/dex/file/EncodedField.java index 62a5789d4..146a60452 100644 --- a/dx/src/com/android/dx/dex/file/EncodedField.java +++ b/dx/src/com/android/dx/dex/file/EncodedField.java @@ -30,13 +30,13 @@ import java.io.PrintWriter; */ public final class EncodedField extends EncodedMember implements Comparable { - /** non-null; constant for the field */ + /** {@code non-null;} constant for the field */ private final CstFieldRef field; /** * Constructs an instance. * - * @param field non-null; constant for the field + * @param field {@code non-null;} constant for the field * @param accessFlags access flags */ public EncodedField(CstFieldRef field, int accessFlags) { @@ -122,7 +122,7 @@ public final class EncodedField extends EncodedMember /** * Gets the constant for the field. * - * @return non-null; the constant + * @return {@code non-null;} the constant */ public CstFieldRef getRef() { return field; diff --git a/dx/src/com/android/dx/dex/file/EncodedMember.java b/dx/src/com/android/dx/dex/file/EncodedMember.java index 3ae0d0902..68119f3d4 100644 --- a/dx/src/com/android/dx/dex/file/EncodedMember.java +++ b/dx/src/com/android/dx/dex/file/EncodedMember.java @@ -51,14 +51,14 @@ public abstract class EncodedMember implements ToHuman { /** * Gets the name. * - * @return non-null; the name + * @return {@code non-null;} the name */ public abstract CstUtf8 getName(); /** * Does a human-friendly dump of this instance. * - * @param out non-null; where to dump + * @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); @@ -66,20 +66,20 @@ public abstract class EncodedMember implements ToHuman { /** * Populates a {@link DexFile} with items from within this instance. * - * @param file non-null; the file to populate + * @param file {@code non-null;} the file to populate */ public abstract void addContents(DexFile file); /** * Encodes this instance to the given output. * - * @param file non-null; file this instance is part of - * @param out non-null; where to write to - * @param lastIndex >= 0; the previous member index value encoded, or - * 0 if this is the first element to encode - * @param dumpSeq >= 0; sequence number of this instance for + * @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 >= 0; the member index value that was encoded + * @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/dx/dex/file/EncodedMethod.java b/dx/src/com/android/dx/dex/file/EncodedMethod.java index 319fbb72b..dff1a0732 100644 --- a/dx/src/com/android/dx/dex/file/EncodedMethod.java +++ b/dx/src/com/android/dx/dex/file/EncodedMethod.java @@ -32,23 +32,23 @@ import java.io.PrintWriter; */ public final class EncodedMethod extends EncodedMember implements Comparable { - /** non-null; constant for the method */ + /** {@code non-null;} constant for the method */ private final CstMethodRef method; /** - * null-ok; code for the method, if the method is neither - * abstract nor native + * {@code null-ok;} code for the method, if the method is neither + * {@code abstract} nor {@code native} */ private final CodeItem code; /** * Constructs an instance. * - * @param method non-null; constant for the method + * @param method {@code non-null;} constant for the method * @param accessFlags access flags - * @param code null-ok; code for the method, if it is neither - * abstract nor native - * @param throwsList non-null; list of possibly-thrown exceptions, + * @param code {@code null-ok;} code for the method, if it is neither + * {@code abstract} nor {@code native} + * @param throwsList {@code non-null;} list of possibly-thrown exceptions, * just used in generating debugging output (listings) */ public EncodedMethod(CstMethodRef method, int accessFlags, @@ -148,7 +148,7 @@ public final class EncodedMethod extends EncodedMember /** * Gets the constant for the method. * - * @return non-null; the constant + * @return {@code non-null;} the constant */ public final CstMethodRef getRef() { return method; diff --git a/dx/src/com/android/dx/dex/file/FieldAnnotationStruct.java b/dx/src/com/android/dx/dex/file/FieldAnnotationStruct.java index e6169bd21..6a76ca926 100644 --- a/dx/src/com/android/dx/dex/file/FieldAnnotationStruct.java +++ b/dx/src/com/android/dx/dex/file/FieldAnnotationStruct.java @@ -27,17 +27,17 @@ import com.android.dx.util.ToHuman; */ public final class FieldAnnotationStruct implements ToHuman, Comparable { - /** non-null; the field in question */ + /** {@code non-null;} the field in question */ private final CstFieldRef field; - /** non-null; the associated annotations */ + /** {@code non-null;} the associated annotations */ private AnnotationSetItem annotations; /** * Constructs an instance. * - * @param field non-null; the field in question - * @param annotations non-null; the associated annotations + * @param field {@code non-null;} the field in question + * @param annotations {@code non-null;} the associated annotations */ public FieldAnnotationStruct(CstFieldRef field, AnnotationSetItem annotations) { @@ -105,7 +105,7 @@ public final class FieldAnnotationStruct /** * Gets the field this item is for. * - * @return non-null; the field + * @return {@code non-null;} the field */ public CstFieldRef getField() { return field; @@ -114,7 +114,7 @@ public final class FieldAnnotationStruct /** * Gets the associated annotations. * - * @return non-null; the annotations + * @return {@code non-null;} the annotations */ public Annotations getAnnotations() { return annotations.getAnnotations(); diff --git a/dx/src/com/android/dx/dex/file/FieldIdItem.java b/dx/src/com/android/dx/dex/file/FieldIdItem.java index d098d52ab..d6d01d518 100644 --- a/dx/src/com/android/dx/dex/file/FieldIdItem.java +++ b/dx/src/com/android/dx/dex/file/FieldIdItem.java @@ -25,7 +25,7 @@ public final class FieldIdItem extends MemberIdItem { /** * Constructs an instance. * - * @param field non-null; the constant for the field + * @param field {@code non-null;} the constant for the field */ public FieldIdItem(CstFieldRef field) { super(field); @@ -49,7 +49,7 @@ public final class FieldIdItem extends MemberIdItem { /** * Gets the field constant. * - * @return non-null; the constant + * @return {@code non-null;} the constant */ public CstFieldRef getFieldRef() { return (CstFieldRef) getRef(); diff --git a/dx/src/com/android/dx/dex/file/FieldIdsSection.java b/dx/src/com/android/dx/dex/file/FieldIdsSection.java index fddf55f47..59ef2294a 100644 --- a/dx/src/com/android/dx/dex/file/FieldIdsSection.java +++ b/dx/src/com/android/dx/dex/file/FieldIdsSection.java @@ -25,11 +25,11 @@ import java.util.Collection; import java.util.TreeMap; /** - * Field refs list section of a .dex file. + * Field refs list section of a {@code .dex} file. */ public final class FieldIdsSection extends MemberIdsSection { /** - * non-null; map from field constants to {@link + * {@code non-null;} map from field constants to {@link * FieldIdItem} instances */ private final TreeMap fieldIds; @@ -37,7 +37,7 @@ public final class FieldIdsSection extends MemberIdsSection { /** * Constructs an instance. The file offset is initially unknown. * - * @param file non-null; file that this instance is part of + * @param file {@code non-null;} file that this instance is part of */ public FieldIdsSection(DexFile file) { super("field_ids", file); @@ -72,7 +72,7 @@ public final class FieldIdsSection extends MemberIdsSection { /** * Writes the portion of the file header that refers to this instance. * - * @param out non-null; where to write + * @param out {@code non-null;} where to write */ public void writeHeaderPart(AnnotatedOutput out) { throwIfNotPrepared(); @@ -92,8 +92,8 @@ public final class FieldIdsSection extends MemberIdsSection { /** * Interns an element into this instance. * - * @param field non-null; the reference to intern - * @return non-null; the interned reference + * @param field {@code non-null;} the reference to intern + * @return {@code non-null;} the interned reference */ public FieldIdItem intern(CstFieldRef field) { if (field == null) { @@ -116,8 +116,8 @@ public final class FieldIdsSection extends MemberIdsSection { * Gets the index of the given reference, which must have been added * to this instance. * - * @param ref non-null; the reference to look up - * @return >= 0; the reference's index + * @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) { diff --git a/dx/src/com/android/dx/dex/file/HeaderItem.java b/dx/src/com/android/dx/dex/file/HeaderItem.java index 55c1f1c5f..65938599d 100644 --- a/dx/src/com/android/dx/dex/file/HeaderItem.java +++ b/dx/src/com/android/dx/dex/file/HeaderItem.java @@ -21,11 +21,11 @@ import com.android.dx.util.AnnotatedOutput; import com.android.dx.util.Hex; /** - * File header section of a .dex file. + * File header section of a {@code .dex} file. */ public final class HeaderItem extends IndexedItem { /** - * non-null; the file format magic number, represented as the + * {@code non-null;} the file format magic number, represented as the * low-order bytes of a string */ private static final String MAGIC = "dex\n035\0"; diff --git a/dx/src/com/android/dx/dex/file/HeaderSection.java b/dx/src/com/android/dx/dex/file/HeaderSection.java index 9022e0f2b..5bc627819 100644 --- a/dx/src/com/android/dx/dex/file/HeaderSection.java +++ b/dx/src/com/android/dx/dex/file/HeaderSection.java @@ -23,16 +23,16 @@ import java.util.Collections; import java.util.List; /** - * File header section of a .dex file. + * File header section of a {@code .dex} file. */ public final class HeaderSection extends UniformItemSection { - /** non-null; the list of the one item in the section */ + /** {@code non-null;} the list of the one item in the section */ private final List list; /** * Constructs an instance. The file offset is initially unknown. * - * @param file non-null; file that this instance is part of + * @param file {@code non-null;} file that this instance is part of */ public HeaderSection(DexFile file) { super(null, file, 4); diff --git a/dx/src/com/android/dx/dex/file/IdItem.java b/dx/src/com/android/dx/dex/file/IdItem.java index 8342514be..5d7c9e320 100644 --- a/dx/src/com/android/dx/dex/file/IdItem.java +++ b/dx/src/com/android/dx/dex/file/IdItem.java @@ -23,7 +23,7 @@ import com.android.dx.rop.cst.CstType; */ public abstract class IdItem extends IndexedItem { /** - * non-null; the type constant for the defining class of + * {@code non-null;} the type constant for the defining class of * the reference */ private final CstType type; @@ -31,7 +31,7 @@ public abstract class IdItem extends IndexedItem { /** * Constructs an instance. * - * @param type non-null; the type constant for the defining + * @param type {@code non-null;} the type constant for the defining * class of the reference */ public IdItem(CstType type) { @@ -53,7 +53,7 @@ public abstract class IdItem extends IndexedItem { * Gets the type constant for the defining class of the * reference. * - * @return non-null; the type constant + * @return {@code non-null;} the type constant */ public final CstType getDefiningClass() { return type; diff --git a/dx/src/com/android/dx/dex/file/IndexedItem.java b/dx/src/com/android/dx/dex/file/IndexedItem.java index 9bf7fd29a..32d69eaea 100644 --- a/dx/src/com/android/dx/dex/file/IndexedItem.java +++ b/dx/src/com/android/dx/dex/file/IndexedItem.java @@ -20,7 +20,7 @@ package com.android.dx.dex.file; * An item in a Dalvik file which is referenced by index. */ public abstract class IndexedItem extends Item { - /** >= -1; assigned index of the item, or -1 if not + /** {@code >= -1;} assigned index of the item, or {@code -1} if not * yet assigned */ private int index; @@ -34,7 +34,7 @@ public abstract class IndexedItem extends Item { /** * Gets whether or not this instance has been assigned an index. * - * @return true iff 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); @@ -43,7 +43,7 @@ public abstract class IndexedItem extends Item { /** * Gets the item index. * - * @return >= 0; the index + * @return {@code >= 0;} the index * @throws RuntimeException thrown if the item index is not yet assigned */ public final int getIndex() { @@ -56,10 +56,10 @@ public abstract class IndexedItem extends Item { /** * Sets the item index. This method may only ever be called once - * per instance, and this will throw a RuntimeException if + * per instance, and this will throw a {@code RuntimeException} if * called a second (or subsequent) time. * - * @param index >= 0; the item index + * @param index {@code >= 0;} the item index */ public final void setIndex(int index) { if (this.index != -1) { @@ -73,7 +73,7 @@ public abstract class IndexedItem extends Item { * Gets the index of this item as a string, suitable for including in * annotations. * - * @return non-null; the index string + * @return {@code non-null;} the index string */ public final String indexString() { return '[' + Integer.toHexString(index) + ']'; diff --git a/dx/src/com/android/dx/dex/file/Item.java b/dx/src/com/android/dx/dex/file/Item.java index 3708d4561..057f218b2 100644 --- a/dx/src/com/android/dx/dex/file/Item.java +++ b/dx/src/com/android/dx/dex/file/Item.java @@ -33,7 +33,7 @@ public abstract class Item { /** * Returns the item type for this instance. * - * @return non-null; the item type + * @return {@code non-null;} the item type */ public abstract ItemType itemType(); @@ -41,7 +41,7 @@ public abstract class Item { * Returns the human name for the particular type of item this * instance is. * - * @return non-null; the name + * @return {@code non-null;} the name */ public final String typeName() { return itemType().toHuman(); @@ -50,7 +50,7 @@ public abstract class Item { /** * Gets the size of this instance when written, in bytes. * - * @return >= 0; the write size + * @return {@code >= 0;} the write size */ public abstract int writeSize(); @@ -62,7 +62,7 @@ public abstract class Item { *

    Note: Subclasses must override this to do something * appropriate.

    * - * @param file non-null; the file to populate + * @param file {@code non-null;} the file to populate */ public abstract void addContents(DexFile file); @@ -73,8 +73,8 @@ public abstract class Item { * note the written offset and will also throw an exception if this * instance has already been written. * - * @param file non-null; the file to use for reference - * @param out non-null; where to write to + * @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/dx/dex/file/ItemType.java b/dx/src/com/android/dx/dex/file/ItemType.java index ffa65735f..83843b7a4 100644 --- a/dx/src/com/android/dx/dex/file/ItemType.java +++ b/dx/src/com/android/dx/dex/file/ItemType.java @@ -48,17 +48,17 @@ public enum ItemType implements ToHuman { /** value when represented in a {@link MapItem} */ private final int mapValue; - /** non-null; name of the type */ + /** {@code non-null;} name of the type */ private final String typeName; - /** non-null; the short human name */ + /** {@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 non-null; name of the type + * @param typeName {@code non-null;} name of the type */ private ItemType(int mapValue, String typeName) { this.mapValue = mapValue; @@ -84,7 +84,7 @@ public enum ItemType implements ToHuman { /** * Gets the type name. * - * @return non-null; the type name + * @return {@code non-null;} the type name */ public String getTypeName() { return typeName; diff --git a/dx/src/com/android/dx/dex/file/MapItem.java b/dx/src/com/android/dx/dex/file/MapItem.java index 5e7465ce0..c728dd7cd 100644 --- a/dx/src/com/android/dx/dex/file/MapItem.java +++ b/dx/src/com/android/dx/dex/file/MapItem.java @@ -28,29 +28,29 @@ 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 uints */ + /** write size of this class, in bytes: three {@code uint}s */ private static final int WRITE_SIZE = (4 * 3); - /** non-null; item type this instance covers */ + /** {@code non-null;} item type this instance covers */ private final ItemType type; - /** non-null; section this instance covers */ + /** {@code non-null;} section this instance covers */ private final Section section; /** - * null-ok; first item covered or null if this is + * {@code null-ok;} first item covered or {@code null} if this is * a self-reference */ private final Item firstItem; /** - * null-ok; last item covered or null if this is + * {@code null-ok;} last item covered or {@code null} if this is * a self-reference */ private final Item lastItem; /** - * > 0; count of items covered; 1 if this + * {@code > 0;} count of items covered; {@code 1} if this * is a self-reference */ private final int itemCount; @@ -60,8 +60,8 @@ public final class MapItem extends OffsettedItem { * the contents of the given array of sections, adding it to the * given map section. * - * @param sections non-null; the sections - * @param mapSection non-null; the section that the resulting map + * @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, @@ -115,11 +115,11 @@ public final class MapItem extends OffsettedItem { /** * Constructs an instance. * - * @param type non-null; item type this instance covers - * @param section non-null; section this instance covers - * @param firstItem non-null; first item covered - * @param lastItem non-null; last item covered - * @param itemCount > 0; count of items covered + * @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) { @@ -154,9 +154,9 @@ public final class MapItem extends OffsettedItem { /** * Constructs a self-referential instance. This instance is meant to - * represent the section containing the map_list. + * represent the section containing the {@code map_list}. * - * @param section non-null; section this instance covers + * @param section {@code non-null;} section this instance covers */ private MapItem(Section section) { super(ALIGNMENT, WRITE_SIZE); diff --git a/dx/src/com/android/dx/dex/file/MemberIdItem.java b/dx/src/com/android/dx/dex/file/MemberIdItem.java index d43715259..574d41395 100644 --- a/dx/src/com/android/dx/dex/file/MemberIdItem.java +++ b/dx/src/com/android/dx/dex/file/MemberIdItem.java @@ -29,13 +29,13 @@ public abstract class MemberIdItem extends IdItem { /** size of instances when written out to a file, in bytes */ public static final int WRITE_SIZE = 8; - /** non-null; the constant for the member */ + /** {@code non-null;} the constant for the member */ private final CstMemberRef cst; /** * Constructs an instance. * - * @param cst non-null; the constant for the member + * @param cst {@code non-null;} the constant for the member */ public MemberIdItem(CstMemberRef cst) { super(cst.getDefiningClass()); @@ -86,7 +86,7 @@ public abstract class MemberIdItem extends IdItem { * this item, in order that it may be written out. Subclasses must * override this to get whatever it is they need to store. * - * @param file non-null; the file being written + * @param file {@code non-null;} the file being written * @return the index in question */ protected abstract int getTypoidIdx(DexFile file); @@ -96,14 +96,14 @@ public abstract class MemberIdItem extends IdItem { * this item, for listing-generating purposes. Subclasses must override * this. * - * @return non-null; the name in question + * @return {@code non-null;} the name in question */ protected abstract String getTypoidName(); /** * Gets the member constant. * - * @return non-null; the constant + * @return {@code non-null;} the constant */ public final CstMemberRef getRef() { return cst; diff --git a/dx/src/com/android/dx/dex/file/MemberIdsSection.java b/dx/src/com/android/dx/dex/file/MemberIdsSection.java index 885b5590a..20b160551 100644 --- a/dx/src/com/android/dx/dex/file/MemberIdsSection.java +++ b/dx/src/com/android/dx/dex/file/MemberIdsSection.java @@ -17,15 +17,15 @@ package com.android.dx.dex.file; /** - * Member (field or method) refs list section of a .dex file. + * Member (field or method) refs list section of a {@code .dex} file. */ public abstract class MemberIdsSection extends UniformItemSection { /** * Constructs an instance. The file offset is initially unknown. * - * @param name null-ok; the name of this instance, for annotation + * @param name {@code null-ok;} the name of this instance, for annotation * purposes - * @param file non-null; file that this instance is part of + * @param file {@code non-null;} file that this instance is part of */ public MemberIdsSection(String name, DexFile file) { super(name, file, 4); diff --git a/dx/src/com/android/dx/dex/file/MethodAnnotationStruct.java b/dx/src/com/android/dx/dex/file/MethodAnnotationStruct.java index 175c1d29d..3c254a199 100644 --- a/dx/src/com/android/dx/dex/file/MethodAnnotationStruct.java +++ b/dx/src/com/android/dx/dex/file/MethodAnnotationStruct.java @@ -27,17 +27,17 @@ import com.android.dx.util.ToHuman; */ public final class MethodAnnotationStruct implements ToHuman, Comparable { - /** non-null; the method in question */ + /** {@code non-null;} the method in question */ private final CstMethodRef method; - /** non-null; the associated annotations */ + /** {@code non-null;} the associated annotations */ private AnnotationSetItem annotations; /** * Constructs an instance. * - * @param method non-null; the method in question - * @param annotations non-null; the associated annotations + * @param method {@code non-null;} the method in question + * @param annotations {@code non-null;} the associated annotations */ public MethodAnnotationStruct(CstMethodRef method, AnnotationSetItem annotations) { @@ -105,7 +105,7 @@ public final class MethodAnnotationStruct /** * Gets the method this item is for. * - * @return non-null; the method + * @return {@code non-null;} the method */ public CstMethodRef getMethod() { return method; @@ -114,7 +114,7 @@ public final class MethodAnnotationStruct /** * Gets the associated annotations. * - * @return non-null; the annotations + * @return {@code non-null;} the annotations */ public Annotations getAnnotations() { return annotations.getAnnotations(); diff --git a/dx/src/com/android/dx/dex/file/MethodIdItem.java b/dx/src/com/android/dx/dex/file/MethodIdItem.java index 5d78e961c..bbd6c932b 100644 --- a/dx/src/com/android/dx/dex/file/MethodIdItem.java +++ b/dx/src/com/android/dx/dex/file/MethodIdItem.java @@ -25,7 +25,7 @@ public final class MethodIdItem extends MemberIdItem { /** * Constructs an instance. * - * @param method non-null; the constant for the method + * @param method {@code non-null;} the constant for the method */ public MethodIdItem(CstBaseMethodRef method) { super(method); @@ -49,7 +49,7 @@ public final class MethodIdItem extends MemberIdItem { /** * Gets the method constant. * - * @return non-null; the constant + * @return {@code non-null;} the constant */ public CstBaseMethodRef getMethodRef() { return (CstBaseMethodRef) getRef(); diff --git a/dx/src/com/android/dx/dex/file/MethodIdsSection.java b/dx/src/com/android/dx/dex/file/MethodIdsSection.java index 6ba7cac95..f3e7deefd 100644 --- a/dx/src/com/android/dx/dex/file/MethodIdsSection.java +++ b/dx/src/com/android/dx/dex/file/MethodIdsSection.java @@ -25,11 +25,11 @@ import java.util.Collection; import java.util.TreeMap; /** - * Method refs list section of a .dex file. + * Method refs list section of a {@code .dex} file. */ public final class MethodIdsSection extends MemberIdsSection { /** - * non-null; map from method constants to {@link + * {@code non-null;} map from method constants to {@link * MethodIdItem} instances */ private final TreeMap methodIds; @@ -37,7 +37,7 @@ public final class MethodIdsSection extends MemberIdsSection { /** * Constructs an instance. The file offset is initially unknown. * - * @param file non-null; file that this instance is part of + * @param file {@code non-null;} file that this instance is part of */ public MethodIdsSection(DexFile file) { super("method_ids", file); @@ -72,7 +72,7 @@ public final class MethodIdsSection extends MemberIdsSection { /** * Writes the portion of the file header that refers to this instance. * - * @param out non-null; where to write + * @param out {@code non-null;} where to write */ public void writeHeaderPart(AnnotatedOutput out) { throwIfNotPrepared(); @@ -92,8 +92,8 @@ public final class MethodIdsSection extends MemberIdsSection { /** * Interns an element into this instance. * - * @param method non-null; the reference to intern - * @return non-null; the interned reference + * @param method {@code non-null;} the reference to intern + * @return {@code non-null;} the interned reference */ public MethodIdItem intern(CstBaseMethodRef method) { if (method == null) { @@ -116,8 +116,8 @@ public final class MethodIdsSection extends MemberIdsSection { * Gets the index of the given reference, which must have been added * to this instance. * - * @param ref non-null; the reference to look up - * @return >= 0; the reference's index + * @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) { diff --git a/dx/src/com/android/dx/dex/file/MixedItemSection.java b/dx/src/com/android/dx/dex/file/MixedItemSection.java index f03a9a387..0929fe761 100644 --- a/dx/src/com/android/dx/dex/file/MixedItemSection.java +++ b/dx/src/com/android/dx/dex/file/MixedItemSection.java @@ -31,7 +31,7 @@ import java.util.NoSuchElementException; import java.util.TreeMap; /** - * A section of a .dex file which consists of a sequence of + * A section of a {@code .dex} file which consists of a sequence of * {@link OffsettedItem} objects, which may each be of a different concrete * class and/or size. * @@ -50,7 +50,7 @@ public final class MixedItemSection extends Section { INSTANCE; }; - /** non-null; sorter which sorts instances by type */ + /** {@code non-null;} sorter which sorts instances by type */ private static final Comparator TYPE_SORTER = new Comparator() { public int compare(OffsettedItem item1, OffsettedItem item2) { @@ -60,17 +60,17 @@ public final class MixedItemSection extends Section { } }; - /** non-null; the items in this part */ + /** {@code non-null;} the items in this part */ private final ArrayList items; - /** non-null; items that have been explicitly interned */ + /** {@code non-null;} items that have been explicitly interned */ private final HashMap interns; - /** non-null; how to sort the items */ + /** {@code non-null;} how to sort the items */ private final SortType sort; /** - * >= -1; the current size of this part, in bytes, or -1 + * {@code >= -1;} the current size of this part, in bytes, or {@code -1} * if not yet calculated */ private int writeSize; @@ -78,10 +78,10 @@ public final class MixedItemSection extends Section { /** * Constructs an instance. The file offset is initially unknown. * - * @param name null-ok; the name of this instance, for annotation + * @param name {@code null-ok;} the name of this instance, for annotation * purposes - * @param file non-null; file that this instance is part of - * @param alignment > 0; alignment requirement for the final output; + * @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 */ @@ -118,7 +118,7 @@ public final class MixedItemSection extends Section { /** * Gets the size of this instance, in items. * - * @return >= 0; the size + * @return {@code >= 0;} the size */ public int size() { return items.size(); @@ -127,7 +127,7 @@ public final class MixedItemSection extends Section { /** * Writes the portion of the file header that refers to this instance. * - * @param out non-null; where to write + * @param out {@code non-null;} where to write */ public void writeHeaderPart(AnnotatedOutput out) { throwIfNotPrepared(); @@ -164,7 +164,7 @@ public final class MixedItemSection extends Section { * same item to more than one instance, nor to add the same items * multiple times to a single instance. * - * @param item non-null; the item to add + * @param item {@code non-null;} the item to add */ public void add(OffsettedItem item) { throwIfPrepared(); @@ -187,8 +187,8 @@ public final class MixedItemSection extends Section { * (which may not be the one passed in). This will add the item if no * equal item has been added. * - * @param item non-null; the item to intern - * @return non-null; the equivalent interned instance + * @param item {@code non-null;} the item to intern + * @return {@code non-null;} the equivalent interned instance */ public T intern(T item) { throwIfPrepared(); @@ -207,8 +207,8 @@ public final class MixedItemSection extends Section { /** * Gets an item which was previously interned. * - * @param item non-null; the item to look for - * @return non-null; the equivalent already-interned instance + * @param item {@code non-null;} the item to look for + * @return {@code non-null;} the equivalent already-interned instance */ public T get(T item) { throwIfNotPrepared(); @@ -227,9 +227,9 @@ public final class MixedItemSection extends Section { * given type. If there are none, this writes nothing. If there are any, * then the index is preceded by the given intro string. * - * @param out non-null; where to write to - * @param itemType non-null; the item type of interest - * @param intro non-null; the introductory string for non-empty indices + * @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) { @@ -285,7 +285,7 @@ public final class MixedItemSection extends Section { /** * 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 place, + * 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. diff --git a/dx/src/com/android/dx/dex/file/OffsettedItem.java b/dx/src/com/android/dx/dex/file/OffsettedItem.java index 030c37089..c8e2d747b 100644 --- a/dx/src/com/android/dx/dex/file/OffsettedItem.java +++ b/dx/src/com/android/dx/dex/file/OffsettedItem.java @@ -24,32 +24,32 @@ import com.android.dx.util.ExceptionWithContext; */ public abstract class OffsettedItem extends Item implements Comparable { - /** > 0; alignment requirement */ + /** {@code > 0;} alignment requirement */ private final int alignment; - /** >= -1; the size of this instance when written, in bytes, or - * -1 if not yet known */ + /** {@code >= -1;} the size of this instance when written, in bytes, or + * {@code -1} if not yet known */ private int writeSize; /** - * null-ok; section the item was added to, or null if + * {@code null-ok;} section the item was added to, or {@code null} if * not yet added */ private Section addedTo; /** - * >= -1; assigned offset of the item from the start of its section, - * or -1 if not yet assigned + * {@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 0 - * if handed null. + * Gets the absolute offset of the given item, returning {@code 0} + * if handed {@code null}. * - * @param item null-ok; the item in question - * @return >= 0; the item's absolute offset, or 0 - * if item == 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) { @@ -62,10 +62,10 @@ public abstract class OffsettedItem extends Item /** * Constructs an instance. The offset is initially unassigned. * - * @param alignment > 0; output alignment requirement; must be a + * @param alignment {@code > 0;} output alignment requirement; must be a * power of 2 - * @param writeSize >= -1; the size of this instance when written, - * in bytes, or -1 if not immediately known + * @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); @@ -131,7 +131,7 @@ public abstract class OffsettedItem extends Item * per instance, and only if the size was unknown upon instance * creation. * - * @param writeSize > 0; the write size, in bytes + * @param writeSize {@code > 0;} the write size, in bytes */ public final void setWriteSize(int writeSize) { if (writeSize < 0) { @@ -182,7 +182,7 @@ public abstract class OffsettedItem extends Item * Gets the relative item offset. The offset is from the start of * the section which the instance was written to. * - * @return >= 0; the offset + * @return {@code >= 0;} the offset * @throws RuntimeException thrown if the offset is not yet known */ public final int getRelativeOffset() { @@ -197,7 +197,7 @@ public abstract class OffsettedItem extends Item * Gets the absolute item offset. The offset is from the start of * the file which the instance was written to. * - * @return >= 0; the offset + * @return {@code >= 0;} the offset * @throws RuntimeException thrown if the offset is not yet known */ public final int getAbsoluteOffset() { @@ -213,10 +213,11 @@ public abstract class OffsettedItem extends Item * the given offset. It is only valid to call this method once per * instance. * - * @param addedTo non-null; the section this instance has been added to - * @param offset >= 0; the desired offset from the start of the + * @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 >= 0; the offset that this instance should be placed at + * @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) { @@ -247,7 +248,7 @@ public abstract class OffsettedItem extends Item * Gets the alignment requirement of this instance. An instance should * only be written when so aligned. * - * @return > 0; the alignment requirement; must be a power of 2 + * @return {@code > 0;} the alignment requirement; must be a power of 2 */ public final int getAlignment() { return alignment; @@ -257,7 +258,7 @@ public abstract class OffsettedItem extends Item * Gets the absolute offset of this item as a string, suitable for * including in annotations. * - * @return non-null; the offset string + * @return {@code non-null;} the offset string */ public final String offsetString() { return '[' + Integer.toHexString(getAbsoluteOffset()) + ']'; @@ -266,7 +267,7 @@ public abstract class OffsettedItem extends Item /** * Gets a short human-readable string representing this instance. * - * @return non-null; the human form + * @return {@code non-null;} the human form */ public abstract String toHuman(); @@ -277,8 +278,8 @@ public abstract class OffsettedItem extends Item * class needs to actually sort, then it should override this * method. * - * @param other non-null; instance to compare to - * @return -1, 0, or 1, depending + * @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) { @@ -293,8 +294,8 @@ public abstract class OffsettedItem extends Item * know its write size up-front, then this method is responsible * for setting it. * - * @param addedTo non-null; the section this instance has been added to - * @param offset >= 0; the offset from the start of the + * @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) { @@ -306,8 +307,8 @@ public abstract class OffsettedItem extends Item * the given data section. This is called by {@link #writeTo}, * which will have taken care of ensuring alignment. * - * @param file non-null; the file to use for reference - * @param out non-null; where to write to + * @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/dx/dex/file/ParameterAnnotationStruct.java b/dx/src/com/android/dx/dex/file/ParameterAnnotationStruct.java index 0c2d28692..46d045012 100644 --- a/dx/src/com/android/dx/dex/file/ParameterAnnotationStruct.java +++ b/dx/src/com/android/dx/dex/file/ParameterAnnotationStruct.java @@ -30,20 +30,20 @@ import java.util.ArrayList; */ public final class ParameterAnnotationStruct implements ToHuman, Comparable { - /** non-null; the method in question */ + /** {@code non-null;} the method in question */ private final CstMethodRef method; - /** non-null; the associated annotations list */ + /** {@code non-null;} the associated annotations list */ private final AnnotationsList annotationsList; - /** non-null; the associated annotations list, as an item */ + /** {@code non-null;} the associated annotations list, as an item */ private final UniformListItem annotationsItem; /** * Constructs an instance. * - * @param method non-null; the method in question - * @param annotationsList non-null; the associated annotations list + * @param method {@code non-null;} the method in question + * @param annotationsList {@code non-null;} the associated annotations list */ public ParameterAnnotationStruct(CstMethodRef method, AnnotationsList annotationsList) { @@ -144,7 +144,7 @@ public final class ParameterAnnotationStruct /** * Gets the method this item is for. * - * @return non-null; the method + * @return {@code non-null;} the method */ public CstMethodRef getMethod() { return method; @@ -153,7 +153,7 @@ public final class ParameterAnnotationStruct /** * Gets the associated annotations list. * - * @return non-null; the annotations list + * @return {@code non-null;} the annotations list */ public AnnotationsList getAnnotationsList() { return annotationsList; diff --git a/dx/src/com/android/dx/dex/file/ProtoIdItem.java b/dx/src/com/android/dx/dex/file/ProtoIdItem.java index a144c304e..afc227c2d 100644 --- a/dx/src/com/android/dx/dex/file/ProtoIdItem.java +++ b/dx/src/com/android/dx/dex/file/ProtoIdItem.java @@ -31,14 +31,14 @@ public final class ProtoIdItem extends IndexedItem { /** size of instances when written out to a file, in bytes */ public static final int WRITE_SIZE = 12; - /** non-null; the wrapped prototype */ + /** {@code non-null;} the wrapped prototype */ private final Prototype prototype; - /** non-null; the short-form of the prototype */ + /** {@code non-null;} the short-form of the prototype */ private final CstUtf8 shortForm; /** - * null-ok; the list of parameter types or null if this + * {@code null-ok;} the list of parameter types or {@code null} if this * prototype has no parameters */ private TypeListItem parameterTypes; @@ -46,7 +46,7 @@ public final class ProtoIdItem extends IndexedItem { /** * Constructs an instance. * - * @param prototype non-null; the constant for the prototype + * @param prototype {@code non-null;} the constant for the prototype */ public ProtoIdItem(Prototype prototype) { if (prototype == null) { @@ -64,8 +64,8 @@ public final class ProtoIdItem extends IndexedItem { /** * Creates the short-form of the given prototype. * - * @param prototype non-null; the prototype - * @return non-null; the short form + * @param prototype {@code non-null;} the prototype + * @return {@code non-null;} the short form */ private static CstUtf8 makeShortForm(Prototype prototype) { StdTypeList parameters = prototype.getParameterTypes(); @@ -84,7 +84,7 @@ public final class ProtoIdItem extends IndexedItem { /** * Gets the short-form character for the given type. * - * @param type non-null; the type + * @param type {@code non-null;} the type * @return the corresponding short-form character */ private static char shortFormCharFor(Type type) { diff --git a/dx/src/com/android/dx/dex/file/ProtoIdsSection.java b/dx/src/com/android/dx/dex/file/ProtoIdsSection.java index 852ab9d5f..8a95434fd 100644 --- a/dx/src/com/android/dx/dex/file/ProtoIdsSection.java +++ b/dx/src/com/android/dx/dex/file/ProtoIdsSection.java @@ -26,18 +26,18 @@ import java.util.TreeMap; /** * Proto (method prototype) identifiers list section of a - * .dex file. + * {@code .dex} file. */ public final class ProtoIdsSection extends UniformItemSection { /** - * non-null; map from method prototypes to {@link ProtoIdItem} instances + * {@code non-null;} map from method prototypes to {@link ProtoIdItem} instances */ private final TreeMap protoIds; /** * Constructs an instance. The file offset is initially unknown. * - * @param file non-null; file that this instance is part of + * @param file {@code non-null;} file that this instance is part of */ public ProtoIdsSection(DexFile file) { super("proto_ids", file, 4); @@ -60,7 +60,7 @@ public final class ProtoIdsSection extends UniformItemSection { /** * Writes the portion of the file header that refers to this instance. * - * @param out non-null; where to write + * @param out {@code non-null;} where to write */ public void writeHeaderPart(AnnotatedOutput out) { throwIfNotPrepared(); @@ -84,8 +84,8 @@ public final class ProtoIdsSection extends UniformItemSection { /** * Interns an element into this instance. * - * @param prototype non-null; the prototype to intern - * @return non-null; the interned reference + * @param prototype {@code non-null;} the prototype to intern + * @return {@code non-null;} the interned reference */ public ProtoIdItem intern(Prototype prototype) { if (prototype == null) { @@ -108,8 +108,8 @@ public final class ProtoIdsSection extends UniformItemSection { * Gets the index of the given prototype, which must have * been added to this instance. * - * @param prototype non-null; the prototype to look up - * @return >= 0; the reference's index + * @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) { diff --git a/dx/src/com/android/dx/dex/file/Section.java b/dx/src/com/android/dx/dex/file/Section.java index 9f7657c6c..f5b43af8e 100644 --- a/dx/src/com/android/dx/dex/file/Section.java +++ b/dx/src/com/android/dx/dex/file/Section.java @@ -21,22 +21,22 @@ import com.android.dx.util.AnnotatedOutput; import java.util.Collection; /** - * A section of a .dex file. Each section consists of a list + * A section of a {@code .dex} file. Each section consists of a list * of items of some sort or other. */ public abstract class Section { - /** null-ok; name of this part, for annotation purposes */ + /** {@code null-ok;} name of this part, for annotation purposes */ private final String name; - /** non-null; file that this instance is part of */ + /** {@code non-null;} file that this instance is part of */ private final DexFile file; - /** > 0; alignment requirement for the final output; + /** {@code > 0;} alignment requirement for the final output; * must be a power of 2 */ private final int alignment; - /** >= -1; offset from the start of the file to this part, or - * -1 if not yet known */ + /** {@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 @@ -47,7 +47,7 @@ public abstract class Section { * Validates an alignment. * * @param alignment the alignment - * @throws IllegalArgumentException thrown if alignment + * @throws IllegalArgumentException thrown if {@code alignment} * isn't a positive power of 2 */ public static void validateAlignment(int alignment) { @@ -60,10 +60,10 @@ public abstract class Section { /** * Constructs an instance. The file offset is initially unknown. * - * @param name null-ok; the name of this instance, for annotation + * @param name {@code null-ok;} the name of this instance, for annotation * purposes - * @param file non-null; file that this instance is part of - * @param alignment > 0; alignment requirement for the final output; + * @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) { @@ -83,7 +83,7 @@ public abstract class Section { /** * Gets the file that this instance is part of. * - * @return non-null; the file + * @return {@code non-null;} the file */ public final DexFile getFile() { return file; @@ -92,7 +92,7 @@ public abstract class Section { /** * Gets the alignment for this instance's final output. * - * @return > 0; the alignment + * @return {@code > 0;} the alignment */ public final int getAlignment() { return alignment; @@ -102,7 +102,7 @@ public abstract class Section { * 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 >= 0; the file offset + * @return {@code >= 0;} the file offset */ public final int getFileOffset() { if (fileOffset < 0) { @@ -116,9 +116,9 @@ public abstract class Section { * Sets the file offset. It is only valid to call this method once * once per instance. * - * @param fileOffset >= 0; the desired offset from the start of the + * @param fileOffset {@code >= 0;} the desired offset from the start of the * file where this for this instance - * @return >= 0; the offset that this instance should be placed at + * @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) { @@ -141,7 +141,7 @@ public abstract class Section { /** * Writes this instance to the given raw data object. * - * @param out non-null; where to write to + * @param out {@code non-null;} where to write to */ public final void writeTo(AnnotatedOutput out) { throwIfNotPrepared(); @@ -174,8 +174,8 @@ public abstract class Section { * once this instance has been assigned a file offset (via {@link * #setFileOffset}). * - * @param relative >= 0; the relative offset - * @return >= 0; the corresponding absolute file offset + * @param relative {@code >= 0;} the relative offset + * @return {@code >= 0;} the corresponding absolute file offset */ public final int getAbsoluteOffset(int relative) { if (relative < 0) { @@ -198,8 +198,8 @@ public abstract class Section { *

    Note: Subclasses must implement this as appropriate for * their contents.

    * - * @param item non-null; the item in question - * @return >= 0; the item's absolute file offset + * @param item {@code non-null;} the item in question + * @return {@code >= 0;} the item's absolute file offset */ public abstract int getAbsoluteItemOffset(Item item); @@ -219,7 +219,7 @@ public abstract class Section { * Gets the collection of all the items in this section. * It is not valid to attempt to change the returned list. * - * @return non-null; the items + * @return {@code non-null;} the items */ public abstract Collection items(); @@ -231,7 +231,7 @@ public abstract class Section { /** * Gets the size of this instance when output, in bytes. * - * @return >= 0; the size of this instance, in bytes + * @return {@code >= 0;} the size of this instance, in bytes */ public abstract int writeSize(); @@ -258,7 +258,7 @@ public abstract class Section { /** * Aligns the output of the given data to the alignment of this instance. * - * @param out non-null; the output to align + * @param out {@code non-null;} the output to align */ protected final void align(AnnotatedOutput out) { out.alignTo(alignment); @@ -267,19 +267,19 @@ public abstract class Section { /** * Writes this instance to the given raw data object. This gets * called by {@link #writeTo} after aligning the cursor of - * out and verifying that either the assigned file - * offset matches the actual cursor out or that the + * {@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 out's cursor. + * assigned to {@code out}'s cursor. * - * @param out non-null; where to write to + * @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 null-ok; name of this part, 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/dx/dex/file/Statistics.java b/dx/src/com/android/dx/dex/file/Statistics.java index b11ab6eb1..9a2efb3b0 100644 --- a/dx/src/com/android/dx/dex/file/Statistics.java +++ b/dx/src/com/android/dx/dex/file/Statistics.java @@ -26,7 +26,7 @@ import java.util.TreeMap; * Statistics about the contents of a file. */ public final class Statistics { - /** non-null; data about each type of item */ + /** {@code non-null;} data about each type of item */ private final HashMap dataMap; /** @@ -39,7 +39,7 @@ public final class Statistics { /** * Adds the given item to the statistics. * - * @param item non-null; the item to add + * @param item {@code non-null;} the item to add */ public void add(Item item) { String typeName = item.typeName(); @@ -55,7 +55,7 @@ public final class Statistics { /** * Adds the given list of items to the statistics. * - * @param list non-null; the list of items to add + * @param list {@code non-null;} the list of items to add */ public void addAll(Section list) { Collection items = list.items(); @@ -67,7 +67,7 @@ public final class Statistics { /** * Writes the statistics as an annotation. * - * @param out non-null; where to write to + * @param out {@code non-null;} where to write to */ public final void writeAnnotation(AnnotatedOutput out) { if (dataMap.size() == 0) { @@ -109,26 +109,26 @@ public final class Statistics { * Statistical data about a particular class. */ private static class Data { - /** non-null; name to use as a label */ + /** {@code non-null;} name to use as a label */ private final String name; - /** >= 0; number of instances */ + /** {@code >= 0;} number of instances */ private int count; - /** >= 0; total size of instances in bytes */ + /** {@code >= 0;} total size of instances in bytes */ private int totalSize; - /** >= 0; largest size of any individual item */ + /** {@code >= 0;} largest size of any individual item */ private int largestSize; - /** >= 0; smallest size of any individual item */ + /** {@code >= 0;} smallest size of any individual item */ private int smallestSize; /** * Constructs an instance for the given item. * - * @param item non-null; item in question - * @param name non-null; type name to use + * @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(); @@ -143,7 +143,7 @@ public final class Statistics { /** * Incorporates a new item. This assumes the type name matches. * - * @param item non-null; item to incorporate + * @param item {@code non-null;} item to incorporate */ public void add(Item item) { int size = item.writeSize(); @@ -163,7 +163,7 @@ public final class Statistics { /** * Writes this instance as an annotation. * - * @param out non-null; where to write to + * @param out {@code non-null;} where to write to */ public void writeAnnotation(AnnotatedOutput out) { out.annotate(toHuman()); diff --git a/dx/src/com/android/dx/dex/file/StringDataItem.java b/dx/src/com/android/dx/dex/file/StringDataItem.java index 49eea5757..b9eeb9bfb 100644 --- a/dx/src/com/android/dx/dex/file/StringDataItem.java +++ b/dx/src/com/android/dx/dex/file/StringDataItem.java @@ -26,13 +26,13 @@ import com.android.dx.util.Leb128Utils; * Representation of string data for a particular string, in a Dalvik file. */ public final class StringDataItem extends OffsettedItem { - /** non-null; the string value */ + /** {@code non-null;} the string value */ private final CstUtf8 value; /** * Constructs an instance. * - * @param value non-null; the string value + * @param value {@code non-null;} the string value */ public StringDataItem(CstUtf8 value) { super(1, writeSize(value)); @@ -43,8 +43,8 @@ public final class StringDataItem extends OffsettedItem { /** * Gets the write size for a given value. * - * @param value non-null; the string value - * @return >= 2 the write size, in bytes + * @param value {@code non-null;} the string value + * @return {@code >= 2}; the write size, in bytes */ private static int writeSize(CstUtf8 value) { int utf16Size = value.getUtf16Size(); diff --git a/dx/src/com/android/dx/dex/file/StringIdItem.java b/dx/src/com/android/dx/dex/file/StringIdItem.java index e80a7f877..401a0bef0 100644 --- a/dx/src/com/android/dx/dex/file/StringIdItem.java +++ b/dx/src/com/android/dx/dex/file/StringIdItem.java @@ -28,16 +28,16 @@ public final class StringIdItem /** size of instances when written out to a file, in bytes */ public static final int WRITE_SIZE = 4; - /** non-null; the string value */ + /** {@code non-null;} the string value */ private final CstUtf8 value; - /** null-ok; associated string data object, if known */ + /** {@code null-ok;} associated string data object, if known */ private StringDataItem data; /** * Constructs an instance. * - * @param value non-null; the string value + * @param value {@code non-null;} the string value */ public StringIdItem(CstUtf8 value) { if (value == null) { @@ -110,7 +110,7 @@ public final class StringIdItem /** * Gets the string value. * - * @return non-null; the value + * @return {@code non-null;} the value */ public CstUtf8 getValue() { return value; @@ -119,7 +119,7 @@ public final class StringIdItem /** * Gets the associated data object for this instance, if known. * - * @return null-ok; the associated data object or null + * @return {@code null-ok;} the associated data object or {@code null} * if not yet known */ public StringDataItem getData() { diff --git a/dx/src/com/android/dx/dex/file/StringIdsSection.java b/dx/src/com/android/dx/dex/file/StringIdsSection.java index 17fbb578b..b2e8683ed 100644 --- a/dx/src/com/android/dx/dex/file/StringIdsSection.java +++ b/dx/src/com/android/dx/dex/file/StringIdsSection.java @@ -27,12 +27,12 @@ import java.util.Collection; import java.util.TreeMap; /** - * Strings list section of a .dex file. + * Strings list section of a {@code .dex} file. */ public final class StringIdsSection extends UniformItemSection { /** - * non-null; map from string constants to {@link + * {@code non-null;} map from string constants to {@link * StringIdItem} instances */ private final TreeMap strings; @@ -40,7 +40,7 @@ public final class StringIdsSection /** * Constructs an instance. The file offset is initially unknown. * - * @param file non-null; file that this instance is part of + * @param file {@code non-null;} file that this instance is part of */ public StringIdsSection(DexFile file) { super("string_ids", file, 4); @@ -79,7 +79,7 @@ public final class StringIdsSection /** * Writes the portion of the file header that refers to this instance. * - * @param out non-null; where to write + * @param out {@code non-null;} where to write */ public void writeHeaderPart(AnnotatedOutput out) { throwIfNotPrepared(); @@ -99,9 +99,9 @@ public final class StringIdsSection /** * Interns an element into this instance. * - * @param string non-null; the string to intern, as a regular Java - * String - * @return non-null; the interned string + * @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) { CstUtf8 utf8 = new CstUtf8(string); @@ -111,8 +111,8 @@ public final class StringIdsSection /** * Interns an element into this instance. * - * @param string non-null; the string to intern, as a {@link CstString} - * @return non-null; the interned string + * @param string {@code non-null;} the string to intern, as a {@link CstString} + * @return {@code non-null;} the interned string */ public StringIdItem intern(CstString string) { CstUtf8 utf8 = string.getString(); @@ -122,8 +122,8 @@ public final class StringIdsSection /** * Interns an element into this instance. * - * @param string non-null; the string to intern, as a constant - * @return non-null; the interned string + * @param string {@code non-null;} the string to intern, as a constant + * @return {@code non-null;} the interned string */ public StringIdItem intern(CstUtf8 string) { return intern(new StringIdItem(string)); @@ -132,8 +132,8 @@ public final class StringIdsSection /** * Interns an element into this instance. * - * @param string non-null; the string to intern - * @return non-null; the interned string + * @param string {@code non-null;} the string to intern + * @return {@code non-null;} the interned string */ public StringIdItem intern(StringIdItem string) { if (string == null) { @@ -156,7 +156,7 @@ public final class StringIdsSection /** * Interns the components of a name-and-type into this instance. * - * @param nat non-null; the name-and-type + * @param nat {@code non-null;} the name-and-type */ public void intern(CstNat nat) { intern(nat.getName()); @@ -167,8 +167,8 @@ public final class StringIdsSection * Gets the index of the given string, which must have been added * to this instance. * - * @param string non-null; the string to look up - * @return >= 0; the string's index + * @param string {@code non-null;} the string to look up + * @return {@code >= 0;} the string's index */ public int indexOf(CstUtf8 string) { if (string == null) { @@ -190,8 +190,8 @@ public final class StringIdsSection * Gets the index of the given string, which must have been added * to this instance. * - * @param string non-null; the string to look up - * @return >= 0; the string's index + * @param string {@code non-null;} the string to look up + * @return {@code >= 0;} the string's index */ public int indexOf(CstString string) { return indexOf(string.getString()); diff --git a/dx/src/com/android/dx/dex/file/TypeIdItem.java b/dx/src/com/android/dx/dex/file/TypeIdItem.java index f3402e643..01b1417c0 100644 --- a/dx/src/com/android/dx/dex/file/TypeIdItem.java +++ b/dx/src/com/android/dx/dex/file/TypeIdItem.java @@ -31,7 +31,7 @@ public final class TypeIdItem extends IdItem { /** * Constructs an instance. * - * @param type non-null; the constant for the type + * @param type {@code non-null;} the constant for the type */ public TypeIdItem(CstType type) { super(type); diff --git a/dx/src/com/android/dx/dex/file/TypeIdsSection.java b/dx/src/com/android/dx/dex/file/TypeIdsSection.java index 296263f09..b1b9c58a3 100644 --- a/dx/src/com/android/dx/dex/file/TypeIdsSection.java +++ b/dx/src/com/android/dx/dex/file/TypeIdsSection.java @@ -26,18 +26,18 @@ import java.util.Collection; import java.util.TreeMap; /** - * Type identifiers list section of a .dex file. + * Type identifiers list section of a {@code .dex} file. */ public final class TypeIdsSection extends UniformItemSection { /** - * non-null; map from types to {@link TypeIdItem} instances + * {@code non-null;} map from types to {@link TypeIdItem} instances */ private final TreeMap typeIds; /** * Constructs an instance. The file offset is initially unknown. * - * @param file non-null; file that this instance is part of + * @param file {@code non-null;} file that this instance is part of */ public TypeIdsSection(DexFile file) { super("type_ids", file, 4); @@ -73,7 +73,7 @@ public final class TypeIdsSection extends UniformItemSection { /** * Writes the portion of the file header that refers to this instance. * - * @param out non-null; where to write + * @param out {@code non-null;} where to write */ public void writeHeaderPart(AnnotatedOutput out) { throwIfNotPrepared(); @@ -97,8 +97,8 @@ public final class TypeIdsSection extends UniformItemSection { /** * Interns an element into this instance. * - * @param type non-null; the type to intern - * @return non-null; the interned reference + * @param type {@code non-null;} the type to intern + * @return {@code non-null;} the interned reference */ public TypeIdItem intern(Type type) { if (type == null) { @@ -120,8 +120,8 @@ public final class TypeIdsSection extends UniformItemSection { /** * Interns an element into this instance. * - * @param type non-null; the type to intern - * @return non-null; the interned reference + * @param type {@code non-null;} the type to intern + * @return {@code non-null;} the interned reference */ public TypeIdItem intern(CstType type) { if (type == null) { @@ -145,8 +145,8 @@ public final class TypeIdsSection extends UniformItemSection { * Gets the index of the given type, which must have * been added to this instance. * - * @param type non-null; the type to look up - * @return >= 0; the reference's index + * @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) { @@ -168,8 +168,8 @@ public final class TypeIdsSection extends UniformItemSection { * Gets the index of the given type, which must have * been added to this instance. * - * @param type non-null; the type to look up - * @return >= 0; the reference's index + * @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) { diff --git a/dx/src/com/android/dx/dex/file/TypeListItem.java b/dx/src/com/android/dx/dex/file/TypeListItem.java index 6557ca415..3278aefff 100644 --- a/dx/src/com/android/dx/dex/file/TypeListItem.java +++ b/dx/src/com/android/dx/dex/file/TypeListItem.java @@ -36,13 +36,13 @@ public final class TypeListItem extends OffsettedItem { /** header size in bytes */ private static final int HEADER_SIZE = 4; - /** non-null; the actual list */ + /** {@code non-null;} the actual list */ private final TypeList list; /** * Constructs an instance. * - * @param list non-null; the actual list + * @param list {@code non-null;} the actual list */ public TypeListItem(TypeList list) { super(ALIGNMENT, (list.size() * ELEMENT_SIZE) + HEADER_SIZE); @@ -81,7 +81,7 @@ public final class TypeListItem extends OffsettedItem { /** * Gets the underlying list. * - * @return non-null; the list + * @return {@code non-null;} the list */ public TypeList getList() { return list; diff --git a/dx/src/com/android/dx/dex/file/UniformItemSection.java b/dx/src/com/android/dx/dex/file/UniformItemSection.java index 602bc2dcb..e18243898 100644 --- a/dx/src/com/android/dx/dex/file/UniformItemSection.java +++ b/dx/src/com/android/dx/dex/file/UniformItemSection.java @@ -22,7 +22,7 @@ import com.android.dx.util.AnnotatedOutput; import java.util.Collection; /** - * A section of a .dex file which consists of a sequence of + * A section of a {@code .dex} file which consists of a sequence of * {@link Item} objects. Each of the items must have the same size in * the output. */ @@ -30,10 +30,10 @@ public abstract class UniformItemSection extends Section { /** * Constructs an instance. The file offset is initially unknown. * - * @param name null-ok; the name of this instance, for annotation + * @param name {@code null-ok;} the name of this instance, for annotation * purposes - * @param file non-null; file that this instance is part of - * @param alignment > 0; alignment requirement for the final output; + * @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) { @@ -60,8 +60,8 @@ public abstract class UniformItemSection extends Section { * if this instance isn't the sort that maps constants to {@link * IndexedItem} instances. * - * @param cst non-null; constant to look for - * @return non-null; the corresponding item found in this instance + * @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); diff --git a/dx/src/com/android/dx/dex/file/UniformListItem.java b/dx/src/com/android/dx/dex/file/UniformListItem.java index 3af39428a..3c1f4d366 100644 --- a/dx/src/com/android/dx/dex/file/UniformListItem.java +++ b/dx/src/com/android/dx/dex/file/UniformListItem.java @@ -28,8 +28,8 @@ import java.util.List; * alignment. * *

    This class inherits its alignment from its items, bumped up to - * 4 if the items have a looser alignment requirement. If - * it is more than 4, then there will be a gap after the + * {@code 4} if the items have a looser alignment requirement. If + * it is more than {@code 4}, then there will be a gap after the * output list size (which is four bytes) and before the first item.

    * * @param type of element contained in an instance @@ -39,18 +39,18 @@ public final class UniformListItem /** the size of the list header */ private static final int HEADER_SIZE = 4; - /** non-null; the item type */ + /** {@code non-null;} the item type */ private final ItemType itemType; - /** non-null; the contents */ + /** {@code non-null;} the contents */ private final List items; /** * Constructs an instance. It is illegal to modify the given list once * it is used to construct an instance of this class. * - * @param itemType non-null; the type of the item - * @param items non-null and non-empty; list of items to represent + * @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 items) { super(getAlignment(items), writeSize(items)); @@ -68,8 +68,8 @@ public final class UniformListItem * requirement implied by the given list. See the header comment for * more details. * - * @param items non-null; list of items being represented - * @return >= 4; the alignment requirement + * @param items {@code non-null;} list of items being represented + * @return {@code >= 4;} the alignment requirement */ private static int getAlignment(List items) { try { @@ -87,8 +87,8 @@ public final class UniformListItem /** * Calculates the write size for the given list. * - * @param items non-null; the list in question - * @return >= 0; the write size + * @param items {@code non-null;} the list in question + * @return {@code >= 0;} the write size */ private static int writeSize(List items) { /* @@ -148,7 +148,7 @@ public final class UniformListItem /** * Gets the underlying list of items. * - * @return non-null; the list + * @return {@code non-null;} the list */ public final List getItems() { return items; @@ -204,7 +204,7 @@ public final class UniformListItem /** * Get the size of the header of this list. * - * @return >= 0; the header size + * @return {@code >= 0;} the header size */ private int headerSize() { /* diff --git a/dx/src/com/android/dx/dex/file/ValueEncoder.java b/dx/src/com/android/dx/dex/file/ValueEncoder.java index 02a34194f..f7e364aa7 100644 --- a/dx/src/com/android/dx/dex/file/ValueEncoder.java +++ b/dx/src/com/android/dx/dex/file/ValueEncoder.java @@ -43,69 +43,69 @@ import com.android.dx.util.Hex; import java.util.Collection; /** - * Handler for writing out encoded_values and parts + * Handler for writing out {@code encoded_values} and parts * thereof. */ public final class ValueEncoder { - /** annotation value type constant: byte */ + /** annotation value type constant: {@code byte} */ private static final int VALUE_BYTE = 0x00; - /** annotation value type constant: short */ + /** annotation value type constant: {@code short} */ private static final int VALUE_SHORT = 0x02; - /** annotation value type constant: char */ + /** annotation value type constant: {@code char} */ private static final int VALUE_CHAR = 0x03; - /** annotation value type constant: int */ + /** annotation value type constant: {@code int} */ private static final int VALUE_INT = 0x04; - /** annotation value type constant: long */ + /** annotation value type constant: {@code long} */ private static final int VALUE_LONG = 0x06; - /** annotation value type constant: float */ + /** annotation value type constant: {@code float} */ private static final int VALUE_FLOAT = 0x10; - /** annotation value type constant: double */ + /** annotation value type constant: {@code double} */ private static final int VALUE_DOUBLE = 0x11; - /** annotation value type constant: string */ + /** annotation value type constant: {@code string} */ private static final int VALUE_STRING = 0x17; - /** annotation value type constant: type */ + /** annotation value type constant: {@code type} */ private static final int VALUE_TYPE = 0x18; - /** annotation value type constant: field */ + /** annotation value type constant: {@code field} */ private static final int VALUE_FIELD = 0x19; - /** annotation value type constant: method */ + /** annotation value type constant: {@code method} */ private static final int VALUE_METHOD = 0x1a; - /** annotation value type constant: enum */ + /** annotation value type constant: {@code enum} */ private static final int VALUE_ENUM = 0x1b; - /** annotation value type constant: array */ + /** annotation value type constant: {@code array} */ private static final int VALUE_ARRAY = 0x1c; - /** annotation value type constant: annotation */ + /** annotation value type constant: {@code annotation} */ private static final int VALUE_ANNOTATION = 0x1d; - /** annotation value type constant: null */ + /** annotation value type constant: {@code null} */ private static final int VALUE_NULL = 0x1e; - /** annotation value type constant: boolean */ + /** annotation value type constant: {@code boolean} */ private static final int VALUE_BOOLEAN = 0x1f; - /** non-null; file being written */ + /** {@code non-null;} file being written */ private final DexFile file; - /** non-null; output stream to write to */ + /** {@code non-null;} output stream to write to */ private final AnnotatedOutput out; /** * Construct an instance. * - * @param file non-null; file being written - * @param out non-null; output stream to write to + * @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) { @@ -123,7 +123,7 @@ public final class ValueEncoder { /** * Writes out the encoded form of the given constant. * - * @param cst non-null; the constant to write + * @param cst {@code non-null;} the constant to write */ public void writeConstant(Constant cst) { int type = constantToValueType(cst); @@ -209,8 +209,8 @@ public final class ValueEncoder { /** * Gets the value type for the given constant. * - * @param cst non-null; the constant - * @return the value type; one of the VALUE_* constants + * @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) { @@ -257,15 +257,15 @@ public final class ValueEncoder { /** * Writes out the encoded form of the given array, that is, as - * an encoded_array and not including a - * value_type prefix. If the output stream keeps - * (debugging) annotations and topLevel is - * true, then this method will write (debugging) + * 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 non-null; array instance to write - * @param topLevel true iff the given annotation is the - * top-level annotation or false if it is a sub-annotation + * @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) { @@ -295,15 +295,15 @@ public final class ValueEncoder { /** * Writes out the encoded form of the given annotation, that is, - * as an encoded_annotation and not including a - * value_type prefix. If the output stream keeps - * (debugging) annotations and topLevel is - * true, then this method will write (debugging) + * 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 non-null; annotation instance to write - * @param topLevel true iff the given annotation is the - * top-level annotation or false if it is a sub-annotation + * @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) { @@ -361,8 +361,8 @@ public final class ValueEncoder { * Gets the colloquial type name and human form of the type of the * given constant, when used as an encoded value. * - * @param cst non-null; the constant - * @return non-null; its type name and human form + * @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); @@ -385,7 +385,7 @@ public final class ValueEncoder { * for any signed integral type. * * @param type the type constant - * @param value long bits of the value + * @param value {@code long} bits of the value */ private void writeSignedIntegralValue(int type, long value) { /* @@ -422,7 +422,7 @@ public final class ValueEncoder { * for any unsigned integral type. * * @param type the type constant - * @param value long bits of the value + * @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. @@ -453,7 +453,7 @@ public final class ValueEncoder { * right-zero-extended value. * * @param type the type constant - * @param value long bits of the value + * @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. @@ -484,12 +484,12 @@ public final class ValueEncoder { /** - * Helper for addContents() methods, which adds + * Helper for {@code addContents()} methods, which adds * contents for a particular {@link Annotation}, calling itself * recursively should it encounter a nested annotation. * - * @param file non-null; the file to add to - * @param annotation non-null; the annotation to add contents for + * @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(); @@ -504,19 +504,16 @@ public final class ValueEncoder { } /** - * Helper for addContents() methods, which adds + * 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 non-null; the file to add to - * @param cst non-null; the constant to add contents for + * @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) { - TypeIdsSection typeIds = file.getTypeIds(); - StringIdsSection stringIds = file.getStringIds(); - if (cst instanceof CstAnnotation) { addContents(file, ((CstAnnotation) cst).getAnnotation()); } else if (cst instanceof CstArray) { diff --git a/dx/src/com/android/dx/rop/annotation/Annotation.java b/dx/src/com/android/dx/rop/annotation/Annotation.java index b7cf16494..6154c618e 100644 --- a/dx/src/com/android/dx/rop/annotation/Annotation.java +++ b/dx/src/com/android/dx/rop/annotation/Annotation.java @@ -41,20 +41,20 @@ import java.util.TreeMap; */ public final class Annotation extends MutabilityControl implements Comparable, ToHuman { - /** non-null; type of the annotation */ + /** {@code non-null;} type of the annotation */ private final CstType type; - /** non-null; the visibility of the annotation */ + /** {@code non-null;} the visibility of the annotation */ private final AnnotationVisibility visibility; - /** non-null; map from names to {@link NameValuePair} instances */ + /** {@code non-null;} map from names to {@link NameValuePair} instances */ private final TreeMap elements; /** * Construct an instance. It initially contains no elements. * - * @param type non-null; type of the annotation - * @param visibility non-null; the visibility of the annotation + * @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) { @@ -165,7 +165,7 @@ public final class Annotation extends MutabilityControl /** * Gets the type of this instance. * - * @return non-null; the type + * @return {@code non-null;} the type */ public CstType getType() { return type; @@ -174,7 +174,7 @@ public final class Annotation extends MutabilityControl /** * Gets the visibility of this instance. * - * @return non-null; the visibility + * @return {@code non-null;} the visibility */ public AnnotationVisibility getVisibility() { return visibility; @@ -185,7 +185,7 @@ public final class Annotation extends MutabilityControl * If there is a preexisting element with the same name, it will be * replaced by this method. * - * @param pair non-null; the (name, value) pair to place into this instance + * @param pair {@code non-null;} the (name, value) pair to place into this instance */ public void put(NameValuePair pair) { throwIfImmutable(); @@ -202,7 +202,7 @@ public final class Annotation extends MutabilityControl * It is an error to call this method if there is a preexisting element * with the same name. * - * @param pair non-null; the (name, value) pair to add to this instance + * @param pair {@code non-null;} the (name, value) pair to add to this instance */ public void add(NameValuePair pair) { throwIfImmutable(); @@ -224,7 +224,7 @@ public final class Annotation extends MutabilityControl * Gets the set of name-value pairs contained in this instance. The * result is always unmodifiable. * - * @return non-null; the set of name-value pairs + * @return {@code non-null;} the set of name-value pairs */ public Collection getNameValuePairs() { return Collections.unmodifiableCollection(elements.values()); diff --git a/dx/src/com/android/dx/rop/annotation/AnnotationVisibility.java b/dx/src/com/android/dx/rop/annotation/AnnotationVisibility.java index c53fcd804..26246bb10 100644 --- a/dx/src/com/android/dx/rop/annotation/AnnotationVisibility.java +++ b/dx/src/com/android/dx/rop/annotation/AnnotationVisibility.java @@ -27,13 +27,13 @@ public enum AnnotationVisibility implements ToHuman { SYSTEM("system"), EMBEDDED("embedded"); - /** non-null; the human-oriented string representation */ + /** {@code non-null;} the human-oriented string representation */ private final String human; /** * Constructs an instance. * - * @param human non-null; the human-oriented string representation + * @param human {@code non-null;} the human-oriented string representation */ private AnnotationVisibility(String human) { this.human = human; diff --git a/dx/src/com/android/dx/rop/annotation/Annotations.java b/dx/src/com/android/dx/rop/annotation/Annotations.java index c1da88364..dcb74a182 100644 --- a/dx/src/com/android/dx/rop/annotation/Annotations.java +++ b/dx/src/com/android/dx/rop/annotation/Annotations.java @@ -29,14 +29,14 @@ import java.util.TreeMap; */ public final class Annotations extends MutabilityControl implements Comparable { - /** non-null; immutable empty instance */ + /** {@code non-null;} immutable empty instance */ public static final Annotations EMPTY = new Annotations(); static { EMPTY.setImmutable(); } - /** non-null; map from types to annotations */ + /** {@code non-null;} map from types to annotations */ private final TreeMap annotations; /** @@ -44,9 +44,9 @@ public final class Annotations extends MutabilityControl * two given instances. The two instances must contain disjoint sets * of types. * - * @param a1 non-null; an instance - * @param a2 non-null; the other instance - * @return non-null; the combination + * @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) { @@ -64,9 +64,9 @@ public final class Annotations extends MutabilityControl * given instance with the given additional annotation. The latter's * type must not already appear in the former. * - * @param annotations non-null; the instance to augment - * @param annotation non-null; the additional annotation - * @return non-null; the combination + * @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, @@ -152,7 +152,7 @@ public final class Annotations extends MutabilityControl /** * Gets the number of elements in this instance. * - * @return >= 0; the size + * @return {@code >= 0;} the size */ public int size() { return annotations.size(); @@ -162,7 +162,7 @@ public final class Annotations extends MutabilityControl * Adds an element to this instance. There must not already be an * element of the same type. * - * @param annotation non-null; the element to add + * @param annotation {@code non-null;} the element to add * @throws IllegalArgumentException thrown if there is a duplicate type */ public void add(Annotation annotation) { @@ -186,7 +186,7 @@ public final class Annotations extends MutabilityControl * Adds all of the elements of the given instance to this one. The * instances must not have any duplicate types. * - * @param toAdd non-null; the annotations to add + * @param toAdd {@code non-null;} the annotations to add * @throws IllegalArgumentException thrown if there is a duplicate type */ public void addAll(Annotations toAdd) { @@ -205,7 +205,7 @@ public final class Annotations extends MutabilityControl * Gets the set of annotations contained in this instance. The * result is always unmodifiable. * - * @return non-null; the set of annotations + * @return {@code non-null;} the set of annotations */ public Collection getAnnotations() { return Collections.unmodifiableCollection(annotations.values()); diff --git a/dx/src/com/android/dx/rop/annotation/AnnotationsList.java b/dx/src/com/android/dx/rop/annotation/AnnotationsList.java index 43a07ba94..0f4207b0b 100644 --- a/dx/src/com/android/dx/rop/annotation/AnnotationsList.java +++ b/dx/src/com/android/dx/rop/annotation/AnnotationsList.java @@ -23,7 +23,7 @@ import com.android.dx.util.FixedSizeList; */ public final class AnnotationsList extends FixedSizeList { - /** non-null; immutable empty instance */ + /** {@code non-null;} immutable empty instance */ public static final AnnotationsList EMPTY = new AnnotationsList(0); /** @@ -32,9 +32,9 @@ public final class AnnotationsList * same number of elements, and each pair of elements must contain * disjoint sets of types. * - * @param list1 non-null; an instance - * @param list2 non-null; the other instance - * @return non-null; the combination + * @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) { @@ -57,7 +57,7 @@ public final class AnnotationsList } /** - * Constructs an instance. All indices initially contain null. + * Constructs an instance. All indices initially contain {@code null}. * * @param size the size of the list */ @@ -68,10 +68,10 @@ public final class AnnotationsList /** * 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 NullPointerException. + * do that, this will throw {@code NullPointerException}. * - * @param n >= 0, < size(); which index - * @return non-null; element at that index + * @param n {@code >= 0, < size();} which index + * @return {@code non-null;} element at that index */ public Annotations get(int n) { return (Annotations) get0(n); @@ -81,8 +81,8 @@ public final class AnnotationsList * Sets the element at the given index. The given element must be * immutable. * - * @param n >= 0, < size(); which index - * @param a null-ok; the element to set at n + * @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(); diff --git a/dx/src/com/android/dx/rop/annotation/NameValuePair.java b/dx/src/com/android/dx/rop/annotation/NameValuePair.java index dadabaa71..7137a6025 100644 --- a/dx/src/com/android/dx/rop/annotation/NameValuePair.java +++ b/dx/src/com/android/dx/rop/annotation/NameValuePair.java @@ -24,17 +24,17 @@ import com.android.dx.rop.cst.CstUtf8; * A (name, value) pair. These are used as the contents of an annotation. */ public final class NameValuePair implements Comparable { - /** non-null; the name */ + /** {@code non-null;} the name */ private final CstUtf8 name; - /** non-null; the value */ + /** {@code non-null;} the value */ private final Constant value; /** * Construct an instance. * - * @param name non-null; the name - * @param value non-null; the value + * @param name {@code non-null;} the name + * @param value {@code non-null;} the value */ public NameValuePair(CstUtf8 name, Constant value) { if (name == null) { @@ -95,7 +95,7 @@ public final class NameValuePair implements Comparable { /** * Gets the name. * - * @return non-null; the name + * @return {@code non-null;} the name */ public CstUtf8 getName() { return name; @@ -104,7 +104,7 @@ public final class NameValuePair implements Comparable { /** * Gets the value. * - * @return non-null; the valute + * @return {@code non-null;} the value */ public Constant getValue() { return value; diff --git a/dx/src/com/android/dx/rop/code/AccessFlags.java b/dx/src/com/android/dx/rop/code/AccessFlags.java index 265cfa619..b76b610e1 100644 --- a/dx/src/com/android/dx/rop/code/AccessFlags.java +++ b/dx/src/com/android/dx/rop/code/AccessFlags.java @@ -23,8 +23,8 @@ import com.android.dx.util.Hex; * related utilities. Although, at the rop layer, flags are generally * ignored, this is the layer of communication, and as such, this * package is where these definitions belong. The flag definitions are - * identical to Java access flags, but ACC_SUPER isn't - * used at all in translated code, and ACC_SYNCHRONIZED + * identical to Java access flags, but {@code ACC_SUPER} isn't + * used at all in translated code, and {@code ACC_SYNCHRONIZED} * is only used in a very limited way. */ public final class AccessFlags { @@ -44,13 +44,13 @@ public final class AccessFlags { public static final int ACC_FINAL = 0x0010; /** - * synchronized method; only valid in dex files for native + * synchronized method; only valid in dex files for {@code native} * methods */ public static final int ACC_SYNCHRONIZED = 0x0020; /** - * class with new-style invokespecial for superclass + * class with new-style {@code invokespecial} for superclass * method access */ public static final int ACC_SUPER = 0x0020; @@ -77,7 +77,7 @@ public final class AccessFlags { public static final int ACC_ABSTRACT = 0x0400; /** - * method with strict floating point (strictfp) + * method with strict floating point ({@code strictfp}) * behavior */ public static final int ACC_STRICT = 0x0800; @@ -98,7 +98,7 @@ public final class AccessFlags { public static final int ACC_CONSTRUCTOR = 0x10000; /** - * method was declared synchronized; has no effect on + * 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; @@ -147,7 +147,7 @@ public final class AccessFlags { * as defined on classes (not fields or methods). * * @param flags the flags - * @return non-null; human-oriented string + * @return {@code non-null;} human-oriented string */ public static String classString(int flags) { return humanHelper(flags, CLASS_FLAGS, CONV_CLASS); @@ -158,7 +158,7 @@ public final class AccessFlags { * as defined on inner classes. * * @param flags the flags - * @return non-null; human-oriented string + * @return {@code non-null;} human-oriented string */ public static String innerClassString(int flags) { return humanHelper(flags, INNER_CLASS_FLAGS, CONV_CLASS); @@ -169,7 +169,7 @@ public final class AccessFlags { * as defined on fields (not classes or methods). * * @param flags the flags - * @return non-null; human-oriented string + * @return {@code non-null;} human-oriented string */ public static String fieldString(int flags) { return humanHelper(flags, FIELD_FLAGS, CONV_FIELD); @@ -180,106 +180,106 @@ public final class AccessFlags { * as defined on methods (not classes or fields). * * @param flags the flags - * @return non-null; human-oriented string + * @return {@code non-null;} human-oriented string */ public static String methodString(int flags) { return humanHelper(flags, METHOD_FLAGS, CONV_METHOD); } /** - * Returns whether the flag ACC_PUBLIC is on in the given + * Returns whether the flag {@code ACC_PUBLIC} is on in the given * flags. * * @param flags the flags to check - * @return the value of the ACC_PUBLIC flag + * @return the value of the {@code ACC_PUBLIC} flag */ public static boolean isPublic(int flags) { return (flags & ACC_PUBLIC) != 0; } /** - * Returns whether the flag ACC_PROTECTED is on in the given + * Returns whether the flag {@code ACC_PROTECTED} is on in the given * flags. * * @param flags the flags to check - * @return the value of the ACC_PROTECTED flag + * @return the value of the {@code ACC_PROTECTED} flag */ public static boolean isProtected(int flags) { return (flags & ACC_PROTECTED) != 0; } /** - * Returns whether the flag ACC_PRIVATE is on in the given + * Returns whether the flag {@code ACC_PRIVATE} is on in the given * flags. * * @param flags the flags to check - * @return the value of the ACC_PRIVATE flag + * @return the value of the {@code ACC_PRIVATE} flag */ public static boolean isPrivate(int flags) { return (flags & ACC_PRIVATE) != 0; } /** - * Returns whether the flag ACC_STATIC is on in the given + * Returns whether the flag {@code ACC_STATIC} is on in the given * flags. * * @param flags the flags to check - * @return the value of the ACC_STATIC flag + * @return the value of the {@code ACC_STATIC} flag */ public static boolean isStatic(int flags) { return (flags & ACC_STATIC) != 0; } /** - * Returns whether the flag ACC_SYNCHRONIZED is on in + * Returns whether the flag {@code ACC_SYNCHRONIZED} is on in * the given flags. * * @param flags the flags to check - * @return the value of the ACC_SYNCHRONIZED flag + * @return the value of the {@code ACC_SYNCHRONIZED} flag */ public static boolean isSynchronized(int flags) { return (flags & ACC_SYNCHRONIZED) != 0; } /** - * Returns whether the flag ACC_ABSTRACT is on in the given + * Returns whether the flag {@code ACC_ABSTRACT} is on in the given * flags. * * @param flags the flags to check - * @return the value of the ACC_ABSTRACT flag + * @return the value of the {@code ACC_ABSTRACT} flag */ public static boolean isAbstract(int flags) { return (flags & ACC_ABSTRACT) != 0; } /** - * Returns whether the flag ACC_NATIVE is on in the given + * Returns whether the flag {@code ACC_NATIVE} is on in the given * flags. * * @param flags the flags to check - * @return the value of the ACC_NATIVE flag + * @return the value of the {@code ACC_NATIVE} flag */ public static boolean isNative(int flags) { return (flags & ACC_NATIVE) != 0; } /** - * Returns whether the flag ACC_ANNOTATION is on in the given + * Returns whether the flag {@code ACC_ANNOTATION} is on in the given * flags. * * @param flags the flags to check - * @return the value of the ACC_ANNOTATION flag + * @return the value of the {@code ACC_ANNOTATION} flag */ public static boolean isAnnotation(int flags) { return (flags & ACC_ANNOTATION) != 0; } /** - * Returns whether the flag ACC_DECLARED_SYNCHRONIZED is + * 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 ACC_DECLARED_SYNCHRONIZED flag + * @return the value of the {@code ACC_DECLARED_SYNCHRONIZED} flag */ public static boolean isDeclaredSynchronized(int flags) { return (flags & ACC_DECLARED_SYNCHRONIZED) != 0; @@ -291,8 +291,8 @@ public final class AccessFlags { * * @param flags the defined flags * @param mask mask for the "defined" bits - * @param what what the flags represent (one of CONV_*) - * @return non-null; human-oriented string + * @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); diff --git a/dx/src/com/android/dx/rop/code/BasicBlock.java b/dx/src/com/android/dx/rop/code/BasicBlock.java index 66db5aa29..7bb2d9b03 100644 --- a/dx/src/com/android/dx/rop/code/BasicBlock.java +++ b/dx/src/com/android/dx/rop/code/BasicBlock.java @@ -25,34 +25,34 @@ import com.android.dx.util.LabeledItem; * Basic block of register-based instructions. */ public final class BasicBlock implements LabeledItem { - /** >= 0; target label for this block */ + /** {@code >= 0;} target label for this block */ private final int label; - /** non-null; list of instructions in this block */ + /** {@code non-null;} list of instructions in this block */ private final InsnList insns; /** - * non-null; full list of successors that this block may + * {@code non-null;} full list of successors that this block may * branch to */ private final IntList successors; /** - * >= -1; the primary / standard-flow / "default" successor, or - * -1 if this block has no successors (that is, it + * {@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 null. + * Constructs an instance. The predecessor set is set to {@code null}. * - * @param label >= 0; target label for this block - * @param insns non-null; list of instructions in this block - * @param successors non-null; full list of successors that this + * @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 >= -1; the primary / standard-flow / - * "default" successor, or -1 if this block has no + * @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) */ @@ -116,7 +116,7 @@ public final class BasicBlock implements LabeledItem { * {@inheritDoc} * * Instances of this class compare by identity. That is, - * x.equals(y) is only true if x == y. + * {@code x.equals(y)} is only true if {@code x == y}. */ @Override public boolean equals(Object other) { @@ -137,7 +137,7 @@ public final class BasicBlock implements LabeledItem { /** * Gets the target label of this block. * - * @return >= 0; the label + * @return {@code >= 0;} the label */ public int getLabel() { return label; @@ -146,7 +146,7 @@ public final class BasicBlock implements LabeledItem { /** * Gets the list of instructions inside this block. * - * @return non-null; the instruction list + * @return {@code non-null;} the instruction list */ public InsnList getInsns() { return insns; @@ -155,7 +155,7 @@ public final class BasicBlock implements LabeledItem { /** * Gets the list of successors that this block may branch to. * - * @return non-null; the successors list + * @return {@code non-null;} the successors list */ public IntList getSuccessors() { return successors; @@ -164,7 +164,7 @@ public final class BasicBlock implements LabeledItem { /** * Gets the primary successor of this block. * - * @return >= -1; the primary successor, or -1 if this + * @return {@code >= -1;} the primary successor, or {@code -1} if this * block has no successors at all */ public int getPrimarySuccessor() { @@ -175,7 +175,7 @@ public final class BasicBlock implements LabeledItem { * Gets the secondary successor of this block. It is only valid to call * this method on blocks that have exactly two successors. * - * @return >= 0; the secondary successor + * @return {@code >= 0;} the secondary successor */ public int getSecondarySuccessor() { if (successors.size() != 2) { @@ -193,9 +193,9 @@ public final class BasicBlock implements LabeledItem { /** * Gets the first instruction of this block. This is just a - * convenient shorthand for getInsns().get(0). + * convenient shorthand for {@code getInsns().get(0)}. * - * @return non-null; the first instruction + * @return {@code non-null;} the first instruction */ public Insn getFirstInsn() { return insns.get(0); @@ -203,9 +203,9 @@ public final class BasicBlock implements LabeledItem { /** * Gets the last instruction of this block. This is just a - * convenient shorthand for getInsns().getLast(). + * convenient shorthand for {@code getInsns().getLast()}. * - * @return non-null; the last instruction + * @return {@code non-null;} the last instruction */ public Insn getLastInsn() { return insns.getLast(); @@ -213,9 +213,9 @@ public final class BasicBlock implements LabeledItem { /** * Returns whether this block might throw an exception. This is - * just a convenient shorthand for getLastInsn().canThrow(). + * just a convenient shorthand for {@code getLastInsn().canThrow()}. * - * @return true iff this block might throw an + * @return {@code true} iff this block might throw an * exception */ public boolean canThrow() { @@ -228,7 +228,7 @@ public final class BasicBlock implements LabeledItem { * the block to see if it could throw, and if so, whether it in fact * has any associated handlers. * - * @return true iff this block has any associated + * @return {@code true} iff this block has any associated * exception handlers */ public boolean hasExceptionHandlers() { @@ -241,9 +241,9 @@ public final class BasicBlock implements LabeledItem { * 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 null). + * empty list (not {@code null}). * - * @return non-null; the exception handler types associated with + * @return {@code non-null;} the exception handler types associated with * this block */ public TypeList getExceptionHandlerTypes() { @@ -257,7 +257,7 @@ public final class BasicBlock implements LabeledItem { * amount. * * @param delta the amount to offset register numbers by - * @return non-null; an appropriately-constructed instance + * @return {@code non-null;} an appropriately-constructed instance */ public BasicBlock withRegisterOffset(int delta) { return new BasicBlock(label, insns.withRegisterOffset(delta), diff --git a/dx/src/com/android/dx/rop/code/BasicBlockList.java b/dx/src/com/android/dx/rop/code/BasicBlockList.java index 6564318e1..0627425da 100644 --- a/dx/src/com/android/dx/rop/code/BasicBlockList.java +++ b/dx/src/com/android/dx/rop/code/BasicBlockList.java @@ -27,14 +27,14 @@ import com.android.dx.util.LabeledList; */ public final class BasicBlockList extends LabeledList { /** - * >= -1; the count of registers required by this method or - * -1 if not yet calculated + * {@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 null, - * and the first-block label is initially -1. + * 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 */ @@ -45,7 +45,7 @@ public final class BasicBlockList extends LabeledList { } /** - * Constructs a mutable copy for getMutableCopy(). + * Constructs a mutable copy for {@code getMutableCopy()}. * * @param old block to copy */ @@ -58,10 +58,10 @@ public final class BasicBlockList extends LabeledList { /** * 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 NullPointerException. + * do that, this will throw {@code NullPointerException}. * - * @param n >= 0, < size(); which index - * @return non-null; element at that index + * @param n {@code >= 0, < size();} which index + * @return {@code non-null;} element at that index */ public BasicBlock get(int n) { return (BasicBlock) get0(n); @@ -70,8 +70,8 @@ public final class BasicBlockList extends LabeledList { /** * Sets the basic block at the given index. * - * @param n >= 0, < size(); which index - * @param bb null-ok; the element to set at n + * @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); @@ -86,7 +86,7 @@ public final class BasicBlockList extends LabeledList { * instance's instructions (indirectly through {@link BasicBlock} * instances). * - * @return >= 0; the register count + * @return {@code >= 0;} the register count */ public int getRegCount() { if (regCount == -1) { @@ -102,7 +102,7 @@ public final class BasicBlockList extends LabeledList { * Gets the total instruction count for this instance. This is the * sum of the instruction counts of each block. * - * @return >= 0; the total instruction count + * @return {@code >= 0;} the total instruction count */ public int getInstructionCount() { int sz = size(); @@ -122,7 +122,7 @@ public final class BasicBlockList extends LabeledList { * Gets the total instruction count for this instance, ignoring * mark-local instructions which are not actually emitted. * - * @return >= 0; the total instruction count + * @return {@code >= 0;} the total instruction count */ public int getEffectiveInstructionCount() { int sz = size(); @@ -151,8 +151,8 @@ public final class BasicBlockList extends LabeledList { /** * Gets the first block in the list with the given label, if any. * - * @param label >= 0; the label to look for - * @return non-null; the so-labelled block + * @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) { @@ -169,7 +169,7 @@ public final class BasicBlockList extends LabeledList { /** * Visits each instruction of each block in the list, in order. * - * @param visitor non-null; visitor to use + * @param visitor {@code non-null;} visitor to use */ public void forEachInsn(Insn.Visitor visitor) { int sz = size(); @@ -188,7 +188,7 @@ public final class BasicBlockList extends LabeledList { * original. * * @param delta the amount to offset register numbers by - * @return non-null; an appropriately-constructed instance + * @return {@code non-null;} an appropriately-constructed instance */ public BasicBlockList withRegisterOffset(int delta) { int sz = size(); @@ -211,7 +211,7 @@ public final class BasicBlockList extends LabeledList { /** * Returns a mutable copy of this list. * - * @return non-null; an appropriately-constructed instance + * @return {@code non-null;} an appropriately-constructed instance */ public BasicBlockList getMutableCopy() { return new BasicBlockList(this); @@ -222,10 +222,10 @@ public final class BasicBlockList extends LabeledList { * 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 null. + * this returns {@code null}. * - * @param block non-null; the block in question - * @return null-ok; the preferred successor, if any + * @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(); @@ -252,9 +252,9 @@ public final class BasicBlockList extends LabeledList { * Compares the catches of two blocks for equality. This includes * both the catch types and target labels. * - * @param block1 non-null; one block to compare - * @param block2 non-null; the other block to compare - * @return true if the two blocks' non-primary successors + * @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, @@ -313,7 +313,7 @@ public final class BasicBlockList extends LabeledList { */ private static class RegCountVisitor implements Insn.Visitor { - /** >= 0; register count in-progress */ + /** {@code >= 0;} register count in-progress */ private int regCount; /** @@ -326,7 +326,7 @@ public final class BasicBlockList extends LabeledList { /** * Gets the register count. * - * @return >= 0; the count + * @return {@code >= 0;} the count */ public int getRegCount() { return regCount; @@ -363,9 +363,9 @@ public final class BasicBlockList extends LabeledList { } /** - * Helper for all the visit* methods. + * Helper for all the {@code visit*} methods. * - * @param insn non-null; instruction being visited + * @param insn {@code non-null;} instruction being visited */ private void visit(Insn insn) { RegisterSpec result = insn.getResult(); @@ -385,7 +385,7 @@ public final class BasicBlockList extends LabeledList { /** * Processes the given register spec. * - * @param spec non-null; the register spec + * @param spec {@code non-null;} the register spec */ private void processReg(RegisterSpec spec) { int reg = spec.getNextReg(); diff --git a/dx/src/com/android/dx/rop/code/ConservativeTranslationAdvice.java b/dx/src/com/android/dx/rop/code/ConservativeTranslationAdvice.java index a9da10932..1ecf02c15 100644 --- a/dx/src/com/android/dx/rop/code/ConservativeTranslationAdvice.java +++ b/dx/src/com/android/dx/rop/code/ConservativeTranslationAdvice.java @@ -18,11 +18,11 @@ package com.android.dx.rop.code; /** * Implementation of {@link TranslationAdvice} which conservatively answers - * false to all methods. + * {@code false} to all methods. */ public final class ConservativeTranslationAdvice implements TranslationAdvice { - /** non-null; standard instance of this class */ + /** {@code non-null;} standard instance of this class */ public static final ConservativeTranslationAdvice THE_ONE = new ConservativeTranslationAdvice(); diff --git a/dx/src/com/android/dx/rop/code/CstInsn.java b/dx/src/com/android/dx/rop/code/CstInsn.java index d1cf523a5..26df1a9bc 100644 --- a/dx/src/com/android/dx/rop/code/CstInsn.java +++ b/dx/src/com/android/dx/rop/code/CstInsn.java @@ -23,17 +23,17 @@ import com.android.dx.rop.cst.Constant; */ public abstract class CstInsn extends Insn { - /** non-null; the constant */ + /** {@code non-null;} the constant */ private final Constant cst; /** * Constructs an instance. * - * @param opcode non-null; the opcode - * @param position non-null; source position - * @param result null-ok; spec for the result, if any - * @param sources non-null; specs for all the sources - * @param cst non-null; constant + * @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) { @@ -55,7 +55,7 @@ public abstract class CstInsn /** * Gets the constant. * - * @return non-null; the constant + * @return {@code non-null;} the constant */ public Constant getConstant() { return cst; diff --git a/dx/src/com/android/dx/rop/code/DexTranslationAdvice.java b/dx/src/com/android/dx/rop/code/DexTranslationAdvice.java index 1c2382464..8dbc00b1a 100644 --- a/dx/src/com/android/dx/rop/code/DexTranslationAdvice.java +++ b/dx/src/com/android/dx/rop/code/DexTranslationAdvice.java @@ -25,7 +25,7 @@ import com.android.dx.rop.type.Type; */ public final class DexTranslationAdvice implements TranslationAdvice { - /** non-null; standard instance of this class */ + /** {@code non-null;} standard instance of this class */ public static final DexTranslationAdvice THE_ONE = new DexTranslationAdvice(); @@ -98,8 +98,8 @@ public final class DexTranslationAdvice /** * Calculates the total rop width of the list of SSA registers * - * @param sources non-null; list of SSA registers - * @return >= 0 rop-form width in register units + * @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(); diff --git a/dx/src/com/android/dx/rop/code/Exceptions.java b/dx/src/com/android/dx/rop/code/Exceptions.java index 3ef4879e6..f99a760d5 100644 --- a/dx/src/com/android/dx/rop/code/Exceptions.java +++ b/dx/src/com/android/dx/rop/code/Exceptions.java @@ -23,78 +23,78 @@ import com.android.dx.rop.type.Type; * Common exception types. */ public final class Exceptions { - /** non-null; the type java.lang.ArithmeticException */ + /** {@code non-null;} the type {@code java.lang.ArithmeticException} */ public static final Type TYPE_ArithmeticException = Type.intern("Ljava/lang/ArithmeticException;"); /** - * non-null; the type - * java.lang.ArrayIndexOutOfBoundsException + * {@code non-null;} the type + * {@code java.lang.ArrayIndexOutOfBoundsException} */ public static final Type TYPE_ArrayIndexOutOfBoundsException = Type.intern("Ljava/lang/ArrayIndexOutOfBoundsException;"); - /** non-null; the type java.lang.ArrayStoreException */ + /** {@code non-null;} the type {@code java.lang.ArrayStoreException} */ public static final Type TYPE_ArrayStoreException = Type.intern("Ljava/lang/ArrayStoreException;"); - /** non-null; the type java.lang.ClassCastException */ + /** {@code non-null;} the type {@code java.lang.ClassCastException} */ public static final Type TYPE_ClassCastException = Type.intern("Ljava/lang/ClassCastException;"); - /** non-null; the type java.lang.Error */ + /** {@code non-null;} the type {@code java.lang.Error} */ public static final Type TYPE_Error = Type.intern("Ljava/lang/Error;"); /** - * non-null; the type - * java.lang.IllegalMonitorStateException + * {@code non-null;} the type + * {@code java.lang.IllegalMonitorStateException} */ public static final Type TYPE_IllegalMonitorStateException = Type.intern("Ljava/lang/IllegalMonitorStateException;"); - /** non-null; the type java.lang.NegativeArraySizeException */ + /** {@code non-null;} the type {@code java.lang.NegativeArraySizeException} */ public static final Type TYPE_NegativeArraySizeException = Type.intern("Ljava/lang/NegativeArraySizeException;"); - /** non-null; the type java.lang.NullPointerException */ + /** {@code non-null;} the type {@code java.lang.NullPointerException} */ public static final Type TYPE_NullPointerException = Type.intern("Ljava/lang/NullPointerException;"); - /** non-null; the list [java.lang.Error] */ + /** {@code non-null;} the list {@code [java.lang.Error]} */ public static final StdTypeList LIST_Error = StdTypeList.make(TYPE_Error); /** - * non-null; the list [java.lang.Error, - * java.lang.ArithmeticException] + * {@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); /** - * non-null; the list [java.lang.Error, - * java.lang.ClassCastException] + * {@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); /** - * non-null; the list [java.lang.Error, - * java.lang.NegativeArraySizeException] + * {@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); /** - * non-null; the list [java.lang.Error, - * java.lang.NullPointerException] + * {@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); /** - * non-null; the list [java.lang.Error, + * {@code non-null;} the list {@code [java.lang.Error, * java.lang.NullPointerException, - * java.lang.ArrayIndexOutOfBoundsException] + * java.lang.ArrayIndexOutOfBoundsException]} */ public static final StdTypeList LIST_Error_Null_ArrayIndexOutOfBounds = StdTypeList.make(TYPE_Error, @@ -102,10 +102,10 @@ public final class Exceptions { TYPE_ArrayIndexOutOfBoundsException); /** - * non-null; the list [java.lang.Error, + * {@code non-null;} the list {@code [java.lang.Error, * java.lang.NullPointerException, * java.lang.ArrayIndexOutOfBoundsException, - * java.lang.ArrayStoreException] + * java.lang.ArrayStoreException]} */ public static final StdTypeList LIST_Error_Null_ArrayIndex_ArrayStore = StdTypeList.make(TYPE_Error, @@ -114,9 +114,9 @@ public final class Exceptions { TYPE_ArrayStoreException); /** - * non-null; the list [java.lang.Error, + * {@code non-null;} the list {@code [java.lang.Error, * java.lang.NullPointerException, - * java.lang.IllegalMonitorStateException] + * java.lang.IllegalMonitorStateException]} */ public static final StdTypeList LIST_Error_Null_IllegalMonitorStateException = diff --git a/dx/src/com/android/dx/rop/code/FillArrayDataInsn.java b/dx/src/com/android/dx/rop/code/FillArrayDataInsn.java index 3798afbf2..0fc7d2b61 100644 --- a/dx/src/com/android/dx/rop/code/FillArrayDataInsn.java +++ b/dx/src/com/android/dx/rop/code/FillArrayDataInsn.java @@ -42,11 +42,11 @@ public final class FillArrayDataInsn /** * Constructs an instance. * - * @param opcode non-null; the opcode - * @param position non-null; source position - * @param sources non-null; specs for all the sources - * @param initValues non-null; list of initial values to fill the array - * @param cst non-null; type of the new array + * @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, @@ -71,7 +71,7 @@ public final class FillArrayDataInsn /** * Return the list of init values - * @return non-null; list of init values + * @return {@code non-null;} list of init values */ public ArrayList getInitValues() { return initValues; @@ -79,7 +79,7 @@ public final class FillArrayDataInsn /** * Return the type of the newly created array - * @return non-null; array type + * @return {@code non-null;} array type */ public Constant getConstant() { return arrayType; diff --git a/dx/src/com/android/dx/rop/code/Insn.java b/dx/src/com/android/dx/rop/code/Insn.java index b1ad0ade4..77ab9c043 100644 --- a/dx/src/com/android/dx/rop/code/Insn.java +++ b/dx/src/com/android/dx/rop/code/Insn.java @@ -30,25 +30,25 @@ import com.android.dx.util.ToHuman; * information. */ public abstract class Insn implements ToHuman { - /** non-null; opcode */ + /** {@code non-null;} opcode */ private final Rop opcode; - /** non-null; source position */ + /** {@code non-null;} source position */ private final SourcePosition position; - /** null-ok; spec for the result of this instruction, if any */ + /** {@code null-ok;} spec for the result of this instruction, if any */ private final RegisterSpec result; - /** non-null; specs for all the sources of this instruction */ + /** {@code non-null;} specs for all the sources of this instruction */ private final RegisterSpecList sources; /** * Constructs an instance. * - * @param opcode non-null; the opcode - * @param position non-null; source position - * @param result null-ok; spec for the result, if any - * @param sources non-null; specs for all the sources + * @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) { @@ -74,7 +74,7 @@ public abstract class Insn implements ToHuman { * {@inheritDoc} * * Instances of this class compare by identity. That is, - * x.equals(y) is only true if x == y. + * {@code x.equals(y)} is only true if {@code x == y}. */ @Override public final boolean equals(Object other) { @@ -102,7 +102,7 @@ public abstract class Insn implements ToHuman { /** * Gets a human-oriented (and slightly lossy) string for this instance. * - * @return non-null; the human string form + * @return {@code non-null;} the human string form */ public String toHuman() { return toHumanWithInline(getInlineString()); @@ -112,7 +112,7 @@ public abstract class Insn implements ToHuman { * Gets an "inline" string portion for toHuman(), if available. This * is the portion that appears after the Rop opcode * - * @return null-ok; if non-null, the inline text for toHuman() + * @return {@code null-ok;} if non-null, the inline text for toHuman() */ public String getInlineString() { return null; @@ -121,7 +121,7 @@ public abstract class Insn implements ToHuman { /** * Gets the opcode. * - * @return non-null; the opcode + * @return {@code non-null;} the opcode */ public final Rop getOpcode() { return opcode; @@ -130,17 +130,17 @@ public abstract class Insn implements ToHuman { /** * Gets the source position. * - * @return non-null; 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 null + * Gets the result spec, if any. A return value of {@code null} * means this instruction returns nothing. * - * @return null-ok; the result spec, if any + * @return {@code null-ok;} the result spec, if any */ public final RegisterSpec getResult() { return result; @@ -149,10 +149,10 @@ public abstract class Insn implements ToHuman { /** * 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 mark-local insns + * may be the result register, or for {@code mark-local} insns * it may be the source. * - * @return null-ok; a named register spec or null + * @return {@code null-ok;} a named register spec or null */ public final RegisterSpec getLocalAssignment() { RegisterSpec assignment; @@ -178,7 +178,7 @@ public abstract class Insn implements ToHuman { /** * Gets the source specs. * - * @return non-null; the source specs + * @return {@code non-null;} the source specs */ public final RegisterSpecList getSources() { return sources; @@ -186,9 +186,9 @@ public abstract class Insn implements ToHuman { /** * Gets whether this instruction can possibly throw an exception. This - * is just a convenient wrapper for getOpcode().canThrow(). + * is just a convenient wrapper for {@code getOpcode().canThrow()}. * - * @return true iff this instruction can possibly throw + * @return {@code true} iff this instruction can possibly throw */ public final boolean canThrow() { return opcode.canThrow(); @@ -202,7 +202,7 @@ public abstract class Insn implements ToHuman { * exceptions. To determine whether this instruction can throw, * use {@link #canThrow}. * - * @return non-null; the catches list + * @return {@code non-null;} the catches list */ public abstract TypeList getCatches(); @@ -210,7 +210,7 @@ public abstract class Insn implements ToHuman { * Calls the appropriate method on the given visitor, depending on the * class of this instance. Subclasses must override this. * - * @param visitor non-null; the visitor to call on + * @param visitor {@code non-null;} the visitor to call on */ public abstract void accept(Visitor visitor); @@ -221,8 +221,8 @@ public abstract class Insn implements ToHuman { * throw. To determine whether this instruction can throw, use * {@link #canThrow}. * - * @param type non-null; type to append to the catch list - * @return non-null; an appropriately-constructed instance + * @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); @@ -231,7 +231,7 @@ public abstract class Insn implements ToHuman { * register references have been offset by the given delta. * * @param delta the amount to offset register references by - * @return non-null; an appropriately-constructed instance + * @return {@code non-null;} an appropriately-constructed instance */ public abstract Insn withRegisterOffset(int delta); @@ -239,10 +239,10 @@ public abstract class Insn implements ToHuman { * Returns an instance that is just like this one, except that, if * possible, the insn is converted into a version in which the last * source (if it is a constant) is represented directly rather than - * as a register reference. this is returned in cases where + * as a register reference. {@code this} is returned in cases where * the translation is not possible. * - * @return non-null; an appropriately-constructed instance + * @return {@code non-null;} an appropriately-constructed instance */ public Insn withLastSourceLiteral() { return this; @@ -251,7 +251,7 @@ public abstract class Insn implements ToHuman { /** * Returns an exact copy of this Insn * - * @return non-null; an appropriately-constructed instance + * @return {@code non-null;} an appropriately-constructed instance */ public Insn copy() { return withRegisterOffset(0); @@ -270,8 +270,8 @@ public abstract class Insn implements ToHuman { } /** - * Compares Insn contents, since Insn.equals() is defined - * to be an identity compare. Insn's are contentEquals() + * 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. * @@ -290,9 +290,9 @@ public abstract class Insn implements ToHuman { * Returns an instance that is just like this one, except * with new result and source registers. * - * @param result null-ok; new result register - * @param sources non-null; new sources registers - * @return non-null; an appropriately-constructed instance + * @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); @@ -301,8 +301,8 @@ public abstract class Insn implements ToHuman { * Returns the string form of this instance, with the given bit added in * the standard location for an inline argument. * - * @param extra null-ok; the inline argument string - * @return non-null; the string form + * @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); @@ -334,8 +334,8 @@ public abstract class Insn implements ToHuman { * Returns the human string form of this instance, with the given * bit added in the standard location for an inline argument. * - * @param extra null-ok; the inline argument string - * @return non-null; the human string form + * @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); @@ -380,42 +380,42 @@ public abstract class Insn implements ToHuman { /** * Visits a {@link PlainInsn}. * - * @param insn non-null; the instruction to visit + * @param insn {@code non-null;} the instruction to visit */ public void visitPlainInsn(PlainInsn insn); /** * Visits a {@link PlainCstInsn}. * - * @param insn non-null; the instruction to visit + * @param insn {@code non-null;} the instruction to visit */ public void visitPlainCstInsn(PlainCstInsn insn); /** * Visits a {@link SwitchInsn}. * - * @param insn non-null; the instruction to visit + * @param insn {@code non-null;} the instruction to visit */ public void visitSwitchInsn(SwitchInsn insn); /** * Visits a {@link ThrowingCstInsn}. * - * @param insn non-null; the instruction to visit + * @param insn {@code non-null;} the instruction to visit */ public void visitThrowingCstInsn(ThrowingCstInsn insn); /** * Visits a {@link ThrowingInsn}. * - * @param insn non-null; the instruction to visit + * @param insn {@code non-null;} the instruction to visit */ public void visitThrowingInsn(ThrowingInsn insn); /** * Visits a {@link FillArrayDataInsn}. * - * @param insn non-null; the instruction to visit + * @param insn {@code non-null;} the instruction to visit */ public void visitFillArrayDataInsn(FillArrayDataInsn insn); } diff --git a/dx/src/com/android/dx/rop/code/InsnList.java b/dx/src/com/android/dx/rop/code/InsnList.java index 34f124cca..493f7fceb 100644 --- a/dx/src/com/android/dx/rop/code/InsnList.java +++ b/dx/src/com/android/dx/rop/code/InsnList.java @@ -24,7 +24,7 @@ import com.android.dx.util.FixedSizeList; public final class InsnList extends FixedSizeList { /** - * Constructs an instance. All indices initially contain null. + * Constructs an instance. All indices initially contain {@code null}. * * @param size the size of the list */ @@ -35,10 +35,10 @@ public final class InsnList /** * 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 NullPointerException. + * do that, this will throw {@code NullPointerException}. * - * @param n >= 0, < size(); which index - * @return non-null; element at that index + * @param n {@code >= 0, < size();} which index + * @return {@code non-null;} element at that index */ public Insn get(int n) { return (Insn) get0(n); @@ -47,8 +47,8 @@ public final class InsnList /** * Sets the instruction at the given index. * - * @param n >= 0, < size(); which index - * @param insn non-null; the instruction to set at n + * @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); @@ -56,9 +56,9 @@ public final class InsnList /** * Gets the last instruction. This is just a convenient shorthand for - * get(size() - 1). + * {@code get(size() - 1)}. * - * @return non-null; the last instruction + * @return {@code non-null;} the last instruction */ public Insn getLast() { return get(size() - 1); @@ -67,7 +67,7 @@ public final class InsnList /** * Visits each instruction in the list, in order. * - * @param visitor non-null; visitor to use + * @param visitor {@code non-null;} visitor to use */ public void forEach(Insn.Visitor visitor) { int sz = size(); @@ -78,9 +78,9 @@ public final class InsnList } /** - * Compares the contents of this InsnList with another. + * 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 Insn.contentEquals(). + * also return true to {@code Insn.contentEquals()}. * * @param b to compare * @return true in the case described above. @@ -108,7 +108,7 @@ public final class InsnList * original. * * @param delta the amount to offset register numbers by - * @return non-null; an appropriately-constructed instance + * @return {@code non-null;} an appropriately-constructed instance */ public InsnList withRegisterOffset(int delta) { int sz = size(); diff --git a/dx/src/com/android/dx/rop/code/LocalItem.java b/dx/src/com/android/dx/rop/code/LocalItem.java index bac6ce2a4..7d6bebe09 100644 --- a/dx/src/com/android/dx/rop/code/LocalItem.java +++ b/dx/src/com/android/dx/rop/code/LocalItem.java @@ -22,10 +22,10 @@ import com.android.dx.rop.cst.CstUtf8; * A local variable item: either a name or a signature or both. */ public class LocalItem implements Comparable { - /** null-ok; local variable name */ + /** {@code null-ok;} local variable name */ private final CstUtf8 name; - /** null-ok; local variable signature */ + /** {@code null-ok;} local variable signature */ private final CstUtf8 signature; /** @@ -33,9 +33,9 @@ public class LocalItem implements Comparable { * * TODO: intern these * - * @param name null-ok; local variable name - * @param signature null-ok; local variable signature - * @return non-null; appropriate instance. + * @param name {@code null-ok;} local variable name + * @param signature {@code null-ok;} local variable signature + * @return {@code non-null;} appropriate instance. */ public static LocalItem make(CstUtf8 name, CstUtf8 signature) { if (name == null && signature == null) { @@ -48,8 +48,8 @@ public class LocalItem implements Comparable { /** * Constructs instance. * - * @param name null-ok; local variable name - * @param signature null-ok; local variable signature + * @param name {@code null-ok;} local variable name + * @param signature {@code null-ok;} local variable signature */ private LocalItem(CstUtf8 name, CstUtf8 signature) { this.name = name; @@ -126,7 +126,7 @@ public class LocalItem implements Comparable { /** * Gets name. * - * @return null-ok; name + * @return {@code null-ok;} name */ public CstUtf8 getName() { return name; @@ -135,7 +135,7 @@ public class LocalItem implements Comparable { /** * Gets signature. * - * @return null-ok; signature + * @return {@code null-ok;} signature */ public CstUtf8 getSignature() { return signature; diff --git a/dx/src/com/android/dx/rop/code/LocalVariableExtractor.java b/dx/src/com/android/dx/rop/code/LocalVariableExtractor.java index 2d4cbced8..db142c28e 100644 --- a/dx/src/com/android/dx/rop/code/LocalVariableExtractor.java +++ b/dx/src/com/android/dx/rop/code/LocalVariableExtractor.java @@ -24,23 +24,23 @@ import com.android.dx.util.IntList; * a method. */ public final class LocalVariableExtractor { - /** non-null; method being extracted from */ + /** {@code non-null;} method being extracted from */ private final RopMethod method; - /** non-null; block list for the method */ + /** {@code non-null;} block list for the method */ private final BasicBlockList blocks; - /** non-null; result in-progress */ + /** {@code non-null;} result in-progress */ private final LocalVariableInfo resultInfo; - /** non-null; work set indicating blocks needing to be processed */ + /** {@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 non-null; the method to extract from - * @return non-null; the extracted information + * @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); @@ -50,7 +50,7 @@ public final class LocalVariableExtractor { /** * Constructs an instance. This method is private. Use {@link #extract}. * - * @param method non-null; the method to extract from + * @param method {@code non-null;} the method to extract from */ private LocalVariableExtractor(RopMethod method) { if (method == null) { @@ -69,7 +69,7 @@ public final class LocalVariableExtractor { /** * Does the extraction. * - * @return non-null; the extracted information + * @return {@code non-null;} the extracted information */ private LocalVariableInfo doit() { for (int label = method.getFirstLabel(); @@ -86,7 +86,7 @@ public final class LocalVariableExtractor { /** * Processes a single block. * - * @param label >= 0; label of the block to process + * @param label {@code >= 0;} label of the block to process */ private void processBlock(int label) { RegisterSpecSet primaryState = resultInfo.mutableCopyOfStarts(label); @@ -101,7 +101,6 @@ public final class LocalVariableExtractor { * state *before* executing it to be what is merged into * exception targets. */ - Insn lastInsn = insns.getLast(); boolean canThrowDuringLastInsn = block.hasExceptionHandlers() && (insns.getLast().getResult() != null); int freezeSecondaryStateAt = insnSz - 1; diff --git a/dx/src/com/android/dx/rop/code/LocalVariableInfo.java b/dx/src/com/android/dx/rop/code/LocalVariableInfo.java index 29c239b23..fa5e7cc8c 100644 --- a/dx/src/com/android/dx/rop/code/LocalVariableInfo.java +++ b/dx/src/com/android/dx/rop/code/LocalVariableInfo.java @@ -27,30 +27,30 @@ import java.util.HashMap; */ public final class LocalVariableInfo extends MutabilityControl { - /** >= 0; the register count for the method */ + /** {@code >= 0;} the register count for the method */ private final int regCount; /** - * non-null; {@link RegisterSpecSet} to use when indicating a block + * {@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; /** - * non-null; array consisting of register sets representing the + * {@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; - /** non-null; map from instructions to the variable each assigns */ + /** {@code non-null;} map from instructions to the variable each assigns */ private final HashMap insnAssignments; /** * Constructs an instance. * - * @param method non-null; the method being represented by this instance + * @param method {@code non-null;} the method being represented by this instance */ public LocalVariableInfo(RopMethod method) { if (method == null) { @@ -73,8 +73,8 @@ public final class LocalVariableInfo * Sets the register set associated with the start of the block with * the given label. * - * @param label >= 0; the block label - * @param specs non-null; the register set to associate with the block + * @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(); @@ -98,12 +98,12 @@ public final class LocalVariableInfo * merge the two sets and call {@link #setStarts} on the result of the * merge. * - * @param label >= 0; the block label - * @param specs non-null; the register set to merge into the start set + * @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 true if the merge resulted in an actual change + * @return {@code true} if the merge resulted in an actual change * to the associated set (including storing one for the first time) or - * false if there was no change + * {@code false} if there was no change */ public boolean mergeStarts(int label, RegisterSpecSet specs) { RegisterSpecSet start = getStarts0(label); @@ -132,8 +132,8 @@ public final class LocalVariableInfo * 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 >= 0; the block label - * @return non-null; the associated register set + * @param label {@code >= 0;} the block label + * @return {@code non-null;} the associated register set */ public RegisterSpecSet getStarts(int label) { RegisterSpecSet result = getStarts0(label); @@ -144,10 +144,10 @@ public final class LocalVariableInfo /** * Gets the register set associated with the start of the given * block. This is just convenient shorthand for - * getStarts(block.getLabel()). + * {@code getStarts(block.getLabel())}. * - * @param block non-null; the block in question - * @return non-null; the associated register set + * @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()); @@ -159,8 +159,8 @@ public final class LocalVariableInfo * newly-allocated empty {@link RegisterSpecSet} of appropriate * max size if there is not yet any set associated with the block. * - * @param label >= 0; the block label - * @return non-null; the associated register set + * @param label {@code >= 0;} the block label + * @return {@code non-null;} the associated register set */ public RegisterSpecSet mutableCopyOfStarts(int label) { RegisterSpecSet result = getStarts0(label); @@ -180,8 +180,8 @@ public final class LocalVariableInfo * simple type and the one in the instruction can be an arbitrary * {@link TypeBearer} (such as a constant value). * - * @param insn non-null; the instruction in question - * @param spec non-null; the associated register spec + * @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(); @@ -201,8 +201,8 @@ public final class LocalVariableInfo * Gets the named register being assigned by the given instruction, if * previously stored in this instance. * - * @param insn non-null; instruction in question - * @return null-ok; the named register being assigned, if any + * @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); @@ -211,7 +211,7 @@ public final class LocalVariableInfo /** * Gets the number of assignments recorded by this instance. * - * @return >= 0; the number of assignments + * @return {@code >= 0;} the number of assignments */ public int getAssignmentCount() { return insnAssignments.size(); @@ -235,8 +235,8 @@ public final class LocalVariableInfo * Helper method, to get the starts for a label, throwing the * right exception for range problems. * - * @param label >= 0; the block label - * @return null-ok; associated register set or null if there + * @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) { diff --git a/dx/src/com/android/dx/rop/code/PlainCstInsn.java b/dx/src/com/android/dx/rop/code/PlainCstInsn.java index 908b3cb34..7a3ac38b5 100644 --- a/dx/src/com/android/dx/rop/code/PlainCstInsn.java +++ b/dx/src/com/android/dx/rop/code/PlainCstInsn.java @@ -30,11 +30,11 @@ public final class PlainCstInsn /** * Constructs an instance. * - * @param opcode non-null; the opcode - * @param position non-null; source position - * @param result null-ok; spec for the result, if any - * @param sources non-null; specs for all the sources - * @param cst non-null; the constant + * @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, diff --git a/dx/src/com/android/dx/rop/code/PlainInsn.java b/dx/src/com/android/dx/rop/code/PlainInsn.java index 4c5c9b701..d1db646d0 100644 --- a/dx/src/com/android/dx/rop/code/PlainInsn.java +++ b/dx/src/com/android/dx/rop/code/PlainInsn.java @@ -31,10 +31,10 @@ public final class PlainInsn /** * Constructs an instance. * - * @param opcode non-null; the opcode - * @param position non-null; source position - * @param result null-ok; spec for the result, if any - * @param sources non-null; specs for all the sources + * @param 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) { @@ -57,10 +57,10 @@ public final class PlainInsn /** * Constructs a single-source instance. * - * @param opcode non-null; the opcode - * @param position non-null; source position - * @param result null-ok; spec for the result, if any - * @param source non-null; spec for the source + * @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) { diff --git a/dx/src/com/android/dx/rop/code/RegOps.java b/dx/src/com/android/dx/rop/code/RegOps.java index f201f68da..2084a69a0 100644 --- a/dx/src/com/android/dx/rop/code/RegOps.java +++ b/dx/src/com/android/dx/rop/code/RegOps.java @@ -21,283 +21,279 @@ import com.android.dx.util.Hex; /** * All the register-based opcodes, and related utilities. * - *

    Note: Opcode descriptions use a rough pseudocode. r - * is the result register, x is the first argument, - * y is the second argument, and z is the + *

    Note: Opcode descriptions use a rough pseudocode. {@code r} + * is the result register, {@code x} is the first argument, + * {@code y} is the second argument, and {@code z} is the * third argument. The expression which describes * the operation uses Java-ish syntax but is preceded by type indicators for * each of the values. */ public final class RegOps { - /** nop() */ + /** {@code nop()} */ public static final int NOP = 1; - /** T: any type; r,x: T :: r = x; */ + /** {@code T: any type; r,x: T :: r = x;} */ public static final int MOVE = 2; - /** T: any type; r,param(x): T :: r = param(x) */ + /** {@code T: any type; r,param(x): T :: r = param(x)} */ public static final int MOVE_PARAM = 3; /** - * T: Throwable; r: T :: r = caught_exception. + * {@code T: Throwable; r: T :: r = caught_exception}. * Note: 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; - /** T: any type; r, literal: T :: r = literal; */ + /** {@code T: any type; r, literal: T :: r = literal;} */ public static final int CONST = 5; - /** goto label */ + /** {@code goto label} */ public static final int GOTO = 6; /** - * T: int or Object; x,y: T :: if (x == y) goto - * label + * {@code T: int or Object; x,y: T :: if (x == y) goto + * label} */ public static final int IF_EQ = 7; /** - * T: int or Object; x,y: T :: if (x != y) goto - * label + * {@code T: int or Object; x,y: T :: if (x != y) goto + * label} */ public static final int IF_NE = 8; - /** x,y: int :: if (x < y) goto label */ + /** {@code x,y: int :: if (x < y) goto label} */ public static final int IF_LT = 9; - /** x,y: int :: if (x >= y) goto label */ + /** {@code x,y: int :: if (x >= y) goto label} */ public static final int IF_GE = 10; - /** x,y: int :: if (x <= y) goto label */ + /** {@code x,y: int :: if (x <= y) goto label} */ public static final int IF_LE = 11; - /** x,y: int :: if (x > y) goto label */ + /** {@code x,y: int :: if (x > y) goto label} */ public static final int IF_GT = 12; - /** x: int :: goto table[x] */ + /** {@code x: int :: goto table[x]} */ public static final int SWITCH = 13; - /** T: any numeric type; r,x,y: T :: r = x + y */ + /** {@code T: any numeric type; r,x,y: T :: r = x + y} */ public static final int ADD = 14; - /** T: any numeric type; r,x,y: T :: r = x - y */ + /** {@code T: any numeric type; r,x,y: T :: r = x - y} */ public static final int SUB = 15; - /** T: any numeric type; r,x,y: T :: r = x * y */ + /** {@code T: any numeric type; r,x,y: T :: r = x * y} */ public static final int MUL = 16; - /** T: any numeric type; r,x,y: T :: r = x / y */ + /** {@code T: any numeric type; r,x,y: T :: r = x / y} */ public static final int DIV = 17; /** - * T: any numeric type; r,x,y: T :: r = x % y + * {@code T: any numeric type; r,x,y: T :: r = x % y} * (Java-style remainder) */ public static final int REM = 18; - /** T: any numeric type; r,x: T :: r = -x */ + /** {@code T: any numeric type; r,x: T :: r = -x} */ public static final int NEG = 19; - /** T: any integral type; r,x,y: T :: r = x & y */ + /** {@code T: any integral type; r,x,y: T :: r = x & y} */ public static final int AND = 20; - /** T: any integral type; r,x,y: T :: r = x | y */ + /** {@code T: any integral type; r,x,y: T :: r = x | y} */ public static final int OR = 21; - /** T: any integral type; r,x,y: T :: r = x ^ y */ + /** {@code T: any integral type; r,x,y: T :: r = x ^ y} */ public static final int XOR = 22; /** - * T: any integral type; r,x: T; y: int :: r = x << - * y + * {@code T: any integral type; r,x: T; y: int :: r = x << y} */ public static final int SHL = 23; /** - * T: any integral type; r,x: T; y: int :: r = x >> - * y (signed right-shift) + * {@code T: any integral type; r,x: T; y: int :: r = x >> y} + * (signed right-shift) */ public static final int SHR = 24; /** - * T: any integral type; r,x: T; y: int :: r = x - * >>> y (unsigned right-shift) + * {@code T: any integral type; r,x: T; y: int :: r = x >>> y} + * (unsigned right-shift) */ public static final int USHR = 25; - /** T: any integral type; r,x: T :: r = ~x */ + /** {@code T: any integral type; r,x: T :: r = ~x} */ public static final int NOT = 26; /** - * T: any numeric type; r: int; x,y: T :: r = (x == y) ? 0 - * : (x > y) ? 1 : -1 (Java-style "cmpl" where a NaN is + * {@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; /** - * 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 + * {@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; /** - * T: any numeric type; U: any numeric type; r: T; x: U :: - * r = (T) x (numeric type conversion between the four + * {@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; /** - * r,x: int :: r = (x << 24) >> 24 (Java-style + * {@code r,x: int :: r = (x << 24) >> 24} (Java-style * convert int to byte) */ public static final int TO_BYTE = 30; /** - * r,x: int :: r = x & 0xffff (Java-style - * convert int to char) + * {@code r,x: int :: r = x & 0xffff} (Java-style convert int to char) */ public static final int TO_CHAR = 31; /** - * r,x: int :: r = (x << 16) >> 16 (Java-style + * {@code r,x: int :: r = (x << 16) >> 16} (Java-style * convert int to short) */ public static final int TO_SHORT = 32; - /** T: return type for the method; x: T; return x */ + /** {@code T: return type for the method; x: T; return x} */ public static final int RETURN = 33; - /** T: any type; r: int; x: T[]; :: r = x.length */ + /** {@code T: any type; r: int; x: T[]; :: r = x.length} */ public static final int ARRAY_LENGTH = 34; - /** x: Throwable :: throw(x) */ + /** {@code x: Throwable :: throw(x)} */ public static final int THROW = 35; - /** x: Object :: monitorenter(x) */ + /** {@code x: Object :: monitorenter(x)} */ public static final int MONITOR_ENTER = 36; - /** x: Object :: monitorexit(x) */ + /** {@code x: Object :: monitorexit(x)} */ public static final int MONITOR_EXIT = 37; - /** T: any type; r: T; x: T[]; y: int :: r = x[y] */ + /** {@code T: any type; r: T; x: T[]; y: int :: r = x[y]} */ public static final int AGET = 38; - /** T: any type; x: T; y: T[]; z: int :: x[y] = z */ + /** {@code T: any type; x: T; y: T[]; z: int :: x[y] = z} */ public static final int APUT = 39; /** - * T: any non-array object type :: r = - * alloc(T) (allocate heap space for an object) + * {@code T: any non-array object type :: r = + * alloc(T)} (allocate heap space for an object) */ public static final int NEW_INSTANCE = 40; - /** T: any array type; r: T; x: int :: r = new T[x] */ + /** {@code T: any array type; r: T; x: int :: r = new T[x]} */ public static final int NEW_ARRAY = 41; /** - * T: any array type; r: T; x: int; v0..vx: T :: r = new T[x] - * {v0, ..., vx} + * {@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; /** - * T: any object type; x: Object :: (T) x (can - * throw ClassCastException) + * {@code T: any object type; x: Object :: (T) x} (can + * throw {@code ClassCastException}) */ public static final int CHECK_CAST = 43; /** - * T: any object type; x: Object :: x instanceof - * T + * {@code T: any object type; x: Object :: x instanceof T} */ public static final int INSTANCE_OF = 44; /** - * T: any type; r: T; x: Object; f: instance field spec of - * type T :: r = x.f + * {@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; /** - * T: any type; r: T; f: static field spec of type T :: r = - * f + * {@code T: any type; r: T; f: static field spec of type T :: r = + * f} */ public static final int GET_STATIC = 46; /** - * T: any type; x: T; y: Object; f: instance field spec of type - * T :: y.f = x + * {@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; /** - * T: any type; f: static field spec of type T; x: T :: f = - * x + * {@code T: any type; f: static field spec of type T; x: T :: f = x} */ public static final int PUT_STATIC = 48; /** - * Tr, T0, T1...: any types; r: Tr; m: static method spec; - * y0: T0; y1: T1 ... :: r = m(y0, y1, ...) (call static + * {@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; /** - * Tr, T0, T1...: any types; r: Tr; x: Object; m: instance method - * spec; y0: T0; y1: T1 ... :: r = x.m(y0, y1, ...) (call normal + * {@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; /** - * Tr, T0, T1...: any types; r: Tr; x: Object; m: instance method - * spec; y0: T0; y1: T1 ... :: r = x.m(y0, y1, ...) (call + * {@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; /** - * Tr, T0, T1...: any types; r: Tr; x: Object; m: instance method - * spec; y0: T0; y1: T1 ... :: r = x.m(y0, y1, ...) (call + * {@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; /** - * Tr, T0, T1...: any types; r: Tr; x: Object; m: interface + * {@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) + * ...)} (call interface method) */ public static final int INVOKE_INTERFACE = 53; /** - * T0: any type; (mark beginning or end of local variable - * name + * {@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; /** - * T: Any type; r: T :: r = return_type. + * {@code T: Any type; r: T :: r = return_type}. * Note: This opcode should only ever be used in the * first instruction of a block following an invoke-*. */ public static final int MOVE_RESULT = 55; /** - * T: Any type; r: T :: r = return_type. + * {@code T: Any type; r: T :: r = return_type}. * Note: 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; - /** T: Any primitive type; v0..vx: T :: {v0, ..., vx} */ + /** {@code T: Any primitive type; v0..vx: T :: {v0, ..., vx}} */ public static final int FILL_ARRAY_DATA = 57; /** @@ -310,8 +306,8 @@ public final class RegOps { /** * Gets the name of the given opcode. * - * @param opcode >= 0, <= 255; the opcode - * @return non-null; its name + * @param opcode {@code >= 0, <= 255;} the opcode + * @return {@code non-null;} its name */ public static String opName(int opcode) { switch (opcode) { diff --git a/dx/src/com/android/dx/rop/code/RegisterSpec.java b/dx/src/com/android/dx/rop/code/RegisterSpec.java index 73af91fbc..1f1476780 100644 --- a/dx/src/com/android/dx/rop/code/RegisterSpec.java +++ b/dx/src/com/android/dx/rop/code/RegisterSpec.java @@ -30,33 +30,33 @@ import java.util.HashMap; */ public final class RegisterSpec implements TypeBearer, ToHuman, Comparable { - /** non-null; string to prefix register numbers with */ + /** {@code non-null;} string to prefix register numbers with */ public static final String PREFIX = "v"; - /** non-null; intern table for instances */ + /** {@code non-null;} intern table for instances */ private static final HashMap theInterns = new HashMap(1000); - /** non-null; common comparison instance used while interning */ + /** {@code non-null;} common comparison instance used while interning */ private static final ForComparison theInterningItem = new ForComparison(); - /** >= 0; register number */ + /** {@code >= 0;} register number */ private final int reg; - /** non-null; type loaded or stored */ + /** {@code non-null;} type loaded or stored */ private final TypeBearer type; - /** null-ok; local variable info associated with this register, if any */ + /** {@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 >= 0; the register number - * @param type non-null; the type (or possibly actual value) which + * @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 null-ok; the associated local variable, if any - * @return non-null; an appropriately-constructed instance + * @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) { @@ -77,10 +77,10 @@ public final class RegisterSpec * no variable info. This method is allowed to return shared * instances (but doesn't necessarily do so). * - * @param reg >= 0; the register number - * @param type non-null; the type (or possibly actual value) which + * @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 non-null; an appropriately-constructed instance + * @return {@code non-null;} an appropriately-constructed instance */ public static RegisterSpec make(int reg, TypeBearer type) { return intern(reg, type, null); @@ -91,11 +91,11 @@ public final class RegisterSpec * variable info. This method is allowed to return shared * instances (but doesn't necessarily do so). * - * @param reg >= 0; the register number - * @param type non-null; the type (or possibly actual value) which + * @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 non-null; the associated local variable - * @return non-null; an appropriately-constructed instance + * @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) { @@ -111,12 +111,12 @@ public final class RegisterSpec * variable info. This method is allowed to return shared * instances (but doesn't necessarily do so). * - * @param reg >= 0; the register number - * @param type non-null; the type (or possibly actual value) which + * @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 null-ok; the associated variable info or null for + * @param local {@code null-ok;} the associated variable info or null for * none - * @return non-null; an appropriately-constructed instance + * @return {@code non-null;} an appropriately-constructed instance */ public static RegisterSpec makeLocalOptional( int reg, TypeBearer type, LocalItem local) { @@ -127,8 +127,8 @@ public final class RegisterSpec /** * Gets the string form for the given register number. * - * @param reg >= 0; the register number - * @return non-null; the string form + * @param reg {@code >= 0;} the register number + * @return {@code non-null;} the string form */ public static String regString(int reg) { return PREFIX + reg; @@ -138,10 +138,10 @@ public final class RegisterSpec * Constructs an instance. This constructor is private. Use * {@link #make}. * - * @param reg >= 0; the register number - * @param type non-null; the type (or possibly actual value) which + * @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 null-ok; the associated local variable, if any + * @param local {@code null-ok;} the associated local variable, if any */ private RegisterSpec(int reg, TypeBearer type, LocalItem local) { if (reg < 0) { @@ -178,7 +178,7 @@ public final class RegisterSpec * to ignore whatever arbitrary extra stuff might be carried around * by an outer {@link TypeBearer}. * - * @param other null-ok; spec to compare to + * @param other {@code null-ok;} spec to compare to * @return {@code true} iff {@code this} and {@code other} are equal * in the stated way */ @@ -195,7 +195,7 @@ public final class RegisterSpec * This is useful to determine if two instances refer to the "same" * local variable. * - * @param other null-ok; spec to compare to + * @param other {@code null-ok;} spec to compare to * @return {@code true} iff {@code this} and {@code other} are equal * in the stated way */ @@ -230,8 +230,8 @@ public final class RegisterSpec * Compares by (in priority order) register number, unwrapped type * (that is types not {@link TypeBearer}s, and local info. * - * @param other non-null; spec to compare to - * @return {@code -1..1}; standard result of comparison + * @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) { @@ -316,7 +316,7 @@ public final class RegisterSpec /** * Gets the register number. * - * @return >= 0; the register number + * @return {@code >= 0;} the register number */ public int getReg() { return reg; @@ -326,7 +326,7 @@ public final class RegisterSpec * Gets the type (or actual value) which is loaded from or stored * to the register associated with this instance. * - * @return non-null; the type + * @return {@code non-null;} the type */ public TypeBearer getTypeBearer() { return type; @@ -335,7 +335,7 @@ public final class RegisterSpec /** * Gets the variable info associated with this instance, if any. * - * @return null-ok; the variable info, or null if this + * @return {@code null-ok;} the variable info, or {@code null} if this * instance has none */ public LocalItem getLocalItem() { @@ -349,7 +349,7 @@ public final class RegisterSpec * be used to determine the minimum required register count * implied by this instance. * - * @return >= 0; the required registers size + * @return {@code >= 0;} the required registers size */ public int getNextReg() { return reg + getCategory(); @@ -357,11 +357,11 @@ public final class RegisterSpec /** * Gets the category of this instance's type. This is just a convenient - * shorthand for getType().getCategory(). + * shorthand for {@code getType().getCategory()}. * * @see #isCategory1 * @see #isCategory2 - * @return 1..2; the category of this instance's type + * @return {@code 1..2;} the category of this instance's type */ public int getCategory() { return type.getType().getCategory(); @@ -369,7 +369,7 @@ public final class RegisterSpec /** * Gets whether this instance's type is category 1. This is just a - * convenient shorthand for getType().isCategory1(). + * convenient shorthand for {@code getType().isCategory1()}. * * @see #getCategory * @see #isCategory2 @@ -381,7 +381,7 @@ public final class RegisterSpec /** * Gets whether this instance's type is category 2. This is just a - * convenient shorthand for getType().isCategory2(). + * convenient shorthand for {@code getType().isCategory2()}. * * @see #getCategory * @see #isCategory1 @@ -394,7 +394,7 @@ public final class RegisterSpec /** * Gets the string form for just the register number of this instance. * - * @return non-null; the register string form + * @return {@code non-null;} the register string form */ public String regString() { return regString(reg); @@ -405,28 +405,28 @@ public final class RegisterSpec * and the given one, if any. The intersection is defined as follows: * *

      - *
    • If other is null, then the result - * is null. + *
    • If {@code other} is {@code null}, then the result + * is {@code null}. *
    • If the register numbers don't match, then the intersection - * is null. Otherwise, the register number of the + * is {@code null}. Otherwise, the register number of the * intersection is the same as the one in the two instances.
    • - *
    • If the types returned by getType() are not - * equals(), then the intersection is null.
    • - *
    • If the type bearers returned by getTypeBearer() - * are equals(), then the intersection's type bearer + *
    • If the types returned by {@code getType()} are not + * {@code equals()}, then the intersection is null.
    • + *
    • 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 getType() of this instance.
    • - *
    • If the locals are equals(), then the local info + * type bearer is the {@code getType()} of this instance.
    • + *
    • 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 null.
    • + * the local info of the intersection is {@code null}. *
    * - * @param other null-ok; instance to intersect with (or null) + * @param other {@code null-ok;} instance to intersect with (or {@code null}) * @param localPrimary whether local variables are primary to the - * intersection; if true, then the only non-null + * intersection; if {@code true}, then the only non-null * results occur when registers being intersected have equal local - * infos (or both have null local infos) - * @return null-ok; the intersection + * infos (or both have {@code null} local infos) + * @return {@code null-ok;} the intersection */ public RegisterSpec intersect(RegisterSpec other, boolean localPrimary) { if (this == other) { @@ -471,8 +471,8 @@ public final class RegisterSpec * Returns an instance that is identical to this one, except that the * register number is replaced by the given one. * - * @param newReg >= 0; the new register number - * @return non-null; an appropriately-constructed instance + * @param newReg {@code >= 0;} the new register number + * @return {@code non-null;} an appropriately-constructed instance */ public RegisterSpec withReg(int newReg) { if (reg == newReg) { @@ -486,8 +486,8 @@ public final class RegisterSpec * Returns an instance that is identical to this one, except that * the type is replaced by the given one. * - * @param newType non-null; the new type - * @return non-null; an appropriately-constructed instance + * @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); @@ -498,7 +498,7 @@ public final class RegisterSpec * register number is offset by the given amount. * * @param delta the amount to offset the register number by - * @return non-null; an appropriately-constructed instance + * @return {@code non-null;} an appropriately-constructed instance */ public RegisterSpec withOffset(int delta) { if (delta == 0) { @@ -514,7 +514,7 @@ public final class RegisterSpec * (thereby stripping off non-type information) with any * initialization information stripped away as well. * - * @return non-null; an appropriately-constructed instance + * @return {@code non-null;} an appropriately-constructed instance */ public RegisterSpec withSimpleType() { TypeBearer orig = type; @@ -541,7 +541,7 @@ public final class RegisterSpec * Returns an instance that is identical to this one except that the * local variable is as specified in the parameter. * - * @param local null-ok; the local item or null for none + * @param local {@code null-ok;} the local item or null for none * @return an appropriate instance */ public RegisterSpec withLocalItem(LocalItem local) { @@ -559,7 +559,7 @@ public final class RegisterSpec * Helper for {@link #toString} and {@link #toHuman}. * * @param human whether to be human-oriented - * @return non-null; the string form + * @return {@code non-null;} the string form */ private String toString0(boolean human) { StringBuffer sb = new StringBuffer(40); @@ -588,27 +588,31 @@ public final class RegisterSpec /** * Holder of register spec data for the purposes of comparison (so that - * RegisterSpec itself can still keep final + * {@code RegisterSpec} itself can still keep {@code final} * instance variables. */ private static class ForComparison { - /** >= 0; register number */ + /** {@code >= 0;} register number */ private int reg; - /** non-null; type loaded or stored */ + /** {@code non-null;} type loaded or stored */ private TypeBearer type; - /** null-ok; local variable associated with this register, if any */ + /** + * {@code null-ok;} local variable associated with this + * register, if any + */ private LocalItem local; /** * Set all the instance variables. * - * @param reg >= 0; the register number - * @param type non-null; the type (or possibly actual value) which - * is loaded from or stored to the indicated register - * @param local null-ok; the associated local variable, if any - * @return non-null; an appropriately-constructed instance + * @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; @@ -617,10 +621,10 @@ public final class RegisterSpec } /** - * Construct a RegisterSpec of this instance's + * Construct a {@code RegisterSpec} of this instance's * contents. * - * @return non-null; an appropriately-constructed instance + * @return {@code non-null;} an appropriately-constructed instance */ public RegisterSpec toRegisterSpec() { return new RegisterSpec(reg, type, local); diff --git a/dx/src/com/android/dx/rop/code/RegisterSpecList.java b/dx/src/com/android/dx/rop/code/RegisterSpecList.java index 28657a197..5a02a8d31 100644 --- a/dx/src/com/android/dx/rop/code/RegisterSpecList.java +++ b/dx/src/com/android/dx/rop/code/RegisterSpecList.java @@ -25,14 +25,14 @@ import com.android.dx.util.FixedSizeList; */ public final class RegisterSpecList extends FixedSizeList implements TypeList { - /** non-null; no-element instance */ + /** {@code non-null;} no-element instance */ public static final RegisterSpecList EMPTY = new RegisterSpecList(0); /** * Makes a single-element instance. * - * @param spec non-null; the element - * @return non-null; an appropriately-constructed instance + * @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); @@ -43,9 +43,9 @@ public final class RegisterSpecList /** * Makes a two-element instance. * - * @param spec0 non-null; the first element - * @param spec1 non-null; the second element - * @return non-null; an appropriately-constructed instance + * @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) { @@ -58,10 +58,10 @@ public final class RegisterSpecList /** * Makes a three-element instance. * - * @param spec0 non-null; the first element - * @param spec1 non-null; the second element - * @param spec2 non-null; the third element - * @return non-null; an appropriately-constructed instance + * @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) { @@ -75,11 +75,11 @@ public final class RegisterSpecList /** * Makes a four-element instance. * - * @param spec0 non-null; the first element - * @param spec1 non-null; the second element - * @param spec2 non-null; the third element - * @param spec3 non-null; the fourth element - * @return non-null; an appropriately-constructed instance + * @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, @@ -93,7 +93,7 @@ public final class RegisterSpecList } /** - * Constructs an instance. All indices initially contain null. + * Constructs an instance. All indices initially contain {@code null}. * * @param size the size of the list */ @@ -126,10 +126,10 @@ public final class RegisterSpecList /** * 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 NullPointerException. + * will throw {@code NullPointerException}. * - * @param n >= 0, < size(); which element - * @return non-null; the indicated element + * @param n {@code >= 0, < size();} which element + * @return {@code non-null;} the indicated element */ public RegisterSpec get(int n) { return (RegisterSpec) get0(n); @@ -180,8 +180,8 @@ public final class RegisterSpecList /** * Sets the element at the given index. * - * @param n >= 0, < size(); which element - * @param spec non-null; the value to store + * @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); @@ -193,7 +193,7 @@ public final class RegisterSpecList * to plus the widest width (largest category) of the type used in * that register. * - * @return >= 0; the required registers size + * @return {@code >= 0;} the required registers size */ public int getRegistersSize() { int sz = size(); @@ -217,8 +217,8 @@ public final class RegisterSpecList * except that it has an additional element prepended to the original. * Mutability of the result is inherited from the original. * - * @param spec non-null; the new first spec (to prepend) - * @return non-null; an appropriately-constructed instance + * @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(); @@ -241,7 +241,7 @@ public final class RegisterSpecList * except that its first element is removed. Mutability of the * result is inherited from the original. * - * @return non-null; an appropriately-constructed instance + * @return {@code non-null;} an appropriately-constructed instance */ public RegisterSpecList withoutFirst() { int newSize = size() - 1; @@ -268,7 +268,7 @@ public final class RegisterSpecList * except that its last element is removed. Mutability of the * result is inherited from the original. * - * @return non-null; an appropriately-constructed instance + * @return {@code non-null;} an appropriately-constructed instance */ public RegisterSpecList withoutLast() { int newSize = size() - 1; @@ -296,7 +296,7 @@ public final class RegisterSpecList * of the result is inherited from the original. * * @param delta the amount to offset the register numbers by - * @return non-null; an appropriately-constructed instance + * @return {@code non-null;} an appropriately-constructed instance */ public RegisterSpecList withOffset(int delta) { int sz = size(); @@ -329,7 +329,7 @@ public final class RegisterSpecList * * @param base the base register number * @param duplicateFirst whether to duplicate the first number - * @return non-null; an appropriately-constructed instance + * @return {@code non-null;} an appropriately-constructed instance */ public RegisterSpecList withSequentialRegisters(int base, boolean duplicateFirst) { diff --git a/dx/src/com/android/dx/rop/code/RegisterSpecSet.java b/dx/src/com/android/dx/rop/code/RegisterSpecSet.java index adc77c35e..68009d997 100644 --- a/dx/src/com/android/dx/rop/code/RegisterSpecSet.java +++ b/dx/src/com/android/dx/rop/code/RegisterSpecSet.java @@ -25,23 +25,23 @@ import com.android.dx.rop.cst.CstUtf8; */ public final class RegisterSpecSet extends MutabilityControl { - /** non-null; no-element instance */ + /** {@code non-null;} no-element instance */ public static final RegisterSpecSet EMPTY = new RegisterSpecSet(0); /** - * non-null; array of register specs, where each element is - * null or is an instance whose reg + * {@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; - /** >= -1; size of the set or -1 if not yet calculated */ + /** {@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 >= 0; the maximum register number (exclusive) that + * @param maxSize {@code >= 0;} the maximum register number (exclusive) that * may be represented in this instance */ public RegisterSpecSet(int maxSize) { @@ -127,7 +127,7 @@ public final class RegisterSpecSet * is also the maximum-plus-one of register numbers that may be * represented. * - * @return >= 0; the maximum size + * @return {@code >= 0;} the maximum size */ public int getMaxSize() { return specs.length; @@ -136,7 +136,7 @@ public final class RegisterSpecSet /** * Gets the current size of this instance. * - * @return >= 0; the size + * @return {@code >= 0;} the size */ public int size() { int result = size; @@ -160,9 +160,9 @@ public final class RegisterSpecSet /** * Gets the element with the given register number, if any. * - * @param reg >= 0; the desired register number - * @return null-ok; the element with the given register number or - * null if there is none + * @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 { @@ -176,11 +176,11 @@ public final class RegisterSpecSet /** * Gets the element with the same register number as the given * spec, if any. This is just a convenient shorthand for - * get(spec.getReg()). + * {@code get(spec.getReg())}. * - * @param spec non-null; spec with the desired register number - * @return null-ok; the element with the matching register number or - * null if there is none + * @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()); @@ -192,8 +192,8 @@ public final class RegisterSpecSet * none. This ignores the register number of the given spec but * matches on everything else. * - * @param spec non-null; local to look for - * @return null-ok; first register found that matches, if any + * @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; @@ -217,8 +217,8 @@ public final class RegisterSpecSet * 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 non-null; local item to search for - * @return null-ok; first register found with matching name and signature + * @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; @@ -238,7 +238,7 @@ public final class RegisterSpecSet * Removes a spec from the set. Only the register number * of the parameter is significant. * - * @param toRemove non-null; register to remove. + * @param toRemove {@code non-null;} register to remove. */ public void remove(RegisterSpec toRemove) { try { @@ -258,7 +258,7 @@ public final class RegisterSpecSet * a category-2 register, then the immediately subsequent element * is nullified. * - * @param spec non-null; the register spec to put in the instance + * @param spec {@code non-null;} the register spec to put in the instance */ public void put(RegisterSpec spec) { throwIfImmutable(); @@ -293,7 +293,7 @@ public final class RegisterSpecSet /** * Put the entire contents of the given set into this one. * - * @param set non-null; the set to put into this instance + * @param set {@code non-null;} the set to put into this instance */ public void putAll(RegisterSpecSet set) { int max = set.getMaxSize(); @@ -312,11 +312,11 @@ public final class RegisterSpecSet * {@link RegisterSpec#intersect} of corresponding elements from * this instance and the given one where both are non-null. * - * @param other non-null; set to intersect with + * @param other {@code non-null;} set to intersect with * @param localPrimary whether local variables are primary to - * the intersection; if true, then the only non-null + * the intersection; if {@code true}, then the only non-null * result elements occur when registers being intersected have - * equal names (or both have null names) + * equal names (or both have {@code null} names) */ public void intersect(RegisterSpecSet other, boolean localPrimary) { throwIfImmutable(); @@ -352,7 +352,7 @@ public final class RegisterSpecSet * of the result is inherited from the original. * * @param delta the amount to offset the register numbers by - * @return non-null; an appropriately-constructed instance + * @return {@code non-null;} an appropriately-constructed instance */ public RegisterSpecSet withOffset(int delta) { int len = specs.length; @@ -377,7 +377,7 @@ public final class RegisterSpecSet /** * Makes and return a mutable copy of this instance. * - * @return non-null; the mutable copy + * @return {@code non-null;} the mutable copy */ public RegisterSpecSet mutableCopy() { int len = specs.length; diff --git a/dx/src/com/android/dx/rop/code/Rop.java b/dx/src/com/android/dx/rop/code/Rop.java index f918e126c..fbd9a16f6 100644 --- a/dx/src/com/android/dx/rop/code/Rop.java +++ b/dx/src/com/android/dx/rop/code/Rop.java @@ -25,7 +25,7 @@ import com.android.dx.util.Hex; * Class that describes all the immutable parts of register-based operations. */ public final class Rop { - /** minimum BRANCH_* value */ + /** minimum {@code BRANCH_*} value */ public static final int BRANCH_MIN = 1; /** indicates a non-branching op */ @@ -46,26 +46,26 @@ public final class Rop { /** indicates a throw-style branch (both always-throws and may-throw) */ public static final int BRANCH_THROW = 6; - /** maximum BRANCH_* value */ + /** maximum {@code BRANCH_*} value */ public static final int BRANCH_MAX = 6; /** the opcode; one of the constants in {@link RegOps} */ private final int opcode; /** - * non-null; result type of this operation; {@link Type#VOID} for + * {@code non-null;} result type of this operation; {@link Type#VOID} for * no-result operations */ private final Type result; - /** non-null; types of all the sources of this operation */ + /** {@code non-null;} types of all the sources of this operation */ private final TypeList sources; - /** non-null; list of possible types thrown by this operation */ + /** {@code non-null;} list of possible types thrown by this operation */ private final TypeList exceptions; /** - * the branchingness of this op; one of the BRANCH_* + * the branchingness of this op; one of the {@code BRANCH_*} * constants in this class */ private final int branchingness; @@ -73,7 +73,7 @@ public final class Rop { /** whether this is a function/method call op or similar */ private final boolean isCallLike; - /** null-ok; nickname, if specified (used for debugging) */ + /** {@code null-ok;} nickname, if specified (used for debugging) */ private final String nickname; /** @@ -81,15 +81,15 @@ public final class Rop { * public constructors. * * @param opcode the opcode; one of the constants in {@link RegOps} - * @param result non-null; result type of this operation; {@link + * @param result {@code non-null;} result type of this operation; {@link * Type#VOID} for no-result operations - * @param sources non-null; types of all the sources of this operation - * @param exceptions non-null; list of possible types thrown by this + * @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 - * BRANCH_* constants + * {@code BRANCH_*} constants * @param isCallLike whether the op is a function/method call or similar - * @param nickname null-ok; optional nickname (used for debugging) + * @param nickname {@code null-ok;} optional nickname (used for debugging) */ public Rop(int opcode, Type result, TypeList sources, TypeList exceptions, int branchingness, boolean isCallLike, @@ -129,14 +129,14 @@ public final class Rop { * call-like op (see {@link #isCallLike}). * * @param opcode the opcode; one of the constants in {@link RegOps} - * @param result non-null; result type of this operation; {@link + * @param result {@code non-null;} result type of this operation; {@link * Type#VOID} for no-result operations - * @param sources non-null; types of all the sources of this operation - * @param exceptions non-null; list of possible types thrown by this + * @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 - * BRANCH_* constants - * @param nickname null-ok; optional nickname (used for debugging) + * {@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) { @@ -149,12 +149,12 @@ public final class Rop { * call-like op (see {@link #isCallLike}). * * @param opcode the opcode; one of the constants in {@link RegOps} - * @param result non-null; result type of this operation; {@link + * @param result {@code non-null;} result type of this operation; {@link * Type#VOID} for no-result operations - * @param sources non-null; types of all the sources of this operation + * @param sources {@code non-null;} types of all the sources of this operation * @param branchingness the branchingness of this op; one of the - * BRANCH_* constants - * @param nickname null-ok; optional nickname (used for debugging) + * {@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) { @@ -164,14 +164,14 @@ public final class Rop { /** * Constructs a non-branching no-exception instance. The - * branchingness is always BRANCH_NONE, + * {@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 non-null; result type of this operation; {@link + * @param result {@code non-null;} result type of this operation; {@link * Type#VOID} for no-result operations - * @param sources non-null; types of all the sources of this operation - * @param nickname null-ok; optional nickname (used for debugging) + * @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, @@ -180,16 +180,16 @@ public final class Rop { /** * Constructs a non-empty exceptions instance. Its - * branchingness is always BRANCH_THROW, + * {@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 non-null; result type of this operation; {@link + * @param result {@code non-null;} result type of this operation; {@link * Type#VOID} for no-result operations - * @param sources non-null; types of all the sources of this operation - * @param exceptions non-null; list of possible types thrown by this + * @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 null-ok; optional nickname (used for debugging) + * @param nickname {@code null-ok;} optional nickname (used for debugging) */ public Rop(int opcode, Type result, TypeList sources, TypeList exceptions, String nickname) { @@ -200,11 +200,11 @@ public final class Rop { /** * Constructs a non-nicknamed instance with non-empty exceptions, which * is always a call-like op (see {@link #isCallLike}). Its - * branchingness is always BRANCH_THROW. + * {@code branchingness} is always {@code BRANCH_THROW}. * * @param opcode the opcode; one of the constants in {@link RegOps} - * @param sources non-null; types of all the sources of this operation - * @param exceptions non-null; list of possible types thrown by this + * @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) { @@ -317,7 +317,7 @@ public final class Rop { * Gets the result type. A return value of {@link Type#VOID} * means this operation returns nothing. * - * @return null-ok; the result spec + * @return {@code null-ok;} the result spec */ public Type getResult() { return result; @@ -326,7 +326,7 @@ public final class Rop { /** * Gets the source types. * - * @return non-null; the source types + * @return {@code non-null;} the source types */ public TypeList getSources() { return sources; @@ -335,7 +335,7 @@ public final class Rop { /** * Gets the list of exception types that might be thrown. * - * @return non-null; the list of exception types + * @return {@code non-null;} the list of exception types */ public TypeList getExceptions() { return exceptions; @@ -353,7 +353,7 @@ public final class Rop { /** * Gets whether this opcode is a function/method call or similar. * - * @return true iff this opcode is call-like + * @return {@code true} iff this opcode is call-like */ public boolean isCallLike() { return isCallLike; @@ -384,7 +384,7 @@ public final class Rop { * Gets the nickname. If this instance has no nickname, this returns * the result of calling {@link #toString}. * - * @return non-null; the nickname + * @return {@code non-null;} the nickname */ public String getNickname() { if (nickname != null) { @@ -397,9 +397,9 @@ public final class Rop { /** * Gets whether this operation can possibly throw an exception. This * is just a convenient wrapper for - * getExceptions().size() != 0. + * {@code getExceptions().size() != 0}. * - * @return true iff this operation can possibly throw + * @return {@code true} iff this operation can possibly throw */ public final boolean canThrow() { return (exceptions.size() != 0); diff --git a/dx/src/com/android/dx/rop/code/RopMethod.java b/dx/src/com/android/dx/rop/code/RopMethod.java index 0c0d8f1a7..39575322a 100644 --- a/dx/src/com/android/dx/rop/code/RopMethod.java +++ b/dx/src/com/android/dx/rop/code/RopMethod.java @@ -24,20 +24,20 @@ import com.android.dx.util.IntList; * All of the parts that make up a method at the rop layer. */ public final class RopMethod { - /** non-null; basic block list of the method */ + /** {@code non-null;} basic block list of the method */ private final BasicBlockList blocks; - /** >= 0; label for the block which starts the method */ + /** {@code >= 0;} label for the block which starts the method */ private final int firstLabel; /** - * null-ok; array of predecessors for each block, indexed by block + * {@code null-ok;} array of predecessors for each block, indexed by block * label */ private IntList[] predecessors; /** - * null-ok; the predecessors for the implicit "exit" block, that is + * {@code null-ok;} the predecessors for the implicit "exit" block, that is * the labels for the blocks that return, if calculated */ private IntList exitPredecessors; @@ -45,8 +45,8 @@ public final class RopMethod { /** * Constructs an instance. * - * @param blocks non-null; basic block list of the method - * @param firstLabel >= 0; the label of the first block to execute + * @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) { @@ -67,7 +67,7 @@ public final class RopMethod { /** * Gets the basic block list for this method. * - * @return non-null; the list + * @return {@code non-null;} the list */ public BasicBlockList getBlocks() { return blocks; @@ -77,7 +77,7 @@ public final class RopMethod { * Gets the label for the first block in the method that this list * represents. * - * @return >= 0; the first-block label + * @return {@code >= 0;} the first-block label */ public int getFirstLabel() { return firstLabel; @@ -87,8 +87,8 @@ public final class RopMethod { * Gets the predecessors associated with the given block. This throws * an exception if there is no block with the given label. * - * @param label >= 0; the label of the block in question - * @return non-null; the predecessors of that block + * @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) { @@ -107,7 +107,7 @@ public final class RopMethod { /** * Gets the exit predecessors for this instance. * - * @return non-null; the exit predecessors + * @return {@code non-null;} the exit predecessors */ public IntList getExitPredecessors() { if (exitPredecessors == null) { @@ -124,7 +124,7 @@ public final class RopMethod { * amount. * * @param delta the amount to offset register numbers by - * @return non-null; an appropriately-constructed instance + * @return {@code non-null;} an appropriately-constructed instance */ public RopMethod withRegisterOffset(int delta) { RopMethod result = new RopMethod(blocks.withRegisterOffset(delta), diff --git a/dx/src/com/android/dx/rop/code/Rops.java b/dx/src/com/android/dx/rop/code/Rops.java index b662656f7..15c2e17ed 100644 --- a/dx/src/com/android/dx/rop/code/Rops.java +++ b/dx/src/com/android/dx/rop/code/Rops.java @@ -30,32 +30,32 @@ import com.android.dx.rop.type.TypeList; * Standard instances of {@link Rop}. */ public final class Rops { - /** nop() */ + /** {@code nop()} */ public static final Rop NOP = new Rop(RegOps.NOP, Type.VOID, StdTypeList.EMPTY, "nop"); - /** r,x: int :: r = x; */ + /** {@code r,x: int :: r = x;} */ public static final Rop MOVE_INT = new Rop(RegOps.MOVE, Type.INT, StdTypeList.INT, "move-int"); - /** r,x: long :: r = x; */ + /** {@code r,x: long :: r = x;} */ public static final Rop MOVE_LONG = new Rop(RegOps.MOVE, Type.LONG, StdTypeList.LONG, "move-long"); - /** r,x: float :: r = x; */ + /** {@code r,x: float :: r = x;} */ public static final Rop MOVE_FLOAT = new Rop(RegOps.MOVE, Type.FLOAT, StdTypeList.FLOAT, "move-float"); - /** r,x: double :: r = x; */ + /** {@code r,x: double :: r = x;} */ public static final Rop MOVE_DOUBLE = new Rop(RegOps.MOVE, Type.DOUBLE, StdTypeList.DOUBLE, "move-double"); - /** r,x: Object :: r = x; */ + /** {@code r,x: Object :: r = x;} */ public static final Rop MOVE_OBJECT = new Rop(RegOps.MOVE, Type.OBJECT, StdTypeList.OBJECT, "move-object"); /** - * r,x: ReturnAddress :: r = x; + * {@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. @@ -64,756 +64,756 @@ public final class Rops { new Rop(RegOps.MOVE, Type.RETURN_ADDRESS, StdTypeList.RETURN_ADDRESS, "move-return-address"); - /** r,param(x): int :: r = param(x); */ + /** {@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"); - /** r,param(x): long :: r = param(x); */ + /** {@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"); - /** r,param(x): float :: r = param(x); */ + /** {@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"); - /** r,param(x): double :: r = param(x); */ + /** {@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"); - /** r,param(x): Object :: r = param(x); */ + /** {@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"); - /** r, literal: int :: r = literal; */ + /** {@code r, literal: int :: r = literal;} */ public static final Rop CONST_INT = new Rop(RegOps.CONST, Type.INT, StdTypeList.EMPTY, "const-int"); - /** r, literal: long :: r = literal; */ + /** {@code r, literal: long :: r = literal;} */ public static final Rop CONST_LONG = new Rop(RegOps.CONST, Type.LONG, StdTypeList.EMPTY, "const-long"); - /** r, literal: float :: r = literal; */ + /** {@code r, literal: float :: r = literal;} */ public static final Rop CONST_FLOAT = new Rop(RegOps.CONST, Type.FLOAT, StdTypeList.EMPTY, "const-float"); - /** r, literal: double :: r = literal; */ + /** {@code r, literal: double :: r = literal;} */ public static final Rop CONST_DOUBLE = new Rop(RegOps.CONST, Type.DOUBLE, StdTypeList.EMPTY, "const-double"); - /** r, literal: Object :: r = literal; */ + /** {@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"); - /** r, literal: Object :: r = literal; */ + /** {@code r, literal: Object :: r = literal;} */ public static final Rop CONST_OBJECT_NOTHROW = new Rop(RegOps.CONST, Type.OBJECT, StdTypeList.EMPTY, "const-object-nothrow"); - /** goto label */ + /** {@code goto label} */ public static final Rop GOTO = new Rop(RegOps.GOTO, Type.VOID, StdTypeList.EMPTY, Rop.BRANCH_GOTO, "goto"); - /** x: int :: if (x == 0) goto label */ + /** {@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"); - /** x: int :: if (x != 0) goto label */ + /** {@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"); - /** x: int :: if (x < 0) goto label */ + /** {@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"); - /** x: int :: if (x >= 0) goto label */ + /** {@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"); - /** x: int :: if (x <= 0) goto label */ + /** {@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"); - /** x: int :: if (x > 0) goto label */ + /** {@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"); - /** x: Object :: if (x == null) goto label */ + /** {@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"); - /** x: Object :: if (x != null) goto label */ + /** {@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"); - /** x,y: int :: if (x == y) goto label */ + /** {@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"); - /** x,y: int :: if (x != y) goto label */ + /** {@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"); - /** x,y: int :: if (x < y) goto label */ + /** {@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"); - /** x,y: int :: if (x >= y) goto label */ + /** {@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"); - /** x,y: int :: if (x <= y) goto label */ + /** {@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"); - /** x,y: int :: if (x > y) goto label */ + /** {@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"); - /** x,y: Object :: if (x == y) goto label */ + /** {@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"); - /** x,y: Object :: if (x != y) goto label */ + /** {@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"); - /** x: int :: goto switchtable[x] */ + /** {@code x: int :: goto switchtable[x]} */ public static final Rop SWITCH = new Rop(RegOps.SWITCH, Type.VOID, StdTypeList.INT, Rop.BRANCH_SWITCH, "switch"); - /** r,x,y: int :: r = x + y; */ + /** {@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"); - /** r,x,y: long :: r = x + y; */ + /** {@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"); - /** r,x,y: float :: r = x + y; */ + /** {@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"); - /** r,x,y: double :: r = x + y; */ + /** {@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"); - /** r,x,y: int :: r = x - y; */ + /** {@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"); - /** r,x,y: long :: r = x - y; */ + /** {@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"); - /** r,x,y: float :: r = x - y; */ + /** {@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"); - /** r,x,y: double :: r = x - y; */ + /** {@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"); - /** r,x,y: int :: r = x * y; */ + /** {@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"); - /** r,x,y: long :: r = x * y; */ + /** {@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"); - /** r,x,y: float :: r = x * y; */ + /** {@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"); - /** r,x,y: double :: r = x * y; */ + /** {@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"); - /** r,x,y: int :: r = x / y; */ + /** {@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"); - /** r,x,y: long :: r = x / y; */ + /** {@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"); - /** r,x,y: float :: r = x / y; */ + /** {@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"); - /** r,x,y: double :: r = x / y; */ + /** {@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"); - /** r,x,y: int :: r = x % y; */ + /** {@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"); - /** r,x,y: long :: r = x % y; */ + /** {@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"); - /** r,x,y: float :: r = x % y; */ + /** {@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"); - /** r,x,y: double :: r = x % y; */ + /** {@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"); - /** r,x: int :: r = -x; */ + /** {@code r,x: int :: r = -x;} */ public static final Rop NEG_INT = new Rop(RegOps.NEG, Type.INT, StdTypeList.INT, "neg-int"); - /** r,x: long :: r = -x; */ + /** {@code r,x: long :: r = -x;} */ public static final Rop NEG_LONG = new Rop(RegOps.NEG, Type.LONG, StdTypeList.LONG, "neg-long"); - /** r,x: float :: r = -x; */ + /** {@code r,x: float :: r = -x;} */ public static final Rop NEG_FLOAT = new Rop(RegOps.NEG, Type.FLOAT, StdTypeList.FLOAT, "neg-float"); - /** r,x: double :: r = -x; */ + /** {@code r,x: double :: r = -x;} */ public static final Rop NEG_DOUBLE = new Rop(RegOps.NEG, Type.DOUBLE, StdTypeList.DOUBLE, "neg-double"); - /** r,x,y: int :: r = x & y; */ + /** {@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"); - /** r,x,y: long :: r = x & y; */ + /** {@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"); - /** r,x,y: int :: r = x | y; */ + /** {@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"); - /** r,x,y: long :: r = x | y; */ + /** {@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"); - /** r,x,y: int :: r = x ^ y; */ + /** {@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"); - /** r,x,y: long :: r = x ^ y; */ + /** {@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"); - /** r,x,y: int :: r = x << y; */ + /** {@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"); - /** r,x: long; y: int :: r = x << y; */ + /** {@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"); - /** r,x,y: int :: r = x >> y; */ + /** {@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"); - /** r,x: long; y: int :: r = x >> y; */ + /** {@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"); - /** r,x,y: int :: r = x >>> y; */ + /** {@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"); - /** r,x: long; y: int :: r = x >>> y; */ + /** {@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"); - /** r,x: int :: r = ~x; */ + /** {@code r,x: int :: r = ~x;} */ public static final Rop NOT_INT = new Rop(RegOps.NOT, Type.INT, StdTypeList.INT, "not-int"); - /** r,x: long :: r = ~x; */ + /** {@code r,x: long :: r = ~x;} */ public static final Rop NOT_LONG = new Rop(RegOps.NOT, Type.LONG, StdTypeList.LONG, "not-long"); - /** r,x,c: int :: r = x + c; */ + /** {@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"); - /** r,x,c: long :: r = x + c; */ + /** {@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"); - /** r,x,c: float :: r = x + c; */ + /** {@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"); - /** r,x,c: double :: r = x + c; */ + /** {@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"); - /** r,x,c: int :: r = x - c; */ + /** {@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"); - /** r,x,c: long :: r = x - c; */ + /** {@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"); - /** r,x,c: float :: r = x - c; */ + /** {@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"); - /** r,x,c: double :: r = x - c; */ + /** {@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"); - /** r,x,c: int :: r = x * c; */ + /** {@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"); - /** r,x,c: long :: r = x * c; */ + /** {@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"); - /** r,x,c: float :: r = x * c; */ + /** {@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"); - /** r,x,c: double :: r = x * c; */ + /** {@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"); - /** r,x,c: int :: r = x / c; */ + /** {@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"); - /** r,x,c: long :: r = x / c; */ + /** {@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"); - /** r,x,c: float :: r = x / c; */ + /** {@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"); - /** r,x,c: double :: r = x / c; */ + /** {@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"); - /** r,x,c: int :: r = x % c; */ + /** {@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"); - /** r,x,c: long :: r = x % c; */ + /** {@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"); - /** r,x,c: float :: r = x % c; */ + /** {@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"); - /** r,x,c: double :: r = x % c; */ + /** {@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"); - /** r,x,c: int :: r = x & c; */ + /** {@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"); - /** r,x,c: long :: r = x & c; */ + /** {@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"); - /** r,x,c: int :: r = x | c; */ + /** {@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"); - /** r,x,c: long :: r = x | c; */ + /** {@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"); - /** r,x,c: int :: r = x ^ c; */ + /** {@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"); - /** r,x,c: long :: r = x ^ c; */ + /** {@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"); - /** r,x,c: int :: r = x << c; */ + /** {@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"); - /** r,x: long; c: int :: r = x << c; */ + /** {@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"); - /** r,x,c: int :: r = x >> c; */ + /** {@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"); - /** r,x: long; c: int :: r = x >> c; */ + /** {@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"); - /** r,x,c: int :: r = x >>> c; */ + /** {@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"); - /** r,x: long; c: int :: r = x >>> c; */ + /** {@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"); - /** r: int; x,y: long :: r = cmp(x, y); */ + /** {@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"); - /** r: int; x,y: float :: r = cmpl(x, y); */ + /** {@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"); - /** r: int; x,y: double :: r = cmpl(x, y); */ + /** {@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"); - /** r: int; x,y: float :: r = cmpg(x, y); */ + /** {@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"); - /** r: int; x,y: double :: r = cmpg(x, y); */ + /** {@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"); - /** r: int; x: long :: r = (int) x */ + /** {@code r: int; x: long :: r = (int) x} */ public static final Rop CONV_L2I = new Rop(RegOps.CONV, Type.INT, StdTypeList.LONG, "conv-l2i"); - /** r: int; x: float :: r = (int) x */ + /** {@code r: int; x: float :: r = (int) x} */ public static final Rop CONV_F2I = new Rop(RegOps.CONV, Type.INT, StdTypeList.FLOAT, "conv-f2i"); - /** r: int; x: double :: r = (int) x */ + /** {@code r: int; x: double :: r = (int) x} */ public static final Rop CONV_D2I = new Rop(RegOps.CONV, Type.INT, StdTypeList.DOUBLE, "conv-d2i"); - /** r: long; x: int :: r = (long) x */ + /** {@code r: long; x: int :: r = (long) x} */ public static final Rop CONV_I2L = new Rop(RegOps.CONV, Type.LONG, StdTypeList.INT, "conv-i2l"); - /** r: long; x: float :: r = (long) x */ + /** {@code r: long; x: float :: r = (long) x} */ public static final Rop CONV_F2L = new Rop(RegOps.CONV, Type.LONG, StdTypeList.FLOAT, "conv-f2l"); - /** r: long; x: double :: r = (long) x */ + /** {@code r: long; x: double :: r = (long) x} */ public static final Rop CONV_D2L = new Rop(RegOps.CONV, Type.LONG, StdTypeList.DOUBLE, "conv-d2l"); - /** r: float; x: int :: r = (float) x */ + /** {@code r: float; x: int :: r = (float) x} */ public static final Rop CONV_I2F = new Rop(RegOps.CONV, Type.FLOAT, StdTypeList.INT, "conv-i2f"); - /** r: float; x: long :: r = (float) x */ + /** {@code r: float; x: long :: r = (float) x} */ public static final Rop CONV_L2F = new Rop(RegOps.CONV, Type.FLOAT, StdTypeList.LONG, "conv-l2f"); - /** r: float; x: double :: r = (float) x */ + /** {@code r: float; x: double :: r = (float) x} */ public static final Rop CONV_D2F = new Rop(RegOps.CONV, Type.FLOAT, StdTypeList.DOUBLE, "conv-d2f"); - /** r: double; x: int :: r = (double) x */ + /** {@code r: double; x: int :: r = (double) x} */ public static final Rop CONV_I2D = new Rop(RegOps.CONV, Type.DOUBLE, StdTypeList.INT, "conv-i2d"); - /** r: double; x: long :: r = (double) x */ + /** {@code r: double; x: long :: r = (double) x} */ public static final Rop CONV_L2D = new Rop(RegOps.CONV, Type.DOUBLE, StdTypeList.LONG, "conv-l2d"); - /** r: double; x: float :: r = (double) x */ + /** {@code r: double; x: float :: r = (double) x} */ public static final Rop CONV_F2D = new Rop(RegOps.CONV, Type.DOUBLE, StdTypeList.FLOAT, "conv-f2d"); /** - * r,x: int :: r = (x << 24) >> 24 (Java-style + * {@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"); /** - * r,x: int :: r = x & 0xffff (Java-style + * {@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"); /** - * r,x: int :: r = (x << 16) >> 16 (Java-style + * {@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"); - /** return void */ + /** {@code return void} */ public static final Rop RETURN_VOID = new Rop(RegOps.RETURN, Type.VOID, StdTypeList.EMPTY, Rop.BRANCH_RETURN, "return-void"); - /** x: int; return x */ + /** {@code x: int; return x} */ public static final Rop RETURN_INT = new Rop(RegOps.RETURN, Type.VOID, StdTypeList.INT, Rop.BRANCH_RETURN, "return-int"); - /** x: long; return x */ + /** {@code x: long; return x} */ public static final Rop RETURN_LONG = new Rop(RegOps.RETURN, Type.VOID, StdTypeList.LONG, Rop.BRANCH_RETURN, "return-long"); - /** x: float; return x */ + /** {@code x: float; return x} */ public static final Rop RETURN_FLOAT = new Rop(RegOps.RETURN, Type.VOID, StdTypeList.FLOAT, Rop.BRANCH_RETURN, "return-float"); - /** x: double; return x */ + /** {@code x: double; return x} */ public static final Rop RETURN_DOUBLE = new Rop(RegOps.RETURN, Type.VOID, StdTypeList.DOUBLE, Rop.BRANCH_RETURN, "return-double"); - /** x: Object; return x */ + /** {@code x: Object; return x} */ public static final Rop RETURN_OBJECT = new Rop(RegOps.RETURN, Type.VOID, StdTypeList.OBJECT, Rop.BRANCH_RETURN, "return-object"); - /** T: any type; r: int; x: T[]; :: r = x.length */ + /** {@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"); - /** x: Throwable :: throw(x) */ + /** {@code x: Throwable :: throw(x)} */ public static final Rop THROW = new Rop(RegOps.THROW, Type.VOID, StdTypeList.THROWABLE, StdTypeList.THROWABLE, "throw"); - /** x: Object :: monitorenter(x) */ + /** {@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"); - /** x: Object :: monitorexit(x) */ + /** {@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"); - /** r,y: int; x: int[] :: r = x[y] */ + /** {@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"); - /** r: long; x: long[]; y: int :: r = x[y] */ + /** {@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"); - /** r: float; x: float[]; y: int :: r = x[y] */ + /** {@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"); - /** r: double; x: double[]; y: int :: r = x[y] */ + /** {@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"); - /** r: Object; x: Object[]; y: int :: r = x[y] */ + /** {@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"); - /** r: boolean; x: boolean[]; y: int :: r = x[y] */ + /** {@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"); - /** r: byte; x: byte[]; y: int :: r = x[y] */ + /** {@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"); - /** r: char; x: char[]; y: int :: r = x[y] */ + /** {@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"); - /** r: short; x: short[]; y: int :: r = x[y] */ + /** {@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"); - /** x,z: int; y: int[] :: y[z] = x */ + /** {@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"); - /** x: long; y: long[]; z: int :: y[z] = x */ + /** {@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"); - /** x: float; y: float[]; z: int :: y[z] = x */ + /** {@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"); - /** x: double; y: double[]; z: int :: y[z] = x */ + /** {@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"); - /** x: Object; y: Object[]; z: int :: y[z] = x */ + /** {@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"); - /** x: boolean; y: boolean[]; z: int :: y[z] = x */ + /** {@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"); - /** x: byte; y: byte[]; z: int :: y[z] = x */ + /** {@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"); - /** x: char; y: char[]; z: int :: y[z] = x */ + /** {@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"); - /** x: short; y: short[]; z: int :: y[z] = x */ + /** {@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"); /** - * T: any non-array object type :: r = - * alloc(T) (allocate heap space for an object) + * {@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"); - /** r: int[]; x: int :: r = new int[x] */ + /** {@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"); - /** r: long[]; x: int :: r = new long[x] */ + /** {@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"); - /** r: float[]; x: int :: r = new float[x] */ + /** {@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"); - /** r: double[]; x: int :: r = new double[x] */ + /** {@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"); - /** r: boolean[]; x: int :: r = new boolean[x] */ + /** {@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"); - /** r: byte[]; x: int :: r = new byte[x] */ + /** {@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"); - /** r: char[]; x: int :: r = new char[x] */ + /** {@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"); - /** r: short[]; x: int :: r = new short[x] */ + /** {@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"); /** - * T: any non-array object type; x: Object :: (T) x (can - * throw ClassCastException) + * {@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"); /** - * T: any non-array object type; x: Object :: x instanceof - * T. Note: This is listed as throwing Error + * {@code T: any non-array object type; x: Object :: x instanceof + * T}. Note: This is listed as throwing {@code Error} * explicitly because the op can throw, but there are no * other predefined exceptions for it. */ @@ -822,24 +822,24 @@ public final class Rops { Exceptions.LIST_Error, "instance-of"); /** - * r: int; x: Object; f: instance field spec of - * type int :: r = x.f + * {@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"); /** - * r: long; x: Object; f: instance field spec of - * type long :: r = x.f + * {@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"); /** - * r: float; x: Object; f: instance field spec of - * type float :: r = x.f + * {@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, @@ -847,8 +847,8 @@ public final class Rops { "get-field-float"); /** - * r: double; x: Object; f: instance field spec of - * type double :: r = x.f + * {@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, @@ -856,8 +856,8 @@ public final class Rops { "get-field-double"); /** - * r: Object; x: Object; f: instance field spec of - * type Object :: r = x.f + * {@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, @@ -865,8 +865,8 @@ public final class Rops { "get-field-object"); /** - * r: boolean; x: Object; f: instance field spec of - * type boolean :: r = x.f + * {@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, @@ -874,8 +874,8 @@ public final class Rops { "get-field-boolean"); /** - * r: byte; x: Object; f: instance field spec of - * type byte :: r = x.f + * {@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, @@ -883,8 +883,8 @@ public final class Rops { "get-field-byte"); /** - * r: char; x: Object; f: instance field spec of - * type char :: r = x.f + * {@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, @@ -892,105 +892,78 @@ public final class Rops { "get-field-char"); /** - * r: short; x: Object; f: instance field spec of - * type short :: r = x.f + * {@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"); - /** - * r: int; f: static field spec of type int :: r = - * f - */ + /** {@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"); - /** - * r: long; f: static field spec of type long :: r = - * f - */ + /** {@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"); - /** - * r: float; f: static field spec of type float :: r = - * f - */ + /** {@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"); - /** - * r: double; f: static field spec of type double :: r = - * f - */ + /** {@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"); - /** - * r: Object; f: static field spec of type Object :: r = - * f - */ + /** {@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"); - /** - * r: boolean; f: static field spec of type boolean :: r = - * f - */ + /** {@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"); - /** - * r: byte; f: static field spec of type byte :: r = - * f - */ + /** {@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"); - /** - * r: char; f: static field spec of type char :: r = - * f - */ + /** {@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"); - /** - * r: short; f: static field spec of type short :: r = - * f - */ + /** {@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"); /** - * x: int; y: Object; f: instance field spec of type - * int :: y.f = x + * {@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"); /** - * x: long; y: Object; f: instance field spec of type - * long :: y.f = x + * {@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"); /** - * x: float; y: Object; f: instance field spec of type - * float :: y.f = x + * {@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, @@ -998,8 +971,8 @@ public final class Rops { "put-field-float"); /** - * x: double; y: Object; f: instance field spec of type - * double :: y.f = x + * {@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, @@ -1007,8 +980,8 @@ public final class Rops { "put-field-double"); /** - * x: Object; y: Object; f: instance field spec of type - * Object :: y.f = x + * {@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, @@ -1016,8 +989,8 @@ public final class Rops { "put-field-object"); /** - * x: int; y: Object; f: instance field spec of type - * boolean :: y.f = x + * {@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, @@ -1025,8 +998,8 @@ public final class Rops { "put-field-boolean"); /** - * x: int; y: Object; f: instance field spec of type - * byte :: y.f = x + * {@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, @@ -1034,8 +1007,8 @@ public final class Rops { "put-field-byte"); /** - * x: int; y: Object; f: instance field spec of type - * char :: y.f = x + * {@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, @@ -1043,112 +1016,88 @@ public final class Rops { "put-field-char"); /** - * x: int; y: Object; f: instance field spec of type - * short :: y.f = x + * {@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"); - /** - * f: static field spec of type int; x: int :: f = - * x - */ + /** {@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"); - /** - * f: static field spec of type long; x: long :: f = - * x - */ + /** {@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"); - /** - * f: static field spec of type float; x: float :: f = - * x - */ + /** {@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"); - /** - * f: static field spec of type double; x: double :: f = - * x - */ + /** {@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"); - /** - * f: static field spec of type Object; x: Object :: f = - * x - */ + /** {@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"); /** - * f: static field spec of type boolean; x: boolean :: f = - * x + * {@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"); - /** - * f: static field spec of type byte; x: byte :: f = - * x - */ + /** {@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"); - /** - * f: static field spec of type char; x: char :: f = - * x - */ + /** {@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"); - /** - * f: static field spec of type short; x: short :: f = - * x - */ + /** {@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"); - /** x: Int :: local variable begins in x */ + /** {@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"); - /** x: Long :: local variable begins in x */ + /** {@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"); - /** x: Float :: local variable begins in x */ + /** {@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"); - /** x: Double :: local variable begins in x */ + /** {@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"); - /** x: Object :: local variable begins in x */ + /** {@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"); - /** T: Any primitive type; v0..vx: T :: {v0, ..., vx} */ + /** {@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"); @@ -1164,13 +1113,14 @@ public final class Rops { * match what is returned. TODO: Revisit this issue.

    * * @param opcode the opcode - * @param dest non-null; destination type, or {@link Type#VOID} if none - * @param sources non-null; list of source types - * @param cst null-ok; associated constant, if any - * @return non-null; an appropriate instance + * @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) { + Constant cst) { switch (opcode) { case RegOps.NOP: return NOP; case RegOps.MOVE: return opMove(dest); @@ -1216,19 +1166,31 @@ public final class Rops { 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 Object[] in this context. - source = Type.OBJECT_ARRAY; - } - return opAget(source.getComponentType()); + /* + * 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 Object[] in this context. - source = Type.OBJECT_ARRAY; - } - return opAput(source.getComponentType()); + /* + * 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()); @@ -1275,11 +1237,11 @@ public final class Rops { } /** - * Returns the appropriate move rop for the given type. The + * Returns the appropriate {@code move} rop for the given type. The * result is a shared instance. * - * @param type non-null; type of value being moved - * @return non-null; an appropriate instance + * @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()) { @@ -1295,11 +1257,11 @@ public final class Rops { } /** - * Returns the appropriate move-param rop for the + * Returns the appropriate {@code move-param} rop for the * given type. The result is a shared instance. * - * @param type non-null; type of value being moved - * @return non-null; an appropriate instance + * @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()) { @@ -1314,11 +1276,11 @@ public final class Rops { } /** - * Returns the appropriate move-exception rop for the + * Returns the appropriate {@code move-exception} rop for the * given type. The result may be a shared instance. * - * @param type non-null; type of the exception - * @return non-null; an appropriate instance + * @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(), @@ -1326,11 +1288,11 @@ public final class Rops { } /** - * Returns the appropriate move-result rop for the + * Returns the appropriate {@code move-result} rop for the * given type. The result may be a shared instance. * - * @param type non-null; type of the parameter - * @return non-null; an appropriate instance + * @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(), @@ -1338,11 +1300,11 @@ public final class Rops { } /** - * Returns the appropriate move-result-pseudo rop for the + * Returns the appropriate {@code move-result-pseudo} rop for the * given type. The result may be a shared instance. * - * @param type non-null; type of the parameter - * @return non-null; an appropriate instance + * @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(), @@ -1350,11 +1312,11 @@ public final class Rops { } /** - * Returns the appropriate const rop for the given + * Returns the appropriate {@code const} rop for the given * type. The result is a shared instance. * - * @param type non-null; type of the constant - * @return non-null; an appropriate instance + * @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) { @@ -1373,11 +1335,11 @@ public final class Rops { } /** - * Returns the appropriate if-eq rop for the given + * Returns the appropriate {@code if-eq} rop for the given * sources. The result is a shared instance. * - * @param types non-null; source types - * @return non-null; an appropriate 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, @@ -1385,11 +1347,11 @@ public final class Rops { } /** - * Returns the appropriate if-ne rop for the given + * Returns the appropriate {@code if-ne} rop for the given * sources. The result is a shared instance. * - * @param types non-null; source types - * @return non-null; an appropriate 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, @@ -1397,60 +1359,60 @@ public final class Rops { } /** - * Returns the appropriate if-lt rop for the given + * Returns the appropriate {@code if-lt} rop for the given * sources. The result is a shared instance. * - * @param types non-null; source types - * @return non-null; an appropriate 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 if-ge rop for the given + * Returns the appropriate {@code if-ge} rop for the given * sources. The result is a shared instance. * - * @param types non-null; source types - * @return non-null; an appropriate 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 if-gt rop for the given + * Returns the appropriate {@code if-gt} rop for the given * sources. The result is a shared instance. * - * @param types non-null; source types - * @return non-null; an appropriate 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 if-le rop for the given + * Returns the appropriate {@code if-le} rop for the given * sources. The result is a shared instance. * - * @param types non-null; source types - * @return non-null; an appropriate 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 if*-related methods, which + * 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 non-null; the types - * @param intZ non-null; the int-to-0 comparison - * @param objZ null-ok; the object-to-null comparison - * @param intInt non-null; the int-to-int comparison - * @param objObj non-null; the object-to-object comparison - * @return non-null; the appropriate instance + * @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) { @@ -1490,11 +1452,11 @@ public final class Rops { } /** - * Returns the appropriate add rop for the given + * Returns the appropriate {@code add} rop for the given * types. The result is a shared instance. * - * @param types non-null; types of the sources - * @return non-null; an appropriate instance + * @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, @@ -1503,11 +1465,11 @@ public final class Rops { } /** - * Returns the appropriate sub rop for the given + * Returns the appropriate {@code sub} rop for the given * types. The result is a shared instance. * - * @param types non-null; types of the sources - * @return non-null; an appropriate instance + * @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, @@ -1516,11 +1478,11 @@ public final class Rops { } /** - * Returns the appropriate mul rop for the given + * Returns the appropriate {@code mul} rop for the given * types. The result is a shared instance. * - * @param types non-null; types of the sources - * @return non-null; an appropriate instance + * @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, @@ -1529,11 +1491,11 @@ public final class Rops { } /** - * Returns the appropriate div rop for the given + * Returns the appropriate {@code div} rop for the given * types. The result is a shared instance. * - * @param types non-null; types of the sources - * @return non-null; an appropriate instance + * @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, @@ -1542,11 +1504,11 @@ public final class Rops { } /** - * Returns the appropriate rem rop for the given + * Returns the appropriate {@code rem} rop for the given * types. The result is a shared instance. * - * @param types non-null; types of the sources - * @return non-null; an appropriate instance + * @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, @@ -1555,11 +1517,11 @@ public final class Rops { } /** - * Returns the appropriate and rop for the given + * Returns the appropriate {@code and} rop for the given * types. The result is a shared instance. * - * @param types non-null; types of the sources - * @return non-null; an appropriate instance + * @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, @@ -1567,11 +1529,11 @@ public final class Rops { } /** - * Returns the appropriate or rop for the given + * Returns the appropriate {@code or} rop for the given * types. The result is a shared instance. * - * @param types non-null; types of the sources - * @return non-null; an appropriate instance + * @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, @@ -1579,11 +1541,11 @@ public final class Rops { } /** - * Returns the appropriate xor rop for the given + * Returns the appropriate {@code xor} rop for the given * types. The result is a shared instance. * - * @param types non-null; types of the sources - * @return non-null; an appropriate instance + * @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, @@ -1591,11 +1553,11 @@ public final class Rops { } /** - * Returns the appropriate shl rop for the given + * Returns the appropriate {@code shl} rop for the given * types. The result is a shared instance. * - * @param types non-null; types of the sources - * @return non-null; an appropriate instance + * @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, @@ -1603,11 +1565,11 @@ public final class Rops { } /** - * Returns the appropriate shr rop for the given + * Returns the appropriate {@code shr} rop for the given * types. The result is a shared instance. * - * @param types non-null; types of the sources - * @return non-null; an appropriate instance + * @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, @@ -1615,11 +1577,11 @@ public final class Rops { } /** - * Returns the appropriate ushr rop for the given + * Returns the appropriate {@code ushr} rop for the given * types. The result is a shared instance. * - * @param types non-null; types of the sources - * @return non-null; an appropriate instance + * @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, @@ -1630,16 +1592,16 @@ public final class Rops { * Returns the appropriate binary arithmetic rop for the given type * and arguments. The result is a shared instance. * - * @param types non-null; sources of the operation - * @param int1 non-null; the int-to-constant rop - * @param long1 non-null; the long-to-constant rop - * @param float1 null-ok; the float-to-constant rop, if any - * @param double1 null-ok; the double-to-constant rop, if any - * @param int2 non-null; the int-to-int rop - * @param long2 non-null; the long-to-long or long-to-int rop - * @param float2 null-ok; the float-to-float rop, if any - * @param double2 null-ok; the double-to-double rop, if any - * @return non-null; an appropriate instance + * @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, @@ -1676,11 +1638,11 @@ public final class Rops { } /** - * Returns the appropriate neg rop for the given type. The + * Returns the appropriate {@code neg} rop for the given type. The * result is a shared instance. * - * @param type non-null; type of value being operated on - * @return non-null; an appropriate instance + * @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()) { @@ -1694,11 +1656,11 @@ public final class Rops { } /** - * Returns the appropriate not rop for the given type. The + * Returns the appropriate {@code not} rop for the given type. The * result is a shared instance. * - * @param type non-null; type of value being operated on - * @return non-null; an appropriate instance + * @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()) { @@ -1710,11 +1672,11 @@ public final class Rops { } /** - * Returns the appropriate cmpl rop for the given type. The + * Returns the appropriate {@code cmpl} rop for the given type. The * result is a shared instance. * - * @param type non-null; type of value being compared - * @return non-null; an appropriate instance + * @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()) { @@ -1727,11 +1689,11 @@ public final class Rops { } /** - * Returns the appropriate cmpg rop for the given type. The + * Returns the appropriate {@code cmpg} rop for the given type. The * result is a shared instance. * - * @param type non-null; type of value being compared - * @return non-null; an appropriate instance + * @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()) { @@ -1743,12 +1705,12 @@ public final class Rops { } /** - * Returns the appropriate conv rop for the given types. The + * Returns the appropriate {@code conv} rop for the given types. The * result is a shared instance. * - * @param dest non-null; target value type - * @param source non-null; source value type - * @return non-null; an appropriate instance + * @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(); @@ -1788,11 +1750,11 @@ public final class Rops { } /** - * Returns the appropriate return rop for the given type. The + * Returns the appropriate {@code return} rop for the given type. The * result is a shared instance. * - * @param type non-null; type of value being returned - * @return non-null; an appropriate instance + * @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()) { @@ -1808,11 +1770,11 @@ public final class Rops { } /** - * Returns the appropriate aget rop for the given type. The + * Returns the appropriate {@code aget} rop for the given type. The * result is a shared instance. * - * @param type non-null; element type of array being accessed - * @return non-null; an appropriate instance + * @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()) { @@ -1831,11 +1793,11 @@ public final class Rops { } /** - * Returns the appropriate aput rop for the given type. The + * Returns the appropriate {@code aput} rop for the given type. The * result is a shared instance. * - * @param type non-null; element type of array being accessed - * @return non-null; an appropriate instance + * @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()) { @@ -1854,11 +1816,11 @@ public final class Rops { } /** - * Returns the appropriate new-array rop for the given + * Returns the appropriate {@code new-array} rop for the given * type. The result is a shared instance. * - * @param arrayType non-null; array type of array being created - * @return non-null; an appropriate instance + * @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(); @@ -1884,12 +1846,12 @@ public final class Rops { } /** - * Returns the appropriate filled-new-array rop for the given + * Returns the appropriate {@code filled-new-array} rop for the given * type. The result may be a shared instance. * - * @param arrayType non-null; type of array being created - * @param count >= 0; number of elements that the array should have - * @return non-null; an appropriate 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(); @@ -1916,11 +1878,11 @@ public final class Rops { } /** - * Returns the appropriate get-field rop for the given + * Returns the appropriate {@code get-field} rop for the given * type. The result is a shared instance. * - * @param type non-null; type of the field in question - * @return non-null; an appropriate instance + * @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()) { @@ -1939,11 +1901,11 @@ public final class Rops { } /** - * Returns the appropriate put-field rop for the given + * Returns the appropriate {@code put-field} rop for the given * type. The result is a shared instance. * - * @param type non-null; type of the field in question - * @return non-null; an appropriate instance + * @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()) { @@ -1962,11 +1924,11 @@ public final class Rops { } /** - * Returns the appropriate get-static rop for the given + * Returns the appropriate {@code get-static} rop for the given * type. The result is a shared instance. * - * @param type non-null; type of the field in question - * @return non-null; an appropriate instance + * @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()) { @@ -1985,11 +1947,11 @@ public final class Rops { } /** - * Returns the appropriate put-static rop for the given + * Returns the appropriate {@code put-static} rop for the given * type. The result is a shared instance. * - * @param type non-null; type of the field in question - * @return non-null; an appropriate instance + * @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()) { @@ -2008,11 +1970,11 @@ public final class Rops { } /** - * Returns the appropriate invoke-static rop for the + * Returns the appropriate {@code invoke-static} rop for the * given type. The result is typically a newly-allocated instance. * - * @param meth non-null; descriptor of the method - * @return non-null; an appropriate instance + * @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, @@ -2021,12 +1983,12 @@ public final class Rops { } /** - * Returns the appropriate invoke-virtual rop for the + * Returns the appropriate {@code invoke-virtual} rop for the * given type. The result is typically a newly-allocated instance. * - * @param meth non-null; descriptor of the method, including the - * this parameter - * @return non-null; an appropriate 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, @@ -2035,12 +1997,12 @@ public final class Rops { } /** - * Returns the appropriate invoke-super rop for the + * Returns the appropriate {@code invoke-super} rop for the * given type. The result is typically a newly-allocated instance. * - * @param meth non-null; descriptor of the method, including the - * this parameter - * @return non-null; an appropriate 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, @@ -2049,12 +2011,12 @@ public final class Rops { } /** - * Returns the appropriate invoke-direct rop for the + * Returns the appropriate {@code invoke-direct} rop for the * given type. The result is typically a newly-allocated instance. * - * @param meth non-null; descriptor of the method, including the - * this parameter - * @return non-null; an appropriate 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, @@ -2063,12 +2025,12 @@ public final class Rops { } /** - * Returns the appropriate invoke-interface rop for the + * Returns the appropriate {@code invoke-interface} rop for the * given type. The result is typically a newly-allocated instance. * - * @param meth non-null; descriptor of the method, including the - * this parameter - * @return non-null; an appropriate 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, @@ -2077,11 +2039,11 @@ public final class Rops { } /** - * Returns the appropriate mark-local rop for the given type. + * Returns the appropriate {@code mark-local} rop for the given type. * The result is a shared instance. * - * @param type non-null; type of value being marked - * @return non-null; an appropriate instance + * @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()) { @@ -2105,7 +2067,7 @@ public final class Rops { /** * Throws the right exception to complain about a bogus type. * - * @param type non-null; the bad type + * @param type {@code non-null;} the bad type * @return never */ private static Rop throwBadType(TypeBearer type) { @@ -2115,7 +2077,7 @@ public final class Rops { /** * Throws the right exception to complain about a bogus list of types. * - * @param types non-null; the bad types + * @param types {@code non-null;} the bad types * @return never */ private static Rop throwBadTypes(TypeList types) { diff --git a/dx/src/com/android/dx/rop/code/SourcePosition.java b/dx/src/com/android/dx/rop/code/SourcePosition.java index da66c7de2..f32caa15e 100644 --- a/dx/src/com/android/dx/rop/code/SourcePosition.java +++ b/dx/src/com/android/dx/rop/code/SourcePosition.java @@ -24,21 +24,21 @@ import com.android.dx.util.Hex; * line number and original bytecode address. */ public final class SourcePosition { - /** non-null; convenient "no information known" instance */ + /** {@code non-null;} convenient "no information known" instance */ public static final SourcePosition NO_INFO = new SourcePosition(null, -1, -1); - /** null-ok; name of the file of origin or null if unknown */ + /** {@code null-ok;} name of the file of origin or {@code null} if unknown */ private final CstUtf8 sourceFile; /** - * >= -1; the bytecode address, or -1 if that + * {@code >= -1;} the bytecode address, or {@code -1} if that * information is unknown */ private final int address; /** - * >= -1; the line number, or -1 if that + * {@code >= -1;} the line number, or {@code -1} if that * information is unknown */ private final int line; @@ -46,11 +46,11 @@ public final class SourcePosition { /** * Constructs an instance. * - * @param sourceFile null-ok; name of the file of origin or - * null if unknown - * @param address >= -1; original bytecode address or -1 + * @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 >= -1; original line number or -1 if + * @param line {@code >= -1;} original line number or {@code -1} if * unknown */ public SourcePosition(CstUtf8 sourceFile, int address, int line) { @@ -118,8 +118,8 @@ public final class SourcePosition { * Returns whether the lines match between this instance and * the one given. * - * @param other non-null; the instance to compare to - * @return true iff the lines match + * @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); @@ -129,8 +129,8 @@ public final class SourcePosition { * Returns whether the lines and files match between this instance and * the one given. * - * @param other non-null; the instance to compare to - * @return true iff the lines and files match + * @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) && @@ -141,7 +141,7 @@ public final class SourcePosition { /** * Gets the source file, if known. * - * @return null-ok; the source file or null if unknown + * @return {@code null-ok;} the source file or {@code null} if unknown */ public CstUtf8 getSourceFile() { return sourceFile; @@ -150,7 +150,7 @@ public final class SourcePosition { /** * Gets the original bytecode address. * - * @return >= -1; the address or -1 if unknown + * @return {@code >= -1;} the address or {@code -1} if unknown */ public int getAddress() { return address; @@ -159,7 +159,7 @@ public final class SourcePosition { /** * Gets the original line number. * - * @return >= -1; the original line number or -1 if + * @return {@code >= -1;} the original line number or {@code -1} if * unknown */ public int getLine() { diff --git a/dx/src/com/android/dx/rop/code/SwitchInsn.java b/dx/src/com/android/dx/rop/code/SwitchInsn.java index fdf1a468f..586205bdf 100644 --- a/dx/src/com/android/dx/rop/code/SwitchInsn.java +++ b/dx/src/com/android/dx/rop/code/SwitchInsn.java @@ -26,17 +26,17 @@ import com.android.dx.util.IntList; */ public final class SwitchInsn extends Insn { - /** non-null; list of switch cases */ + /** {@code non-null;} list of switch cases */ private final IntList cases; /** * Constructs an instance. * - * @param opcode non-null; the opcode - * @param position non-null; source position - * @param result null-ok; spec for the result, if any - * @param sources non-null; specs for all the sources - * @param cases non-null; list of switch cases + * @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) { @@ -90,7 +90,7 @@ public final class SwitchInsn * {@inheritDoc} * *

    SwitchInsn always compares false. The current use for this method - * never encounters SwitchInsns + * never encounters {@code SwitchInsn}s */ @Override public boolean contentEquals(Insn b) { @@ -111,7 +111,7 @@ public final class SwitchInsn /** * Gets the list of switch cases. * - * @return non-null; the case list + * @return {@code non-null;} the case list */ public IntList getCases() { return cases; diff --git a/dx/src/com/android/dx/rop/code/ThrowingCstInsn.java b/dx/src/com/android/dx/rop/code/ThrowingCstInsn.java index 49ebc91a5..b14e7588d 100644 --- a/dx/src/com/android/dx/rop/code/ThrowingCstInsn.java +++ b/dx/src/com/android/dx/rop/code/ThrowingCstInsn.java @@ -26,17 +26,17 @@ import com.android.dx.rop.type.TypeList; */ public final class ThrowingCstInsn extends CstInsn { - /** non-null; list of exceptions caught */ + /** {@code non-null;} list of exceptions caught */ private final TypeList catches; /** * Constructs an instance. * - * @param opcode non-null; the opcode - * @param position non-null; source position - * @param sources non-null; specs for all the sources - * @param catches non-null; list of exceptions caught - * @param cst non-null; the constant + * @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, diff --git a/dx/src/com/android/dx/rop/code/ThrowingInsn.java b/dx/src/com/android/dx/rop/code/ThrowingInsn.java index 24a5bed9b..78dc87489 100644 --- a/dx/src/com/android/dx/rop/code/ThrowingInsn.java +++ b/dx/src/com/android/dx/rop/code/ThrowingInsn.java @@ -20,22 +20,22 @@ import com.android.dx.rop.type.Type; import com.android.dx.rop.type.TypeList; /** - * Instruction which possibly throws. The successors list in the + * Instruction which possibly throws. The {@code successors} list in the * basic block an instance of this class is inside corresponds in-order to * the list of exceptions handled by this instruction, with the * no-exception case appended as the final target. */ public final class ThrowingInsn extends Insn { - /** non-null; list of exceptions caught */ + /** {@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 non-null; the catches list - * @return non-null; the string form + * @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); @@ -54,10 +54,10 @@ public final class ThrowingInsn /** * Constructs an instance. * - * @param opcode non-null; the opcode - * @param position non-null; source position - * @param sources non-null; specs for all the sources - * @param catches non-null; list of exceptions caught + * @param 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, diff --git a/dx/src/com/android/dx/rop/code/TranslationAdvice.java b/dx/src/com/android/dx/rop/code/TranslationAdvice.java index 8c2cde984..832d84df6 100644 --- a/dx/src/com/android/dx/rop/code/TranslationAdvice.java +++ b/dx/src/com/android/dx/rop/code/TranslationAdvice.java @@ -30,10 +30,10 @@ public interface TranslationAdvice { * last argument must have a type which indicates it is a known constant.) * The instruction associated must have exactly two sources. * - * @param opcode non-null; the opcode - * @param sourceA non-null; the first source - * @param sourceB non-null; the second source - * @return true iff the target can represent the operation + * @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, @@ -43,9 +43,9 @@ public interface TranslationAdvice { * Returns true if the translation target requires the sources of the * specified opcode to be in order and contiguous (eg, for an invoke-range) * - * @param opcode non-null; opcode - * @param sources non-null; source list - * @return true iff the target requires the sources to be + * @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); diff --git a/dx/src/com/android/dx/rop/cst/Constant.java b/dx/src/com/android/dx/rop/cst/Constant.java index 0f440103c..64231d3b3 100644 --- a/dx/src/com/android/dx/rop/cst/Constant.java +++ b/dx/src/com/android/dx/rop/cst/Constant.java @@ -24,11 +24,11 @@ import com.android.dx.util.ToHuman; public abstract class Constant implements ToHuman, Comparable { /** - * Returns true if this instance is a category-2 constant, + * Returns {@code true} if this instance is a category-2 constant, * meaning it takes up two slots in the constant pool, or - * false if this instance is category-1. + * {@code false} if this instance is category-1. * - * @return true iff this instance is category-2 + * @return {@code true} iff this instance is category-2 */ public abstract boolean isCategory2(); @@ -36,7 +36,7 @@ public abstract class Constant * Returns the human name for the particular type of constant * this instance is. * - * @return non-null; the name + * @return {@code non-null;} the name */ public abstract String typeName(); @@ -60,8 +60,8 @@ public abstract class Constant * Compare the values of this and another instance, which are guaranteed * to be of the same class. Subclasses must implement this. * - * @param other non-null; the instance to compare to - * @return -1, 0, or 1, as usual + * @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/dx/rop/cst/ConstantPool.java b/dx/src/com/android/dx/rop/cst/ConstantPool.java index 9a64a2aab..efc394daa 100644 --- a/dx/src/com/android/dx/rop/cst/ConstantPool.java +++ b/dx/src/com/android/dx/rop/cst/ConstantPool.java @@ -23,47 +23,47 @@ package com.android.dx.rop.cst; public interface ConstantPool { /** * Get the "size" of the constant pool. This corresponds to the - * class file field constant_pool_count, and is in fact + * 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 0 is always invalid. + * as element {@code 0} is always invalid. * - * @return >= 1; the size + * @return {@code >= 1;} the size */ public int size(); /** - * Get the nth entry in the constant pool, which must + * Get the {@code n}th entry in the constant pool, which must * be valid. * - * @param n n >= 0, n < size(); the constant pool index - * @return non-null; the corresponding entry - * @throws IllegalArgumentException thrown if n is + * @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 nth entry in the constant pool, which must - * be valid unless n == 0, in which case null + * 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 n >= 0, n < size(); the constant pool index - * @return null-ok; the corresponding entry, if n != 0 - * @throws IllegalArgumentException thrown if n is + * @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 nth entry in the constant pool, or - * null if the index is in-range but invalid. In - * particular, null is returned for index 0 + * 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, Long and Double + * two slots (that is, {@code Long} and {@code Double} * entries). * - * @param n n >= 0, n < size(); the constant pool index - * @return null-ok; the corresponding entry, or null if + * @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/dx/rop/cst/CstAnnotation.java b/dx/src/com/android/dx/rop/cst/CstAnnotation.java index d6dc1f255..1385798c9 100644 --- a/dx/src/com/android/dx/rop/cst/CstAnnotation.java +++ b/dx/src/com/android/dx/rop/cst/CstAnnotation.java @@ -22,13 +22,13 @@ import com.android.dx.rop.annotation.Annotation; * Constant type that represents an annotation. */ public final class CstAnnotation extends Constant { - /** non-null; the actual annotation */ + /** {@code non-null;} the actual annotation */ private final Annotation annotation; /** * Constructs an instance. * - * @param annotation non-null; the annotation to hold + * @param annotation {@code non-null;} the annotation to hold */ public CstAnnotation(Annotation annotation) { if (annotation == null) { @@ -88,7 +88,7 @@ public final class CstAnnotation extends Constant { /** * Get the underlying annotation. * - * @return non-null; the annotation + * @return {@code non-null;} the annotation */ public Annotation getAnnotation() { return annotation; diff --git a/dx/src/com/android/dx/rop/cst/CstArray.java b/dx/src/com/android/dx/rop/cst/CstArray.java index 69c0aeffa..8b521bdc9 100644 --- a/dx/src/com/android/dx/rop/cst/CstArray.java +++ b/dx/src/com/android/dx/rop/cst/CstArray.java @@ -24,13 +24,13 @@ import com.android.dx.util.FixedSizeList; * may be of any type other than {@link CstUtf8}. */ public final class CstArray extends Constant { - /** non-null; the actual list of contents */ + /** {@code non-null;} the actual list of contents */ private final List list; /** * Constructs an instance. * - * @param list non-null; the actual list of contents + * @param list {@code non-null;} the actual list of contents */ public CstArray(List list) { if (list == null) { @@ -90,7 +90,7 @@ public final class CstArray extends Constant { /** * Get the underlying list. * - * @return non-null; the list + * @return {@code non-null;} the list */ public List getList() { return list; @@ -103,7 +103,7 @@ public final class CstArray extends Constant { extends FixedSizeList implements Comparable { /** * Constructs an instance. All indices initially contain - * null. + * {@code null}. * * @param size the size of the list */ @@ -138,10 +138,10 @@ public final class CstArray extends Constant { /** * 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 NullPointerException. + * do that, this will throw {@code NullPointerException}. * - * @param n >= 0, < size(); which index - * @return non-null; element at that index + * @param n {@code >= 0, < size();} which index + * @return {@code non-null;} element at that index */ public Constant get(int n) { return (Constant) get0(n); @@ -150,8 +150,8 @@ public final class CstArray extends Constant { /** * Sets the element at the given index. * - * @param n >= 0, < size(); which index - * @param a null-ok; the element to set at n + * @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) { if (a instanceof CstUtf8) { diff --git a/dx/src/com/android/dx/rop/cst/CstBaseMethodRef.java b/dx/src/com/android/dx/rop/cst/CstBaseMethodRef.java index c8856019d..039d7edb5 100644 --- a/dx/src/com/android/dx/rop/cst/CstBaseMethodRef.java +++ b/dx/src/com/android/dx/rop/cst/CstBaseMethodRef.java @@ -28,20 +28,20 @@ import com.android.dx.rop.type.TypeBearer; */ public abstract class CstBaseMethodRef extends CstMemberRef { - /** non-null; the raw prototype for this method */ + /** {@code non-null;} the raw prototype for this method */ private final Prototype prototype; /** - * null-ok; the prototype for this method taken to be an instance - * method, or null if not yet calculated + * {@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 non-null; the type of the defining class - * @param nat non-null; the name-and-type + * @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); @@ -53,9 +53,9 @@ public abstract class CstBaseMethodRef /** * Gets the raw prototype of this method. This doesn't include a - * this argument. + * {@code this} argument. * - * @return non-null; the method prototype + * @return {@code non-null;} the method prototype */ public final Prototype getPrototype() { return prototype; @@ -63,14 +63,14 @@ public abstract class CstBaseMethodRef /** * Gets the prototype of this method as either a - * static or instance method. In the case of a - * static method, this is the same as the raw + * {@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 this argument as the first + * appropriately-typed {@code this} argument as the first * one. * * @param isStatic whether the method should be considered static - * @return non-null; the method prototype + * @return {@code non-null;} the method prototype */ public final Prototype getPrototype(boolean isStatic) { if (isStatic) { @@ -102,7 +102,7 @@ public abstract class CstBaseMethodRef * * In this case, this method returns the return type of this method. * - * @return non-null; the method's return type + * @return {@code non-null;} the method's return type */ public final Type getType() { return prototype.getReturnType(); @@ -111,15 +111,15 @@ public abstract class CstBaseMethodRef /** * 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 static or + * 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 - * getPrototype().getParameterTypes().getWordCount(), - * plus 1 if the method is to be treated as an + * {@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 >= 0; the argument word count + * @return {@code >= 0;} the argument word count */ public final int getParameterWordCount(boolean isStatic) { return getPrototype(isStatic).getParameterTypes().getWordCount(); @@ -128,9 +128,9 @@ public abstract class CstBaseMethodRef /** * Gets whether this is a reference to an instance initialization * method. This is just a convenient shorthand for - * getNat().isInstanceInit(). + * {@code getNat().isInstanceInit()}. * - * @return true iff this is a reference to an + * @return {@code true} iff this is a reference to an * instance initialization method */ public final boolean isInstanceInit() { @@ -140,9 +140,9 @@ public abstract class CstBaseMethodRef /** * Gets whether this is a reference to a class initialization * method. This is just a convenient shorthand for - * getNat().isClassInit(). + * {@code getNat().isClassInit()}. * - * @return true iff this is a reference to an + * @return {@code true} iff this is a reference to an * instance initialization method */ public final boolean isClassInit() { diff --git a/dx/src/com/android/dx/rop/cst/CstBoolean.java b/dx/src/com/android/dx/rop/cst/CstBoolean.java index ab25d5bcf..8c290ef20 100644 --- a/dx/src/com/android/dx/rop/cst/CstBoolean.java +++ b/dx/src/com/android/dx/rop/cst/CstBoolean.java @@ -19,33 +19,33 @@ package com.android.dx.rop.cst; import com.android.dx.rop.type.Type; /** - * Constants of type boolean. + * Constants of type {@code boolean}. */ public final class CstBoolean extends CstLiteral32 { - /** non-null; instance representing false */ + /** {@code non-null;} instance representing {@code false} */ public static final CstBoolean VALUE_FALSE = new CstBoolean(false); - /** non-null; instance representing 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 boolean value - * @return non-null; the appropriate 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 int value. This + * Makes an instance for the given {@code int} value. This * will return an already-allocated instance. * - * @param value must be either 0 or 1 - * @return non-null; the appropriate 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) { @@ -60,7 +60,7 @@ public final class CstBoolean /** * Constructs an instance. This constructor is private; use {@link #make}. * - * @param value the boolean value + * @param value the {@code boolean} value */ private CstBoolean(boolean value) { super(value ? 1 : 0); @@ -89,7 +89,7 @@ public final class CstBoolean } /** - * Gets the boolean value. + * Gets the {@code boolean} value. * * @return the value */ diff --git a/dx/src/com/android/dx/rop/cst/CstByte.java b/dx/src/com/android/dx/rop/cst/CstByte.java index ffc3206b6..a8af9f786 100644 --- a/dx/src/com/android/dx/rop/cst/CstByte.java +++ b/dx/src/com/android/dx/rop/cst/CstByte.java @@ -20,30 +20,30 @@ import com.android.dx.rop.type.Type; import com.android.dx.util.Hex; /** - * Constants of type byte. + * Constants of type {@code byte}. */ public final class CstByte extends CstLiteral32 { - /** non-null; the value 0 as an instance of this class */ + /** {@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 byte value + * @param value the {@code byte} value */ public static CstByte make(byte value) { return new CstByte(value); } /** - * Makes an instance for the given int value. This + * 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 byte - * @return non-null; the appropriate 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; @@ -59,7 +59,7 @@ public final class CstByte /** * Constructs an instance. This constructor is private; use {@link #make}. * - * @param value the byte value + * @param value the {@code byte} value */ private CstByte(byte value) { super(value); @@ -89,7 +89,7 @@ public final class CstByte } /** - * Gets the byte value. + * Gets the {@code byte} value. * * @return the value */ diff --git a/dx/src/com/android/dx/rop/cst/CstChar.java b/dx/src/com/android/dx/rop/cst/CstChar.java index a31bd7fc0..0a87cbc65 100644 --- a/dx/src/com/android/dx/rop/cst/CstChar.java +++ b/dx/src/com/android/dx/rop/cst/CstChar.java @@ -20,30 +20,30 @@ import com.android.dx.rop.type.Type; import com.android.dx.util.Hex; /** - * Constants of type char. + * Constants of type {@code char}. */ public final class CstChar extends CstLiteral32 { - /** non-null; the value 0 as an instance of this class */ + /** {@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 char value + * @param value the {@code char} value */ public static CstChar make(char value) { return new CstChar(value); } /** - * Makes an instance for the given int value. This + * 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 char - * @return non-null; the appropriate 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; @@ -59,7 +59,7 @@ public final class CstChar /** * Constructs an instance. This constructor is private; use {@link #make}. * - * @param value the char value + * @param value the {@code char} value */ private CstChar(char value) { super(value); @@ -89,7 +89,7 @@ public final class CstChar } /** - * Gets the char value. + * Gets the {@code char} value. * * @return the value */ diff --git a/dx/src/com/android/dx/rop/cst/CstDouble.java b/dx/src/com/android/dx/rop/cst/CstDouble.java index 451666791..df4a2cf79 100644 --- a/dx/src/com/android/dx/rop/cst/CstDouble.java +++ b/dx/src/com/android/dx/rop/cst/CstDouble.java @@ -20,15 +20,15 @@ import com.android.dx.rop.type.Type; import com.android.dx.util.Hex; /** - * Constants of type CONSTANT_Double_info. + * Constants of type {@code CONSTANT_Double_info}. */ public final class CstDouble extends CstLiteral64 { - /** non-null; instance representing 0 */ + /** {@code non-null;} instance representing {@code 0} */ public static final CstDouble VALUE_0 = new CstDouble(Double.doubleToLongBits(0.0)); - /** non-null; instance representing 1 */ + /** {@code non-null;} instance representing {@code 1} */ public static final CstDouble VALUE_1 = new CstDouble(Double.doubleToLongBits(1.0)); @@ -36,7 +36,7 @@ public final class CstDouble * Makes an instance for the given value. This may (but does not * necessarily) return an already-allocated instance. * - * @param bits the double value as long bits + * @param bits the {@code double} value as {@code long} bits */ public static CstDouble make(long bits) { /* @@ -49,7 +49,7 @@ public final class CstDouble /** * Constructs an instance. This constructor is private; use {@link #make}. * - * @param bits the double value as long bits + * @param bits the {@code double} value as {@code long} bits */ private CstDouble(long bits) { super(bits); @@ -80,7 +80,7 @@ public final class CstDouble } /** - * Gets the double value. + * Gets the {@code double} value. * * @return the value */ diff --git a/dx/src/com/android/dx/rop/cst/CstEnumRef.java b/dx/src/com/android/dx/rop/cst/CstEnumRef.java index f5aec0549..78cab9df3 100644 --- a/dx/src/com/android/dx/rop/cst/CstEnumRef.java +++ b/dx/src/com/android/dx/rop/cst/CstEnumRef.java @@ -23,13 +23,13 @@ import com.android.dx.rop.type.Type; * value of an enumerated type. */ public final class CstEnumRef extends CstMemberRef { - /** null-ok; the corresponding field ref, lazily initialized */ + /** {@code null-ok;} the corresponding field ref, lazily initialized */ private CstFieldRef fieldRef; /** * Constructs an instance. * - * @param nat non-null; the name-and-type; the defining class is derived + * @param nat {@code non-null;} the name-and-type; the defining class is derived * from this */ public CstEnumRef(CstNat nat) { @@ -56,7 +56,7 @@ public final class CstEnumRef extends CstMemberRef { /** * Get a {@link CstFieldRef} that corresponds with this instance. * - * @return non-null; the corresponding field reference + * @return {@code non-null;} the corresponding field reference */ public CstFieldRef getFieldRef() { if (fieldRef == null) { diff --git a/dx/src/com/android/dx/rop/cst/CstFieldRef.java b/dx/src/com/android/dx/rop/cst/CstFieldRef.java index 306eca928..497531fa5 100644 --- a/dx/src/com/android/dx/rop/cst/CstFieldRef.java +++ b/dx/src/com/android/dx/rop/cst/CstFieldRef.java @@ -19,7 +19,7 @@ package com.android.dx.rop.cst; import com.android.dx.rop.type.Type; /** - * Constants of type CONSTANT_Fieldref_info. + * Constants of type {@code CONSTANT_Fieldref_info}. */ public final class CstFieldRef extends CstMemberRef { /** @@ -27,10 +27,10 @@ public final class CstFieldRef extends CstMemberRef { * 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 - * java.lang.Integer.TYPE. + * {@code java.lang.Integer.TYPE}. * - * @param primitiveType non-null; the primitive type - * @return non-null; the corresponding static field + * @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), @@ -40,8 +40,8 @@ public final class CstFieldRef extends CstMemberRef { /** * Constructs an instance. * - * @param definingClass non-null; the type of the defining class - * @param nat non-null; the name-and-type + * @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); @@ -56,7 +56,7 @@ public final class CstFieldRef extends CstMemberRef { /** * Returns the type of this field. * - * @return non-null; the field's type + * @return {@code non-null;} the field's type */ public Type getType() { return getNat().getFieldType(); diff --git a/dx/src/com/android/dx/rop/cst/CstFloat.java b/dx/src/com/android/dx/rop/cst/CstFloat.java index 08b7f764e..531a20d96 100644 --- a/dx/src/com/android/dx/rop/cst/CstFloat.java +++ b/dx/src/com/android/dx/rop/cst/CstFloat.java @@ -20,24 +20,24 @@ import com.android.dx.rop.type.Type; import com.android.dx.util.Hex; /** - * Constants of type CONSTANT_Float_info. + * Constants of type {@code CONSTANT_Float_info}. */ public final class CstFloat extends CstLiteral32 { - /** non-null; instance representing 0 */ + /** {@code non-null;} instance representing {@code 0} */ public static final CstFloat VALUE_0 = make(Float.floatToIntBits(0.0f)); - /** non-null; instance representing 1 */ + /** {@code non-null;} instance representing {@code 1} */ public static final CstFloat VALUE_1 = make(Float.floatToIntBits(1.0f)); - /** non-null; instance representing 2 */ + /** {@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 float value as int bits + * @param bits the {@code float} value as {@code int} bits */ public static CstFloat make(int bits) { /* @@ -50,7 +50,7 @@ public final class CstFloat /** * Constructs an instance. This constructor is private; use {@link #make}. * - * @param bits the float value as int bits + * @param bits the {@code float} value as {@code int} bits */ private CstFloat(int bits) { super(bits); @@ -81,7 +81,7 @@ public final class CstFloat } /** - * Gets the float value. + * Gets the {@code float} value. * * @return the value */ diff --git a/dx/src/com/android/dx/rop/cst/CstInteger.java b/dx/src/com/android/dx/rop/cst/CstInteger.java index d3fafccd7..8fae4fa57 100644 --- a/dx/src/com/android/dx/rop/cst/CstInteger.java +++ b/dx/src/com/android/dx/rop/cst/CstInteger.java @@ -20,40 +20,40 @@ import com.android.dx.rop.type.Type; import com.android.dx.util.Hex; /** - * Constants of type CONSTANT_Integer_info. + * Constants of type {@code CONSTANT_Integer_info}. */ public final class CstInteger extends CstLiteral32 { - /** non-null; array of cached instances */ + /** {@code non-null;} array of cached instances */ private static final CstInteger[] cache = new CstInteger[511]; - /** non-null; instance representing -1 */ + /** {@code non-null;} instance representing {@code -1} */ public static final CstInteger VALUE_M1 = make(-1); - /** non-null; instance representing 0 */ + /** {@code non-null;} instance representing {@code 0} */ public static final CstInteger VALUE_0 = make(0); - /** non-null; instance representing 1 */ + /** {@code non-null;} instance representing {@code 1} */ public static final CstInteger VALUE_1 = make(1); - /** non-null; instance representing 2 */ + /** {@code non-null;} instance representing {@code 2} */ public static final CstInteger VALUE_2 = make(2); - /** non-null; instance representing 3 */ + /** {@code non-null;} instance representing {@code 3} */ public static final CstInteger VALUE_3 = make(3); - /** non-null; instance representing 4 */ + /** {@code non-null;} instance representing {@code 4} */ public static final CstInteger VALUE_4 = make(4); - /** non-null; instance representing 5 */ + /** {@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 int value - * @return non-null; the appropriate instance + * @param value the {@code int} value + * @return {@code non-null;} the appropriate instance */ public static CstInteger make(int value) { /* @@ -76,7 +76,7 @@ public final class CstInteger /** * Constructs an instance. This constructor is private; use {@link #make}. * - * @param value the int value + * @param value the {@code int} value */ private CstInteger(int value) { super(value); @@ -106,7 +106,7 @@ public final class CstInteger } /** - * Gets the int value. + * Gets the {@code int} value. * * @return the value */ diff --git a/dx/src/com/android/dx/rop/cst/CstInterfaceMethodRef.java b/dx/src/com/android/dx/rop/cst/CstInterfaceMethodRef.java index f169ec939..55a75995a 100644 --- a/dx/src/com/android/dx/rop/cst/CstInterfaceMethodRef.java +++ b/dx/src/com/android/dx/rop/cst/CstInterfaceMethodRef.java @@ -17,12 +17,12 @@ package com.android.dx.rop.cst; /** - * Constants of type CONSTANT_InterfaceMethodref_info. + * Constants of type {@code CONSTANT_InterfaceMethodref_info}. */ public final class CstInterfaceMethodRef extends CstBaseMethodRef { /** - * null-ok; normal {@link CstMethodRef} that corresponds to this + * {@code null-ok;} normal {@link CstMethodRef} that corresponds to this * instance, if calculated */ private CstMethodRef methodRef; @@ -30,8 +30,8 @@ public final class CstInterfaceMethodRef /** * Constructs an instance. * - * @param definingClass non-null; the type of the defining class - * @param nat non-null; the name-and-type + * @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); @@ -48,7 +48,7 @@ public final class CstInterfaceMethodRef * Gets a normal (non-interface) {@link CstMethodRef} that corresponds to * this instance. * - * @return non-null; an appropriate instance + * @return {@code non-null;} an appropriate instance */ public CstMethodRef toMethodRef() { if (methodRef == null) { diff --git a/dx/src/com/android/dx/rop/cst/CstKnownNull.java b/dx/src/com/android/dx/rop/cst/CstKnownNull.java index 853e57e56..09dde1b31 100644 --- a/dx/src/com/android/dx/rop/cst/CstKnownNull.java +++ b/dx/src/com/android/dx/rop/cst/CstKnownNull.java @@ -19,10 +19,10 @@ package com.android.dx.rop.cst; import com.android.dx.rop.type.Type; /** - * Constant type to represent a known-null value. + * Constant type to represent a known-{@code null} value. */ public final class CstKnownNull extends CstLiteralBits { - /** non-null; unique instance of this class */ + /** {@code non-null;} unique instance of this class */ public static final CstKnownNull THE_ONE = new CstKnownNull(); /** diff --git a/dx/src/com/android/dx/rop/cst/CstLiteral32.java b/dx/src/com/android/dx/rop/cst/CstLiteral32.java index 31e96dd63..c6e3021eb 100644 --- a/dx/src/com/android/dx/rop/cst/CstLiteral32.java +++ b/dx/src/com/android/dx/rop/cst/CstLiteral32.java @@ -21,13 +21,13 @@ package com.android.dx.rop.cst; */ public abstract class CstLiteral32 extends CstLiteralBits { - /** the value as int bits */ + /** the value as {@code int} bits */ private final int bits; /** * Constructs an instance. * - * @param bits the value as int bits + * @param bits the value as {@code int} bits */ /*package*/ CstLiteral32(int bits) { this.bits = bits; diff --git a/dx/src/com/android/dx/rop/cst/CstLiteral64.java b/dx/src/com/android/dx/rop/cst/CstLiteral64.java index dd7d24dea..d0b27d2b0 100644 --- a/dx/src/com/android/dx/rop/cst/CstLiteral64.java +++ b/dx/src/com/android/dx/rop/cst/CstLiteral64.java @@ -21,13 +21,13 @@ package com.android.dx.rop.cst; */ public abstract class CstLiteral64 extends CstLiteralBits { - /** the value as long bits */ + /** the value as {@code long} bits */ private final long bits; /** * Constructs an instance. * - * @param bits the value as long bits + * @param bits the value as {@code long} bits */ /*package*/ CstLiteral64(long bits) { this.bits = bits; diff --git a/dx/src/com/android/dx/rop/cst/CstLiteralBits.java b/dx/src/com/android/dx/rop/cst/CstLiteralBits.java index 98a3f0e53..64154874b 100644 --- a/dx/src/com/android/dx/rop/cst/CstLiteralBits.java +++ b/dx/src/com/android/dx/rop/cst/CstLiteralBits.java @@ -23,18 +23,18 @@ public abstract class CstLiteralBits extends TypedConstant { /** * Returns whether or not this instance's value may be accurately - * represented as an int. The rule is that if there - * is an int which may be sign-extended to yield this - * instance's value, then this method returns true. - * Otherwise, it returns false. + * 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 true iff this instance fits in an int + * @return {@code true} iff this instance fits in an {@code int} */ public abstract boolean fitsInInt(); /** - * Gets the value as int bits. If this instance contains - * more bits than fit in an int, then this returns only + * 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 @@ -42,8 +42,8 @@ public abstract class CstLiteralBits public abstract int getIntBits(); /** - * Gets the value as long bits. If this instance contains - * fewer bits than fit in a long, then the result of this + * 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 diff --git a/dx/src/com/android/dx/rop/cst/CstLong.java b/dx/src/com/android/dx/rop/cst/CstLong.java index 377eb9312..c89a33992 100644 --- a/dx/src/com/android/dx/rop/cst/CstLong.java +++ b/dx/src/com/android/dx/rop/cst/CstLong.java @@ -20,21 +20,21 @@ import com.android.dx.rop.type.Type; import com.android.dx.util.Hex; /** - * Constants of type CONSTANT_Long_info. + * Constants of type {@code CONSTANT_Long_info}. */ public final class CstLong extends CstLiteral64 { - /** non-null; instance representing 0 */ + /** {@code non-null;} instance representing {@code 0} */ public static final CstLong VALUE_0 = make(0); - /** non-null; instance representing 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 long value + * @param value the {@code long} value */ public static CstLong make(long value) { /* @@ -47,7 +47,7 @@ public final class CstLong /** * Constructs an instance. This constructor is private; use {@link #make}. * - * @param value the long value + * @param value the {@code long} value */ private CstLong(long value) { super(value); @@ -77,7 +77,7 @@ public final class CstLong } /** - * Gets the long value. + * Gets the {@code long} value. * * @return the value */ diff --git a/dx/src/com/android/dx/rop/cst/CstMemberRef.java b/dx/src/com/android/dx/rop/cst/CstMemberRef.java index dbaad47eb..bae47c210 100644 --- a/dx/src/com/android/dx/rop/cst/CstMemberRef.java +++ b/dx/src/com/android/dx/rop/cst/CstMemberRef.java @@ -17,20 +17,20 @@ package com.android.dx.rop.cst; /** - * Constants of type CONSTANT_*ref_info. + * Constants of type {@code CONSTANT_*ref_info}. */ public abstract class CstMemberRef extends TypedConstant { - /** non-null; the type of the defining class */ + /** {@code non-null;} the type of the defining class */ private final CstType definingClass; - /** non-null; the name-and-type */ + /** {@code non-null;} the name-and-type */ private final CstNat nat; /** * Constructs an instance. * - * @param definingClass non-null; the type of the defining class - * @param nat non-null; the name-and-type + * @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) { @@ -68,7 +68,7 @@ public abstract class CstMemberRef extends TypedConstant { * *

    Note: This implementation just compares the defining * class and name, and it is up to subclasses to compare the rest - * after calling super.compareTo0().

    + * after calling {@code super.compareTo0()}.

    */ @Override protected int compareTo0(Constant other) { @@ -105,7 +105,7 @@ public abstract class CstMemberRef extends TypedConstant { /** * Gets the type of the defining class. * - * @return non-null; the type of defining class + * @return {@code non-null;} the type of defining class */ public final CstType getDefiningClass() { return definingClass; @@ -114,7 +114,7 @@ public abstract class CstMemberRef extends TypedConstant { /** * Gets the defining name-and-type. * - * @return non-null; the name-and-type + * @return {@code non-null;} the name-and-type */ public final CstNat getNat() { return nat; diff --git a/dx/src/com/android/dx/rop/cst/CstMethodRef.java b/dx/src/com/android/dx/rop/cst/CstMethodRef.java index 766c9bfba..77c97e9d1 100644 --- a/dx/src/com/android/dx/rop/cst/CstMethodRef.java +++ b/dx/src/com/android/dx/rop/cst/CstMethodRef.java @@ -17,15 +17,15 @@ package com.android.dx.rop.cst; /** - * Constants of type CONSTANT_Methodref_info. + * Constants of type {@code CONSTANT_Methodref_info}. */ public final class CstMethodRef extends CstBaseMethodRef { /** * Constructs an instance. * - * @param definingClass non-null; the type of the defining class - * @param nat non-null; the name-and-type + * @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); diff --git a/dx/src/com/android/dx/rop/cst/CstNat.java b/dx/src/com/android/dx/rop/cst/CstNat.java index 106b599cc..5270fd2c7 100644 --- a/dx/src/com/android/dx/rop/cst/CstNat.java +++ b/dx/src/com/android/dx/rop/cst/CstNat.java @@ -19,29 +19,29 @@ package com.android.dx.rop.cst; import com.android.dx.rop.type.Type; /** - * Constants of type CONSTANT_NameAndType_info. + * Constants of type {@code CONSTANT_NameAndType_info}. */ public final class CstNat extends Constant { /** - * non-null; the instance for name TYPE and descriptor - * java.lang.Class, which is useful when dealing with + * {@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 CstUtf8("TYPE"), new CstUtf8("Ljava/lang/Class;")); - /** non-null; the name */ + /** {@code non-null;} the name */ private final CstUtf8 name; - /** non-null; the descriptor (type) */ + /** {@code non-null;} the descriptor (type) */ private final CstUtf8 descriptor; /** * Constructs an instance. * - * @param name non-null; the name - * @param descriptor non-null; the descriptor + * @param name {@code non-null;} the name + * @param descriptor {@code non-null;} the descriptor */ public CstNat(CstUtf8 name, CstUtf8 descriptor) { if (name == null) { @@ -108,7 +108,7 @@ public final class CstNat extends Constant { /** * Gets the name. * - * @return non-null; the name + * @return {@code non-null;} the name */ public CstUtf8 getName() { return name; @@ -117,7 +117,7 @@ public final class CstNat extends Constant { /** * Gets the descriptor. * - * @return non-null; the descriptor + * @return {@code non-null;} the descriptor */ public CstUtf8 getDescriptor() { return descriptor; @@ -127,7 +127,7 @@ public final class CstNat extends Constant { * Returns an unadorned but human-readable version of the name-and-type * value. * - * @return non-null; the human form + * @return {@code non-null;} the human form */ public String toHuman() { return name.toHuman() + ':' + descriptor.toHuman(); @@ -138,7 +138,7 @@ public final class CstNat extends Constant { * This method is only valid to call if the descriptor in fact describes * a field (and not a method). * - * @return non-null; the field type + * @return {@code non-null;} the field type */ public Type getFieldType() { return Type.intern(descriptor.getString()); @@ -147,9 +147,9 @@ public final class CstNat extends Constant { /** * Gets whether this instance has the name of a standard instance * initialization method. This is just a convenient shorthand for - * getName().getString().equals("<init>"). + * {@code getName().getString().equals("")}. * - * @return true iff this is a reference to an + * @return {@code true} iff this is a reference to an * instance initialization method */ public final boolean isInstanceInit() { @@ -159,9 +159,9 @@ public final class CstNat extends Constant { /** * Gets whether this instance has the name of a standard class * initialization method. This is just a convenient shorthand for - * getName().getString().equals("<clinit>"). + * {@code getName().getString().equals("")}. * - * @return true iff this is a reference to an + * @return {@code true} iff this is a reference to an * instance initialization method */ public final boolean isClassInit() { diff --git a/dx/src/com/android/dx/rop/cst/CstShort.java b/dx/src/com/android/dx/rop/cst/CstShort.java index 38042541a..4ac2f68da 100644 --- a/dx/src/com/android/dx/rop/cst/CstShort.java +++ b/dx/src/com/android/dx/rop/cst/CstShort.java @@ -20,31 +20,31 @@ import com.android.dx.rop.type.Type; import com.android.dx.util.Hex; /** - * Constants of type short. + * Constants of type {@code short}. */ public final class CstShort extends CstLiteral32 { - /** non-null; the value 0 as an instance of this class */ + /** {@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 short value - * @return non-null; the appropriate 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 int value. This + * 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 short - * @return non-null; the appropriate 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; @@ -60,7 +60,7 @@ public final class CstShort /** * Constructs an instance. This constructor is private; use {@link #make}. * - * @param value the short value + * @param value the {@code short} value */ private CstShort(short value) { super(value); @@ -90,7 +90,7 @@ public final class CstShort } /** - * Gets the short value. + * Gets the {@code short} value. * * @return the value */ diff --git a/dx/src/com/android/dx/rop/cst/CstString.java b/dx/src/com/android/dx/rop/cst/CstString.java index 89a4c8bfe..ce00f5275 100644 --- a/dx/src/com/android/dx/rop/cst/CstString.java +++ b/dx/src/com/android/dx/rop/cst/CstString.java @@ -19,17 +19,17 @@ package com.android.dx.rop.cst; import com.android.dx.rop.type.Type; /** - * Constants of type CONSTANT_String_info. + * Constants of type {@code CONSTANT_String_info}. */ public final class CstString extends TypedConstant { - /** non-null; the string value */ + /** {@code non-null;} the string value */ private final CstUtf8 string; /** * Constructs an instance. * - * @param string non-null; the string value + * @param string {@code non-null;} the string value */ public CstString(CstUtf8 string) { if (string == null) { @@ -42,7 +42,7 @@ public final class CstString /** * Constructs an instance. * - * @param string non-null; the string value + * @param string {@code non-null;} the string value */ public CstString(String string) { this(new CstUtf8(string)); @@ -101,7 +101,7 @@ public final class CstString /** * Gets the string value. * - * @return non-null; the string value + * @return {@code non-null;} the string value */ public CstUtf8 getString() { return string; diff --git a/dx/src/com/android/dx/rop/cst/CstType.java b/dx/src/com/android/dx/rop/cst/CstType.java index 02df28d98..6dc986785 100644 --- a/dx/src/com/android/dx/rop/cst/CstType.java +++ b/dx/src/com/android/dx/rop/cst/CstType.java @@ -24,69 +24,69 @@ import java.util.HashMap; * Constants that represent an arbitrary type (reference or primitive). */ public final class CstType extends TypedConstant { - /** non-null; map of interned types */ + /** {@code non-null;} map of interned types */ private static final HashMap interns = new HashMap(100); - /** non-null; instance corresponding to the class Object */ + /** {@code non-null;} instance corresponding to the class {@code Object} */ public static final CstType OBJECT = intern(Type.OBJECT); - /** non-null; instance corresponding to the class Boolean */ + /** {@code non-null;} instance corresponding to the class {@code Boolean} */ public static final CstType BOOLEAN = intern(Type.BOOLEAN_CLASS); - /** non-null; instance corresponding to the class Byte */ + /** {@code non-null;} instance corresponding to the class {@code Byte} */ public static final CstType BYTE = intern(Type.BYTE_CLASS); - /** non-null; instance corresponding to the class Character */ + /** {@code non-null;} instance corresponding to the class {@code Character} */ public static final CstType CHARACTER = intern(Type.CHARACTER_CLASS); - /** non-null; instance corresponding to the class Double */ + /** {@code non-null;} instance corresponding to the class {@code Double} */ public static final CstType DOUBLE = intern(Type.DOUBLE_CLASS); - /** non-null; instance corresponding to the class Float */ + /** {@code non-null;} instance corresponding to the class {@code Float} */ public static final CstType FLOAT = intern(Type.FLOAT_CLASS); - /** non-null; instance corresponding to the class Long */ + /** {@code non-null;} instance corresponding to the class {@code Long} */ public static final CstType LONG = intern(Type.LONG_CLASS); - /** non-null; instance corresponding to the class Integer */ + /** {@code non-null;} instance corresponding to the class {@code Integer} */ public static final CstType INTEGER = intern(Type.INTEGER_CLASS); - /** non-null; instance corresponding to the class Short */ + /** {@code non-null;} instance corresponding to the class {@code Short} */ public static final CstType SHORT = intern(Type.SHORT_CLASS); - /** non-null; instance corresponding to the class Void */ + /** {@code non-null;} instance corresponding to the class {@code Void} */ public static final CstType VOID = intern(Type.VOID_CLASS); - /** non-null; instance corresponding to the type boolean[] */ + /** {@code non-null;} instance corresponding to the type {@code boolean[]} */ public static final CstType BOOLEAN_ARRAY = intern(Type.BOOLEAN_ARRAY); - /** non-null; instance corresponding to the type byte[] */ + /** {@code non-null;} instance corresponding to the type {@code byte[]} */ public static final CstType BYTE_ARRAY = intern(Type.BYTE_ARRAY); - /** non-null; instance corresponding to the type char[] */ + /** {@code non-null;} instance corresponding to the type {@code char[]} */ public static final CstType CHAR_ARRAY = intern(Type.CHAR_ARRAY); - /** non-null; instance corresponding to the type double[] */ + /** {@code non-null;} instance corresponding to the type {@code double[]} */ public static final CstType DOUBLE_ARRAY = intern(Type.DOUBLE_ARRAY); - /** non-null; instance corresponding to the type float[] */ + /** {@code non-null;} instance corresponding to the type {@code float[]} */ public static final CstType FLOAT_ARRAY = intern(Type.FLOAT_ARRAY); - /** non-null; instance corresponding to the type long[] */ + /** {@code non-null;} instance corresponding to the type {@code long[]} */ public static final CstType LONG_ARRAY = intern(Type.LONG_ARRAY); - /** non-null; instance corresponding to the type int[] */ + /** {@code non-null;} instance corresponding to the type {@code int[]} */ public static final CstType INT_ARRAY = intern(Type.INT_ARRAY); - /** non-null; instance corresponding to the type short[] */ + /** {@code non-null;} instance corresponding to the type {@code short[]} */ public static final CstType SHORT_ARRAY = intern(Type.SHORT_ARRAY); - /** non-null; the underlying type */ + /** {@code non-null;} the underlying type */ private final Type type; /** - * null-ok; the type descriptor corresponding to this instance, if + * {@code null-ok;} the type descriptor corresponding to this instance, if * calculated */ private CstUtf8 descriptor; @@ -95,10 +95,10 @@ public final class CstType extends TypedConstant { * 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 - * java.lang.Integer. + * {@code java.lang.Integer}. * - * @param primitiveType non-null; the primitive type - * @return non-null; the corresponding wrapper class + * @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()) { @@ -119,8 +119,8 @@ public final class CstType extends TypedConstant { /** * Returns an interned instance of this class for the given type. * - * @param type non-null; the underlying type - * @return non-null; an appropriately-constructed instance + * @param type {@code non-null;} the underlying type + * @return {@code non-null;} an appropriately-constructed instance */ public static CstType intern(Type type) { CstType cst = interns.get(type); @@ -136,7 +136,7 @@ public final class CstType extends TypedConstant { /** * Constructs an instance. * - * @param type non-null; the underlying type + * @param type {@code non-null;} the underlying type */ public CstType(Type type) { if (type == null) { @@ -207,9 +207,9 @@ public final class CstType extends TypedConstant { /** * Gets the underlying type (as opposed to the type corresponding * to this instance as a constant, which is always - * Class). + * {@code Class}). * - * @return non-null; the type corresponding to the name + * @return {@code non-null;} the type corresponding to the name */ public Type getClassType() { return type; @@ -218,7 +218,7 @@ public final class CstType extends TypedConstant { /** * Gets the type descriptor for this instance. * - * @return non-null; the descriptor + * @return {@code non-null;} the descriptor */ public CstUtf8 getDescriptor() { if (descriptor == null) { diff --git a/dx/src/com/android/dx/rop/cst/CstUtf8.java b/dx/src/com/android/dx/rop/cst/CstUtf8.java index f0ca5f5b9..2c7a1df83 100644 --- a/dx/src/com/android/dx/rop/cst/CstUtf8.java +++ b/dx/src/com/android/dx/rop/cst/CstUtf8.java @@ -20,19 +20,19 @@ import com.android.dx.util.ByteArray; import com.android.dx.util.Hex; /** - * Constants of type CONSTANT_Utf8_info. + * Constants of type {@code CONSTANT_Utf8_info}. */ public final class CstUtf8 extends Constant { /** - * non-null; instance representing "", that is, the + * {@code non-null;} instance representing {@code ""}, that is, the * empty string */ public static final CstUtf8 EMPTY_STRING = new CstUtf8(""); - /** non-null; the UTF-8 value as a string */ + /** {@code non-null;} the UTF-8 value as a string */ private final String string; - /** non-null; the UTF-8 value as bytes */ + /** {@code non-null;} the UTF-8 value as bytes */ private final ByteArray bytes; /** @@ -40,8 +40,8 @@ public final class CstUtf8 extends Constant { * differs from normal UTF-8 in the handling of character '\0' and * surrogate pairs. * - * @param string non-null; the string to convert - * @return non-null; the UTF-8 bytes for it + * @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(); @@ -73,8 +73,8 @@ public final class CstUtf8 extends Constant { /** * Converts an array of UTF-8 bytes into a string. * - * @param bytes non-null; the bytes to convert - * @return non-null; the converted 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(); @@ -173,9 +173,9 @@ public final class CstUtf8 extends Constant { } /** - * Constructs an instance from a String. + * Constructs an instance from a {@code String}. * - * @param string non-null; the UTF-8 value as a string + * @param string {@code non-null;} the UTF-8 value as a string */ public CstUtf8(String string) { if (string == null) { @@ -189,7 +189,7 @@ public final class CstUtf8 extends Constant { /** * Constructs an instance from some UTF-8 bytes. * - * @param bytes non-null; array of the UTF-8 bytes + * @param bytes {@code non-null;} array of the UTF-8 bytes */ public CstUtf8(ByteArray bytes) { if (bytes == null) { @@ -299,7 +299,7 @@ public final class CstUtf8 extends Constant { * Gets the value as a human-oriented string, surrounded by double * quotes. * - * @return non-null; the quoted string + * @return {@code non-null;} the quoted string */ public String toQuoted() { return '\"' + toHuman() + '\"'; @@ -310,8 +310,8 @@ public final class CstUtf8 extends Constant { * quotes, but ellipsizes the result if it is longer than the given * maximum length * - * @param maxLength >= 5; the maximum length of the string to return - * @return non-null; the quoted string + * @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(); @@ -332,7 +332,7 @@ public final class CstUtf8 extends Constant { * Gets the UTF-8 value as a string. * The returned string is always already interned. * - * @return non-null; the UTF-8 value as a string + * @return {@code non-null;} the UTF-8 value as a string */ public String getString() { return string; @@ -341,7 +341,7 @@ public final class CstUtf8 extends Constant { /** * Gets the UTF-8 value as UTF-8 encoded bytes. * - * @return non-null; an array of the UTF-8 bytes + * @return {@code non-null;} an array of the UTF-8 bytes */ public ByteArray getBytes() { return bytes; @@ -351,7 +351,7 @@ public final class CstUtf8 extends Constant { * 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 >= 0; the UTF-8 size + * @return {@code >= 0;} the UTF-8 size */ public int getUtf8Size() { return bytes.size(); @@ -360,10 +360,10 @@ public final class CstUtf8 extends Constant { /** * 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 length of the - * Java String representation of this instance. + * instance. This is the same as the {@code length} of the + * Java {@code String} representation of this instance. * - * @return >= 0; the UTF-16 size + * @return {@code >= 0;} the UTF-16 size */ public int getUtf16Size() { return string.length(); diff --git a/dx/src/com/android/dx/rop/cst/StdConstantPool.java b/dx/src/com/android/dx/rop/cst/StdConstantPool.java index 69791020f..82c3ab760 100644 --- a/dx/src/com/android/dx/rop/cst/StdConstantPool.java +++ b/dx/src/com/android/dx/rop/cst/StdConstantPool.java @@ -26,16 +26,16 @@ import com.android.dx.util.MutabilityControl; */ public final class StdConstantPool extends MutabilityControl implements ConstantPool { - /** non-null; array of entries */ + /** {@code non-null;} array of entries */ private final Constant[] entries; /** - * Constructs an instance. All indices initially contain null. + * Constructs an instance. All indices initially contain {@code null}. * * @param size the size of the pool; this corresponds to the - * class file field constant_pool_count, and is in fact + * 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 0 is always invalid. + * as element {@code 0} is always invalid. */ public StdConstantPool(int size) { super(size > 1); @@ -90,8 +90,8 @@ public final class StdConstantPool /** * Sets the entry at the given index. * - * @param n >= 1, < size(); which entry - * @param cst null-ok; the constant to store + * @param n {@code >= 1, < size();} which entry + * @param cst {@code null-ok;} the constant to store */ public void set(int n, Constant cst) { throwIfImmutable(); diff --git a/dx/src/com/android/dx/rop/cst/TypedConstant.java b/dx/src/com/android/dx/rop/cst/TypedConstant.java index 54472b058..823d9c4fa 100644 --- a/dx/src/com/android/dx/rop/cst/TypedConstant.java +++ b/dx/src/com/android/dx/rop/cst/TypedConstant.java @@ -26,7 +26,7 @@ public abstract class TypedConstant /** * {@inheritDoc} * - * This implentation always returns this. + * This implentation always returns {@code this}. */ public final TypeBearer getFrameType() { return this; diff --git a/dx/src/com/android/dx/rop/cst/Zeroes.java b/dx/src/com/android/dx/rop/cst/Zeroes.java index 3379b6cc4..8bed657a2 100644 --- a/dx/src/com/android/dx/rop/cst/Zeroes.java +++ b/dx/src/com/android/dx/rop/cst/Zeroes.java @@ -30,10 +30,10 @@ public final class Zeroes { } /** - * Gets the "zero" (or null) value for the given type. + * Gets the "zero" (or {@code null}) value for the given type. * - * @param type non-null; the type in question - * @return non-null; its "zero" value + * @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()) { diff --git a/dx/src/com/android/dx/rop/package-info.java b/dx/src/com/android/dx/rop/package-info.java index 97fe9de73..aaf21eed3 100644 --- a/dx/src/com/android/dx/rop/package-info.java +++ b/dx/src/com/android/dx/rop/package-info.java @@ -19,7 +19,7 @@ package com.android.dx.rop; /** *

    An Introduction to Rop Form

    * - * This package contains classes associated with dx's Rop + * This package contains classes associated with dx's {@code Rop} * intermediate form.

    * * The Rop form is intended to represent the instructions and the control-flow @@ -33,16 +33,16 @@ package com.android.dx.rop; *

  • {@link BasicBlock} and its per-method container, {@link BasicBlockList}, * the representation of control flow elements. *
  • {@link Insn} and its subclasses along with its per-basic block - * container {@link InsnList}. Insn instances represent + * container {@link InsnList}. {@code Insn} instances represent * individual instructions in the abstract register machine. *
  • {@link RegisterSpec} and its container {@link RegisterSpecList}. A * register spec encodes register number, register width, type information, * and potentially local variable information as well for instruction sources * and results. *
  • {@link Rop} instances represent opcodes in the abstract machine. Many - * Rop instances are singletons defined in static fields in + * {@code Rop} instances are singletons defined in static fields in * {@link Rops}. The rest are constructed dynamically using static methods - * in Rops + * in {@code Rops} *
  • {@link RegOps} lists numeric constants for the opcodes *
  • {@link Constant} and its subclasses represent constant data values * that opcodes may refer to. @@ -62,8 +62,8 @@ package com.android.dx.rop; * bytecode. Blocks that don't originate directly from source bytecode have * labels generated for them in a mostly arbitrary order.

    * - * Blocks are referred to by their label, for the most part, because - * BasicBlock instances are immutable and thus any modification to + * Blocks are referred to by their label, for the most part, because + * {@code BasicBlock} instances are immutable and thus any modification to * the control flow graph or the instruction list results in replacement * instances (with identical labels) being created.

    * @@ -105,7 +105,7 @@ package com.android.dx.rop; * instruction where a catch block exists inside the current method for that * exception class. Since the only possible path is the exception path, only * the exception path (which cannot be a primary successor) is a successor. - * An example of this is shown in dx/tests/092-ssa-cfg-edge-cases. + * An example of this is shown in {@code dx/tests/092-ssa-cfg-edge-cases}. * *

    Rop Instructions

    * @@ -123,18 +123,18 @@ package com.android.dx.rop; * Rops#MOVE_RESULT move-result} or {@link Rops#MOVE_RESULT_PSEUDO * move-result-pseudo} instructions at the top of the primary successor block. * - * Only a single move-result or move-result-pseudo + * Only a single {@code move-result} or {@code move-result-pseudo} * may exist in any block and it must be exactly the first instruction in the * block. * - * A move-result instruction is used for the results of call-like - * instructions. If the value produced by a move-result is not + * A {@code move-result} instruction is used for the results of call-like + * instructions. If the value produced by a {@code move-result} is not * used by the method, it may be eliminated as dead code. * - * A move-result-pseudo instruction is used for the results of + * A {@code move-result-pseudo} instruction is used for the results of * non-call-like throwing instructions. It may never be considered dead code * since the final dex instruction will always indicate a result register. - * If a required move-result-pseudo instruction is not found + * If a required {@code move-result-pseudo} instruction is not found * during conversion to dex bytecode, an exception will be thrown. * *

    move-exception

    @@ -148,25 +148,25 @@ package com.android.dx.rop; *

    move-param

    * * A {@link RegOps.MOVE_PARAM move-param} instruction represents a method - * parameter. Every move-param instruction is a + * parameter. Every {@code move-param} instruction is a * {@link PlainCstInsn}. The index of the method parameter they refer to is * carried as the {@link CstInteger integer constant} associated with the * instruction. * - * Any number of move-param instructions referring to the same + * Any number of {@code move-param} instructions referring to the same * parameter index may be included in a method's instruction lists. They * have no restrictions on placement beyond those of any other * {@link Rop.BRANCH_NONE} instruction. Note that the SSA optimizer arranges the * parameter assignments to align with the dex bytecode calling conventions. * With parameter assignments so arranged, the - * {@link com.android.dx.dex.code.RopTranslator} sees Rop move-param + * {@link com.android.dx.dex.code.RopTranslator} sees Rop {@code move-param} * instructions as unnecessary in dex form and eliminates them. * *

    mark-local

    * * A {@link RegOps.MARK_LOCAL mark-local} instruction indicates that a local * variable becomes live in a specified register specified register for the - * purposes of debug information. A mark-local instruction has + * purposes of debug information. A {@code mark-local} instruction has * a single source (the register which will now be considered a local variable) * and no results. The instruction has no side effect.

    * @@ -179,23 +179,22 @@ package com.android.dx.rop; * an assignment occurring. A common example of this is occurs in the Rop * representation of the following code:

    * - * + *

      * try {
      *     Object foo = null;
      *     foo = new Object();
    - * } catch (Throwable ex) {
    - * }
    - * 
    - *
    - * An object's initialization occurs in two steps. First, a new-instance
    - *  instruction is executed, whose result is stored in a register.
    - * However, that register can not yet be considered to contain "foo". That's
    - * because the instance's constructor method must be called via an
    - * invoke instruction. The constructor method, however, may
    + * } catch (Throwable ex) { }
    + * 
    + * + * An object's initialization occurs in two steps. First, a + * {@code new-instance} instruction is executed, whose result is stored in a + * register. However, that register can not yet be considered to contain + * "foo". That's because the instance's constructor method must be called + * via an {@code invoke} instruction. The constructor method, however, may * throw an exception. And if an exception occurs, then "foo" should remain - * null. So "foo" becomes the value of the result of the new-instance - * instruction after the (void) constructor method is invoked and - * returns successfully. In such a case, a mark-local will + * null. So "foo" becomes the value of the result of the {@code new-instance} + * instruction after the (void) constructor method is invoked and + * returns successfully. In such a case, a {@code mark-local} will * typically occur at the beginning of the primary successor block following * the invocation to the constructor. */ diff --git a/dx/src/com/android/dx/rop/type/Prototype.java b/dx/src/com/android/dx/rop/type/Prototype.java index a6ee74252..7e6ab59d5 100644 --- a/dx/src/com/android/dx/rop/type/Prototype.java +++ b/dx/src/com/android/dx/rop/type/Prototype.java @@ -21,23 +21,23 @@ import java.util.HashMap; /** * Representation of a method decriptor. Instances of this class are * generally interned and may be usefully compared with each other - * using ==. + * using {@code ==}. */ public final class Prototype implements Comparable { - /** non-null; intern table mapping string descriptors to instances */ + /** {@code non-null;} intern table mapping string descriptors to instances */ private static final HashMap internTable = new HashMap(500); - /** non-null; method descriptor */ + /** {@code non-null;} method descriptor */ private final String descriptor; - /** non-null; return type */ + /** {@code non-null;} return type */ private final Type returnType; - /** non-null; list of parameter types */ + /** {@code non-null;} list of parameter types */ private final StdTypeList parameterTypes; - /** null-ok; list of parameter frame types, if calculated */ + /** {@code null-ok;} list of parameter frame types, if calculated */ private StdTypeList parameterFrameTypes; /** @@ -45,8 +45,8 @@ public final class Prototype implements Comparable { * given method descriptor. See vmspec-2 sec4.3.3 for details on the * field descriptor syntax. * - * @param descriptor non-null; the descriptor - * @return non-null; the corresponding instance + * @param descriptor {@code non-null;} the descriptor + * @return {@code non-null;} the corresponding instance * @throws IllegalArgumentException thrown if the descriptor has * invalid syntax */ @@ -111,8 +111,8 @@ public final class Prototype implements Comparable { * that there is a '(' at the start of the descriptor and a * single ')' somewhere before the end. * - * @param descriptor non-null; the descriptor string - * @return non-null; array large enough to hold all parsed parameter + * @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) { @@ -153,14 +153,14 @@ public final class Prototype implements Comparable { /** * 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 definer + * method has an uninitialized object of type {@code definer} * as its first argument. * - * @param descriptor non-null; the descriptor string - * @param definer non-null; class the method is defined on + * @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 non-null; the interned instance + * @return {@code non-null;} the interned instance */ public static Prototype intern(String descriptor, Type definer, boolean isStatic, boolean isInit) { @@ -179,11 +179,11 @@ public final class Prototype implements Comparable { /** * Interns an instance which consists of the given number of - * ints along with the given return type + * {@code int}s along with the given return type * - * @param returnType non-null; the return type - * @param count > 0; the number of elements in the prototype - * @return non-null; the interned instance + * @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... @@ -207,7 +207,7 @@ public final class Prototype implements Comparable { * Constructs an instance. This is a private constructor; use one * of the public static methods to get instances. * - * @param descriptor non-null; the descriptor string + * @param descriptor {@code non-null;} the descriptor string */ private Prototype(String descriptor, Type returnType, StdTypeList parameterTypes) { @@ -304,7 +304,7 @@ public final class Prototype implements Comparable { /** * Gets the descriptor string. * - * @return non-null; the descriptor + * @return {@code non-null;} the descriptor */ public String getDescriptor() { return descriptor; @@ -313,7 +313,7 @@ public final class Prototype implements Comparable { /** * Gets the return type. * - * @return non-null; the return type + * @return {@code non-null;} the return type */ public Type getReturnType() { return returnType; @@ -322,7 +322,7 @@ public final class Prototype implements Comparable { /** * Gets the list of parameter types. * - * @return non-null; the list of parameter types + * @return {@code non-null;} the list of parameter types */ public StdTypeList getParameterTypes() { return parameterTypes; @@ -334,7 +334,7 @@ public final class Prototype implements Comparable { * "intlike" types (see {@link Type#isIntlike}) are replaced by * {@link Type#INT}. * - * @return non-null; the list of parameter frame types + * @return {@code non-null;} the list of parameter frame types */ public StdTypeList getParameterFrameTypes() { if (parameterFrameTypes == null) { @@ -360,8 +360,8 @@ public final class Prototype implements Comparable { * except that it has an additional parameter prepended to the original's * argument list. * - * @param param non-null; the new first parameter - * @return non-null; an appropriately-constructed instance + * @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); @@ -380,8 +380,8 @@ public final class Prototype implements Comparable { * there. If a conflicting value is already in the table, then leave it. * Return the interned value. * - * @param desc non-null; instance to make interned - * @return non-null; the actual interned object + * @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) { diff --git a/dx/src/com/android/dx/rop/type/StdTypeList.java b/dx/src/com/android/dx/rop/type/StdTypeList.java index a4c2d44b0..a02381237 100644 --- a/dx/src/com/android/dx/rop/type/StdTypeList.java +++ b/dx/src/com/android/dx/rop/type/StdTypeList.java @@ -23,149 +23,149 @@ import com.android.dx.util.FixedSizeList; */ public final class StdTypeList extends FixedSizeList implements TypeList { - /** non-null; no-element instance */ + /** {@code non-null;} no-element instance */ public static final StdTypeList EMPTY = new StdTypeList(0); - /** non-null; the list [int] */ + /** {@code non-null;} the list {@code [int]} */ public static final StdTypeList INT = StdTypeList.make(Type.INT); - /** non-null; the list [long] */ + /** {@code non-null;} the list {@code [long]} */ public static final StdTypeList LONG = StdTypeList.make(Type.LONG); - /** non-null; the list [float] */ + /** {@code non-null;} the list {@code [float]} */ public static final StdTypeList FLOAT = StdTypeList.make(Type.FLOAT); - /** non-null; the list [double] */ + /** {@code non-null;} the list {@code [double]} */ public static final StdTypeList DOUBLE = StdTypeList.make(Type.DOUBLE); - /** non-null; the list [Object] */ + /** {@code non-null;} the list {@code [Object]} */ public static final StdTypeList OBJECT = StdTypeList.make(Type.OBJECT); - /** non-null; the list [ReturnAddress] */ + /** {@code non-null;} the list {@code [ReturnAddress]} */ public static final StdTypeList RETURN_ADDRESS = StdTypeList.make(Type.RETURN_ADDRESS); - /** non-null; the list [Throwable] */ + /** {@code non-null;} the list {@code [Throwable]} */ public static final StdTypeList THROWABLE = StdTypeList.make(Type.THROWABLE); - /** non-null; the list [int, int] */ + /** {@code non-null;} the list {@code [int, int]} */ public static final StdTypeList INT_INT = StdTypeList.make(Type.INT, Type.INT); - /** non-null; the list [long, long] */ + /** {@code non-null;} the list {@code [long, long]} */ public static final StdTypeList LONG_LONG = StdTypeList.make(Type.LONG, Type.LONG); - /** non-null; the list [float, float] */ + /** {@code non-null;} the list {@code [float, float]} */ public static final StdTypeList FLOAT_FLOAT = StdTypeList.make(Type.FLOAT, Type.FLOAT); - /** non-null; the list [double, double] */ + /** {@code non-null;} the list {@code [double, double]} */ public static final StdTypeList DOUBLE_DOUBLE = StdTypeList.make(Type.DOUBLE, Type.DOUBLE); - /** non-null; the list [Object, Object] */ + /** {@code non-null;} the list {@code [Object, Object]} */ public static final StdTypeList OBJECT_OBJECT = StdTypeList.make(Type.OBJECT, Type.OBJECT); - /** non-null; the list [int, Object] */ + /** {@code non-null;} the list {@code [int, Object]} */ public static final StdTypeList INT_OBJECT = StdTypeList.make(Type.INT, Type.OBJECT); - /** non-null; the list [long, Object] */ + /** {@code non-null;} the list {@code [long, Object]} */ public static final StdTypeList LONG_OBJECT = StdTypeList.make(Type.LONG, Type.OBJECT); - /** non-null; the list [float, Object] */ + /** {@code non-null;} the list {@code [float, Object]} */ public static final StdTypeList FLOAT_OBJECT = StdTypeList.make(Type.FLOAT, Type.OBJECT); - /** non-null; the list [double, Object] */ + /** {@code non-null;} the list {@code [double, Object]} */ public static final StdTypeList DOUBLE_OBJECT = StdTypeList.make(Type.DOUBLE, Type.OBJECT); - /** non-null; the list [long, int] */ + /** {@code non-null;} the list {@code [long, int]} */ public static final StdTypeList LONG_INT = StdTypeList.make(Type.LONG, Type.INT); - /** non-null; the list [int[], int] */ + /** {@code non-null;} the list {@code [int[], int]} */ public static final StdTypeList INTARR_INT = StdTypeList.make(Type.INT_ARRAY, Type.INT); - /** non-null; the list [long[], int] */ + /** {@code non-null;} the list {@code [long[], int]} */ public static final StdTypeList LONGARR_INT = StdTypeList.make(Type.LONG_ARRAY, Type.INT); - /** non-null; the list [float[], int] */ + /** {@code non-null;} the list {@code [float[], int]} */ public static final StdTypeList FLOATARR_INT = StdTypeList.make(Type.FLOAT_ARRAY, Type.INT); - /** non-null; the list [double[], int] */ + /** {@code non-null;} the list {@code [double[], int]} */ public static final StdTypeList DOUBLEARR_INT = StdTypeList.make(Type.DOUBLE_ARRAY, Type.INT); - /** non-null; the list [Object[], int] */ + /** {@code non-null;} the list {@code [Object[], int]} */ public static final StdTypeList OBJECTARR_INT = StdTypeList.make(Type.OBJECT_ARRAY, Type.INT); - /** non-null; the list [boolean[], int] */ + /** {@code non-null;} the list {@code [boolean[], int]} */ public static final StdTypeList BOOLEANARR_INT = StdTypeList.make(Type.BOOLEAN_ARRAY, Type.INT); - /** non-null; the list [byte[], int] */ + /** {@code non-null;} the list {@code [byte[], int]} */ public static final StdTypeList BYTEARR_INT = StdTypeList.make(Type.BYTE_ARRAY, Type.INT); - /** non-null; the list [char[], int] */ + /** {@code non-null;} the list {@code [char[], int]} */ public static final StdTypeList CHARARR_INT = StdTypeList.make(Type.CHAR_ARRAY, Type.INT); - /** non-null; the list [short[], int] */ + /** {@code non-null;} the list {@code [short[], int]} */ public static final StdTypeList SHORTARR_INT = StdTypeList.make(Type.SHORT_ARRAY, Type.INT); - /** non-null; the list [int, int[], 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); - /** non-null; the list [long, long[], 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); - /** non-null; the list [float, float[], 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); - /** non-null; the list [double, double[], 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); - /** non-null; the list [Object, Object[], 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); - /** non-null; the list [int, boolean[], 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); - /** non-null; the list [int, byte[], 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); - /** non-null; the list [int, char[], 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); - /** non-null; the list [int, short[], 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 non-null; the element - * @return non-null; an appropriately-constructed 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); @@ -176,9 +176,9 @@ public final class StdTypeList /** * Makes a two-element instance. * - * @param type0 non-null; the first element - * @param type1 non-null; the second element - * @return non-null; an appropriately-constructed instance + * @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); @@ -190,10 +190,10 @@ public final class StdTypeList /** * Makes a three-element instance. * - * @param type0 non-null; the first element - * @param type1 non-null; the second element - * @param type2 non-null; the third element - * @return non-null; an appropriately-constructed instance + * @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); @@ -206,11 +206,11 @@ public final class StdTypeList /** * Makes a four-element instance. * - * @param type0 non-null; the first element - * @param type1 non-null; the second element - * @param type2 non-null; the third element - * @param type3 non-null; the fourth element - * @return non-null; an appropriately-constructed instance + * @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) { @@ -227,8 +227,8 @@ public final class StdTypeList * is a static method so as to work on arbitrary {@link TypeList} * instances. * - * @param list non-null; the list to convert - * @return non-null; the human form + * @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(); @@ -254,8 +254,8 @@ public final class StdTypeList * is a static method so as to work on arbitrary {@link TypeList} * instances. * - * @param list non-null; the list to inspect - * @return non-null; the hash code + * @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(); @@ -273,8 +273,8 @@ public final class StdTypeList * is a static method so as to work on arbitrary {@link TypeList} * instances. * - * @param list1 non-null; one list to compare - * @param list2 non-null; another list to compare + * @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) { @@ -298,8 +298,8 @@ public final class StdTypeList * is a static method so as to work on arbitrary {@link TypeList} * instances. * - * @param list1 non-null; one list to compare - * @param list2 non-null; another list to compare + * @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) { @@ -324,7 +324,7 @@ public final class StdTypeList } /** - * Constructs an instance. All indices initially contain null. + * Constructs an instance. All indices initially contain {@code null}. * * @param size the size of the list */ @@ -366,10 +366,10 @@ public final class StdTypeList /** * 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 NullPointerException. + * will throw {@code NullPointerException}. * - * @param n >= 0, < size(); which element - * @return non-null; the indicated element + * @param n {@code >= 0, < size();} which element + * @return {@code non-null;} the indicated element */ public Type get(int n) { return (Type) get0(n); @@ -378,8 +378,8 @@ public final class StdTypeList /** * Sets the type at the given index. * - * @param n >= 0, < size(); which element - * @param type non-null; the type to store + * @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); @@ -390,8 +390,8 @@ public final class StdTypeList * except that it has an additional type prepended to the * original. * - * @param type non-null; the new first element - * @return non-null; an appropriately-constructed instance + * @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(); diff --git a/dx/src/com/android/dx/rop/type/Type.java b/dx/src/com/android/dx/rop/type/Type.java index 09ea2e26d..64c3c3069 100644 --- a/dx/src/com/android/dx/rop/type/Type.java +++ b/dx/src/com/android/dx/rop/type/Type.java @@ -24,41 +24,41 @@ import java.util.HashMap; * Representation of a value type, such as may appear in a field, in a * local, on a stack, or in a method descriptor. Instances of this * class are generally interned and may be usefully compared with each - * other using ==. + * other using {@code ==}. */ public final class Type implements TypeBearer, Comparable { - /** non-null; intern table mapping string descriptors to instances */ + /** {@code non-null;} intern table mapping string descriptors to instances */ private static final HashMap internTable = new HashMap(500); - /** basic type constant for void */ + /** basic type constant for {@code void} */ public static final int BT_VOID = 0; - /** basic type constant for boolean */ + /** basic type constant for {@code boolean} */ public static final int BT_BOOLEAN = 1; - /** basic type constant for byte */ + /** basic type constant for {@code byte} */ public static final int BT_BYTE = 2; - /** basic type constant for char */ + /** basic type constant for {@code char} */ public static final int BT_CHAR = 3; - /** basic type constant for double */ + /** basic type constant for {@code double} */ public static final int BT_DOUBLE = 4; - /** basic type constant for float */ + /** basic type constant for {@code float} */ public static final int BT_FLOAT = 5; - /** basic type constant for int */ + /** basic type constant for {@code int} */ public static final int BT_INT = 6; - /** basic type constant for long */ + /** basic type constant for {@code long} */ public static final int BT_LONG = 7; - /** basic type constant for short */ + /** basic type constant for {@code short} */ public static final int BT_SHORT = 8; - /** basic type constant for Object */ + /** basic type constant for {@code Object} */ public static final int BT_OBJECT = 9; /** basic type constant for a return address */ @@ -67,37 +67,37 @@ public final class Type implements TypeBearer, Comparable { /** count of basic type constants */ public static final int BT_COUNT = 11; - /** non-null; instance representing boolean */ + /** {@code non-null;} instance representing {@code boolean} */ public static final Type BOOLEAN = new Type("Z", BT_BOOLEAN); - /** non-null; instance representing byte */ + /** {@code non-null;} instance representing {@code byte} */ public static final Type BYTE = new Type("B", BT_BYTE); - /** non-null; instance representing char */ + /** {@code non-null;} instance representing {@code char} */ public static final Type CHAR = new Type("C", BT_CHAR); - /** non-null; instance representing double */ + /** {@code non-null;} instance representing {@code double} */ public static final Type DOUBLE = new Type("D", BT_DOUBLE); - /** non-null; instance representing float */ + /** {@code non-null;} instance representing {@code float} */ public static final Type FLOAT = new Type("F", BT_FLOAT); - /** non-null; instance representing int */ + /** {@code non-null;} instance representing {@code int} */ public static final Type INT = new Type("I", BT_INT); - /** non-null; instance representing long */ + /** {@code non-null;} instance representing {@code long} */ public static final Type LONG = new Type("J", BT_LONG); - /** non-null; instance representing short */ + /** {@code non-null;} instance representing {@code short} */ public static final Type SHORT = new Type("S", BT_SHORT); - /** non-null; instance representing void */ + /** {@code non-null;} instance representing {@code void} */ public static final Type VOID = new Type("V", BT_VOID); - /** non-null; instance representing a known-null */ + /** {@code non-null;} instance representing a known-{@code null} */ public static final Type KNOWN_NULL = new Type("", BT_OBJECT); - /** non-null; instance representing a subroutine return address */ + /** {@code non-null;} instance representing a subroutine return address */ public static final Type RETURN_ADDRESS = new Type("", BT_ADDR); static { @@ -120,158 +120,158 @@ public final class Type implements TypeBearer, Comparable { } /** - * non-null; instance representing - * java.lang.annotation.Annotation + * {@code non-null;} instance representing + * {@code java.lang.annotation.Annotation} */ public static final Type ANNOTATION = intern("Ljava/lang/annotation/Annotation;"); - /** non-null; instance representing java.lang.Class */ + /** {@code non-null;} instance representing {@code java.lang.Class} */ public static final Type CLASS = intern("Ljava/lang/Class;"); - /** non-null; instance representing java.lang.Cloneable */ + /** {@code non-null;} instance representing {@code java.lang.Cloneable} */ public static final Type CLONEABLE = intern("Ljava/lang/Cloneable;"); - /** non-null; instance representing java.lang.Object */ + /** {@code non-null;} instance representing {@code java.lang.Object} */ public static final Type OBJECT = intern("Ljava/lang/Object;"); - /** non-null; instance representing java.io.Serializable */ + /** {@code non-null;} instance representing {@code java.io.Serializable} */ public static final Type SERIALIZABLE = intern("Ljava/io/Serializable;"); - /** non-null; instance representing java.lang.String */ + /** {@code non-null;} instance representing {@code java.lang.String} */ public static final Type STRING = intern("Ljava/lang/String;"); - /** non-null; instance representing java.lang.Throwable */ + /** {@code non-null;} instance representing {@code java.lang.Throwable} */ public static final Type THROWABLE = intern("Ljava/lang/Throwable;"); /** - * non-null; instance representing java.lang.Boolean; the + * {@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;"); /** - * non-null; instance representing java.lang.Byte; the + * {@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;"); /** - * non-null; instance representing java.lang.Character; the + * {@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;"); /** - * non-null; instance representing java.lang.Double; the + * {@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;"); /** - * non-null; instance representing java.lang.Float; the + * {@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;"); /** - * non-null; instance representing java.lang.Integer; the + * {@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;"); /** - * non-null; instance representing java.lang.Long; the + * {@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;"); /** - * non-null; instance representing java.lang.Short; the + * {@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;"); /** - * non-null; instance representing java.lang.Void; the + * {@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;"); - /** non-null; instance representing boolean[] */ + /** {@code non-null;} instance representing {@code boolean[]} */ public static final Type BOOLEAN_ARRAY = BOOLEAN.getArrayType(); - /** non-null; instance representing byte[] */ + /** {@code non-null;} instance representing {@code byte[]} */ public static final Type BYTE_ARRAY = BYTE.getArrayType(); - /** non-null; instance representing char[] */ + /** {@code non-null;} instance representing {@code char[]} */ public static final Type CHAR_ARRAY = CHAR.getArrayType(); - /** non-null; instance representing double[] */ + /** {@code non-null;} instance representing {@code double[]} */ public static final Type DOUBLE_ARRAY = DOUBLE.getArrayType(); - /** non-null; instance representing float[] */ + /** {@code non-null;} instance representing {@code float[]} */ public static final Type FLOAT_ARRAY = FLOAT.getArrayType(); - /** non-null; instance representing int[] */ + /** {@code non-null;} instance representing {@code int[]} */ public static final Type INT_ARRAY = INT.getArrayType(); - /** non-null; instance representing long[] */ + /** {@code non-null;} instance representing {@code long[]} */ public static final Type LONG_ARRAY = LONG.getArrayType(); - /** non-null; instance representing Object[] */ + /** {@code non-null;} instance representing {@code Object[]} */ public static final Type OBJECT_ARRAY = OBJECT.getArrayType(); - /** non-null; instance representing short[] */ + /** {@code non-null;} instance representing {@code short[]} */ public static final Type SHORT_ARRAY = SHORT.getArrayType(); - /** non-null; field descriptor for the type */ + /** {@code non-null;} field descriptor for the type */ private final String descriptor; /** * basic type corresponding to this type; one of the - * BT_* constants + * {@code BT_*} constants */ private final int basicType; /** - * >= -1; for an uninitialized type, bytecode index that this - * instance was allocated at; Integer.MAX_VALUE if it - * was an incoming uninitialized instance; -1 if this + * {@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 inititialized instance */ private final int newAt; /** - * null-ok; the internal-form class name corresponding to this type, if - * calculated; only valid if this is a reference type and + * {@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; /** - * null-ok; the type corresponding to an array of this type, if + * {@code null-ok;} the type corresponding to an array of this type, if * calculated */ private Type arrayType; /** - * null-ok; the type corresponding to elements of this type, if - * calculated; only valid if this is an array type + * {@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; /** - * null-ok; the type corresponding to the initialized version of + * {@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; @@ -280,11 +280,11 @@ public final class Type implements TypeBearer, Comparable { * 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 not allow - * "V" (that is, type void) as a valid + * {@code "V"} (that is, type {@code void}) as a valid * descriptor. * - * @param descriptor non-null; the descriptor - * @return non-null; the corresponding instance + * @param descriptor {@code non-null;} the descriptor + * @return {@code non-null;} the corresponding instance * @throws IllegalArgumentException thrown if the descriptor has * invalid syntax */ @@ -362,12 +362,12 @@ public final class Type implements TypeBearer, Comparable { /** * Returns the unique instance corresponding to the type with the - * given descriptor, allowing "V" to return the type - * for void. Other than that one caveat, this method + * 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 non-null; the descriptor - * @return non-null; the corresponding instance + * @param descriptor {@code non-null;} the descriptor + * @return {@code non-null;} the corresponding instance * @throws IllegalArgumentException thrown if the descriptor has * invalid syntax */ @@ -388,12 +388,12 @@ public final class Type implements TypeBearer, Comparable { /** * Returns the unique instance corresponding to the type of the * class with the given name. Calling this method is equivalent to - * calling intern(name) if name begins - * with "[" and calling intern("L" + name + ";") + * calling {@code intern(name)} if {@code name} begins + * with {@code "["} and calling {@code intern("L" + name + ";")} * in all other cases. * - * @param name non-null; the name of the class whose type is desired - * @return non-null; the corresponding type + * @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 */ @@ -414,10 +414,10 @@ public final class Type implements TypeBearer, Comparable { * This is a private constructor; use one of the public static * methods to get instances. * - * @param descriptor non-null; the field descriptor for the type + * @param descriptor {@code non-null;} the field descriptor for the type * @param basicType basic type corresponding to this type; one of the - * BT_* constants - * @param newAt >= -1 allocation bytecode index + * {@code BT_*} constants + * @param newAt {@code >= -1;} allocation bytecode index */ private Type(String descriptor, int basicType, int newAt) { if (descriptor == null) { @@ -445,9 +445,9 @@ public final class Type implements TypeBearer, Comparable { * This is a private constructor; use one of the public static * methods to get instances. * - * @param descriptor non-null; the field descriptor for the type + * @param descriptor {@code non-null;} the field descriptor for the type * @param basicType basic type corresponding to this type; one of the - * BT_* constants + * {@code BT_*} constants */ private Type(String descriptor, int basicType) { this(descriptor, basicType, -1); @@ -560,7 +560,7 @@ public final class Type implements TypeBearer, Comparable { /** * Gets the descriptor. * - * @return non-null; the descriptor + * @return {@code non-null;} the descriptor */ public String getDescriptor() { return descriptor; @@ -572,7 +572,7 @@ public final class Type implements TypeBearer, Comparable { * normal reference type (that is, a reference type and * additionally not a return address). * - * @return non-null; the internal-form class name + * @return {@code non-null;} the internal-form class name */ public String getClassName() { if (className == null) { @@ -592,8 +592,8 @@ public final class Type implements TypeBearer, Comparable { } /** - * Gets the category. Most instances are category 1. long - * and double are the only category 2 types. + * Gets the category. Most instances are category 1. {@code long} + * and {@code double} are the only category 2 types. * * @see #isCategory1 * @see #isCategory2 @@ -649,7 +649,7 @@ public final class Type implements TypeBearer, Comparable { /** * 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 - * int. + * {@code int}. * * @return whether this type is "intlike" */ @@ -695,7 +695,7 @@ public final class Type implements TypeBearer, Comparable { * 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 - * getBasicType() == Type.BT_OBJECT. + * {@code getBasicType() == Type.BT_OBJECT}. * * @return whether this type is a normal reference type */ @@ -705,7 +705,7 @@ public final class Type implements TypeBearer, Comparable { /** * Gets whether this type is an array type. If this method returns - * true, then it is safe to use {@link #getComponentType} + * {@code true}, then it is safe to use {@link #getComponentType} * to determine the component type. * * @return whether this type is an array type @@ -726,7 +726,7 @@ public final class Type implements TypeBearer, Comparable { /** * Gets whether this type represents an uninitialized instance. An - * uninitialized instance is what one gets back from the new + * uninitialized instance is what one gets back from the {@code new} * opcode, and remains uninitialized until a valid constructor is * invoked on it. * @@ -738,12 +738,12 @@ public final class Type implements TypeBearer, Comparable { /** * Gets the bytecode index at which this uninitialized type was - * allocated. This returns Integer.MAX_VALUE if this + * allocated. This returns {@code Integer.MAX_VALUE} if this * type is an uninitialized incoming parameter (i.e., the - * this of an <init> method) or - * -1 if this type is in fact initialized. + * {@code this} of an {@code } method) or + * {@code -1} if this type is in fact initialized. * - * @return >= -1; the allocation bytecode index + * @return {@code >= -1;} the allocation bytecode index */ public int getNewAt() { return newAt; @@ -753,7 +753,7 @@ public final class Type implements TypeBearer, Comparable { * Gets the initialized type corresponding to this instance, but only * if this instance is in fact an uninitialized object type. * - * @return non-null; the initialized type + * @return {@code non-null;} the initialized type */ public Type getInitializedType() { if (initializedType == null) { @@ -767,7 +767,7 @@ public final class Type implements TypeBearer, Comparable { /** * Gets the type corresponding to an array of this type. * - * @return non-null; the array type + * @return {@code non-null;} the array type */ public Type getArrayType() { if (arrayType == null) { @@ -781,7 +781,7 @@ public final class Type implements TypeBearer, Comparable { * Gets the component type of this type. This method is only valid on * array types. * - * @return non-null; the component type + * @return {@code non-null;} the component type */ public Type getComponentType() { if (componentType == null) { @@ -800,8 +800,8 @@ public final class Type implements TypeBearer, Comparable { * it is indicated as uninitialized and allocated at the given bytecode * index. This instance must be an initialized object type. * - * @param newAt >= 0; the allocation bytecode index - * @return non-null; an appropriately-constructed instance + * @param newAt {@code >= 0;} the allocation bytecode index + * @return {@code non-null;} an appropriately-constructed instance */ public Type asUninitialized(int newAt) { if (newAt < 0) { @@ -838,8 +838,8 @@ public final class Type implements TypeBearer, Comparable { * there. If a conflicting value is already in the table, then leave it. * Return the interned value. * - * @param type non-null; instance to make interned - * @return non-null; the actual interned object + * @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) { diff --git a/dx/src/com/android/dx/rop/type/TypeBearer.java b/dx/src/com/android/dx/rop/type/TypeBearer.java index b9e4ea576..2f2f2749b 100644 --- a/dx/src/com/android/dx/rop/type/TypeBearer.java +++ b/dx/src/com/android/dx/rop/type/TypeBearer.java @@ -26,40 +26,40 @@ public interface TypeBearer /** * Gets the type associated with this instance. * - * @return non-null; the type + * @return {@code non-null;} the type */ public Type getType(); /** * Gets the frame type corresponding to this type. This method returns - * this, except if {@link Type#isIntlike} on the underlying - * type returns true but the underlying type is not in + * {@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 is INT. + * whose underlying type is {@code INT}. * - * @return non-null; the frame type for this instance + * @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 BT_* constants + * @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 getFrameType().getBasicType(), and - * is the same as calling getFrameType() unless 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 - * BT_INT. + * {@code BT_INT}. * * @see #getBasicType * @see #getFrameType * - * @return the basic frame type; one of the BT_* constants + * @return the basic frame type; one of the {@code BT_*} constants * defined by {@link Type} */ public int getBasicFrameType(); @@ -67,8 +67,8 @@ public interface TypeBearer /** * Returns whether this instance represents a constant value. * - * @return true if this instance represents a constant value - * and false if not + * @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/dx/rop/type/TypeList.java b/dx/src/com/android/dx/rop/type/TypeList.java index 0944fe271..e82cca7c2 100644 --- a/dx/src/com/android/dx/rop/type/TypeList.java +++ b/dx/src/com/android/dx/rop/type/TypeList.java @@ -22,29 +22,29 @@ package com.android.dx.rop.type; public interface TypeList { /** * Returns whether this instance is mutable. Note that the - * TypeList interface itself doesn't provide any + * {@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 true if this instance is mutable or - * false if it is immutable + * @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 >= 0; the size + * @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 NullPointerException. + * will throw {@code NullPointerException}. * - * @param n >= 0, < size(); which element - * @return non-null; the indicated element + * @param n {@code >= 0, < size();} which element + * @return {@code non-null;} the indicated element */ public Type getType(int n); @@ -53,7 +53,7 @@ public interface TypeList { * all the elements of this list. This is a sum of the widths (categories) * of all the elements. * - * @return >= 0; the required number of words + * @return {@code >= 0;} the required number of words */ public int getWordCount(); @@ -62,8 +62,8 @@ public interface TypeList { * the given item is appended to the end and it is guaranteed to be * immutable. * - * @param type non-null; item to append - * @return non-null; an appropriately-constructed instance + * @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/dx/ssa/BasicRegisterMapper.java b/dx/src/com/android/dx/ssa/BasicRegisterMapper.java index 86fcf8131..fdabaab1d 100644 --- a/dx/src/com/android/dx/ssa/BasicRegisterMapper.java +++ b/dx/src/com/android/dx/ssa/BasicRegisterMapper.java @@ -24,17 +24,16 @@ import com.android.dx.util.IntList; * This class maps one register space into another, with * each mapping built up individually and added via addMapping() */ -public class BasicRegisterMapper - extends RegisterMapper { - +public class BasicRegisterMapper extends RegisterMapper { /** indexed by old register, containing new name */ private IntList oldToNew; - /** Running count of used registers in new namespace */ + /** running count of used registers in new namespace */ private int runningCountNewRegisters; /** - * Creates a new OneToOneRegisterMapper + * Creates a new OneToOneRegisterMapper. + * * @param countOldRegisters the number of registers in the old name space */ public BasicRegisterMapper(int countOldRegisters) { @@ -70,15 +69,16 @@ public class BasicRegisterMapper /** * Returns the new-namespace mapping for the specified - * old-namespace register, or -1 if one exists + * old-namespace register, or -1 if one exists. * - * @param oldReg >=0; old-namespace register - * @return new-namespace register or -1 if none. + * @param oldReg {@code >= 0;} old-namespace register + * @return new-namespace register or -1 if none */ public int oldToNew(int oldReg) { - if(oldReg >= oldToNew.size()) { + if (oldReg >= oldToNew.size()) { return -1; } + return oldToNew.get(oldReg); } @@ -88,7 +88,8 @@ public class BasicRegisterMapper sb.append("Old\tNew\n"); int sz = oldToNew.size(); - for(int i = 0; i < sz; i++) { + + for (int i = 0; i < sz; i++) { sb.append(i); sb.append('\t'); sb.append(oldToNew.get(i)); @@ -104,12 +105,12 @@ public class BasicRegisterMapper } /** - * adds a mapping to the mapper. If oldReg has already been mapped, + * Adds a mapping to the mapper. If oldReg has already been mapped, * overwrites previous mapping with new mapping. * - * @param oldReg >=0 - * @param newReg >=0 - * @param category width of reg (1 or 2) + * @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()) { @@ -118,6 +119,7 @@ public class BasicRegisterMapper oldToNew.add(-1); } } + oldToNew.set(oldReg, newReg); if (runningCountNewRegisters < (newReg + category)) { diff --git a/dx/src/com/android/dx/ssa/ConstCollector.java b/dx/src/com/android/dx/ssa/ConstCollector.java index afdede7c2..03252d1a4 100644 --- a/dx/src/com/android/dx/ssa/ConstCollector.java +++ b/dx/src/com/android/dx/ssa/ConstCollector.java @@ -17,19 +17,20 @@ package com.android.dx.ssa; import com.android.dx.rop.code.*; -import com.android.dx.rop.type.TypeBearer; -import com.android.dx.rop.type.StdTypeList; -import com.android.dx.rop.type.Type; import com.android.dx.rop.cst.Constant; -import com.android.dx.rop.cst.TypedConstant; import com.android.dx.rop.cst.CstString; +import com.android.dx.rop.cst.TypedConstant; +import com.android.dx.rop.type.StdTypeList; +import com.android.dx.rop.type.Type; +import com.android.dx.rop.type.TypeBearer; -import java.util.HashMap; import java.util.ArrayList; -import java.util.Iterator; import java.util.Collections; import java.util.Comparator; +import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; /** * Collects constants that are used more than once at the top of the @@ -37,7 +38,6 @@ import java.util.HashSet; * 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; @@ -60,18 +60,20 @@ public class ConstCollector { private final SsaMethod ssaMeth; /** - * Process a method. + * Processes a method. * - * @param ssaMethod non-null; method to process + * @param ssaMethod {@code non-null;} method to process */ public static void process(SsaMethod ssaMethod) { - ConstCollector dc; - - dc = new ConstCollector(ssaMethod); - - dc.run(); + 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; } @@ -111,9 +113,7 @@ public class ConstCollector { SsaBasicBlock successorBlock = entryBlock.getPrimarySuccessor(); - /* - * Insert a block containing the const insn - */ + // Insert a block containing the const insn. SsaBasicBlock constBlock = entryBlock.insertNewSuccessor(successorBlock); @@ -122,18 +122,17 @@ public class ConstCollector { RegisterSpecList.EMPTY, StdTypeList.EMPTY, cst)); - /* - * Insert a block containing the move-result-pseudo insn - */ + // 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( - new PlainInsn( - Rops.opMoveResultPseudo(result.getTypeBearer()), - SourcePosition.NO_INFO, - result, RegisterSpecList.EMPTY)); + resultBlock.addInsnToHead(insn); } newRegs.put(cst, result); @@ -147,7 +146,7 @@ public class ConstCollector { * sorted by most used first. Skips non-collectable consts, such as * non-string object constants * - * @return non-null; list of constants in most-to-least used order + * @return {@code non-null;} list of constants in most-to-least used order */ private ArrayList getConstsSortedByCountUse() { int regSz = ssaMeth.getRegCount(); @@ -155,20 +154,21 @@ public class ConstCollector { final HashMap countUses = new HashMap(); - // Each collected constant can be used by just one local - // (used only if COLLECT_ONE_LOCAL is true) + /* + * Each collected constant can be used by just one local + * (used only if COLLECT_ONE_LOCAL is true). + */ final HashSet usedByLocal = new HashSet(); - // Count how many times each const value is used + // Count how many times each const value is used. for (int i = 0; i < regSz; i++) { SsaInsn insn = ssaMeth.getDefinitionForRegister(i); if (insn == null) continue; RegisterSpec result = insn.getResult(); - - TypeBearer typeBearer = insn.getResult().getTypeBearer(); + TypeBearer typeBearer = result.getTypeBearer(); if (!typeBearer.isConstant()) continue; @@ -177,28 +177,30 @@ public class ConstCollector { if (insn.canThrow()) { /* * Don't move anything other than strings -- the risk - * of changing where an exception is thrown is too high. + * 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. + * 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 + /* + * 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 + // Count one local usage only. continue; } else { usedByLocal.add(cst); @@ -214,18 +216,15 @@ public class ConstCollector { } } - // Collect constants that have been reused - Iterator it = countUses.keySet().iterator(); + // Collect constants that have been reused. ArrayList constantList = new ArrayList(); - while (it.hasNext()) { - TypedConstant cst = it.next(); - - if (countUses.get(cst) > 1) { - constantList.add(cst); + for (Map.Entry entry : countUses.entrySet()) { + if (entry.getValue() > 1) { + constantList.add(entry.getKey()); } } - // Sort by use, with most used at the beginning of the list + // Sort by use, with most used at the beginning of the list. Collections.sort(constantList, new Comparator() { public int compare(Constant a, Constant b) { int ret; @@ -241,47 +240,48 @@ public class ConstCollector { return ret; } + public boolean equals (Object obj) { return obj == this; } }); + return constantList; } /** * Inserts mark-locals if necessary when changing a register. If - * the definition of origReg is associated with a local - * variable, then insert a mark-local for newReg just below - * it. We expect the definition of origReg to ultimately + * 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 non-null; original register - * @param newReg non-null; new register that will replace - * origReg + * @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())) { + 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 + /* + * This is a mark-local. it will be updated when all uses + * are updated. + */ continue; } LocalItem local = localAssignment.getLocalItem(); - /* - * un-associate original use - */ + // Un-associate original use. use.setResultLocal(null); - /* - * now add a mark-local to the new reg immediately after - */ + // Now add a mark-local to the new reg immediately after. newReg = newReg.withLocalItem(local); SsaInsn newInsn @@ -301,15 +301,17 @@ public class ConstCollector { * Updates all uses of various consts to use the values in the newly * assigned registers. * - * @param newRegs non-null; mapping between constant and new reg - * @param origRegCount >=0; original SSA reg count, not including + * @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 newRegs, int origRegCount) { - // Set of constants associated with a local variable - // Used only if COLLECT_ONE_LOCAL is true + /* + * set of constants associated with a local variable; used + * only if COLLECT_ONE_LOCAL is true. + */ final HashSet usedByLocal = new HashSet(); @@ -338,8 +340,11 @@ public class ConstCollector { 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 + /* + * 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 { @@ -349,7 +354,7 @@ public class ConstCollector { } } - // Maps an original const register to the new collected register + // maps an original const register to the new collected register RegisterMapper mapper = new RegisterMapper() { @Override public int getNewRegisterCount() { @@ -359,14 +364,15 @@ public class ConstCollector { @Override public RegisterSpec map(RegisterSpec registerSpec) { if (registerSpec.getReg() == origReg.getReg()) { - return newReg.withLocalItem(registerSpec.getLocalItem()); + return newReg.withLocalItem( + registerSpec.getLocalItem()); } return registerSpec; } }; - for (SsaInsn use: useList[origReg.getReg()]) { + for (SsaInsn use : useList[origReg.getReg()]) { if (use.canThrow() && use.getBlock().getSuccessors().cardinality() > 1) { continue; diff --git a/dx/src/com/android/dx/ssa/DeadCodeRemover.java b/dx/src/com/android/dx/ssa/DeadCodeRemover.java index 4fded44aa..ec960b824 100644 --- a/dx/src/com/android/dx/ssa/DeadCodeRemover.java +++ b/dx/src/com/android/dx/ssa/DeadCodeRemover.java @@ -36,55 +36,55 @@ import java.util.HashSet; * block to entry block. */ public class DeadCodeRemover { - /** method we're processing */ - private SsaMethod ssaMeth; + private final SsaMethod ssaMeth; + /** ssaMeth.getRegCount() */ - private int regCount; + private final int regCount; /** * indexed by register: whether reg should be examined * (does it correspond to a no-side-effect insn?) */ - private BitSet worklist; + private final BitSet worklist; /** use list indexed by register; modified during operation */ - private ArrayList[] useList; + private final ArrayList[] useList; /** * Process a method with the dead-code remver + * * @param ssaMethod method to process */ public static void process(SsaMethod ssaMethod) { - DeadCodeRemover dc; - - dc = new DeadCodeRemover(ssaMethod); - + 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(); } /** - * Run the dead code remover + * Runs the dead code remover. */ private void run() { - HashSet deletedInsns = (HashSet) new HashSet(); ssaMeth.forEachInsn(new NoSideEffectVisitor(worklist)); int regV; - while ( 0 <= (regV = worklist.nextSetBit(0)) ) { + while ( 0 <= (regV = worklist.nextSetBit(0)) ) { worklist.clear(regV); if (useList[regV].size() == 0 @@ -92,7 +92,7 @@ public class DeadCodeRemover { SsaInsn insnS = ssaMeth.getDefinitionForRegister(regV); - // This insn has already been deleted + // This insn has already been deleted. if (deletedInsns.contains(insnS)) { continue; } @@ -101,8 +101,7 @@ public class DeadCodeRemover { int sz = sources.size(); for (int i = 0; i < sz; i++) { - - // Delete this insn from all usage lists + // Delete this insn from all usage lists. RegisterSpec source = sources.get(i); useList[source.getReg()].remove(insnS); @@ -110,14 +109,14 @@ public class DeadCodeRemover { ssaMeth.getDefinitionForRegister( source.getReg()))) { /* - * Only registers who's definition has no side effect - * should be added back to the worklist + * Only registers whose definition has no side effect + * should be added back to the worklist. */ worklist.set(source.getReg()); } } - // Schedule this insn for later deletion + // Schedule this insn for later deletion. deletedInsns.add(insnS); } } @@ -127,7 +126,8 @@ public class DeadCodeRemover { /** * Returns true if the only uses of this register form a circle of - * operations with no side effects + * 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 @@ -139,7 +139,7 @@ public class DeadCodeRemover { return true; } - for (SsaInsn use: useList[regV]) { + for (SsaInsn use : useList[regV]) { if (hasSideEffect(use)) { return false; } @@ -152,7 +152,7 @@ public class DeadCodeRemover { // This register is only used in operations that have no side effect. set.set(regV); - for (SsaInsn use: useList[regV]) { + for (SsaInsn use : useList[regV]) { RegisterSpec result = use.getResult(); if (result == null @@ -167,13 +167,14 @@ public class DeadCodeRemover { /** * Returns true if this insn has a side-effect. Returns true * if the insn is null for reasons stated in the code block. - * @param insn null-ok; instruction in question + * + * @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 + /* While false would seem to make more sense here, true + * prevents us from adding this back to a worklist unnecessarally. */ return true; } @@ -185,7 +186,7 @@ public class DeadCodeRemover { * A callback class used to build up the initial worklist of * registers defined by an instruction with no side effect. */ - static class NoSideEffectVisitor implements SsaInsn.Visitor { + static private class NoSideEffectVisitor implements SsaInsn.Visitor { BitSet noSideEffectRegs; /** @@ -195,13 +196,13 @@ public class DeadCodeRemover { * @param noSideEffectRegs to-build bitset of regs that are * results of regs with no side effects */ - NoSideEffectVisitor(BitSet noSideEffectRegs) { + 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 we're tracking local vars, some moves have side effects. if (!hasSideEffect(insn)) { noSideEffectRegs.set(insn.getResult().getReg()); } @@ -209,7 +210,7 @@ public class DeadCodeRemover { /** {@inheritDoc} */ public void visitPhiInsn (PhiInsn phi) { - // If we're tracking local vars, then some phis have side effects + // If we're tracking local vars, then some phis have side effects. if (!hasSideEffect(phi)) { noSideEffectRegs.set(phi.getResult().getReg()); } diff --git a/dx/src/com/android/dx/ssa/DomFront.java b/dx/src/com/android/dx/ssa/DomFront.java index ea089ec13..3005015b1 100644 --- a/dx/src/com/android/dx/ssa/DomFront.java +++ b/dx/src/com/android/dx/ssa/DomFront.java @@ -30,27 +30,34 @@ import java.util.BitSet; * Harvey, and Kennedy; transliterated to Java. */ public class DomFront { + /** local debug flag */ private static boolean DEBUG = false; + /** {@code non-null;} method being processed */ private final SsaMethod meth; + private final ArrayList nodes; + private final DomInfo[] domInfos; /** * Dominance-frontier information for a single basic block. */ public static class DomInfo { - /** non-null; the dominance frontier set indexed by block index */ - IntSet dominanceFrontiers; - /** >= 0 after run(); the index of the immediate dominator */ - int idom = -1; - /** depth-first traversal index */ - int traversalIndex; + /** + * {@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; } /** * Constructs instance. Call {@link DomFront#run} to process. - * @param meth + * + * @param meth {@code non-null;} method to process */ public DomFront(SsaMethod meth) { this.meth = meth; @@ -67,7 +74,7 @@ public class DomFront { /** * Calculates the dominance frontier information for the method. * - * @return non-null; an array of DomInfo structures + * @return {@code non-null;} an array of DomInfo structures */ public DomInfo[] run() { int szNodes = nodes.size(); @@ -80,8 +87,7 @@ public class DomFront { } } - Dominators methDom = new Dominators(domInfos, false); - methDom.run(meth); + Dominators methDom = Dominators.make(meth, domInfos, false); if (DEBUG) { for (int i = 0; i < szNodes; i++) { @@ -123,7 +129,7 @@ public class DomFront { sb.append('{'); boolean comma = false; - for (SsaBasicBlock child: node.getDomChildren()) { + for (SsaBasicBlock child : node.getDomChildren()) { if (comma) { sb.append(','); } @@ -164,20 +170,25 @@ public class DomFront { SsaBasicBlock nb = nodes.get(b); DomInfo nbInfo = domInfos[b]; BitSet pred = nb.getPredecessors(); + if (pred.cardinality() > 1) { for (int i = pred.nextSetBit(0); i >= 0; i = pred.nextSetBit(i + 1)) { - for(int runnerIndex = i - ; runnerIndex != nbInfo.idom - ;) { - // We can stop if we hit a block we already - // added label to, since we must be at a part - // of the dom tree we have seen before. + 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. + */ DomInfo runnerInfo = domInfos[runnerIndex]; - if (runnerInfo.dominanceFrontiers.has(b)) + + if (runnerInfo.dominanceFrontiers.has(b)) { break; - // "add b to runner's dominance frontier set" + } + + // Add b to runner's dominance frontier set. runnerInfo.dominanceFrontiers.add(b); runnerIndex = runnerInfo.idom; } diff --git a/dx/src/com/android/dx/ssa/Dominators.java b/dx/src/com/android/dx/ssa/Dominators.java index 1af2cbc88..f7d7da6f2 100644 --- a/dx/src/com/android/dx/ssa/Dominators.java +++ b/dx/src/com/android/dx/ssa/Dominators.java @@ -41,31 +41,55 @@ 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 boolean postdom; + /* 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 ArrayList blocks; + private final ArrayList blocks; - private static final class DFSInfo { - int semidom; - SsaBasicBlock parent; - // rep(resentative) is known as "label" in the paper. It is the node - // that our block's DFS info has been unioned to. - SsaBasicBlock rep; - SsaBasicBlock ancestor; - ArrayList bucket; + /** indexed by basic block index */ + private final DFSInfo[] info; - public DFSInfo() { - bucket = new ArrayList(); - } + private final ArrayList 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(); } - /** Indexed by basic block index */ - private DFSInfo[] info; - private ArrayList vertex; + /** + * 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); - private DomFront.DomInfo domInfos[]; + result.run(); + return result; + } private BitSet getSuccs(SsaBasicBlock block) { if (postdom) { @@ -85,6 +109,7 @@ public final class Dominators { /** * Performs path compress on the DFS info. + * * @param in Basic block whose DFS info we are path compressing. */ private void compress(SsaBasicBlock in) { @@ -110,7 +135,7 @@ public final class Dominators { } worklist.remove(wsize - 1); - // Update based on ancestor info + // Update based on ancestor info. if (vabbInfo.ancestor == null) { continue; } @@ -124,42 +149,25 @@ public final class Dominators { } } } + private SsaBasicBlock eval(SsaBasicBlock v) { DFSInfo bbInfo = info[v.getIndex()]; + if (bbInfo.ancestor == null) { return v; } + compress(v); return bbInfo.rep; } /** - * Callback for depth-first walk through control flow graph (either - * from the entry block or the exit block). Records the traversal order - * in the infolist. - */ - private class DfsWalker implements SsaBasicBlock.Visitor { - int dfsNum = 0; - - public void visitBlock (SsaBasicBlock v, SsaBasicBlock parent) { - DFSInfo bbInfo = new DFSInfo(); - bbInfo.semidom = ++dfsNum; - bbInfo.rep = v; - bbInfo.parent = parent; - vertex.add(v); - info[v.getIndex()] = bbInfo; - } - } - - /** - * Performs dominator/post-dominator calculation for the control flow graph. - * @param meth Method to analyze + * Performs dominator/post-dominator calculation for the control + * flow graph. + * + * @param meth {@code non-null;} method to analyze */ - public void run(SsaMethod meth) { - - this.blocks = meth.getBlocks(); - this.info = new DFSInfo[blocks.size() + 2]; - this.vertex = new ArrayList(); + private void run() { SsaBasicBlock root = postdom ? meth.getExitBlock() : meth.getEntryBlock(); @@ -168,8 +176,10 @@ public final class Dominators { domInfos[root.getIndex()].idom = root.getIndex(); } - // First we perform a DFS numbering of the blocks, by numbering the dfs - // tree roots + /* + * First we perform a DFS numbering of the blocks, by + * numbering the dfs tree roots. + */ DfsWalker walker = new DfsWalker(); meth.forEachBlockDepthFirst(postdom, walker); @@ -184,12 +194,15 @@ public final class Dominators { BitSet preds = getPreds(w); for (int j = preds.nextSetBit(0); - j >= 0; - j = preds.nextSetBit(j + 1)) { + 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 + + /* + * 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) { @@ -199,11 +212,14 @@ public final class Dominators { } info[vertex.get(wInfo.semidom).getIndex()].bucket.add(w); - // Normally we would call link here, but in our m log n - // implementation this is equivalent to the following single line + /* + * 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 + // Implicity define idom for each vertex. ArrayList wParentBucket; wParentBucket = info[wInfo.parent.getIndex()].bucket; @@ -219,6 +235,7 @@ public final class Dominators { } } } + // Now explicitly define the immediate dominator of each vertex for (int i = 2; i <= dfsMax; ++i) { SsaBasicBlock w = vertex.get(i); @@ -231,10 +248,38 @@ public final class Dominators { } /** - * @param postdom true for postdom information, false for normal dom info + * 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. */ - public Dominators(DomFront.DomInfo[] domInfos, boolean postdom) { - this.domInfos = domInfos; - this.postdom = postdom; + 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; + } + } + + 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; + + public SsaBasicBlock ancestor; + public ArrayList bucket; + + public DFSInfo() { + bucket = new ArrayList(); + } } } diff --git a/dx/src/com/android/dx/ssa/InterferenceRegisterMapper.java b/dx/src/com/android/dx/ssa/InterferenceRegisterMapper.java index be678ddde..392579d88 100644 --- a/dx/src/com/android/dx/ssa/InterferenceRegisterMapper.java +++ b/dx/src/com/android/dx/ssa/InterferenceRegisterMapper.java @@ -33,7 +33,6 @@ 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 @@ -45,16 +44,15 @@ public class InterferenceRegisterMapper extends BasicRegisterMapper { */ private final ArrayList newRegInterference; - /** - * The interference graph for the old namespace - */ + /** the interference graph for the old namespace */ private final InterferenceGraph oldRegInterference; /** - * @param countOldRegisters number of registers in old namespace. + * Constructs an instance + * + * @param countOldRegisters number of registers in old namespace */ - public InterferenceRegisterMapper( - InterferenceGraph oldRegInterference, + public InterferenceRegisterMapper(InterferenceGraph oldRegInterference, int countOldRegisters) { super(countOldRegisters); @@ -75,8 +73,8 @@ public class InterferenceRegisterMapper extends BasicRegisterMapper { } /** - * Checks to see if old namespace reg oldReg interferes - * with what currently maps to newReg. + * 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 @@ -101,10 +99,10 @@ public class InterferenceRegisterMapper extends BasicRegisterMapper { } /** - * Checks to see if old namespace reg oldReg interferes - * with what currently maps to newReg. + * Checks to see if old namespace reg {@code oldReg} interferes + * with what currently maps to {@code newReg}. * - * @param oldSpec non-null; old namespace register + * @param oldSpec {@code non-null;} old namespace register * @param newReg new namespace register * @return true if oldReg will interfere with newReg */ @@ -115,6 +113,7 @@ public class InterferenceRegisterMapper extends BasicRegisterMapper { /** * 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 */ @@ -134,17 +133,17 @@ public class InterferenceRegisterMapper extends BasicRegisterMapper { * pinned to the specified new-namespace reg + category. Takes into * account the category of the old-namespace registers. * - * @param oldSpecs non-null; set of old-namespace regs - * @param newReg >= 0 new-namespace register - * @param targetCategory 1 or 2; the number of adjacent new-namespace + * @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()); @@ -159,6 +158,7 @@ public class InterferenceRegisterMapper extends BasicRegisterMapper { return true; } } + return false; } } diff --git a/dx/src/com/android/dx/ssa/LiteralOpUpgrader.java b/dx/src/com/android/dx/ssa/LiteralOpUpgrader.java index ad10cd75d..a70b5bb29 100644 --- a/dx/src/com/android/dx/ssa/LiteralOpUpgrader.java +++ b/dx/src/com/android/dx/ssa/LiteralOpUpgrader.java @@ -42,7 +42,7 @@ public class LiteralOpUpgrader { /** * Process a method. * - * @param ssaMethod non-null; method to process + * @param ssaMethod {@code non-null;} method to process */ public static void process(SsaMethod ssaMethod) { LiteralOpUpgrader dc; @@ -135,8 +135,8 @@ public class LiteralOpUpgrader { * * TODO move this somewhere else. * - * @param insn non-null; an SsaInsn containing a PlainInsn - * @param newSources non-null; new sources list for new insn + * @param 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} */ private void replacePlainInsn(NormalSsaInsn insn, diff --git a/dx/src/com/android/dx/ssa/LocalVariableExtractor.java b/dx/src/com/android/dx/ssa/LocalVariableExtractor.java index 21c306b7e..11d53cf2a 100644 --- a/dx/src/com/android/dx/ssa/LocalVariableExtractor.java +++ b/dx/src/com/android/dx/ssa/LocalVariableExtractor.java @@ -33,23 +33,23 @@ import java.util.List; * converted, and adapted through edge-splitting. */ public class LocalVariableExtractor { - /** non-null; method being extracted from */ + /** {@code non-null;} method being extracted from */ private final SsaMethod method; - /** non-null; block list for the method */ + /** {@code non-null;} block list for the method */ private final ArrayList blocks; - /** non-null; result in-progress */ + /** {@code non-null;} result in-progress */ private final LocalVariableInfo resultInfo; - /** non-null; work set indicating blocks needing to be processed */ + /** {@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 non-null; the method to extract from - * @return non-null; the extracted information + * @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); @@ -59,7 +59,7 @@ public class LocalVariableExtractor { /** * Constructs an instance. This method is private. Use {@link #extract}. * - * @param method non-null; the method to extract from + * @param method {@code non-null;} the method to extract from */ private LocalVariableExtractor(SsaMethod method) { if (method == null) { @@ -77,7 +77,7 @@ public class LocalVariableExtractor { /** * Does the extraction. * - * @return non-null; the extracted information + * @return {@code non-null;} the extracted information */ private LocalVariableInfo doit() { @@ -98,7 +98,7 @@ public class LocalVariableExtractor { /** * Processes a single block. * - * @param blockIndex >= 0; block index of the block to process + * @param blockIndex {@code >= 0;} block index of the block to process */ private void processBlock(int blockIndex) { RegisterSpecSet primaryState diff --git a/dx/src/com/android/dx/ssa/LocalVariableInfo.java b/dx/src/com/android/dx/ssa/LocalVariableInfo.java index f7c37d24d..8845270c3 100644 --- a/dx/src/com/android/dx/ssa/LocalVariableInfo.java +++ b/dx/src/com/android/dx/ssa/LocalVariableInfo.java @@ -29,30 +29,30 @@ import java.util.List; * Stolen from {@link com.android.dx.rop.code.LocalVariableInfo}. */ public class LocalVariableInfo extends MutabilityControl { - /** >= 0; the register count for the method */ + /** {@code >= 0;} the register count for the method */ private final int regCount; /** - * non-null; {@link com.android.dx.rop.code.RegisterSpecSet} to use when indicating a block + * {@code non-null;} {@link com.android.dx.rop.code.RegisterSpecSet} to use when indicating a block * that has no locals; it is empty and immutable but has an appropriate * max size for the method */ private final RegisterSpecSet emptySet; /** - * non-null; array consisting of register sets representing the + * {@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; - /** non-null; map from instructions to the variable each assigns */ + /** {@code non-null;} map from instructions to the variable each assigns */ private final HashMap insnAssignments; /** * Constructs an instance. * - * @param method non-null; the method being represented by this instance + * @param method {@code non-null;} the method being represented by this instance */ public LocalVariableInfo(SsaMethod method) { if (method == null) { @@ -74,8 +74,8 @@ public class LocalVariableInfo extends MutabilityControl { * Sets the register set associated with the start of the block with * the given index. * - * @param index >= 0; the block index - * @param specs non-null; the register set to associate with the block + * @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(); @@ -99,12 +99,12 @@ public class LocalVariableInfo extends MutabilityControl { * merge the two sets and call {@link #setStarts} on the result of the * merge. * - * @param index >= 0; the block index - * @param specs non-null; the register set to merge into the start set + * @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 true if the merge resulted in an actual change + * @return {@code true} if the merge resulted in an actual change * to the associated set (including storing one for the first time) or - * false if there was no change + * {@code false} if there was no change */ public boolean mergeStarts(int index, RegisterSpecSet specs) { RegisterSpecSet start = getStarts0(index); @@ -133,8 +133,8 @@ public class LocalVariableInfo extends MutabilityControl { * 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 >= 0; the block index - * @return non-null; the associated register set + * @param index {@code >= 0;} the block index + * @return {@code non-null;} the associated register set */ public RegisterSpecSet getStarts(int index) { RegisterSpecSet result = getStarts0(index); @@ -145,10 +145,10 @@ public class LocalVariableInfo extends MutabilityControl { /** * Gets the register set associated with the start of the given * block. This is just convenient shorthand for - * getStarts(block.getLabel()). + * {@code getStarts(block.getLabel())}. * - * @param block non-null; the block in question - * @return non-null; the associated register set + * @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()); @@ -160,8 +160,8 @@ public class LocalVariableInfo extends MutabilityControl { * newly-allocated empty {@link RegisterSpecSet} of appropriate * max size if there is not yet any set associated with the block. * - * @param index >= 0; the block index - * @return non-null; the associated register set + * @param index {@code >= 0;} the block index + * @return {@code non-null;} the associated register set */ public RegisterSpecSet mutableCopyOfStarts(int index) { RegisterSpecSet result = getStarts0(index); @@ -181,8 +181,8 @@ public class LocalVariableInfo extends MutabilityControl { * simple type and the one in the instruction can be an arbitrary * {@link com.android.dx.rop.type.TypeBearer} (such as a constant value). * - * @param insn non-null; the instruction in question - * @param spec non-null; the associated register spec + * @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(); @@ -202,8 +202,8 @@ public class LocalVariableInfo extends MutabilityControl { * Gets the named register being assigned by the given instruction, if * previously stored in this instance. * - * @param insn non-null; instruction in question - * @return null-ok; the named register being assigned, if any + * @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); @@ -212,7 +212,7 @@ public class LocalVariableInfo extends MutabilityControl { /** * Gets the number of assignments recorded by this instance. * - * @return >= 0; the number of assignments + * @return {@code >= 0;} the number of assignments */ public int getAssignmentCount() { return insnAssignments.size(); @@ -236,8 +236,8 @@ public class LocalVariableInfo extends MutabilityControl { * Helper method, to get the starts for a index, throwing the * right exception for range problems. * - * @param index >= 0; the block index - * @return null-ok; associated register set or null if there + * @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) { diff --git a/dx/src/com/android/dx/ssa/MoveParamCombiner.java b/dx/src/com/android/dx/ssa/MoveParamCombiner.java index a27aec58b..352e3e6ab 100644 --- a/dx/src/com/android/dx/ssa/MoveParamCombiner.java +++ b/dx/src/com/android/dx/ssa/MoveParamCombiner.java @@ -143,8 +143,8 @@ public class MoveParamCombiner { * Returns the parameter index associated with a move-param insn. Does * not verify that the insn is a move-param insn. * - * @param insn non-null; a move-param insn - * @return >=0 parameter index + * @param insn {@code non-null;} a move-param insn + * @return {@code >=0;} parameter index */ private int getParamIndex(NormalSsaInsn insn) { CstInsn cstInsn = (CstInsn)(insn.getOriginalRopInsn()); diff --git a/dx/src/com/android/dx/ssa/NormalSsaInsn.java b/dx/src/com/android/dx/ssa/NormalSsaInsn.java index ad9315a16..d3392ca0f 100644 --- a/dx/src/com/android/dx/ssa/NormalSsaInsn.java +++ b/dx/src/com/android/dx/ssa/NormalSsaInsn.java @@ -19,13 +19,10 @@ package com.android.dx.ssa; import com.android.dx.rop.code.*; /** - * A "normal" (non-phi) instruction in SSA form. Always wraps a ROP insn. + * A "normal" (non-phi) instruction in SSA form. Always wraps a rop insn. */ public final class NormalSsaInsn extends SsaInsn implements Cloneable { - - /** - * ROP insn that we're wrapping - */ + /** {@code non-null;} rop insn that we're wrapping */ private Insn insn; /** @@ -35,21 +32,19 @@ public final class NormalSsaInsn extends SsaInsn implements Cloneable { * @param block block that contains this insn */ NormalSsaInsn(final Insn insn, final SsaBasicBlock block) { - super(block); + super(insn.getResult(), block); this.insn = insn; - this.result = insn.getResult(); } /** {@inheritDoc} */ @Override public final void mapSourceRegisters(RegisterMapper mapper) { - RegisterSpecList oldSources = insn.getSources(); RegisterSpecList newSources = mapper.map(oldSources); if (newSources != oldSources) { - insn = insn.withNewRegisters(result, newSources); - block.getParent().onSourcesChanged(this, oldSources); + insn = insn.withNewRegisters(getResult(), newSources); + getBlock().getParent().onSourcesChanged(this, oldSources); } } @@ -57,7 +52,7 @@ public final class NormalSsaInsn extends SsaInsn implements Cloneable { * Changes one of the insn's sources. New source should be of same type * and category. * - * @param index >=0; index of source to change + * @param index {@code >=0;} index of source to change * @param newSpec spec for new source */ public final void changeOneSource(int index, RegisterSpec newSpec) { @@ -68,6 +63,7 @@ public final class NormalSsaInsn extends SsaInsn implements Cloneable { for (int i = 0; i < sz; i++) { newSources.set(i, i == index ? newSpec : origSources.get(i)); } + newSources.setImmutable(); RegisterSpec origSpec = origSources.get(index); @@ -76,10 +72,10 @@ public final class NormalSsaInsn extends SsaInsn implements Cloneable { * If the register remains unchanged, we're only changing * the type or local var name so don't update use list */ - block.getParent().onSourceChanged(this, origSpec, newSpec); + getBlock().getParent().onSourceChanged(this, origSpec, newSpec); } - insn = insn.withNewRegisters(result, newSources); + insn = insn.withNewRegisters(getResult(), newSources); } /** @@ -90,22 +86,24 @@ public final class NormalSsaInsn extends SsaInsn implements Cloneable { */ public final void setNewSources (RegisterSpecList newSources) { RegisterSpecList origSources = insn.getSources(); + if (origSources.size() != newSources.size()) { throw new RuntimeException("Sources counts don't match"); } - insn = insn.withNewRegisters(result, newSources); + insn = insn.withNewRegisters(getResult(), newSources); } /** {@inheritDoc} */ @Override public NormalSsaInsn clone() { - return (NormalSsaInsn)super.clone(); + return (NormalSsaInsn) super.clone(); } /** - * Like rop.Insn.getSources() - * @return null-ok; sources list + * Like rop.Insn.getSources(). + * + * @return {@code null-ok;} sources list */ public RegisterSpecList getSources() { return insn.getSources(); @@ -119,7 +117,7 @@ public final class NormalSsaInsn extends SsaInsn implements Cloneable { /** {@inheritDoc} */ @Override public Insn toRopInsn() { - return insn.withNewRegisters(result,insn.getSources()); + return insn.withNewRegisters(getResult(), insn.getSources()); } /** @@ -143,7 +141,7 @@ public final class NormalSsaInsn extends SsaInsn implements Cloneable { if (insn.getOpcode().getOpcode() == RegOps.MARK_LOCAL) { assignment = insn.getSources().get(0); } else { - assignment = result; + assignment = getResult(); } if (assignment == null) { @@ -167,8 +165,9 @@ public final class NormalSsaInsn extends SsaInsn implements Cloneable { */ public void upgradeToLiteral() { RegisterSpecList oldSources = insn.getSources(); + insn = insn.withLastSourceLiteral(); - block.getParent().onSourcesChanged(this, oldSources); + getBlock().getParent().onSourcesChanged(this, oldSources); } /** @@ -210,7 +209,7 @@ public final class NormalSsaInsn extends SsaInsn implements Cloneable { /** * {@inheritDoc} * - * TODO increase the scope of this. + * TODO: Increase the scope of this. */ @Override public boolean hasSideEffect() { @@ -221,7 +220,7 @@ public final class NormalSsaInsn extends SsaInsn implements Cloneable { } boolean hasLocalSideEffect - = Optimizer.getPreserveLocals() && getLocalAssignment() != null; + = Optimizer.getPreserveLocals() && getLocalAssignment() != null; switch (opcode.getOpcode()) { case RegOps.MOVE_RESULT: diff --git a/dx/src/com/android/dx/ssa/Optimizer.java b/dx/src/com/android/dx/ssa/Optimizer.java index cee6d7b37..c5f6dc9a2 100644 --- a/dx/src/com/android/dx/ssa/Optimizer.java +++ b/dx/src/com/android/dx/ssa/Optimizer.java @@ -48,7 +48,7 @@ public class Optimizer { } /** - * @return non-null; translation advice + * @return {@code non-null;} translation advice */ public static TranslationAdvice getAdvice() { return advice; @@ -64,7 +64,7 @@ public class Optimizer { * @param isStatic true if this method has no 'this' pointer argument. * @param inPreserveLocals true if local variable info should be preserved, * at the cost of some registers and insns - * @param inAdvice non-null; translation advice + * @param inAdvice {@code non-null;} translation advice * @return optimized method */ public static RopMethod optimize(RopMethod rmeth, int paramWidth, @@ -85,7 +85,7 @@ public class Optimizer { * @param isStatic true if this method has no 'this' pointer argument. * @param inPreserveLocals true if local variable info should be preserved, * at the cost of some registers and insns - * @param inAdvice non-null; translation advice + * @param inAdvice {@code non-null;} translation advice * @param steps set of optional optimization steps to run * @return optimized method */ diff --git a/dx/src/com/android/dx/ssa/PhiInsn.java b/dx/src/com/android/dx/ssa/PhiInsn.java index 1829133bb..64e85acb9 100644 --- a/dx/src/com/android/dx/ssa/PhiInsn.java +++ b/dx/src/com/android/dx/ssa/PhiInsn.java @@ -30,67 +30,53 @@ import java.util.List; * conversion back to ROP form. */ public final class PhiInsn extends SsaInsn { - /** - * the original result register of the phi insn is needed during the - * renaming process after the new result register has already been chosen. + * 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 int ropResultReg; - private ArrayList operands = new ArrayList(); - private RegisterSpecList sources; + private final int ropResultReg; /** - * A single phi operand, consiting of source register and block index - * for move. + * {@code non-null;} operands of the instruction; built up by + * {@link #addPhiOperand} */ - class Operand { - RegisterSpec regSpec; - int blockIndex; - int ropLabel; //mostly for debugging + private final ArrayList operands = new ArrayList(); - Operand (final RegisterSpec regSpec, final int blockIndex, - final int ropLabel){ - this.regSpec = regSpec; - this.blockIndex = blockIndex; - this.ropLabel = ropLabel; - } - } - - public static interface Visitor { - public void visitPhiInsn(PhiInsn insn); - } - - public PhiInsn clone() { - throw new UnsupportedOperationException("can't clone phi"); - } + /** {@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. */ - PhiInsn(final RegisterSpec resultReg, final SsaBasicBlock block) { - super(block); - this.result = resultReg; + 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. */ - PhiInsn(final int resultReg, final SsaBasicBlock block) { - super(block); - + public PhiInsn(final int resultReg, final SsaBasicBlock block) { /* - * The type here is bogus: the type depends on the operand and - * will be derived later. + * The result type here is bogus: The type depends on the + * operand and will be derived later. */ - this.result = RegisterSpec.make(resultReg, Type.VOID); + super(RegisterSpec.make(resultReg, Type.VOID), block); ropResultReg = resultReg; } + /** {@inheritDoc} */ + 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. @@ -100,9 +86,8 @@ public final class PhiInsn extends SsaInsn { * * @param ssaMeth method that contains this insn */ - void updateSourcesToDefinitions(SsaMethod ssaMeth) { - - for (Operand o: operands) { + public void updateSourcesToDefinitions(SsaMethod ssaMeth) { + for (Operand o : operands) { RegisterSpec def = ssaMeth.getDefinitionForRegister( o.regSpec.getReg()).getResult(); @@ -116,37 +101,42 @@ public final class PhiInsn extends SsaInsn { /** * Changes the result type. Used during phi type resolution * - * @param type non-null; new TypeBearer - * @param local null-ok; new local info, if available + * @param type {@code non-null;} new TypeBearer + * @param local {@code null-ok;} new local info, if available */ - void changeResultType(TypeBearer type, LocalItem local) { - result = RegisterSpec.makeLocalOptional(result.getReg(), type, local); + public void changeResultType(TypeBearer type, LocalItem local) { + setResult(RegisterSpec.makeLocalOptional( + getResult().getReg(), type, local)); } /** - * @return the original rop-form result reg. Useful during renaming. + * Gets the original rop-form result reg. This is useful during renaming. + * + * @return the original rop-form result reg */ - int getRopResultReg() { + public int getRopResultReg() { return ropResultReg; } /** - * Add an operand to this phi instruction + * 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 + * @param predBlock predecessor block to be associated with this operand */ public void addPhiOperand(RegisterSpec registerSpec, SsaBasicBlock predBlock) { operands.add(new Operand(registerSpec, predBlock.getIndex(), predBlock.getRopLabel())); - // in case someone has already called getSources() + // 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 */ @@ -157,7 +147,7 @@ public final class PhiInsn extends SsaInsn { /** * {@inheritDoc} * - * Always returns null for PhiInsns + * Always returns null for {@code PhiInsn}s. */ @Override public Rop getOpcode() { @@ -167,18 +157,17 @@ public final class PhiInsn extends SsaInsn { /** * {@inheritDoc} * - * Always returns null for PhiInsns + * Always returns null for {@code PhiInsn}s. */ @Override public Insn getOriginalRopInsn() { return null; } - /** * {@inheritDoc} * - * Always returns false for PhiInsns + * Always returns false for {@code PhiInsn}s. */ @Override public boolean canThrow() { @@ -188,10 +177,10 @@ public final class PhiInsn extends SsaInsn { /** * Gets sources. Constructed lazily from phi operand data structures and * then cached. - * @return sources list + * + * @return {@code non-null;} sources list */ public RegisterSpecList getSources() { - if (sources != null) { return sources; } @@ -219,10 +208,10 @@ public final class PhiInsn extends SsaInsn { public boolean isRegASource(int reg) { /* * Avoid creating a sources list in case it has not already been - * created + * created. */ - for (Operand o: operands) { + for (Operand o : operands) { if (o.regSpec.getReg() == reg) { return true; } @@ -236,12 +225,12 @@ public final class PhiInsn extends SsaInsn { */ public boolean areAllOperandsEqual() { if (operands.size() == 0 ) { - // this should never happen + // This should never happen. return true; } int firstReg = operands.get(0).regSpec.getReg(); - for (Operand o: operands) { + for (Operand o : operands) { if (firstReg != o.regSpec.getReg()) { return false; } @@ -253,19 +242,20 @@ public final class PhiInsn extends SsaInsn { /** {@inheritDoc} */ @Override public final void mapSourceRegisters(RegisterMapper mapper) { - for (Operand o: operands) { + for (Operand o : operands) { RegisterSpec old = o.regSpec; o.regSpec = mapper.map(old); if (old != o.regSpec) { - block.getParent().onSourceChanged(this, old, o.regSpec); + getBlock().getParent().onSourceChanged(this, old, o.regSpec); } } sources = null; } /** - * Always throws an exeption, since - * a phi insn may not be converted back to rop form + * Always throws an exeption, since a phi insn may not be + * converted back to rop form. + * * @return always throws exception */ @Override @@ -276,17 +266,16 @@ public final class PhiInsn extends SsaInsn { /** * Returns the list of predecessor blocks associated with all operands - * that have reg as an operand register. + * 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 + * @return list of predecessor blocks, empty if none */ - public List predBlocksForReg (int reg, SsaMethod ssaMeth) { - ArrayList ret - = (ArrayList)new ArrayList(); + public List predBlocksForReg(int reg, SsaMethod ssaMeth) { + ArrayList ret = new ArrayList(); - for (Operand o: operands) { + for (Operand o : operands) { if (o.regSpec.getReg() == reg) { ret.add(ssaMeth.getBlocks().get(o.blockIndex)); } @@ -297,12 +286,13 @@ public final class PhiInsn extends SsaInsn { /** {@inheritDoc} */ @Override - public boolean isPhiOrMove() { + public boolean isPhiOrMove() { return true; } /** {@inheritDoc} */ - @Override public boolean hasSideEffect() { + @Override + public boolean hasSideEffect() { return Optimizer.getPreserveLocals() && getLocalAssignment() != null; } @@ -312,25 +302,23 @@ public final class PhiInsn extends SsaInsn { v.visitPhiInsn(this); } - /** - * @return human-readable string for listing dumps - */ + /** {@inheritDoc} */ public String toHuman() { return toHumanWithInline(null); } /** - * Returns human-readable string for listing dumps. - * Allows sub-classes to specify extra text - * @param extra null-ok; the argument to print after the opcode + * 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(": "); - sb.append("phi"); + sb.append(": phi"); if (extra != null) { sb.append("("); @@ -338,6 +326,8 @@ public final class PhiInsn extends SsaInsn { sb.append(")"); } + RegisterSpec result = getResult(); + if (result == null) { sb.append(" ."); } else { @@ -361,4 +351,27 @@ public final class PhiInsn extends SsaInsn { 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/dx/ssa/RegisterMapper.java b/dx/src/com/android/dx/ssa/RegisterMapper.java index 98503e2d8..1f45b429a 100644 --- a/dx/src/com/android/dx/ssa/RegisterMapper.java +++ b/dx/src/com/android/dx/ssa/RegisterMapper.java @@ -21,13 +21,12 @@ import com.android.dx.rop.code.RegisterSpecList; import com.android.dx.util.ToHuman; /** - * Represents a mapping between two register numbering schemes. - * Subclasses of this may be mutable, and as such the mapping provided is only - * valid for the lifetime of the method call in which instances of this class - * are passed. + * Represents a mapping between two register numbering schemes. + * Subclasses of this may be mutable, and as such the mapping provided + * is only valid for the lifetime of the method call in which + * instances of this class are passed. */ public abstract class RegisterMapper { - /** * Gets the count of registers (really, the total register width, since * category width is counted) in the new namespace. @@ -47,17 +46,16 @@ public abstract class RegisterMapper { * @return new mapped register list, or old if nothing has changed. */ public final RegisterSpecList map(RegisterSpecList sources) { - RegisterSpecList newSources; - - newSources = new RegisterSpecList(sources.size()); - int sz = sources.size(); + 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; + + // Return the old sources if nothing has changed. + return newSources.equals(sources) ? sources : newSources; } } diff --git a/dx/src/com/android/dx/ssa/SCCP.java b/dx/src/com/android/dx/ssa/SCCP.java index 1d95da63c..73e9b4992 100644 --- a/dx/src/com/android/dx/ssa/SCCP.java +++ b/dx/src/com/android/dx/ssa/SCCP.java @@ -102,11 +102,11 @@ public class SCCP { */ private void addUsersToWorklist(int reg, int latticeValue) { if (latticeValue == VARYING) { - for (SsaInsn insn: ssaMeth.getUseListForRegister(reg)) { + for (SsaInsn insn : ssaMeth.getUseListForRegister(reg)) { varyingWorklist.add(insn); } } else { - for (SsaInsn insn: ssaMeth.getUseListForRegister(reg)) { + for (SsaInsn insn : ssaMeth.getUseListForRegister(reg)) { ssaWorklist.add(insn); } } @@ -137,9 +137,6 @@ public class SCCP { } } - private boolean setLatticeValueTo(int reg, int value) { - return setLatticeValueTo(reg, value, null); - } /** * Simulates a PHI node and set the lattice for the result * to the approriate value. @@ -160,6 +157,7 @@ public class SCCP { 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(); @@ -194,7 +192,7 @@ public class SCCP { * @param block Block to visit */ private void simulateBlock(SsaBasicBlock block) { - for (SsaInsn insn: block.getInsns()) { + for (SsaInsn insn : block.getInsns()) { if (insn instanceof PhiInsn) { simulatePhi((PhiInsn) insn); } else { @@ -464,7 +462,7 @@ public class SCCP { * Update the sources RegisterSpec's of all non-move uses. * These will be used in later steps. */ - for(SsaInsn insn: ssaMeth.getUseListForRegister(reg)) { + for (SsaInsn insn : ssaMeth.getUseListForRegister(reg)) { if (insn.isPhiOrMove()) { continue; } @@ -478,7 +476,6 @@ public class SCCP { RegisterSpec newSpec = spec.withType((TypedConstant)latticeConstants[reg]); - nInsn.changeOneSource(index, newSpec); } } diff --git a/dx/src/com/android/dx/ssa/SetFactory.java b/dx/src/com/android/dx/ssa/SetFactory.java index f34d08d91..92e965fb9 100644 --- a/dx/src/com/android/dx/ssa/SetFactory.java +++ b/dx/src/com/android/dx/ssa/SetFactory.java @@ -59,8 +59,8 @@ public final class SetFactory { /** * Make IntSet for the dominance-frontier sets. * - * @param szBlocks >=0; count of basic blocks in method - * @return non-null; appropriate set + * @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 @@ -72,8 +72,8 @@ public final class SetFactory { * Make IntSet for the interference graph sets. Public because * InterferenceGraph is in another package. * - * @param countRegs >=0; count of SSA registers used in method - * @return non-null; appropriate set + * @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 @@ -84,8 +84,8 @@ public final class SetFactory { /** * Make IntSet for register live in/out sets. * - * @param countRegs >=0; count of SSA registers used in method - * @return non-null; appropriate set + * @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 diff --git a/dx/src/com/android/dx/ssa/SsaBasicBlock.java b/dx/src/com/android/dx/ssa/SsaBasicBlock.java index 99ada7edb..ab0e12258 100644 --- a/dx/src/com/android/dx/ssa/SsaBasicBlock.java +++ b/dx/src/com/android/dx/ssa/SsaBasicBlock.java @@ -36,53 +36,80 @@ import com.android.dx.util.ListIntSet; import java.util.ArrayList; import java.util.BitSet; import java.util.Collections; +import java.util.Comparator; 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 LABEL_COMPARATOR = + new LabelComparator(); - /** non-null; insn list associated with this instance */ + /** {@code non-null;} insn list associated with this instance */ private ArrayList insns; - /** non-null; predecessor set (by block list index) */ + + /** {@code non-null;} predecessor set (by block list index) */ private BitSet predecessors; - /** non-null; successor set (by block list index) */ + + /** {@code non-null;} successor set (by block list index) */ private BitSet successors; + /** - * non-null; ordered successor list + * {@code non-null;} ordered successor list * (same block may be listed more than once) */ private IntList successorList; - /** block list index of primary successor, or -1 for no primary successor */ + + /** + * 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; - /** non-null; method we belong to */ + + /** {@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 domChildren; - /** - * The number of moves added to the end of the block during the + /** + * 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 + + /** + * the number of moves added to the beginning of the block during the * phi-removal process. Retained for subsequent move scheduling. */ private int movesFromPhisAtBeginning = 0; - /** null-ok; indexed by reg: the regs that are live-in at this block */ + /** + * {@code null-ok;} indexed by reg: the regs that are live-in at + * this block + */ private IntSet liveIn; - /** null-ok; indexed by reg: the regs that are live-out at this block */ + + /** + * {@code null-ok;} indexed by reg: the regs that are live-out at + * this block + */ private IntSet liveOut; /** - * Create a new empty basic block + * 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 @@ -106,26 +133,20 @@ public final class SsaBasicBlock { * * @param rmeth original method * @param basicBlockIndex index this block will have - * @param parent method of this block - * predecessor set will be updated. + * @param parent method of this block predecessor set will be + * updated * @return new instance */ public static SsaBasicBlock newFromRop(RopMethod rmeth, int basicBlockIndex, final SsaMethod parent) { - - BasicBlockList ropBlocks; - SsaBasicBlock result; - InsnList ropInsns; - BasicBlock bb; - - ropBlocks = rmeth.getBlocks(); - bb = ropBlocks.get(basicBlockIndex); - - result = new SsaBasicBlock(basicBlockIndex, bb.getLabel(), parent); - - ropInsns = bb.getInsns(); + 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)); } @@ -141,7 +162,6 @@ public final class SsaBasicBlock { = SsaMethod.indexListFromLabelList(ropBlocks, bb.getSuccessors()); - if (result.successorList.size() != 0) { int primarySuccessor = bb.getPrimarySuccessor(); @@ -156,18 +176,18 @@ public final class SsaBasicBlock { * Adds a basic block as a dom child for this block. Used when constructing * the dom tree. * - * @param child non-null; new dom child + * @param child {@code non-null;} new dom child */ - void addDomChild(SsaBasicBlock child) { + public void addDomChild(SsaBasicBlock child) { domChildren.add(child); } /** * Gets the dom children for this node. Don't modify this list. * - * @return non-null; list of dom children + * @return {@code non-null;} list of dom children */ - ArrayList getDomChildren() { + public ArrayList getDomChildren() { return domChildren; } @@ -175,9 +195,9 @@ public final class SsaBasicBlock { * 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 >=0 result reg + * @param reg {@code >=0;} result reg */ - void addPhiInsnForReg(int reg) { + public void addPhiInsnForReg(int reg) { insns.add(0, new PhiInsn(reg, this)); } @@ -186,9 +206,9 @@ public final class SsaBasicBlock { * when the result type or local-association can be determined at phi * insert time. * - * @param resultSpec non-null; reg + * @param resultSpec {@code non-null;} reg */ - void addPhiInsnForReg(RegisterSpec resultSpec) { + public void addPhiInsnForReg(RegisterSpec resultSpec) { insns.add(0, new PhiInsn(resultSpec, this)); } @@ -196,9 +216,9 @@ public final class SsaBasicBlock { * Adds an insn to the head of this basic block, just after any phi * insns. * - * @param insn non-null; rop-form insn to add + * @param insn {@code non-null;} rop-form insn to add */ - void addInsnToHead(Insn insn) { + public void addInsnToHead(Insn insn) { SsaInsn newInsn = SsaInsn.makeFromRop(insn, this); insns.add(getCountPhiInsns(), newInsn); parent.onInsnAdded(newInsn); @@ -208,9 +228,9 @@ public final class SsaBasicBlock { * Replaces the last insn in this block. The provided insn must have * some branchingness. * - * @param insn non-null; rop-form insn to add, which must branch. + * @param insn {@code non-null;} rop-form insn to add, which must branch. */ - void replaceLastInsn(Insn insn) { + public void replaceLastInsn(Insn insn) { if (insn.getOpcode().getBranchingness() == Rop.BRANCH_NONE) { throw new IllegalArgumentException("last insn must branch"); } @@ -225,12 +245,13 @@ public final class SsaBasicBlock { } /** - * Visits each phi insn - * @param v callback + * 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) { @@ -251,7 +272,7 @@ public final class SsaBasicBlock { public void removeAllPhiInsns() { /* * 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(); @@ -259,6 +280,7 @@ public final class SsaBasicBlock { /** * Gets the number of phi insns at the top of this basic block. + * * @return count of phi insns */ private int getCountPhiInsns() { @@ -276,15 +298,15 @@ public final class SsaBasicBlock { } /** - * @return non-null;the (mutable) instruction list for this block, - * with phi insns at the beginning. + * @return {@code non-null;} the (mutable) instruction list for this block, + * with phi insns at the beginning */ public ArrayList getInsns() { return insns; } /** - * @return non-null; the (mutable) list of phi insns for this block + * @return {@code non-null;} the (mutable) list of phi insns for this block */ public List getPhiInsns() { return insns.subList(0, getCountPhiInsns()); @@ -312,29 +334,30 @@ public final class SsaBasicBlock { } /** - * @return non-null;predecessors set, indexed by block index + * @return {@code non-null;} predecessors set, indexed by block index */ public BitSet getPredecessors() { return predecessors; } /** - * @return non-null;successors set, indexed by block index + * @return {@code non-null;} successors set, indexed by block index */ public BitSet getSuccessors() { return successors; } /** - * @return non-null;ordered successor list, containing block indicies + * @return {@code non-null;} ordered successor list, containing block + * indicies */ public IntList getSuccessorList() { return successorList; } /** - * @return >= -1; block index of primary successor or -1 if no - * primary successor. + * @return {@code >= -1;} block index of primary successor or + * {@code -1} if no primary successor */ public int getPrimarySuccessorIndex() { return primarySuccessor; @@ -348,7 +371,8 @@ public final class SsaBasicBlock { } /** - * @return null-ok; the primary successor block or null if there is none. + * @return {@code null-ok;} the primary successor block or {@code null} + * if there is none */ public SsaBasicBlock getPrimarySuccessor() { if (primarySuccessor < 0) { @@ -373,7 +397,7 @@ public final class SsaBasicBlock { } /** - * @return non-null; method that contains this block + * @return {@code non-null;} method that contains this block */ public SsaMethod getParent() { return parent; @@ -383,23 +407,23 @@ public final class SsaBasicBlock { * Inserts a new empty GOTO block as a predecessor to this block. * All previous predecessors will be predecessors to the new block. * - * @return non-null; an appropriately-constructed instance + * @return {@code non-null;} an appropriately-constructed instance */ public SsaBasicBlock insertNewPredecessor() { SsaBasicBlock newPred = parent.makeNewGotoBlock(); - // Update the new block + // Update the new block. newPred.predecessors = predecessors; newPred.successors.set(index) ; newPred.successorList.add(index); newPred.primarySuccessor = index; - // Update us + // Update us. predecessors = new BitSet(parent.getBlocks().size()); predecessors.set(newPred.index); - // Update our (soon-to-be) old predecessors + // Update our (soon-to-be) old predecessors. for (int i = newPred.predecessors.nextSetBit(0); i >= 0; i = newPred.predecessors.nextSetBit(i + 1)) { @@ -412,15 +436,15 @@ public final class SsaBasicBlock { } /** - * Constructs and inserts a new empty GOTO block Z between - * this block (A) and a current successor block - * (B). The new block will replace B as A's successor and + * 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 non-null; an appropriately-constructed instance + * @return {@code non-null;} an appropriately-constructed instance */ public SsaBasicBlock insertNewSuccessor(SsaBasicBlock other) { SsaBasicBlock newSucc = parent.makeNewGotoBlock(); @@ -430,15 +454,15 @@ public final class SsaBasicBlock { + " not successor of " + getRopLabelString()); } - // Update the new block + // 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 + // Update us. for (int i = successorList.size() - 1 ; i >= 0; i--) { - if(successorList.get(i) == other.index) { + if (successorList.get(i) == other.index) { successorList.set(i, newSucc.index); } } @@ -449,7 +473,7 @@ public final class SsaBasicBlock { successors.clear(other.index); successors.set(newSucc.index); - // Update "other" + // Update "other". other.predecessors.set(newSucc.index); other.predecessors.set(index, successors.get(other.index)); @@ -457,17 +481,18 @@ public final class SsaBasicBlock { } /** - * Replace an old successor with a new successor. - * Throws RuntimeException if oldIndex was not a successor. + * 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. + * @param newIndex index of new successor block */ public void replaceSuccessor(int oldIndex, int newIndex) { if (oldIndex == newIndex) { return; } - // Update us + // Update us. successors.set(newIndex); if (primarySuccessor == oldIndex) { @@ -475,17 +500,17 @@ public final class SsaBasicBlock { } for (int i = successorList.size() - 1 ; i >= 0; i--) { - if(successorList.get(i) == oldIndex) { + if (successorList.get(i) == oldIndex) { successorList.set(i, newIndex); } } successors.clear(oldIndex); - // Update new successor + // Update new successor. parent.getBlocks().get(newIndex).predecessors.set(index); - // Update old successor + // Update old successor. parent.getBlocks().get(oldIndex).predecessors.clear(index); } @@ -495,7 +520,7 @@ public final class SsaBasicBlock { * is not an exit predecessor or is the exit block, this block does * nothing. For use by {@link com.android.dx.ssa.SsaMethod#makeExitBlock} * - * @param exitBlock non-null; exit block + * @param exitBlock {@code non-null;} exit block */ public void exitBlockFixup(SsaBasicBlock exitBlock) { if (this == exitBlock) { @@ -519,6 +544,7 @@ public final class SsaBasicBlock { * 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 */ @@ -538,12 +564,13 @@ public final class SsaBasicBlock { 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. + * 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) @@ -557,19 +584,14 @@ public final class SsaBasicBlock { } } 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. + * We can safely add a move to the end of the block just + * before the last instruction, because the final insn does + * not assign to anything. */ - - RegisterSpecList sources; - sources = RegisterSpecList.make(source); - - NormalSsaInsn toAdd; - - toAdd = new NormalSsaInsn( - new PlainInsn(Rops.opMove(result.getType()), - SourcePosition.NO_INFO, result, sources), this); + 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); @@ -578,28 +600,24 @@ public final class SsaBasicBlock { } /** - * Add a move instruction after the phi insn block. + * 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; - sources = RegisterSpecList.make(source); - - NormalSsaInsn toAdd; - - toAdd = new NormalSsaInsn( - new PlainInsn(Rops.opMove(result.getType()), - SourcePosition.NO_INFO, result, sources), this); + 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++; + movesFromPhisAtBeginning++; } /** @@ -612,7 +630,7 @@ public final class SsaBasicBlock { private static void setRegsUsed (BitSet regsUsed, RegisterSpec rs) { regsUsed.set(rs.getReg()); if (rs.getCategory() > 1) { - regsUsed.set(rs.getReg() + 1); + regsUsed.set(rs.getReg() + 1); } } @@ -638,14 +656,16 @@ public final class SsaBasicBlock { * 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 + * 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 toSchedule) { BitSet regsUsedAsSources = new BitSet(parent.getRegCount()); - // TODO get rid of this + + // TODO: Get rid of this. BitSet regsUsedAsResults = new BitSet(parent.getRegCount()); int sz = toSchedule.size(); @@ -680,8 +700,10 @@ public final class SsaBasicBlock { } } - // If we've made no progress in this iteration, there's a - // circular dependency. Split it using the temp reg. + /* + * If we've made no progress in this iteration, there's a + * circular dependency. Split it using the temp reg. + */ if (oldInsertPlace == insertPlace) { SsaInsn insnToSplit = null; @@ -694,43 +716,40 @@ public final class SsaBasicBlock { insn.getSources().get(0))) { insnToSplit = insn; - // We're going to split this insn--move it to the - // front + /* + * We're going to split this insn; move it to the + * front. + */ Collections.swap(toSchedule, insertPlace, i); break; } } - // At least one insn will be set above + // At least one insn will be set above. RegisterSpec result = insnToSplit.getResult(); RegisterSpec tempSpec = result.withReg( parent.borrowSpareRegister(result.getCategory())); - NormalSsaInsn toAdd; - - toAdd = new NormalSsaInsn( - new PlainInsn(Rops.opMove(result.getType()), - SourcePosition.NO_INFO, - tempSpec, - insnToSplit.getSources()), this); + NormalSsaInsn toAdd = new NormalSsaInsn( + new PlainInsn(Rops.opMove(result.getType()), + SourcePosition.NO_INFO, + tempSpec, + insnToSplit.getSources()), this); toSchedule.add(insertPlace++, toAdd); - NormalSsaInsn toReplace; - RegisterSpecList newSources; - - newSources = RegisterSpecList.make(tempSpec); + RegisterSpecList newSources = RegisterSpecList.make(tempSpec); - toReplace = new NormalSsaInsn( - new PlainInsn(Rops.opMove(result.getType()), - SourcePosition.NO_INFO, - result, - newSources), this); + NormalSsaInsn toReplace = new NormalSsaInsn( + new PlainInsn(Rops.opMove(result.getType()), + SourcePosition.NO_INFO, + result, + newSources), this); toSchedule.set(insertPlace, toReplace); - // size has changed + // The size changed. sz = toSchedule.size(); } @@ -740,12 +759,12 @@ public final class SsaBasicBlock { } /** - * Adds regV to the live-out list for this block. - * Called by the liveness analyzer. + * 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) { + public void addLiveOut (int regV) { if (liveOut == null) { liveOut = SetFactory.makeLivenessSet(parent.getRegCount()); } @@ -754,24 +773,24 @@ public final class SsaBasicBlock { } /** - * Adds regV to the live-in list for this block. - * Called by the liveness analyzer. + * 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) { + public void addLiveIn (int regV) { if (liveIn == null) { liveIn = SetFactory.makeLivenessSet(parent.getRegCount()); } - liveIn.add(regV); + liveIn.add(regV); } /** * Returns the set of live-in registers. Valid after register * interference graph has been generated, otherwise empty. * - * @return non-null; live-in register set. + * @return {@code non-null;} live-in register set. */ public IntSet getLiveInRegs() { if (liveIn == null) { @@ -783,8 +802,8 @@ public final class SsaBasicBlock { /** * Returns the set of live-out registers. Valid after register * interference graph has been generated, otherwise empty. - * - * @return non-null; live-out register set. + * + * @return {@code non-null;} live-out register set */ public IntSet getLiveOutRegs() { if (liveOut == null) { @@ -806,15 +825,15 @@ public final class SsaBasicBlock { * that it's either the start block or it has predecessors, which suffices * for all current control flow transformations. * - * @return true if reachable + * @return {@code true} if reachable */ public boolean isReachable() { return index == parent.getEntryBlockIndex() || predecessors.cardinality() > 0; } - + /** - * Sorts move instructions added via addMoveToEnd during + * 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.

    @@ -825,7 +844,6 @@ public final class SsaBasicBlock { * refers value before any other phis have executed. */ public void scheduleMovesFromPhis() { - if (movesFromPhisAtBeginning > 1) { List toSchedule; @@ -835,32 +853,37 @@ public final class SsaBasicBlock { 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. + /* + * 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 + * 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. + /* + * 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) { + /* + * Does the move-exception result reg interfere with the + * phi moves? + */ + for (SsaInsn insn : toSchedule) { if (insn.isResultReg(moveExceptionResult) || insn.isRegASource(moveExceptionResult)) { moveExceptionInterferes = true; @@ -869,81 +892,107 @@ public final class SsaBasicBlock { } if (!moveExceptionInterferes) { - // The easy case + // 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. - - int spareRegister; - RegisterSpec originalResultSpec; - - originalResultSpec = firstNonPhiMoveInsn.getResult(); - spareRegister = parent.borrowSpareRegister( + /* + * 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 + // We now move it to a spare register. firstNonPhiMoveInsn.changeResultReg(spareRegister); - RegisterSpec tempSpec = firstNonPhiMoveInsn.getResult(); + RegisterSpec tempSpec = + firstNonPhiMoveInsn.getResult(); insns.add(0, firstNonPhiMoveInsn); - // And here we move it back - - NormalSsaInsn toAdd; + // And here we move it back. - toAdd = new NormalSsaInsn( - new PlainInsn(Rops.opMove( - tempSpec.getType()), - SourcePosition.NO_INFO, - originalResultSpec, - RegisterSpecList.make(tempSpec)), + 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. + /* + * 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() + // Return registers borrowed here and in scheduleUseBeforeAssigned(). parent.returnSpareRegisters(); } /** - * Visit all insns in this block - * @param visitor callback interface + * Visits all insns in this block. + * + * @param visitor {@code non-null;} callback interface */ public void forEachInsn(SsaInsn.Visitor visitor) { - for (SsaInsn insn: insns) { - insn.accept(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); } } + /** {@inheritDoc} */ public String toString() { return "{" + index + ":" + Hex.u2(ropLabel) + '}'; } /** - * Visitor interface for basic blocks + * Visitor interface for basic blocks. */ public interface Visitor { - /** * Indicates a block has been visited by an iterator method. - * @param v non-null; block visited - * @param parent null-ok; parent node if applicable. + * + * @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 { + /** {@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; + } + } + } } diff --git a/dx/src/com/android/dx/ssa/SsaConverter.java b/dx/src/com/android/dx/ssa/SsaConverter.java index a731fcb87..d5be2871b 100644 --- a/dx/src/com/android/dx/ssa/SsaConverter.java +++ b/dx/src/com/android/dx/ssa/SsaConverter.java @@ -27,22 +27,23 @@ import java.util.BitSet; * Converts ROP methods to SSA Methods */ public class SsaConverter { - public static boolean DEBUG = false; + public static final boolean DEBUG = false; /** - * returns an SSA representation, edge-split and with phi functions placed + * Returns an SSA representation, edge-split and with phi + * functions placed. + * * @param rmeth input * @param paramWidth the total width, in register-units, of the method's * parameters - * @param isStatic true if this method has no 'this' + * @param isStatic {@code true} if this method has no {@code this} * pointer argument * @return output in SSA form */ - public static SsaMethod convertToSsaMethod(RopMethod rmeth, + public static SsaMethod convertToSsaMethod(RopMethod rmeth, int paramWidth, boolean isStatic) { - SsaMethod result; - - result = SsaMethod.newFromRopMethod(rmeth, paramWidth, isStatic); + SsaMethod result + = SsaMethod.newFromRopMethod(rmeth, paramWidth, isStatic); edgeSplit(result); @@ -52,7 +53,7 @@ public class SsaConverter { new SsaRenamer(result).run(); /* - * Exit block, added here, is not considered for edge splitting + * The exit block, added here, is not considered for edge splitting * or phi placement since no actual control flows to it. */ result.makeExitBlock(); @@ -62,10 +63,12 @@ public class SsaConverter { /** * Returns an SSA represention with only the edge-splitter run. + * * @param rmeth method to process * @param paramWidth width of all arguments in the method - * @param isStatic true if this method has no 'this' pointer argument - * @return an SSA represention with only the edge-splitter run. + * @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) { @@ -80,10 +83,12 @@ public class SsaConverter { /** * Returns an SSA represention with only the steps through the * phi placement run. + * * @param rmeth method to process * @param paramWidth width of all arguments in the method - * @param isStatic true if this method has no 'this' pointer argument - * @return an SSA represention with only the edge-splitter run. + * @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) { @@ -100,7 +105,8 @@ public class SsaConverter { } /** - * See Appel section 19.1 + * See Appel section 19.1: + * * Converts CFG into "edge-split" form, such that each node either a * unique successor or unique predecessor.

    * @@ -109,11 +115,10 @@ public class SsaConverter { * 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); @@ -122,13 +127,16 @@ public class SsaConverter { /** * Inserts Z nodes as new predecessors for every node that has multiple * successors and multiple predecessors. - * @param result non-null; method to process + * + * @param result {@code non-null;} method to process */ private static void edgeSplitPredecessors(SsaMethod result) { ArrayList blocks = result.getBlocks(); - - // New blocks are added to the end of the block list during - // this iteration + + /* + * 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)) { @@ -138,12 +146,11 @@ public class SsaConverter { } /** - * @param block non-null; block in question - * @return true if this node needs to have a unique predecessor created for - * it. + * @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. @@ -161,59 +168,68 @@ public class SsaConverter { * 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 blocks = ssaMeth.getBlocks(); - // New blocks are added to the end of the block list during - // this iteration + /* + * 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... + + /* + * Any block that starts with a move-exception and has more than + * one predecessor... + */ if (!block.isExitBlock() - && block.getPredecessors().cardinality() > 1 + && block.getPredecessors().cardinality() > 1 && block.getInsns().get(0).isMoveException()) { - // block.getPredecessors() is changed in the loop below + // 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)) { - + j = preds.nextSetBit(j + 1)) { SsaBasicBlock predecessor = blocks.get(j); + SsaBasicBlock zNode + = predecessor.insertNewSuccessor(block); - SsaBasicBlock zNode = predecessor.insertNewSuccessor(block); - - // Make sure to place the move-exception as the - // first insn... + /* + * 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... + // Remove the move-exception from the original block. block.getInsns().remove(0); } } } /** - * Inserts Z nodes for every node that needs a new + * Inserts Z nodes for every node that needs a new * successor. - * @param result non-null; method to process + * + * @param result {@code non-null;} method to process */ private static void edgeSplitSuccessors(SsaMethod result) { ArrayList blocks = result.getBlocks(); - // New blocks are added to the end of the block list during - // this iteration + /* + * 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 + // 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)) { + for (int j = successors.nextSetBit(0); + j >= 0; j = successors.nextSetBit(j+1)) { SsaBasicBlock succ = blocks.get(j); @@ -225,17 +241,17 @@ public class SsaConverter { } /** - * Returns true if block and successor need a Z-node between them. - * Presently, this is true if the final instruction has any sources - * or results and the current successor block has more than one - * predecessor. + * 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 true if a Z node is needed + * @return {@code true} if a Z node is needed */ private static boolean needsNewSuccessor(SsaBasicBlock block, SsaBasicBlock succ) { - ArrayList insns = block.getInsns(); SsaInsn lastInsn = insns.get(insns.size() - 1); @@ -245,11 +261,14 @@ public class SsaConverter { } /** - * See Appel algorithm 19.6 + * See Appel algorithm 19.6: + * * Place Phi functions in appropriate locations. * - * @param ssaMeth non-null; method to process. Modifications made in-place - * @param localInfo non-null; Local variable info, used when placing phis + * @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 */ private static void placePhiFunctions (SsaMethod ssaMeth, LocalVariableInfo localInfo) { @@ -282,8 +301,7 @@ public class SsaConverter { for (int bi = 0, s = ssaBlocks.size(); bi < s; bi++) { SsaBasicBlock b = ssaBlocks.get(bi); - for (SsaInsn insn: b.getInsns()) { - + for (SsaInsn insn : b.getInsns()) { RegisterSpec rs = insn.getResult(); if (rs != null) { @@ -297,11 +315,8 @@ public class SsaConverter { 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); } } @@ -315,15 +330,14 @@ public class SsaConverter { for (int reg = 0, s = ssaMeth.getRegCount() ; reg < s ; reg++ ) { int workBlockIndex; - /* Worklist set starts out with each node where reg is assigned */ + /* Worklist set starts out with each node where reg is assigned. */ - worklist = (BitSet)(defsites[reg].clone()); + worklist = (BitSet) (defsites[reg].clone()); while (0 <= (workBlockIndex = worklist.nextSetBit(0))) { worklist.clear(workBlockIndex); IntIterator dfIterator - = domInfos[workBlockIndex] - .dominanceFrontiers.iterator(); + = domInfos[workBlockIndex].dominanceFrontiers.iterator(); while (dfIterator.hasNext()) { int dfBlockIndex = dfIterator.next(); @@ -353,11 +367,8 @@ public class SsaConverter { for (int i = 0; i < regCount; i++) { StringBuilder sb = new StringBuilder(); - sb.append('v').append(i).append(": "); - sb.append(phisites[i].toString()); - System.out.println(sb); } } diff --git a/dx/src/com/android/dx/ssa/SsaInsn.java b/dx/src/com/android/dx/ssa/SsaInsn.java index d9e33a0dc..815f82d38 100644 --- a/dx/src/com/android/dx/ssa/SsaInsn.java +++ b/dx/src/com/android/dx/ssa/SsaInsn.java @@ -23,24 +23,34 @@ import com.android.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; - protected RegisterSpec result; - protected final SsaBasicBlock block; + /** {@code null-ok;} result register */ + private RegisterSpec result; /** - * Constructs an instance - * @param block block containing this insn. Can never change. + * 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(final SsaBasicBlock block) { + 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 + * Makes a new SSA insn form a rop insn. * - * @param insn non-null; rop insn - * @param block non-null; owning block - * @return non-null; an appropriately constructed instance + * @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); @@ -58,15 +68,30 @@ public abstract class SsaInsn implements ToHuman, Cloneable { /** * Like {@link com.android.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.dx.rop.code.Insn getSources()}. - * @return non-null; sources list + * + * @return {@code non-null;} sources list */ abstract public RegisterSpecList getSources(); @@ -80,7 +105,8 @@ public abstract class SsaInsn implements ToHuman, Cloneable { } /** - * is the specified reg the result reg? + * 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 @@ -91,9 +117,10 @@ public abstract class SsaInsn implements ToHuman, Cloneable { /** - * Changes the result register if this insn has a result. - * Used during renaming. - * @param reg new result register. + * 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) { @@ -102,10 +129,10 @@ public abstract class SsaInsn implements ToHuman, Cloneable { } /** - * Sets the local association for the result of this insn. - * This is sometimes updated during the SsaRenamer process. + * Sets the local association for the result of this insn. This is + * sometimes updated during the SsaRenamer process. * - * @param local null-ok; New debug/local variable info. + * @param local {@code null-ok;} new debug/local variable info */ public final void setResultLocal(LocalItem local) { LocalItem oldItem = result.getLocalItem(); @@ -120,10 +147,11 @@ public abstract class SsaInsn implements ToHuman, Cloneable { /** * Map registers after register allocation. * - * @param mapper + * @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); @@ -136,13 +164,12 @@ public abstract class SsaInsn implements ToHuman, Cloneable { */ abstract public void mapSourceRegisters(RegisterMapper mapper); - /** - * Returns the Rop opcode for this insn, or null if this is a phi insn + * Returns the Rop opcode for this insn, or null if this is a phi insn. * - * TODO move this up into NormalSsaInsn + * TODO: Move this up into NormalSsaInsn. * - * @return null-ok; Rop opcode if there is one. + * @return {@code null-ok;} Rop opcode if there is one. */ abstract public Rop getOpcode(); @@ -150,20 +177,21 @@ public abstract class SsaInsn implements ToHuman, Cloneable { * Returns the original Rop insn for this insn, or null if this is * a phi insn. * - * TODO move this up into NormalSsaInsn + * TODO: Move this up into NormalSsaInsn. * - * @return null-ok; Rop insn if there is one. + * @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 mark-local insns + * may be the result register, or for {@code mark-local} insns * it may be the source. * - * @return null-ok; a local-associated register spec or null * @see com.android.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) { @@ -176,7 +204,8 @@ public abstract class SsaInsn implements ToHuman, Cloneable { /** * Indicates whether the specified register is amongst the registers * used as sources for this instruction. - * @param reg The register in question + * + * @param reg the register in question * @return true if the reg is a source */ public boolean isRegASource(int reg) { @@ -186,9 +215,9 @@ public abstract class SsaInsn implements ToHuman, Cloneable { /** * Transform back to ROP form. * - * TODO move this up into NormalSsaInsn + * TODO: Move this up into NormalSsaInsn. * - * @return non-null; a ROP representation of this instruction, with + * @return {@code non-null;} a ROP representation of this instruction, with * updated registers. */ public abstract Insn toRopInsn(); @@ -208,8 +237,8 @@ public abstract class SsaInsn implements ToHuman, Cloneable { public abstract boolean hasSideEffect(); /** - * @return true if this is a move (but not a move-operand or move-exception) - * instruction + * @return true if this is a move (but not a move-operand or + * move-exception) instruction */ public boolean isNormalMoveInsn() { return false; @@ -229,8 +258,9 @@ public abstract class SsaInsn implements ToHuman, Cloneable { abstract public boolean canThrow(); /** - * accepts a visitor - * @param v visitor + * Accepts a visitor. + * + * @param v {@code non-null} the visitor */ public abstract void accept(Visitor v); @@ -238,22 +268,21 @@ public abstract class SsaInsn implements ToHuman, Cloneable { * Visitor interface for this class. */ public static interface Visitor { - /** * Any non-phi move instruction - * @param insn non-null; the instruction to visit + * @param insn {@code non-null;} the instruction to visit */ public void visitMoveInsn(NormalSsaInsn insn); /** * Any phi insn - * @param insn non-null; the instruction to visit + * @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 non-null; the instruction to visit + * @param insn {@code non-null;} the instruction to visit */ public void visitNonMoveInsn(NormalSsaInsn insn); } diff --git a/dx/src/com/android/dx/ssa/SsaMethod.java b/dx/src/com/android/dx/ssa/SsaMethod.java index 49f8ea5f7..073e5153c 100644 --- a/dx/src/com/android/dx/ssa/SsaMethod.java +++ b/dx/src/com/android/dx/ssa/SsaMethod.java @@ -36,10 +36,9 @@ import java.util.Stack; import java.util.Set; /** - * A method in SSA form + * A method in SSA form. */ public final class SsaMethod { - /** basic blocks, indexed by block index */ private ArrayList blocks; @@ -48,12 +47,17 @@ public final class SsaMethod { /** * Index of exit block, which exists only in SSA form, - * or or -1 if there is none + * 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 */ @@ -62,7 +66,7 @@ public final class SsaMethod { /** the total width, in register-units, of the method's parameters */ private final int paramWidth; - /** true if this method has no 'this' pointer argument */ + /** true if this method has no {@code this} pointer argument */ private final boolean isStatic; /** @@ -73,6 +77,7 @@ public final class SsaMethod { /** indexed by register: the list of all insns that use a register */ private ArrayList[] useList; + /** A version of useList with each List unmodifiable */ private List[] unmodifiableUseList; @@ -81,47 +86,58 @@ public final class SsaMethod { * are about to be mapped into a non-SSA namespace. When true, * use and def lists are unavailable. * - * TODO remove this mode, plase the functionality elsewhere + * TODO: Remove this mode, and place the functionality elsewhere */ - private boolean backMode = false; + private boolean backMode; /** - * @param rmeth RopMethod to convert from + * @param ropMethod rop-form method to convert from * @param paramWidth the total width, in register-units, of the * method's parameters - * @param isStatic true if this method has no 'this' pointer argument - * @return SsaMethod representation + * @param isStatic {@code true} if this method has no {@code this} + * pointer argument */ - static SsaMethod newFromRopMethod(RopMethod rmeth, int paramWidth, - boolean isStatic) { - SsaMethod result; - - result = new SsaMethod(paramWidth, isStatic); + public static SsaMethod newFromRopMethod(RopMethod ropMethod, + int paramWidth, boolean isStatic) { + SsaMethod result = new SsaMethod(ropMethod, paramWidth, isStatic); - result.maxLabel = rmeth.getBlocks().getMaxLabel(); - result.registerCount = rmeth.getBlocks().getRegCount(); - result.spareRegisterBase = result.registerCount; - - result.convertRopToSsaBlocks(rmeth); + 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 + * 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()); - BitSet result; - - result = new BitSet(blocks.size()); - - for (int i = 0, sz = labelList.size() ; i < sz ; i++) { + for (int i = 0, sz = labelList.size(); i < sz; i++) { result.set(blocks.indexOfLabel(labelList.get(i))); } @@ -130,7 +146,8 @@ public final class SsaMethod { /** * Builds an IntList of block indices from a basic block list and a list - * of labels taken from Rop form + * of labels taken from Rop form. + * * @param ropBlocks Rop blocks * @param labelList list of rop block labels * @return IntList of block indices @@ -138,35 +155,27 @@ public final class SsaMethod { public static IntList indexListFromLabelList(BasicBlockList ropBlocks, IntList labelList) { - IntList result; + IntList result = new IntList(labelList.size()); - result = new IntList(labelList.size()); - - for (int i = 0, sz = labelList.size() ; i < sz ; i++) { + 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; + private void convertRopToSsaBlocks(RopMethod rmeth) { + BasicBlockList ropBlocks = rmeth.getBlocks(); + int sz = ropBlocks.size(); - ropBlocks = rmeth.getBlocks(); - - blocks = new ArrayList(ropBlocks.size() + 2); - - for (int i = 0, sz = ropBlocks.size() ; i < sz ; i++) { - SsaBasicBlock sbb; - - sbb = SsaBasicBlock.newFromRop(rmeth, i, this); + blocks = new ArrayList(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 + // Add an no-op entry block. int origEntryBlockIndex = rmeth.getBlocks() .indexOfLabel(rmeth.getFirstLabel()); @@ -174,18 +183,16 @@ public final class SsaMethod { = blocks.get(origEntryBlockIndex).insertNewPredecessor(); entryBlockIndex = entryBlock.getIndex(); - exitBlockIndex = -1; // this gets made later - + exitBlockIndex = -1; // This gets made later. } - /** * Creates an exit block and attaches it to the CFG if this method * exits. Methods that never exit will not have an exit block. This * is called after edge-splitting and phi insertion, since the edges * going into the exit block should not be considered in those steps. */ - void makeExitBlock() { + /*package*/ void makeExitBlock() { if (exitBlockIndex >= 0) { throw new RuntimeException("must be called at most once"); } @@ -196,7 +203,7 @@ public final class SsaMethod { blocks.add(exitBlock); - for (SsaBasicBlock block: blocks) { + for (SsaBasicBlock block : blocks) { block.exitBlockFixup(exitBlock); } @@ -209,22 +216,10 @@ public final class SsaMethod { } /** - * Constructor - * - * @param paramWidth the total width, in register-units, of the - * method's parameters - * @param isStatic true if this method has no 'this' pointer argument - */ - private SsaMethod(int paramWidth, boolean isStatic) { - this.paramWidth = paramWidth; - this.isStatic = isStatic; - } - - /** - * Gets a new GOTO insn. + * Gets a new {@code GOTO} insn. * * @param block block to which this GOTO will be added - * (not it's destination!) + * (not it's destination!) * @return an appropriately-constructed instance. */ private static SsaInsn getGoto(SsaBasicBlock block) { @@ -232,11 +227,11 @@ public final class SsaMethod { 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 GOTO. Successors and - * predecessors are not yet set. + * 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 */ @@ -265,22 +260,23 @@ public final class SsaMethod { } /** - * @return block index of exit block or -1 if there is none + * @return block index of exit block or {@code -1} if there is none */ public int getExitBlockIndex() { return exitBlockIndex; } /** - * @return null-ok; block of exit block or null if there is none + * @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 -1 for none - * @return rop label or -1 if bi was -1 + * @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) { @@ -297,7 +293,7 @@ public final class SsaMethod { } /** - * @return the total width, in register units, of the method's + * @return the total width, in register units, of the method's * parameters */ public int getParamWidth() { @@ -305,27 +301,25 @@ public final class SsaMethod { } /** - * Returns true if this is a static method. + * Returns {@code true} if this is a static method. * - * @return true if this is a static method + * @return {@code true} if this is a static method */ public boolean isStatic() { return isStatic; } /** - * Borrow a register to use as a temp. Used in the phi removal process. + * 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; - - result = spareRegisterBase + borrowedSpareRegisters; + int result = spareRegisterBase + borrowedSpareRegisters; borrowedSpareRegisters += category; - registerCount = Math.max(registerCount, result + category); return result; @@ -339,7 +333,7 @@ public final class SsaMethod { } /** - * @return non-null; basic block list, do not modify. + * @return {@code non-null;} basic block list. Do not modify. */ public ArrayList getBlocks() { return blocks; @@ -349,12 +343,12 @@ public final class SsaMethod { * Returns the count of reachable blocks in this method: blocks that have * predecessors (or are the start block) * - * @return >= 0; number of reachable basic blocks + * @return {@code >= 0;} number of reachable basic blocks */ public int getCountReachableBlocks() { int ret = 0; - for (SsaBasicBlock b: blocks) { + for (SsaBasicBlock b : blocks) { // Blocks that have been disconnected don't count. if (b.isReachable()) { ret++; @@ -366,16 +360,16 @@ public final class SsaMethod { /** * Remaps unversioned registers. + * * @param mapper maps old registers to new. */ public void mapRegisters(RegisterMapper mapper) { - - for (SsaBasicBlock block: getBlocks()) { - for (SsaInsn insn: block.getInsns()) { + for (SsaBasicBlock block : getBlocks()) { + for (SsaInsn insn : block.getInsns()) { insn.mapRegisters(mapper); } - } - + } + registerCount = mapper.getNewRegisterCount(); spareRegisterBase = registerCount; } @@ -425,7 +419,7 @@ public final class SsaMethod { useList = new ArrayList[registerCount]; - for (int i = 0 ; i < registerCount; i++) { + for (int i = 0; i < registerCount; i++) { useList[i] = new ArrayList(); } @@ -444,7 +438,7 @@ public final class SsaMethod { } /** * Adds specified insn to the uses list for all of its sources. - * @param insn non-null; insn to process + * @param insn {@code non-null;} insn to process */ private void addToUses(SsaInsn insn) { RegisterSpecList rl = insn.getSources(); @@ -458,7 +452,7 @@ public final class SsaMethod { unmodifiableUseList = new List[registerCount]; - for (int i = 0 ; i < registerCount; i++) { + for (int i = 0; i < registerCount; i++) { unmodifiableUseList[i] = Collections.unmodifiableList(useList[i]); } } @@ -466,15 +460,15 @@ public final class SsaMethod { /** * Updates the use list for a single change in source register. * - * @param insn non-null; insn being changed - * @param oldSource null-ok; The source that was used, if applicable - * @param newSource non-null; the new source being used + * @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 */ - void onSourceChanged(SsaInsn insn, + /*package*/ void onSourceChanged(SsaInsn insn, RegisterSpec oldSource, RegisterSpec newSource) { - if (useList == null) return; - + if (oldSource != null) { int reg = oldSource.getReg(); useList[reg].remove(insn); @@ -491,11 +485,13 @@ public final class SsaMethod { /** * Updates the use list for a source list change. * - * @param insn insn non-null; insn being changed. insn.getSources() - * must return the new source list. - * @param oldSources null-ok; list of sources that were previously used. + * @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 */ - void onSourcesChanged(SsaInsn insn, RegisterSpecList oldSources) { + /*package*/ void onSourcesChanged(SsaInsn insn, + RegisterSpecList oldSources) { if (useList == null) return; if (oldSources != null) { @@ -505,27 +501,28 @@ public final class SsaMethod { RegisterSpecList sources = insn.getSources(); int szNew = sources.size(); - for(int i = 0; i < szNew; i++) { + for (int i = 0; i < szNew; i++) { int reg = sources.get(i).getReg(); useList[reg].add(insn); - } + } } /** - * Removes a given insn from the use lists for the given - * oldSources (rather than the sources currently + * 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 non-null; insn in question - * @param oldSources null-ok; registers whose use lists insn - * should be removed form. + * @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++) { + for (int i = 0; i < szNew; i++) { if (!useList[oldSources.get(i).getReg()].remove(insn)) { throw new RuntimeException("use not found"); } @@ -536,35 +533,35 @@ public final class SsaMethod { * Adds an insn to both the use and def lists. For use when adding * a new insn to the method. * - * @param insn non-null; insn to add + * @param insn {@code non-null;} insn to add */ - void onInsnAdded(SsaInsn insn) { + /*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 non-null; insn to remove. - */ - void onInsnRemoved(SsaInsn insn) { - if (useList != null) { - removeFromUseList(insn, insn.getSources()); - } + * 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; - } - } + 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 + * should be called in preference when changes occur if they are * applicable. */ public void onInsnsChanged() { @@ -579,19 +576,22 @@ public final class SsaMethod { /** * Updates a single definition. * - * @param insn non-null; insn who's result should be recorded as + * @param insn {@code non-null;} insn who's result should be recorded as * a definition - * @param oldResult null-ok; a previous result that should be no longer - * considered a definition by this insn + * @param oldResult {@code null-ok;} a previous result that should + * be no longer considered a definition by this insn */ - void updateOneDefinition(SsaInsn insn, RegisterSpec oldResult) { + /*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(); @@ -604,7 +604,8 @@ public final class SsaMethod { } /** - * Returns the list of all source uses (not results) for a register + * Returns the list of all source uses (not results) for a register. + * * @param reg register in question * @return unmodifiable instruction list */ @@ -619,6 +620,7 @@ public final class SsaMethod { /** * Returns a modifiable copy of the register use list. + * * @return modifiable copy of the use-list, indexed by register */ public ArrayList[] getUseListCopy() { @@ -641,7 +643,7 @@ public final class SsaMethod { * local variable. Each SSA reg may be associated with at most one * local var. * - * @param spec non-null; ssa reg + * @param spec {@code non-null;} ssa reg * @return true if reg is ever associated with a local */ public boolean isRegALocal(RegisterSpec spec) { @@ -656,7 +658,7 @@ public final class SsaMethod { if (defn.getLocalAssignment() != null) return true; // If not, is there a mark-local insn? - for (SsaInsn use: getUseListForRegister(spec.getReg())) { + for (SsaInsn use : getUseListForRegister(spec.getReg())) { Insn insn = use.getOriginalRopInsn(); if (insn != null @@ -664,12 +666,13 @@ public final class SsaMethod { return true; } } - + return false; } /** * Sets the new register count after renaming. + * * @param newRegCount new register count */ /*package*/ void setNewRegCount(int newRegCount) { @@ -681,48 +684,52 @@ public final class SsaMethod { /** * Makes a new SSA register. For use after renaming has completed. * - * @return >=0 new SSA register. + * @return {@code >=0;} new SSA register. */ public int makeNewSsaReg() { int reg = registerCount++; spareRegisterBase = registerCount; onInsnsChanged(); - return reg; + return reg; } /** - * Visit all insns in this method - * @param visitor non-null; callback interface + * Visits all insns in this method. + * + * @param visitor {@code non-null;} callback interface */ public void forEachInsn(SsaInsn.Visitor visitor) { - for (SsaBasicBlock block: blocks) { + for (SsaBasicBlock block : blocks) { block.forEachInsn(visitor); } } /** * Visits each phi insn in this method - * @param v non-null; callback + * @param v {@code non-null;} callback. + * */ public void forEachPhiInsn(PhiInsn.Visitor v) { - for (SsaBasicBlock block: blocks) { + for (SsaBasicBlock block : blocks) { block.forEachPhiInsn(v); } } /** - * Walk the basic block tree in depth-first order, calling the visitor + * 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 non-null; callback interface. parentis set + * @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 + + // We push the parent first, then the child on the stack. Stack stack = new Stack(); SsaBasicBlock rootBlock = reverse ? getExitBlock() : getEntryBlock(); @@ -732,7 +739,7 @@ public final class SsaMethod { return; } - stack.add(null); // start with null parent + stack.add(null); // Start with null parent. stack.add(rootBlock); while (stack.size() > 0) { @@ -741,7 +748,7 @@ public final class SsaMethod { if (!visited.get(cur.getIndex())) { BitSet children - = reverse ? cur.getPredecessors() : cur.getSuccessors(); + = reverse ? cur.getPredecessors() : cur.getSuccessors(); for (int i = children.nextSetBit(0); i >= 0 ; i = children.nextSetBit(i + 1)) { stack.add(cur); @@ -755,10 +762,10 @@ public final class SsaMethod { /** * Visits blocks in dom-tree order, starting at the current node. - * The parent parameter of the Visitor.visitBlock callback + * The {@code parent} parameter of the Visitor.visitBlock callback * is currently always set to null. * - * @param v non-null; callback interface + * @param v {@code non-null;} callback interface */ public void forEachBlockDepthFirstDom(SsaBasicBlock.Visitor v) { BitSet visited = new BitSet(getBlocks().size()); @@ -783,12 +790,12 @@ public final class SsaMethod { } /** - * Deletes all insns in the set from this method + * Deletes all insns in the set from this method. * - * @param deletedInsns non-null; insns to delete + * @param deletedInsns {@code non-null;} insns to delete */ public void deleteInsns(Set deletedInsns) { - for (SsaBasicBlock block: getBlocks()) { + for (SsaBasicBlock block : getBlocks()) { ArrayList insns = block.getInsns(); for (int i = insns.size() - 1; i >= 0; i--) { @@ -819,7 +826,7 @@ public final class SsaMethod { } /** - * Set "back-convert mode". Set during back-conversion when registers + * 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. */ diff --git a/dx/src/com/android/dx/ssa/SsaRenamer.java b/dx/src/com/android/dx/ssa/SsaRenamer.java index 64bad2c88..8452c0308 100644 --- a/dx/src/com/android/dx/ssa/SsaRenamer.java +++ b/dx/src/com/android/dx/ssa/SsaRenamer.java @@ -54,11 +54,11 @@ import java.util.HashMap; * current block has been processed, this mapping table is then copied * and used as the initial state for child blocks.

    */ -class SsaRenamer implements Runnable { - +public class SsaRenamer implements Runnable { + /** debug flag */ private static final boolean DEBUG = false; - /** Method we're processing */ + /** method we're processing */ private final SsaMethod ssaMeth; /** next available SSA register */ @@ -68,10 +68,10 @@ class SsaRenamer implements Runnable { private final int ropRegCount; /** - * Indexed by block index; register version state for each block start. + * 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 BlockRenamer instance. + * yet to be processed by a {@code BlockRenamer} instance. */ private final RegisterSpec[][] startsForBlocks; @@ -79,24 +79,25 @@ class SsaRenamer implements Runnable { private final ArrayList ssaRegToLocalItems; /** - * Maps SSA registers back to the original rop number. - * Used for debug only. + * maps SSA registers back to the original rop number. Used for + * debug only. */ private IntList ssaRegToRopReg; /** * Constructs an instance of the renamer * - * @param ssaMeth non-null; un-renamed SSA method that will + * @param ssaMeth {@code non-null;} un-renamed SSA method that will * be renamed. */ - SsaRenamer (final SsaMethod ssaMeth) { + public SsaRenamer(SsaMethod ssaMeth) { ropRegCount = ssaMeth.getRegCount(); this.ssaMeth = ssaMeth; + /* * Reserve the first N registers in the SSA register space for - * "version 0" registers + * "version 0" registers. */ nextSsaReg = ropRegCount; startsForBlocks = new RegisterSpec[ssaMeth.getBlocks().size()][]; @@ -138,10 +139,10 @@ class SsaRenamer implements Runnable { * in-place. */ public void run() { - // Rename each block in dom-tree DFS order. ssaMeth.forEachBlockDepthFirstDom(new SsaBasicBlock.Visitor() { - public void visitBlock (SsaBasicBlock block, SsaBasicBlock unused) { + public void visitBlock (SsaBasicBlock block, + SsaBasicBlock unused) { new BlockRenamer(block).process(); } }); @@ -151,15 +152,17 @@ class SsaRenamer implements Runnable { 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. + /* + * 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 + "[" + for (int i = 0; i < sz; i++) { + int ropReg = ssaRegToRopReg.get(i); + System.out.println(i + "\t" + ropReg + "[" + versions[ropReg] + "]"); versions[ropReg]++; } @@ -167,9 +170,10 @@ class SsaRenamer implements Runnable { } /** - * Duplicates a RegisterSpec array - * @param orig non-null; array to duplicate - * @return non-null; new instance + * 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]; @@ -183,7 +187,7 @@ class SsaRenamer implements Runnable { * Gets a local variable item for a specified register. * * @param ssaReg register in SSA name space - * @return null-ok; Local variable name or null if none + * @return {@code null-ok;} Local variable name or null if none */ private LocalItem getLocalForNewReg(int ssaReg) { if (ssaReg < ssaRegToLocalItems.size()) { @@ -223,8 +227,8 @@ class SsaRenamer implements Runnable { } /** - * Returns true if a and b are equal or are both null - + * 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 @@ -238,26 +242,26 @@ class SsaRenamer implements Runnable { * as appropriate. */ private class BlockRenamer implements SsaInsn.Visitor{ - /** non-null; block we're processing. */ + /** {@code non-null;} block we're processing. */ private final SsaBasicBlock block; /** - * non-null; indexed by old register name. The current top of the - * version stack as seen by this block. It's initialized from - * the ending state of its dom parent, updated as the block's - * instructions are processed, and then copied to each one of its - * dom children. + * {@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. + * contains the set of moves we need to keep to preserve local + * var info. All other moves will be deleted. */ private final HashSet movesToKeep; /** - * Maps the set of insns to replace after renaming is finished + * maps the set of insns to replace after renaming is finished * on the block. */ private final HashMap insnsToReplace; @@ -265,10 +269,10 @@ class SsaRenamer implements Runnable { private final RenamingMapper mapper; /** - * Constructs a block renamer instance. Call process + * Constructs a block renamer instance. Call {@code process} * to process. * - * @param block non-null; block to process + * @param block {@code non-null;} block to process */ BlockRenamer(final SsaBasicBlock block) { this.block = block; @@ -287,8 +291,8 @@ class SsaRenamer implements Runnable { * as the current block's instructions are processed. */ private class RenamingMapper extends RegisterMapper { - - RenamingMapper() { + public RenamingMapper() { + // This space intentionally left blank. } /** {@inheritDoc} */ @@ -304,8 +308,8 @@ class SsaRenamer implements Runnable { int reg = registerSpec.getReg(); - // for debugging: assert that the mapped types are compatible - if(DEBUG) { + // For debugging: assert that the mapped types are compatible. + if (DEBUG) { RegisterSpec newVersion = currentMapping[reg]; if (newVersion.getBasicType() != Type.BT_VOID && registerSpec.getBasicFrameType() @@ -338,7 +342,7 @@ class SsaRenamer implements Runnable { updateSuccessorPhis(); - // Delete all move insns in this block + // Delete all move insns in this block. ArrayList insns = block.getInsns(); int szInsns = insns.size(); @@ -349,29 +353,27 @@ class SsaRenamer implements Runnable { replaceInsn = insnsToReplace.get(insn); if (replaceInsn != null) { - insns.set(i, replaceInsn); + insns.set(i, replaceInsn); } else if (insn.isNormalMoveInsn() && !movesToKeep.contains(insn)) { insns.remove(i); } } - // Store the start states for our dom children + // Store the start states for our dom children. boolean first = true; - for (SsaBasicBlock child: block.getDomChildren()) { + for (SsaBasicBlock child : block.getDomChildren()) { if (child != block) { - RegisterSpec[] childStart; - - // don't bother duplicating the array for the first child - childStart = first ? currentMapping - : dupArray(currentMapping); + // 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 + // currentMapping is owned by a child now. } /** @@ -388,13 +390,13 @@ class SsaRenamer implements Runnable { * local. *

  • ensures that only one SSA register * at a time is considered to be associated with a local variable. When - * currentMapping is updated and the newly added element + * {@code currentMapping} is updated and the newly added element * is named, strip that name from any other SSA registers. * * - * @param ropReg >= 0 Rop register number - * @param ssaReg non-null; An SSA register that has just - * been added to currentMapping + * @param ropReg {@code >= 0;} rop register number + * @param ssaReg {@code non-null;} an SSA register that has just + * been added to {@code currentMapping} */ private void addMapping(int ropReg, RegisterSpec ssaReg) { int ssaRegNum = ssaReg.getReg(); @@ -413,15 +415,15 @@ class SsaRenamer implements Runnable { } } - // All further steps are for registers with local information + // All further steps are for registers with local information. if (ssaRegLocal == null) { return; } - // Record that this SSA reg has been associated with a local + // Record that this SSA reg has been associated with a local. setNameForSsaReg(ssaReg); - // Ensure that no other SSA regs are associated with this local + // Ensure that no other SSA regs are associated with this local. for (int i = currentMapping.length - 1; i >= 0; i--) { RegisterSpec cur = currentMapping[i]; @@ -436,7 +438,7 @@ class SsaRenamer implements Runnable { * {@inheritDoc} * * Phi insns have their result registers renamed. - * */ + */ public void visitPhiInsn(PhiInsn phi) { /* don't process sources for phi's */ processResultReg(phi); @@ -452,7 +454,7 @@ class SsaRenamer implements Runnable { */ public void visitMoveInsn(NormalSsaInsn insn) { /* - * for moves: copy propogate the move if we can, but don't + * 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. */ @@ -464,7 +466,8 @@ class SsaRenamer implements Runnable { insn.mapSourceRegisters(mapper); int ssaSourceReg = insn.getSources().get(0).getReg(); - LocalItem sourceLocal = currentMapping[ropSourceReg].getLocalItem(); + LocalItem sourceLocal + = currentMapping[ropSourceReg].getLocalItem(); LocalItem resultLocal = ropResult.getLocalItem(); /* @@ -475,25 +478,26 @@ class SsaRenamer implements Runnable { */ LocalItem newLocal - = (resultLocal == null) ? sourceLocal : resultLocal; - + = (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? + /* + * 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 + * 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. + * The move source has incomplete type information in null + * object cases, so the result type is used. */ RegisterSpec ssaReg = RegisterSpec.makeLocalOptional( @@ -509,15 +513,12 @@ class SsaRenamer implements Runnable { addMapping(ropResultReg, ssaReg); } else if (onlyOneAssociatedLocal && sourceLocal == null) { - /* * The register was previously unnamed. This means that a * local starts after it's first assignment in SSA form */ - RegisterSpecList ssaSources; - - ssaSources = RegisterSpecList.make( + RegisterSpecList ssaSources = RegisterSpecList.make( RegisterSpec.make(ssaReg.getReg(), ssaReg.getType(), newLocal)); @@ -528,12 +529,12 @@ class SsaRenamer implements Runnable { insnsToReplace.put(insn, newInsn); - // Just map as above + // Just map as above. addMapping(ropResultReg, ssaReg); } else { /* - * Do not copy-propogate, since the two registers - * have two different local-variable names + * Do not copy-propogate, since the two registers have + * two different local-variable names. */ processResultReg(insn); @@ -594,11 +595,12 @@ class SsaRenamer implements Runnable { ropReg = insn.getRopResultReg(); /* - * Never add a version 0 register as a phi operand. - * Version 0 registers represent the initial register state, - * and thus are never significant. Furthermore, - * the register liveness algorithm doesn't properly - * count them as "live in" at the beginning of the method. + * 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]; @@ -611,9 +613,7 @@ class SsaRenamer implements Runnable { BitSet successors = block.getSuccessors(); for (int i = successors.nextSetBit(0); i >= 0; i = successors.nextSetBit(i + 1)) { - SsaBasicBlock successor = ssaMeth.getBlocks().get(i); - successor.forEachPhiInsn(visitor); } } diff --git a/dx/src/com/android/dx/ssa/_tests/_DomFront.java b/dx/src/com/android/dx/ssa/_tests/_DomFront.java index 997da2143..3d891c9bb 100644 --- a/dx/src/com/android/dx/ssa/_tests/_DomFront.java +++ b/dx/src/com/android/dx/ssa/_tests/_DomFront.java @@ -19,7 +19,7 @@ package com.android.dx.ssa._tests; import junit.framework.TestCase; /** - * Test the class com.android.dx.ssa.DomFront. + * Test the class {@code com.android.dx.ssa.DomFront}. */ public class _DomFront extends TestCase { diff --git a/dx/src/com/android/dx/ssa/back/FirstFitAllocator.java b/dx/src/com/android/dx/ssa/back/FirstFitAllocator.java index d3ff7c78d..6416e84e8 100644 --- a/dx/src/com/android/dx/ssa/back/FirstFitAllocator.java +++ b/dx/src/com/android/dx/ssa/back/FirstFitAllocator.java @@ -106,7 +106,6 @@ public class FirstFitAllocator extends RegisterAllocator { } for (int j = i + 1; j < oldRegCount; j++) { - if (mapped.get(j) || isDefinitionMoveParam(j)) { continue; } diff --git a/dx/src/com/android/dx/ssa/back/FirstFitLocalCombiningAllocator.java b/dx/src/com/android/dx/ssa/back/FirstFitLocalCombiningAllocator.java index 14eac9057..0cffcfac1 100644 --- a/dx/src/com/android/dx/ssa/back/FirstFitLocalCombiningAllocator.java +++ b/dx/src/com/android/dx/ssa/back/FirstFitLocalCombiningAllocator.java @@ -40,9 +40,10 @@ import java.util.TreeMap; * kept together if possible. */ public class FirstFitLocalCombiningAllocator extends RegisterAllocator { + /** local debug flag */ private static final boolean DEBUG = false; - /** maps local variable to a list of associated SSA registers*/ + /** maps local variable to a list of associated SSA registers */ private final Map> localVariables; /** list of move-result-pesudo instructions seen in this method */ @@ -57,29 +58,28 @@ public class FirstFitLocalCombiningAllocator extends RegisterAllocator { /** Register mapper which will be our result */ private final InterferenceRegisterMapper mapper; - /** end of rop registers range (starting at 0) reserved for parameters. */ + /** 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. */ + /** set of rop registers reserved for parameters or local variables */ private final BitSet reservedRopRegs; - /** set of Rop registers that have been used by anything.*/ + /** 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*/ + /** true if converter should take steps to minimize rop-form registers */ private final boolean minimizeRegisters; - /** * Constructs instance. * - * @param ssaMeth non-null; method to process + * @param 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( - final SsaMethod ssaMeth, InterferenceGraph interference, + SsaMethod ssaMeth, InterferenceGraph interference, boolean minimizeRegisters) { super(ssaMeth, interference); @@ -122,22 +122,24 @@ public class FirstFitLocalCombiningAllocator extends RegisterAllocator { printLocalVars(); } - if(DEBUG) System.out.println("--->Mapping local-associated params"); + if (DEBUG) System.out.println("--->Mapping local-associated params"); handleLocalAssociatedParams(); - if(DEBUG) System.out.println("--->Mapping other params"); + if (DEBUG) System.out.println("--->Mapping other params"); handleUnassociatedParameters(); - if(DEBUG) System.out.println("--->Mapping invoke-range"); + if (DEBUG) System.out.println("--->Mapping invoke-range"); handleInvokeRangeInsns(); - - if(DEBUG) System.out.println("--->Mapping local-associated non-params"); + + if (DEBUG) { + System.out.println("--->Mapping local-associated non-params"); + } handleLocalAssociatedOther(); - if(DEBUG) System.out.println("--->Mapping check-cast results"); + if (DEBUG) System.out.println("--->Mapping check-cast results"); handleCheckCastResults(); - if(DEBUG) System.out.println("--->Mapping others"); + if (DEBUG) System.out.println("--->Mapping others"); handleNormalUnassociated(); return mapper; @@ -148,13 +150,13 @@ public class FirstFitLocalCombiningAllocator extends RegisterAllocator { */ private void printLocalVars() { System.out.println("Printing local vars"); - for (Map.Entry> e: + for (Map.Entry> e : localVariables.entrySet()) { StringBuilder regs = new StringBuilder(); regs.append('{'); regs.append(' '); - for(RegisterSpec reg: e.getValue()) { + for (RegisterSpec reg : e.getValue()) { regs.append('v'); regs.append(reg.getReg()); regs.append(' '); @@ -165,16 +167,16 @@ public class FirstFitLocalCombiningAllocator extends RegisterAllocator { } /** - * Maps all local-associated parameters to Rop registers. + * Maps all local-associated parameters to rop registers. */ private void handleLocalAssociatedParams() { - for (ArrayList ssaRegs: localVariables.values()) { + for (ArrayList 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++) { + // 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(); @@ -188,31 +190,31 @@ public class FirstFitLocalCombiningAllocator extends RegisterAllocator { } if (paramIndex < 0) { - // this local wasn't a parameter + // This local wasn't a parameter. continue; } - // Any remaining local-associated registers will be mapped later + // Any remaining local-associated registers will be mapped later. tryMapRegs(ssaRegs, paramIndex, paramCategory, true); } } /** * Gets the parameter index for SSA registers that are method parameters. - * -1 is returned for non-parameter registers. + * {@code -1} is returned for non-parameter registers. * - * @param ssaReg >=0 SSA register to look up - * @return parameter index or -1 if not a parameter + * @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; } - + Rop opcode = defInsn.getOpcode(); - // opcode == null for phi insns + // opcode == null for phi insns. if (opcode != null && opcode.getOpcode() == RegOps.MOVE_PARAM) { CstInsn origInsn = (CstInsn) defInsn.getOriginalRopInsn(); return ((CstInteger) origInsn.getConstant()).getValue(); @@ -222,20 +224,21 @@ public class FirstFitLocalCombiningAllocator extends RegisterAllocator { } /** - * 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. + * 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 specs: localVariables.values()) { + for (ArrayList specs : localVariables.values()) { int ropReg = 0; boolean done; do { int maxCategory = 1; - // compute max category for remaining unmapped registers + // Compute max category for remaining unmapped registers. int sz = specs.size(); for (int i = 0; i < sz; i++) { RegisterSpec ssaSpec = specs.get(i); @@ -250,7 +253,7 @@ public class FirstFitLocalCombiningAllocator extends RegisterAllocator { done = tryMapRegs(specs, ropReg, maxCategory, true); - // Increment for next call to findNext + // Increment for next call to findNext. ropReg++; } while (!done); } @@ -261,17 +264,19 @@ public class FirstFitLocalCombiningAllocator extends RegisterAllocator { * used rop space as reserved. SSA registers that don't fit are left * unmapped. * - * @param specs non-null; SSA registers to attempt to map - * @param ropReg >=0 rop register to map to - * @param maxAllowedCategory 1 or 2, maximum category allowed in mapping. - * @param markReserved do so if true - * @return true if all registers wew mapped, false if some remain unmapped. + * @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 specs, int ropReg, int maxAllowedCategory, boolean markReserved) { boolean remaining = false; - for(RegisterSpec spec: specs) { + for (RegisterSpec spec : specs) { if (ssaRegsMapped.get(spec.getReg())) { continue; } @@ -291,11 +296,11 @@ public class FirstFitLocalCombiningAllocator extends RegisterAllocator { /** * Tries to map an SSA register to a rop register. * - * @param ssaSpec non-null; SSA register - * @param ropReg >=0 rop register - * @param maxAllowedCategory 1 or 2, the maximum category that the SSA - * register is allowed to be. - * @return true if map succeeded, false if not. + * @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) { @@ -310,22 +315,22 @@ public class FirstFitLocalCombiningAllocator extends RegisterAllocator { } /** - * Marks a range of Rop registers as "reserved for a local variable" + * Marks a range of rop registers as "reserved for a local variable." * - * @param ropReg >= 0 rop register to reserve - * @param category > 0 width to reserve + * @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 + * Checks to see if any rop registers in the specified range are reserved + * for local variables or parameters. * - * @param ropRangeStart >= 0 lowest Rop register - * @param width > 0 number of Rop registers in range. - * @return true if any register in range is marked reserved + * @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++) { @@ -337,23 +342,23 @@ public class FirstFitLocalCombiningAllocator extends RegisterAllocator { } /** - * Returns true if given rop register represents the "this" pointer - * for a non-static method + * 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 + // "this" is always the first parameter. return startReg == 0 && !ssaMeth.isStatic(); } /** - * Finds a range of unreserved Rop registers. + * Finds a range of unreserved rop registers. * - * @param startReg >= 0; a Rop register to start the search at - * @param width > 0; the width, in registers, required. - * @return >= 0; start of available register range. + * @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) { if (minimizeRegisters && !isThisPointerReg(startReg)) { @@ -381,12 +386,12 @@ public class FirstFitLocalCombiningAllocator extends RegisterAllocator { /** * Finds a range of rop regs that can be used for local variables. - * If MIX_LOCALS_AND_OTHER is false, this means any + * If {@code MIX_LOCALS_AND_OTHER} is {@code false}, this means any * rop register that has not yet been used. * - * @param startReg >= 0; a Rop register to start the search at - * @param width > 0; the width, in registers, required. - * @return >= 0; start of available register range. + * @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) { if (minimizeRegisters && !isThisPointerReg(startReg)) { @@ -418,6 +423,7 @@ public class FirstFitLocalCombiningAllocator extends RegisterAllocator { */ private void handleUnassociatedParameters() { int szSsaRegs = ssaMeth.getRegCount(); + for (int ssaReg = 0; ssaReg < szSsaRegs; ssaReg++) { if (ssaRegsMapped.get(ssaReg)) { // We already did this one above @@ -429,7 +435,7 @@ public class FirstFitLocalCombiningAllocator extends RegisterAllocator { RegisterSpec ssaSpec = getDefinitionSpecForSsaReg(ssaReg); if (paramIndex >= 0) { addMapping(ssaSpec, paramIndex); - } + } } } @@ -437,13 +443,14 @@ public class FirstFitLocalCombiningAllocator extends RegisterAllocator { * Handles all insns that want a register range for their sources. */ private void handleInvokeRangeInsns() { - for(NormalSsaInsn insn: invokeRangeInsns) { + for (NormalSsaInsn insn : invokeRangeInsns) { adjustAndMapSourceRangeRange(insn); } } /** - * Handles check cast results to reuse the same source register if possible + * Handles check cast results to reuse the same source register if + * possible. */ private void handleCheckCastResults() { for (NormalSsaInsn insn : moveResultPseudoInsns) { @@ -498,11 +505,11 @@ public class FirstFitLocalCombiningAllocator extends RegisterAllocator { } /** - * Maps all non-parameter, non-local variable - * registers. + * 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 @@ -525,30 +532,30 @@ public class FirstFitLocalCombiningAllocator extends RegisterAllocator { } /** - * Checks to see if ssaSpec can be mapped to - * ropReg. Checks interference graph and ensures + * 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 non-null; SSA spec + * @param ssaSpec {@code non-null;} SSA spec * @param ropReg prosepctive new-namespace reg - * @return true if mapping is possible + * @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 paramWidth + * 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 true in the case noted above. + * @return {@code true} in the case noted above */ private boolean spansParamRange(int ssaReg, int category) { return ((ssaReg < paramRangeEnd) @@ -561,7 +568,6 @@ public class FirstFitLocalCombiningAllocator extends RegisterAllocator { */ private void analyzeInstructions() { ssaMeth.forEachInsn(new SsaInsn.Visitor() { - /** {@inheritDoc} */ public void visitMoveInsn(NormalSsaInsn insn) { processInsn(insn); @@ -579,15 +585,16 @@ public class FirstFitLocalCombiningAllocator extends RegisterAllocator { /** * This method collects three types of instructions: + * * 1) Adds a local variable assignment to the - * localVariables map. + * {@code localVariables} map. * 2) Add move-result-pseudo to the - * moveResultPseudoInsns list. + * {@code moveResultPseudoInsns} list. * 3) Add invoke-range to the - * invokeRangeInsns list. + * {@code invokeRangeInsns} list. * - * @param insn non-null; insn that may represent a local variable - * assignment. + * @param insn {@code non-null;} insn that may represent a + * local variable assignment */ private void processInsn(SsaInsn insn) { RegisterSpec assignment; @@ -596,7 +603,8 @@ public class FirstFitLocalCombiningAllocator extends RegisterAllocator { if (assignment != null) { LocalItem local = assignment.getLocalItem(); - ArrayList regList = localVariables.get(local); + ArrayList regList + = localVariables.get(local); if (regList == null) { regList = new ArrayList(); @@ -622,16 +630,16 @@ public class FirstFitLocalCombiningAllocator extends RegisterAllocator { } /** - * Adds a mapping from an SSA register to a Rop register. - * canMapReg should have already been called. + * Adds a mapping from an SSA register to a rop register. + * {@link #canMapReg} should have already been called. * - * @param ssaSpec non-null; SSA register to map from - * @param ropReg >=0; Rop register to map to + * @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 + // An assertion. if (ssaRegsMapped.get(ssaReg) || !canMapReg(ssaSpec, ropReg)) { throw new RuntimeException( "attempt to add invalid register mapping"); @@ -639,8 +647,7 @@ public class FirstFitLocalCombiningAllocator extends RegisterAllocator { if (DEBUG) { System.out.printf("Add mapping s%d -> v%d c:%d\n", - ssaSpec.getReg(), ropReg, ssaSpec.getCategory()); - + ssaSpec.getReg(), ropReg, ssaSpec.getCategory()); } int category = ssaSpec.getCategory(); @@ -652,19 +659,18 @@ public class FirstFitLocalCombiningAllocator extends RegisterAllocator { /** * Maps the source registers of the specified instruction such that they - * will fall in a contiguous range in Rop form. Moves are inserted as + * will fall in a contiguous range in rop form. Moves are inserted as * necessary to allow the range to be allocated. * - * @param insn non-null; insn whos sources to process + * @param insn {@code non-null;} insn whos sources to process */ private void adjustAndMapSourceRangeRange(NormalSsaInsn insn) { - int newRegStart; - - newRegStart = findRangeAndAdjust(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(); @@ -686,17 +692,20 @@ public class FirstFitLocalCombiningAllocator extends RegisterAllocator { int szSimilar = similarRegisters.size(); - // Try to map all SSA registers also associated with this local + /* + * Try to map all SSA registers also associated with + * this local. + */ for (int j = 0; j < szSimilar; j++) { RegisterSpec similarSpec = similarRegisters.get(j); int similarReg = similarSpec.getReg(); - // ...and don't map anything that's also a source... + // Don't map anything that's also a source. if (-1 != sources.indexOfRegister(similarReg)) { continue; } - // Registers left unmapped will get handled later + // Registers left unmapped will get handled later. tryMapReg(similarSpec, curRopReg, category); } } @@ -706,12 +715,12 @@ public class FirstFitLocalCombiningAllocator extends RegisterAllocator { /** * 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, + * sources that have already been mapped to rop registers. If that fails, * just find a new contiguous range that doesn't interfere. - - * @param insn non-null; the insn whose sources need to fit. Must be - * last insn in basic block. - * @return >= 0 rop register of start of range + * + * @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(); @@ -727,7 +736,7 @@ public class FirstFitLocalCombiningAllocator extends RegisterAllocator { rangeLength += categoriesForIndex[i]; } - // The highest score of fits tried so far + // the highest score of fits tried so far int maxScore = Integer.MIN_VALUE; // the high scoring range's start int resultRangeStart = -1; @@ -736,7 +745,7 @@ public class FirstFitLocalCombiningAllocator extends RegisterAllocator { /* * First, go through each source that's already been mapped. Try - * to center the range around the Rop register this source is mapped + * to center the range around the rop register this source is mapped * to. */ int rangeStartOffset = 0; @@ -794,13 +803,12 @@ public class FirstFitLocalCombiningAllocator extends RegisterAllocator { } /* - * Now, insert any moves required + * Now, insert any moves required. */ - for (int i = resultMovesRequired.nextSetBit(0); i >= 0 - ; i = resultMovesRequired.nextSetBit(i+1)) { - insn.changeOneSource(i, - insertMoveBefore(insn, sources.get(i))); + for (int i = resultMovesRequired.nextSetBit(0); i >= 0; + i = resultMovesRequired.nextSetBit(i+1)) { + insn.changeOneSource(i, insertMoveBefore(insn, sources.get(i))); } return resultRangeStart; @@ -811,14 +819,14 @@ public class FirstFitLocalCombiningAllocator extends RegisterAllocator { * specified instruction. Does not bother trying to center the range * around an already-mapped source register; * - * @param insn non-null; insn to build range for - * @param rangeLength >=0 length required in register units. - * @param categoriesForIndex non-null; indexed by source index; - * the category for each source. - * @param outMovesRequired non-null; an output parameter indexed by + * @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. + * moves inserted + * @return the rop register that starts the fitting range */ private int findAnyFittingRange(NormalSsaInsn insn, int rangeLength, int[] categoriesForIndex, BitSet outMovesRequired) { @@ -842,15 +850,15 @@ public class FirstFitLocalCombiningAllocator extends RegisterAllocator { * Attempts to build a plan for fitting a range of sources into rop * registers. * - * @param ropReg >=0 rop reg that begins range - * @param insn non-null; insn to plan range for - * @param categoriesForIndex non-null; indexed by source index; - * the category for each source. - * @param outMovesRequired non-null; an output parameter indexed by + * @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. + * moves inserted * @return the width of the fit that that does not involve added moves or - * -1 if "no fit possible" + * {@code -1} if "no fit possible" */ private int fitPlanForRange(int ropReg, NormalSsaInsn insn, int[] categoriesForIndex, BitSet outMovesRequired) { @@ -860,7 +868,7 @@ public class FirstFitLocalCombiningAllocator extends RegisterAllocator { IntSet liveOut = insn.getBlock().getLiveOutRegs(); RegisterSpecList liveOutSpecs = ssaSetToSpecs(liveOut); - // An SSA reg may only be mapped into a range once + // An SSA reg may only be mapped into a range once. BitSet seen = new BitSet(ssaMeth.getRegCount()); for (int i = 0; i < szSources ; i++) { @@ -874,7 +882,7 @@ public class FirstFitLocalCombiningAllocator extends RegisterAllocator { if (ssaRegsMapped.get(ssaReg) && mapper.oldToNew(ssaReg) == ropReg) { - // A register already mapped appropriately + // This is a register that is already mapped appropriately. fitWidth += category; } else if (rangeContainsReserved(ropReg, category)) { fitWidth = -1; @@ -882,19 +890,20 @@ public class FirstFitLocalCombiningAllocator extends RegisterAllocator { } else if (!ssaRegsMapped.get(ssaReg) && canMapReg(ssaSpec, ropReg) && !seen.get(ssaReg)) { - // A register that can be mapped appropriately + // This is a register that can be mapped appropriately. fitWidth += category; } else if (!mapper.areAnyPinned(liveOutSpecs, ropReg, category) && !mapper.areAnyPinned(sources, ropReg, category)) { /* - * A source that can be moved - * We can insert a move as long as: + * 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 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) + * * 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); @@ -912,7 +921,7 @@ public class FirstFitLocalCombiningAllocator extends RegisterAllocator { * Converts a bit set of SSA registers into a RegisterSpecList containing * the definition specs of all the registers. * - * @param ssaSet non-null; set of SSA registers + * @param ssaSet {@code non-null;} set of SSA registers * @return list of RegisterSpecs as noted above */ RegisterSpecList ssaSetToSpecs(IntSet ssaSet) { @@ -924,21 +933,20 @@ public class FirstFitLocalCombiningAllocator extends RegisterAllocator { while (iter.hasNext()) { result.set(i++, getDefinitionSpecForSsaReg(iter.next())); } - + return result; } /** - * Gets a local item associated with an ssa register, if one exists + * Gets a local item associated with an ssa register, if one exists. * - * @param ssaReg >= 0 SSA register - * @return null-ok; associated local item or null + * @param ssaReg {@code >= 0;} SSA register + * @return {@code null-ok;} associated local item or null */ private LocalItem getLocalItemForReg(int ssaReg) { - for(Map.Entry> entry: - localVariables.entrySet()) { - - for (RegisterSpec spec: entry.getValue()) { + for (Map.Entry> entry : + localVariables.entrySet()) { + for (RegisterSpec spec : entry.getValue()) { if (spec.getReg() == ssaReg) { return entry.getKey(); } diff --git a/dx/src/com/android/dx/ssa/back/IdenticalBlockCombiner.java b/dx/src/com/android/dx/ssa/back/IdenticalBlockCombiner.java index abc5fca78..a639af561 100644 --- a/dx/src/com/android/dx/ssa/back/IdenticalBlockCombiner.java +++ b/dx/src/com/android/dx/ssa/back/IdenticalBlockCombiner.java @@ -34,14 +34,14 @@ import java.util.BitSet; * frequently are created when catch blocks are edge-split. */ public class IdenticalBlockCombiner { - - private RopMethod ropMethod; - private BasicBlockList blocks; - private BasicBlockList newBlocks; + private final RopMethod ropMethod; + private final BasicBlockList blocks; + private final BasicBlockList newBlocks; /** - * Constructs instance. Call process() to run. - * @param rm instance to process + * Constructs instance. Call {@code process()} to run. + * + * @param rm {@code non-null;} instance to process */ public IdenticalBlockCombiner(RopMethod rm) { ropMethod = rm; @@ -50,10 +50,11 @@ public class IdenticalBlockCombiner { } /** - * Runs algorithm. TODO: this is n^2, and could be made linear-ish with - * a hash. + * 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 new method that has been processed + * @return {@code non-null;} new method that has been processed */ public RopMethod process() { int szBlocks = blocks.size(); @@ -78,14 +79,15 @@ public class IdenticalBlockCombiner { BasicBlock iBlock = blocks.labelToBlock(iLabel); - if (toDelete.get(iLabel) || iBlock.getSuccessors().size() > 1) { + if (toDelete.get(iLabel) + || iBlock.getSuccessors().size() > 1) { continue; } IntList toCombine = new IntList(); // ...and see if they can be combined with any other preds... - for (int j = i + 1; j 0; i--) { + for (int i = szBlocks - 1; i >= 0; i--) { if (toDelete.get(newBlocks.get(i).getLabel())) { newBlocks.set(i, null); } @@ -113,7 +115,14 @@ public class IdenticalBlockCombiner { return new RopMethod(newBlocks, ropMethod.getFirstLabel()); } - private boolean compareInsns(BasicBlock a, BasicBlock b) { + /** + * 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()); } @@ -131,11 +140,7 @@ public class IdenticalBlockCombiner { for (int i = 0; i < szBetas; i++) { int betaLabel = betaLabels.get(i); BasicBlock bb = blocks.labelToBlock(betaLabel); - - IntList preds; - - preds = ropMethod.labelToPredecessors(bb.getLabel()); - + IntList preds = ropMethod.labelToPredecessors(bb.getLabel()); int szPreds = preds.size(); for (int j = 0; j < szPreds; j++) { @@ -147,19 +152,19 @@ public class IdenticalBlockCombiner { /** * Replaces one of a block's successors with a different label. Constructs - * an updated BasicBlock instance and places it in newBlocks. + * 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; } diff --git a/dx/src/com/android/dx/ssa/back/InterferenceGraph.java b/dx/src/com/android/dx/ssa/back/InterferenceGraph.java index 282420b8f..e6cde622a 100644 --- a/dx/src/com/android/dx/ssa/back/InterferenceGraph.java +++ b/dx/src/com/android/dx/ssa/back/InterferenceGraph.java @@ -34,14 +34,17 @@ import java.util.ArrayList; * A register interference graph */ public class InterferenceGraph { - /** interference graph, indexed by register in both dimensions */ + /** + * {@code non-null;} interference graph, indexed by register in + * both dimensions + */ private final ArrayList interference; /** * Creates a new graph. * - * @param countRegs >=0 the start count of registers in the namespace. - * New registers can be added subsequently. + * @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(countRegs); @@ -83,9 +86,9 @@ public class InterferenceGraph { /** * Merges the interference set for a register into a given bit set * - * @param reg >=0 register - * @param set non-null; interference set; will be merged with set for - * given register + * @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()) { @@ -100,8 +103,10 @@ public class InterferenceGraph { */ private void ensureCapacity(int size) { int countRegs = interference.size(); + interference.ensureCapacity(size); - for (int i = countRegs ; i < size; i++) { + + for (int i = countRegs; i < size; i++) { interference.add(SetFactory.makeInterferenceSet(size)); } } diff --git a/dx/src/com/android/dx/ssa/back/LivenessAnalyzer.java b/dx/src/com/android/dx/ssa/back/LivenessAnalyzer.java index 5ae6e074a..cd3f7d2f6 100644 --- a/dx/src/com/android/dx/ssa/back/LivenessAnalyzer.java +++ b/dx/src/com/android/dx/ssa/back/LivenessAnalyzer.java @@ -28,28 +28,27 @@ import java.util.ArrayList; /** * From Appel "Modern Compiler Implementation in Java" algorithm 19.17 - * Calculate the live ranges for register reg.

    + * Calculate the live ranges for register {@code reg}.

    * * v = regV

    * s = insn

    * M = visitedBlocks

    */ public class LivenessAnalyzer { - /** - * non-null; index by basic block indexed set of basic blocks + * {@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; /** - * non-null; set of blocks remaing to visit as "live out as block" + * {@code non-null;} set of blocks remaing to visit as "live out as block" */ private final BitSet liveOutBlocks; /** - * >=0; SSA register currently being analyzed. + * {@code >=0;} SSA register currently being analyzed. * "v" in the original Appel algorithm. */ private final int regV; @@ -61,33 +60,34 @@ public class LivenessAnalyzer { private final InterferenceGraph interference; /** block "n" in Appel 19.17 */ - SsaBasicBlock blockN; + private SsaBasicBlock blockN; - /** index of statement s in blockN*/ + /** index of statement {@code s} in {@code blockN} */ private int statementIndex; - /** the next function to call. one of the four constants below */ - private int nextFunction; + /** the next function to call */ + private NextFunction nextFunction; - /** constants for nextFunction */ - static final int LIVE_IN_AT_STATEMENT = 1; - static final int LIVE_OUT_AT_STATEMENT = 2; - static final int LIVE_OUT_AT_BLOCK = 3; - static final int DONE = 4; + /** 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 SsaBasicBlock instances and + * live in/out information in {@code SsaBasicBlock} instances and * returning an interference graph. * - * @param ssaMeth non-null; Method to process. - * @return non-null; interference graph indexed by SSA registers in both - * directions. + * @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++) { @@ -102,42 +102,43 @@ public class LivenessAnalyzer { /** * Makes liveness analyzer instance for specific register. * - * @param ssaMeth non-null; method to process + * @param ssaMeth {@code non-null;} method to process * @param reg register whose liveness to analyze - * @param interference non-null; indexed by SSA reg in both dimensions; - * graph to update + * @param interference {@code non-null;} indexed by SSA reg in + * both dimensions; graph to update * */ - private LivenessAnalyzer(final SsaMethod ssaMeth, final int reg, + private LivenessAnalyzer(SsaMethod ssaMeth, int reg, InterferenceGraph interference) { + int blocksSz = ssaMeth.getBlocks().size(); + this.ssaMeth = ssaMeth; this.regV = reg; - visitedBlocks = new BitSet(ssaMeth.getBlocks().size()); - liveOutBlocks = new BitSet(ssaMeth.getBlocks().size()); + 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. + * The algorithm in Appel is presented in partial tail-recursion + * form. Obviously, that's not efficient in java, so this function + * serves as the dispatcher instead. */ private void handleTailRecursion() { - while (nextFunction != DONE) { + while (nextFunction != NextFunction.DONE) { switch (nextFunction) { case LIVE_IN_AT_STATEMENT: - nextFunction = DONE; + nextFunction = NextFunction.DONE; liveInAtStatement(); break; case LIVE_OUT_AT_STATEMENT: - nextFunction = DONE; + nextFunction = NextFunction.DONE; liveOutAtStatement(); break; case LIVE_OUT_AT_BLOCK: - nextFunction = DONE; + nextFunction = NextFunction.DONE; liveOutAtBlock(); break; @@ -147,23 +148,23 @@ public class LivenessAnalyzer { } /** - * From Appel algorithm 19.17 + * From Appel algorithm 19.17. */ public void run() { List useList = ssaMeth.getUseListForRegister(regV); - for (SsaInsn insn: useList) { - nextFunction = DONE; + for (SsaInsn insn : useList) { + nextFunction = NextFunction.DONE; if (insn instanceof PhiInsn) { - // If s is a phi-function with V as it's ith argument + // If s is a phi-function with V as it's ith argument. PhiInsn phi = (PhiInsn) insn; - for (SsaBasicBlock pred: phi.predBlocksForReg(regV, ssaMeth)) { - + for (SsaBasicBlock pred : + phi.predBlocksForReg(regV, ssaMeth)) { blockN = pred; - nextFunction = LIVE_OUT_AT_BLOCK; + nextFunction = NextFunction.LIVE_OUT_AT_BLOCK; handleTailRecursion(); } } else { @@ -175,7 +176,7 @@ public class LivenessAnalyzer { "insn not found in it's own block"); } - nextFunction = LIVE_IN_AT_STATEMENT; + nextFunction = NextFunction.LIVE_IN_AT_STATEMENT; handleTailRecursion(); } } @@ -184,13 +185,13 @@ public class LivenessAnalyzer { while ((nextLiveOutBlock = liveOutBlocks.nextSetBit(0)) >= 0) { blockN = ssaMeth.getBlocks().get(nextLiveOutBlock); liveOutBlocks.clear(nextLiveOutBlock); - nextFunction = LIVE_OUT_AT_BLOCK; + nextFunction = NextFunction.LIVE_OUT_AT_BLOCK; handleTailRecursion(); } } /** - * "v is live-out at n" + * "v is live-out at n." */ private void liveOutAtBlock() { if (! visitedBlocks.get(blockN.getIndex())) { @@ -204,15 +205,14 @@ public class LivenessAnalyzer { // Live out at last statement in blockN statementIndex = insns.size() - 1; - nextFunction = LIVE_OUT_AT_STATEMENT; + nextFunction = NextFunction.LIVE_OUT_AT_STATEMENT; } } /** - * "v is live-in at s" + * "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 @@ -224,23 +224,22 @@ public class LivenessAnalyzer { } else { // Let s' be the statement preceeding s statementIndex -= 1; - nextFunction = LIVE_OUT_AT_STATEMENT; + nextFunction = NextFunction.LIVE_OUT_AT_STATEMENT; } } /** - * "v is live-out at s" + * "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) { + if (rs != null) { interference.add(regV, rs.getReg()); } - nextFunction = LIVE_IN_AT_STATEMENT; + nextFunction = NextFunction.LIVE_IN_AT_STATEMENT; } } @@ -253,12 +252,12 @@ public class LivenessAnalyzer { * as the result of another phi, and the phi removal move scheduler may * generate moves that over-write the live result. * - * @param ssaMeth non-null; method to pricess - * @param interference non-null; interference graph + * @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()) { + for (SsaBasicBlock b : ssaMeth.getBlocks()) { List phis = b.getPhiInsns(); int szPhis = phis.size(); diff --git a/dx/src/com/android/dx/ssa/back/NullRegisterAllocator.java b/dx/src/com/android/dx/ssa/back/NullRegisterAllocator.java index cd3b2f1eb..f6dc961a3 100644 --- a/dx/src/com/android/dx/ssa/back/NullRegisterAllocator.java +++ b/dx/src/com/android/dx/ssa/back/NullRegisterAllocator.java @@ -29,11 +29,9 @@ import java.util.ArrayList; * about normal or wide categories. Used for debugging. */ public class NullRegisterAllocator extends RegisterAllocator { - /** {@inheritDoc} */ - public NullRegisterAllocator( - final SsaMethod ssaMeth, final InterferenceGraph interference) { - + public NullRegisterAllocator(SsaMethod ssaMeth, + InterferenceGraph interference) { super(ssaMeth, interference); } @@ -49,8 +47,7 @@ public class NullRegisterAllocator extends RegisterAllocator { public RegisterMapper allocateRegisters() { int oldRegCount = ssaMeth.getRegCount(); - BasicRegisterMapper mapper - = new BasicRegisterMapper(oldRegCount); + BasicRegisterMapper mapper = new BasicRegisterMapper(oldRegCount); for (int i = 0; i < oldRegCount; i++) { mapper.addMapping(i, i*2, 2); diff --git a/dx/src/com/android/dx/ssa/back/RegisterAllocator.java b/dx/src/com/android/dx/ssa/back/RegisterAllocator.java index 764b03ac2..e75eee186 100644 --- a/dx/src/com/android/dx/ssa/back/RegisterAllocator.java +++ b/dx/src/com/android/dx/ssa/back/RegisterAllocator.java @@ -34,10 +34,9 @@ import java.util.BitSet; import java.util.ArrayList; /** - * Base class of all register allocators + * Base class of all register allocators. */ public abstract class RegisterAllocator { - /** method being processed */ protected final SsaMethod ssaMeth; @@ -45,13 +44,13 @@ public abstract class RegisterAllocator { protected final InterferenceGraph interference; /** - * Creates an instance. Call allocateRegisters to run. + * 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( - final SsaMethod ssaMeth, final InterferenceGraph interference) { + public RegisterAllocator(SsaMethod ssaMeth, + InterferenceGraph interference) { this.ssaMeth = ssaMeth; this.interference = interference; } @@ -61,26 +60,26 @@ public abstract class RegisterAllocator { * of the namespace, and thus should be moved up to the top of the * namespace after phi removal. * - * @return true if params should be moved from low to high. + * @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 SsaMethod + * + * @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 1 for undefined registers. + * Returns {@code 1} for undefined registers. * * @param reg register - * @return 1 or 2 + * @return {@code 1..2} */ - protected int getCategoryForSsaReg(int reg) { - SsaInsn definition; - definition = ssaMeth.getDefinitionForRegister(reg); + protected final int getCategoryForSsaReg(int reg) { + SsaInsn definition = ssaMeth.getDefinitionForRegister(reg); if (definition == null) { // an undefined reg @@ -93,25 +92,26 @@ public abstract class RegisterAllocator { /** * Returns the RegisterSpec of the definition of the register. * - * @param reg >= 0 SSA 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). + * (for the case of "version 0" SSA registers) */ - protected RegisterSpec getDefinitionSpecForSsaReg(int reg) { - SsaInsn definition; - definition = ssaMeth.getDefinitionForRegister(reg); + 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) + * move-param (ie, this is a method parameter). + * * @param reg register in question - * @return true if this is a method parameter + * @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; @@ -127,19 +127,18 @@ public abstract class RegisterAllocator { * interference graph in the process. The insn currently must be the * last insn in a block. * - * @param insn non-null; insn to insert move before, must be last insn - * in block. - * @param reg non-null; SSA register to duplicate - * @return non-null; spec of new SSA register created by move + * @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 insns = block.getInsns(); int insnIndex = insns.indexOf(insn); - if (insnIndex < 0 ) { + if (insnIndex < 0) { throw new IllegalArgumentException ( "specified insn is not in this block"); } @@ -155,19 +154,17 @@ public abstract class RegisterAllocator { } /* - * Get new register and make new move instruction + * Get new register and make new move instruction. */ - // new result must not have associated local variable + // The new result must not have an associated local variable. RegisterSpec newRegSpec = RegisterSpec.make(ssaMeth.makeNewSsaReg(), reg.getTypeBearer()); - SsaInsn toAdd; - - toAdd = SsaInsn.makeFromRop( - new PlainInsn(Rops.opMove(newRegSpec.getType()), - SourcePosition.NO_INFO, newRegSpec, - RegisterSpecList.make(reg)), block); + SsaInsn toAdd = SsaInsn.makeFromRop( + new PlainInsn(Rops.opMove(newRegSpec.getType()), + SourcePosition.NO_INFO, newRegSpec, + RegisterSpecList.make(reg)), block); insns.add(insnIndex, toAdd); @@ -179,17 +176,13 @@ public abstract class RegisterAllocator { */ IntSet liveOut = block.getLiveOutRegs(); - - RegisterSpec result = insn.getResult(); - int resultReg = (result == null) ? -1 : result.getReg(); - IntIterator liveOutIter = liveOut.iterator(); - while(liveOutIter.hasNext()) { + while (liveOutIter.hasNext()) { interference.add(newReg, liveOutIter.next()); } - // Everything that's a source in the last insn interferes + // Everything that's a source in the last insn interferes. RegisterSpecList sources = insn.getSources(); int szSources = sources.size(); diff --git a/dx/src/com/android/dx/ssa/back/SsaToRop.java b/dx/src/com/android/dx/ssa/back/SsaToRop.java index 1c59549af..0ecbead54 100644 --- a/dx/src/com/android/dx/ssa/back/SsaToRop.java +++ b/dx/src/com/android/dx/ssa/back/SsaToRop.java @@ -38,7 +38,9 @@ import com.android.dx.util.IntList; import com.android.dx.util.Hex; import java.util.ArrayList; +import java.util.Arrays; import java.util.BitSet; +import java.util.Comparator; import java.util.HashMap; import java.util.List; @@ -46,55 +48,67 @@ import java.util.List; * Converts a method in SSA form to ROP form. */ public class SsaToRop { - + /** local debug flag */ private static final boolean DEBUG = false; - /** non-null; method to process */ + /** {@code non-null;} method to process */ private final SsaMethod ssaMeth; /** - * true if the converter should attempt to minimize + * {@code true} if the converter should attempt to minimize * the rop-form register count */ private final boolean minimizeRegisters; - /** interference graph */ - private InterferenceGraph interference; + /** {@code non-null;} interference graph */ + private final InterferenceGraph interference; /** * Converts a method in SSA form to ROP form. - * @param ssaMeth input - * @return non-null; rop-form output + * + * @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(); } - private SsaToRop(final SsaMethod ssaMethod, boolean minimizeRegisters) { + /** + * 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() { - interference = LivenessAnalyzer.constructInterferenceGraph(ssaMeth); - if (DEBUG) { interference.dumpToStdout(); } - RegisterAllocator allocator; - RegisterMapper mapper; - - // These are other allocators for debugging or historical comparison - - //allocator = new NullRegisterAllocator(ssaMeth, interference); - //allocator = new FirstFitAllocator(ssaMeth, interference); + // These are other allocators for debugging or historical comparison: + // allocator = new NullRegisterAllocator(ssaMeth, interference); + // allocator = new FirstFitAllocator(ssaMeth, interference); - allocator = new FirstFitLocalCombiningAllocator(ssaMeth, interference, - minimizeRegisters); + RegisterAllocator allocator = + new FirstFitLocalCombiningAllocator(ssaMeth, interference, + minimizeRegisters); - mapper = allocator.allocateRegisters(); + RegisterMapper mapper = allocator.allocateRegisters(); if (DEBUG) { System.out.println("Printing reg map"); @@ -113,22 +127,20 @@ public class SsaToRop { removeEmptyGotos(); - RopMethod ropMethod; - - ropMethod = convertToRop(); - + 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. + * 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 blocks = ssaMeth.getBlocks(); @@ -139,8 +151,7 @@ public class SsaToRop { if ((insns.size() == 1) && (insns.get(0).getOpcode() == Rops.GOTO)) { - - BitSet preds = (BitSet)b.getPredecessors().clone(); + BitSet preds = (BitSet) b.getPredecessors().clone(); for (int i = preds.nextSetBit(0); i >= 0; i = preds.nextSetBit(i + 1)) { @@ -154,89 +165,49 @@ public class SsaToRop { } /** - * This method is not presently used. - * @return a list of registers ordered by most-frequently-used - * to least-frequently-used. Each register is listed once and only once. - */ - public int[] getRegistersByFrequency() { - int regCount = ssaMeth.getRegCount(); - Integer[] ret = new Integer[ssaMeth.getRegCount()]; - - for (int i = 0; i < regCount; i++) { - ret[i] = i; - } - - java.util.Arrays.sort(ret, new java.util.Comparator() { - public int compare (Integer o1, Integer o2) { - return ssaMeth.getUseListForRegister(o2).size() - - ssaMeth.getUseListForRegister(o1).size(); - } - - public boolean equals(Object o) { - return o == this; - } - }); - - int result[] = new int[regCount]; - - for (int i = 0; i < regCount; i++) { - result[i] = ret[i]; - } - - return result; - } - - /** - * See Appel 19.6 - * To remove the phi instructions in an edge-split SSA representation - * we know we can always insert a move in a predecessor block + * See Appel 19.6. To remove the phi instructions in an edge-split + * SSA representation we know we can always insert a move in a + * predecessor block. */ private void removePhiFunctions() { - for (SsaBasicBlock block: ssaMeth.getBlocks()) { - // Add moves in all the pred blocks for each phi insn` - block.forEachPhiInsn(new PhiVisitor(block)); - // Delete the phi insns + ArrayList 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(); } /* - * After all move insns have been added: sort them so they don't - * destructively interfere + * After all move insns have been added, sort them so they don't + * destructively interfere. */ - for (SsaBasicBlock block: ssaMeth.getBlocks()) { + for (SsaBasicBlock block : blocks) { block.scheduleMovesFromPhis(); } } /** - * PhiSuccessorUpdater for adding move instructions to predecessors based - * on phi insns. + * Helper for {@link #removePhiFunctions}: PhiSuccessorUpdater for + * adding move instructions to predecessors based on phi insns. */ - private class PhiVisitor implements PhiInsn.Visitor { - SsaBasicBlock block; + private static class PhiVisitor implements PhiInsn.Visitor { + private final ArrayList blocks; - PhiVisitor (final SsaBasicBlock block) { - this.block = block; + public PhiVisitor(ArrayList blocks) { + this.blocks = blocks; } - public void visitPhiInsn (PhiInsn insn) { - RegisterSpecList sources; - RegisterSpec result; - ArrayList blocks = ssaMeth.getBlocks(); - - sources = insn.getSources(); - result = insn.getResult(); - + public void visitPhiInsn(PhiInsn insn) { + RegisterSpecList sources = insn.getSources(); + RegisterSpec result = insn.getResult(); int sz = sources.size(); - for (int i = 0; i blocks = ssaMeth.getBlocks(); - // Exit block may be null + + // Exit block may be null. SsaBasicBlock exitBlock = ssaMeth.getExitBlock(); int ropBlockCount = ssaMeth.getCountReachableBlocks(); - // Don't count the exit block, if it exists + // Don't count the exit block, if it exists. ropBlockCount -= (exitBlock == null) ? 0 : 1; BasicBlockList result = new BasicBlockList(ropBlockCount); - // Convert all the reachable blocks except the exit block + // Convert all the reachable blocks except the exit block. int ropBlockIndex = 0; - for(SsaBasicBlock b : blocks) { + for (SsaBasicBlock b : blocks) { if (b.isReachable() && b != exitBlock) { result.set(ropBlockIndex++, convertBasicBlock(b)); } @@ -303,8 +268,8 @@ public class SsaToRop { // 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"); + throw new RuntimeException( + "Exit block must have no insns when leaving SSA form"); } return result; @@ -314,11 +279,10 @@ public class SsaToRop { * Validates that a basic block is a valid end predecessor. It must * end in a RETURN or a THROW. Throws a runtime exception on error. * - * @param b non-null; block to validate + * @param b {@code non-null;} block to validate * @throws RuntimeException on error */ private void verifyValidExitPredecessor(SsaBasicBlock b) { - ArrayList insns = b.getInsns(); SsaInsn lastInsn = insns.get(insns.size() - 1); Rop opcode = lastInsn.getOpcode(); @@ -334,22 +298,21 @@ public class SsaToRop { * Converts a single basic block to rop form. * * @param block SSA block to process - * @return ROP block + * @return {@code non-null;} ROP block */ private BasicBlock convertBasicBlock(SsaBasicBlock block) { - BasicBlock result; IntList successorList = block.getRopLabelSuccessorList(); int primarySuccessorLabel = block.getPrimarySuccessorRopLabel(); - // Filter out any reference to the SSA form's exit block - // exit block may be null - SsaBasicBlock exitBlock = ssaMeth.getExitBlock(); + // 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 ( + throw new RuntimeException( "Exit predecessor must have no other successors" + Hex.u2(block.getRopLabel())); } else { @@ -362,7 +325,7 @@ public class SsaToRop { successorList.setImmutable(); - result = new BasicBlock( + BasicBlock result = new BasicBlock( block.getRopLabel(), convertInsns(block.getInsns()), successorList, primarySuccessorLabel); @@ -371,16 +334,14 @@ public class SsaToRop { } /** - * Converts an insn list to rop form - * @param ssaInsns non-null;old instructions - * @return non-null; immutable instruction list + * 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 ssaInsns) { - InsnList result; - int insnCount; - - insnCount = ssaInsns.size(); - result = new InsnList (insnCount); + int insnCount = ssaInsns.size(); + InsnList result = new InsnList(insnCount); for (int i = 0; i < insnCount; i++) { result.set(i, ssaInsns.get(i).toRopInsn()); @@ -390,4 +351,35 @@ public class SsaToRop { return result; } + + /** + * Note: 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]; + + for (int i = 0; i < regCount; i++) { + ret[i] = i; + } + + Arrays.sort(ret, new Comparator() { + public int compare(Integer o1, Integer o2) { + return ssaMeth.getUseListForRegister(o2).size() + - ssaMeth.getUseListForRegister(o1).size(); + } + }); + + int result[] = new int[regCount]; + + for (int i = 0; i < regCount; i++) { + result[i] = ret[i]; + } + + return result; + } } diff --git a/dx/src/com/android/dx/ssa/package-info.java b/dx/src/com/android/dx/ssa/package-info.java index 45d9ad6a3..582a327eb 100644 --- a/dx/src/com/android/dx/ssa/package-info.java +++ b/dx/src/com/android/dx/ssa/package-info.java @@ -19,7 +19,7 @@ package com.android.dx.ssa; /** *

    An introduction to SSA Form

    * - * This package contains classes associated with dx's SSA + * This package contains classes associated with dx's {@code SSA} * intermediate form. This form is a static-single-assignment representation of * Rop-form a method with Rop-form-like instructions (with the addition of a * {@link PhiInsn phi instriction}. This form is intended to make it easy to @@ -47,7 +47,7 @@ package com.android.dx.ssa; *
  • {@link PhiInsn} instances represent "phi" operators defined in SSA * literature. They must be the first N instructions in a basic block. *
  • {@link NormalSsaInsn} instances represent instructions that directly - * correspond to Rop form. + * correspond to {@code Rop} form. * * *

    Classes related to optimization steps

    @@ -74,14 +74,14 @@ package com.android.dx.ssa; * *

    Conversion into SSA Form

    * - * {@link SsaConverter#convertToSsaMethod} takes a RopMethod and - * returns a fully-converted SsaMethod. The conversion process + * {@link SsaConverter#convertToSsaMethod} takes a {@code RopMethod} and + * returns a fully-converted {@code SsaMethod}. The conversion process * is roughly as follows: * *
      *
    1. The Rop-form method, its blocks and their instructions are directly - * wrapped in SsaMethod, SsaBasicBlock and - * SsaInsn instances. Nothing else changes. + * wrapped in {@code SsaMethod}, {@code SsaBasicBlock} and + * {@code SsaInsn} instances. Nothing else changes. *
    2. Critical control-flow graph edges are {@link SsaConverter#edgeSplit * split} and new basic blocks inserted as required to meet the constraints * necessary for the ultimate SSA representation. @@ -89,7 +89,7 @@ package com.android.dx.ssa; * Rop registers to local variables necessary during phi placement. This * step could also be done in Rop form and then updated through the preceding * steps. - *
    3. Phi instructions are {link SsaConverter#placePhiFunctions} + *
    4. {@code Phi} instructions are {link SsaConverter#placePhiFunctions} * placed in a semi-pruned fashion, which requires computation of {@link * Dominators dominance graph} and each node's {@link DomFront * dominance-frontier set}. diff --git a/dx/src/com/android/dx/util/AnnotatedOutput.java b/dx/src/com/android/dx/util/AnnotatedOutput.java index 0d950414e..9b69a36b4 100644 --- a/dx/src/com/android/dx/util/AnnotatedOutput.java +++ b/dx/src/com/android/dx/util/AnnotatedOutput.java @@ -25,7 +25,7 @@ public interface AnnotatedOutput /** * Get whether this instance will actually keep annotations. * - * @return true iff annotations are being kept + * @return {@code true} iff annotations are being kept */ public boolean annotates(); @@ -34,7 +34,7 @@ public interface AnnotatedOutput * Annotators may use the result of calling this method to inform their * annotation activity. * - * @return true iff annotations are to be verbose + * @return {@code true} iff annotations are to be verbose */ public boolean isVerbose(); @@ -44,7 +44,7 @@ public interface AnnotatedOutput * annotation marks all subsequent output until another annotation * call. * - * @param msg non-null; the annotation message + * @param msg {@code non-null;} the annotation message */ public void annotate(String msg); @@ -55,9 +55,9 @@ public interface AnnotatedOutput * previous calls to this method, the new call "consumes" output * after all the output covered by the previous calls. * - * @param amt >= 0; the amount of output for this annotation to + * @param amt {@code >= 0;} the amount of output for this annotation to * cover - * @param msg non-null; the annotation message + * @param msg {@code non-null;} the annotation message */ public void annotate(int amt, String msg); @@ -73,7 +73,7 @@ public interface AnnotatedOutput * output, but annotaters are encouraged to attempt to avoid exceeding * the indicated width. * - * @return >= 1; the maximum width + * @return {@code >= 1;} the maximum width */ public int getAnnotationWidth(); } diff --git a/dx/src/com/android/dx/util/BitIntSet.java b/dx/src/com/android/dx/util/BitIntSet.java index c8588f8c0..db85571f4 100644 --- a/dx/src/com/android/dx/util/BitIntSet.java +++ b/dx/src/com/android/dx/util/BitIntSet.java @@ -44,7 +44,7 @@ public class BitIntSet implements IntSet { /** * Ensures that the bit set has the capacity to represent the given value. * - * @param value >= 0 value to represent + * @param value {@code >= 0;} value to represent */ private void ensureCapacity(int value) { if (value >= Bits.getMax(bits)) { @@ -118,12 +118,6 @@ public class BitIntSet implements IntSet { return ret; } - - /** @inheritDoc */ - public void remove() { - BitIntSet.this.remove(idx); - idx = Bits.findFirst(bits, idx+1); - } }; } diff --git a/dx/src/com/android/dx/util/Bits.java b/dx/src/com/android/dx/util/Bits.java index 0bc124c89..1f45bd3fe 100644 --- a/dx/src/com/android/dx/util/Bits.java +++ b/dx/src/com/android/dx/util/Bits.java @@ -17,7 +17,7 @@ package com.android.dx.util; /** - * Utilities for treating int[]s as bit sets. + * Utilities for treating {@code int[]}s as bit sets. */ public final class Bits { /** @@ -30,8 +30,8 @@ public final class Bits { /** * Constructs a bit set to contain bits up to the given index (exclusive). * - * @param max >= 0; the maximum bit index (exclusive) - * @return non-null; an appropriately-constructed instance + * @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; @@ -41,8 +41,8 @@ public final class Bits { /** * Gets the maximum index (exclusive) for the given bit set. * - * @param bits non-null; bit set in question - * @return >= 0; the maximum index (exclusive) that may be 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; @@ -51,8 +51,8 @@ public final class Bits { /** * Gets the value of the bit at the given index. * - * @param bits non-null; bit set to operate on - * @param idx >= 0, < getMax(set); which bit + * @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) { @@ -64,8 +64,8 @@ public final class Bits { /** * Sets the given bit to the given value. * - * @param bits non-null; bit set to operate on - * @param idx >= 0, < getMax(set); which bit + * @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) { @@ -80,10 +80,10 @@ public final class Bits { } /** - * Sets the given bit to true. + * Sets the given bit to {@code true}. * - * @param bits non-null; bit set to operate on - * @param idx >= 0, < getMax(set); which bit + * @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; @@ -92,10 +92,10 @@ public final class Bits { } /** - * Sets the given bit to false. + * Sets the given bit to {@code false}. * - * @param bits non-null; bit set to operate on - * @param idx >= 0, < getMax(set); which bit + * @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; @@ -105,10 +105,10 @@ public final class Bits { /** * Returns whether or not the given bit set is empty, that is, whether - * no bit is set to true. + * no bit is set to {@code true}. * - * @param bits non-null; bit set to operate on - * @return true iff all bits are false + * @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; @@ -123,10 +123,10 @@ public final class Bits { } /** - * Gets the number of bits set to true in the given bit set. + * Gets the number of bits set to {@code true} in the given bit set. * - * @param bits non-null; bit set to operate on - * @return >= 0; the bit count (aka population count) of the 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; @@ -140,13 +140,13 @@ public final class Bits { } /** - * Returns whether any bits are set to true in the + * Returns whether any bits are set to {@code true} in the * specified range. * - * @param bits non-null; bit set to operate on - * @param start >= 0; index of the first bit in the range (inclusive) - * @param end >= 0; index of the last bit in the range (exclusive) - * @return true if any bit is set to true in + * @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) { @@ -158,10 +158,10 @@ public final class Bits { * Finds the lowest-order bit set at or after the given index in the * given bit set. * - * @param bits non-null; bit set to operate on - * @param idx >= 0; minimum index to return - * @return >= -1; lowest-order bit set at or after idx, - * or -1 if there is no appropriate bit index to return + * @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; @@ -183,12 +183,12 @@ public final class Bits { /** * Finds the lowest-order bit set at or after the given index in the - * given int. + * given {@code int}. * * @param value the value in question * @param idx 0..31 the minimum bit index to return - * @return >= -1; lowest-order bit set at or after idx, - * or -1 if there is no appropriate 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. @@ -197,13 +197,13 @@ public final class Bits { } /** - * Ors bit array b into bit array a. - * a.length must be greater than or equal to - * b.length. + * 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 non-null; int array to be ored with other argument. This + * @param a {@code non-null;} int array to be ored with other argument. This * argument is modified. - * @param b non-null; int array to be ored into a. This + * @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) { diff --git a/dx/src/com/android/dx/util/ByteArray.java b/dx/src/com/android/dx/util/ByteArray.java index 3fcf29362..6bd6e5f10 100644 --- a/dx/src/com/android/dx/util/ByteArray.java +++ b/dx/src/com/android/dx/util/ByteArray.java @@ -21,28 +21,28 @@ import java.io.IOException; import java.io.InputStream; /** - * Wrapper for a byte[], which provides read-only access and + * Wrapper for a {@code byte[]}, which provides read-only access and * can "reveal" a partial slice of the underlying array. * * Note: Multibyte accessors all use big-endian order. */ public final class ByteArray { - /** non-null; underlying array */ + /** {@code non-null;} underlying array */ private final byte[] bytes; - /** >= 0; start index of the slice (inclusive) */ + /** {@code >= 0}; start index of the slice (inclusive) */ private final int start; - /** >= 0, <= bytes.length; size computed as - * end - start (in the constructor) */ + /** {@code >= 0, <= bytes.length}; size computed as + * {@code end - start} (in the constructor) */ private final int size; /** * Constructs an instance. * - * @param bytes non-null; the underlying array - * @param start >= 0; start index of the slice (inclusive) - * @param end >= start, <= bytes.length; end index of + * @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) { @@ -68,9 +68,9 @@ public final class ByteArray { } /** - * Constructs an instance from an entire byte[]. + * Constructs an instance from an entire {@code byte[]}. * - * @param bytes non-null; the underlying array + * @param bytes {@code non-null;} the underlying array */ public ByteArray(byte[] bytes) { this(bytes, 0, bytes.length); @@ -79,7 +79,7 @@ public final class ByteArray { /** * Gets the size of the array, in bytes. * - * @return >= 0; the size + * @return {@code >= 0;} the size */ public int size() { return size; @@ -88,10 +88,10 @@ public final class ByteArray { /** * Returns a slice (that is, a sub-array) of this instance. * - * @param start >= 0; start index of the slice (inclusive) - * @param end >= start, <= size(); end index of + * @param start {@code >= 0;} start index of the slice (inclusive) + * @param end {@code >= start, <= size();} end index of * the slice (exclusive) - * @return non-null; the slice + * @return {@code non-null;} the slice */ public ByteArray slice(int start, int end) { checkOffsets(start, end); @@ -103,9 +103,9 @@ public final class ByteArray { * offset into this instance. * * @param offset offset into this instance - * @param bytes non-null; (alleged) underlying array - * @return corresponding offset into bytes - * @throws IllegalArgumentException thrown if bytes is + * @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) { @@ -117,10 +117,10 @@ public final class ByteArray { } /** - * Gets the signed byte value at a particular offset. + * Gets the {@code signed byte} value at a particular offset. * - * @param off >= 0, < size(); offset to fetch - * @return signed byte at that 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); @@ -128,10 +128,10 @@ public final class ByteArray { } /** - * Gets the signed short value at a particular offset. + * Gets the {@code signed short} value at a particular offset. * - * @param off >= 0, < (size() - 1); offset to fetch - * @return signed short at that 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); @@ -139,10 +139,10 @@ public final class ByteArray { } /** - * Gets the signed int value at a particular offset. + * Gets the {@code signed int} value at a particular offset. * - * @param off >= 0, < (size() - 3); offset to fetch - * @return signed int at that 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); @@ -153,10 +153,10 @@ public final class ByteArray { } /** - * Gets the signed long value at a particular offset. + * Gets the {@code signed long} value at a particular offset. * - * @param off >= 0, < (size() - 7); offset to fetch - * @return signed int at that 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); @@ -173,10 +173,10 @@ public final class ByteArray { } /** - * Gets the unsigned byte value at a particular offset. + * Gets the {@code unsigned byte} value at a particular offset. * - * @param off >= 0, < size(); offset to fetch - * @return unsigned byte at that 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); @@ -184,10 +184,10 @@ public final class ByteArray { } /** - * Gets the unsigned short value at a particular offset. + * Gets the {@code unsigned short} value at a particular offset. * - * @param off >= 0, < (size() - 1); offset to fetch - * @return unsigned short at that 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); @@ -196,11 +196,11 @@ public final class ByteArray { /** * Copies the contents of this instance into the given raw - * byte[] at the given offset. The given array must be + * {@code byte[]} at the given offset. The given array must be * large enough. * - * @param out non-null; array to hold the output - * @param offset non-null; index into out for the first + * @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) { @@ -226,7 +226,7 @@ public final class ByteArray { } /** - * Gets the signed byte value at the given offset, + * Gets the {@code signed byte} value at the given offset, * without doing any argument checking. * * @param off offset to fetch @@ -237,7 +237,7 @@ public final class ByteArray { } /** - * Gets the unsigned byte value at the given offset, + * Gets the {@code unsigned byte} value at the given offset, * without doing any argument checking. * * @param off offset to fetch @@ -248,26 +248,26 @@ public final class ByteArray { } /** - * Gets a DataInputStream that reads from this instance, + * Gets a {@code DataInputStream} that reads from this instance, * with the cursor starting at the beginning of this instance's data. * Note: The returned instance may be cast to {@link #GetCursor} * if needed. * - * @return non-null; an appropriately-constructed - * DataInputStream instance + * @return {@code non-null;} an appropriately-constructed + * {@code DataInputStream} instance */ public MyDataInputStream makeDataInputStream() { return new MyDataInputStream(makeInputStream()); } /** - * Gets a InputStream that reads from this instance, + * Gets a {@code InputStream} that reads from this instance, * with the cursor starting at the beginning of this instance's data. * Note: The returned instance may be cast to {@link #GetCursor} * if needed. * - * @return non-null; an appropriately-constructed - * InputStream instancex + * @return {@code non-null;} an appropriately-constructed + * {@code InputStream} instancex */ public MyInputStream makeInputStream() { return new MyInputStream(); @@ -280,7 +280,7 @@ public final class ByteArray { /** * Gets the current cursor. * - * @return 0..size(); the cursor + * @return {@code 0..size();} the cursor */ public int getCursor(); } @@ -345,7 +345,7 @@ public final class ByteArray { /** * Gets the current cursor. * - * @return 0..size(); the cursor + * @return {@code 0..size();} the cursor */ public int getCursor() { return cursor; @@ -357,8 +357,8 @@ public final class ByteArray { * simply so that the cursor of a wrapped {@link #MyInputStream} * instance may be easily determined. */ - public class MyDataInputStream extends DataInputStream { - /** non-null; the underlying {@link #MyInputStream} */ + public static class MyDataInputStream extends DataInputStream { + /** {@code non-null;} the underlying {@link #MyInputStream} */ private final MyInputStream wrapped; public MyDataInputStream(MyInputStream wrapped) { @@ -370,7 +370,7 @@ public final class ByteArray { /** * Gets the current cursor. * - * @return 0..size(); the cursor + * @return {@code 0..size();} the cursor */ public int getCursor() { return wrapped.getCursor(); diff --git a/dx/src/com/android/dx/util/ByteArrayAnnotatedOutput.java b/dx/src/com/android/dx/util/ByteArrayAnnotatedOutput.java index 457a603c5..5fcf5d8c3 100644 --- a/dx/src/com/android/dx/util/ByteArrayAnnotatedOutput.java +++ b/dx/src/com/android/dx/util/ByteArrayAnnotatedOutput.java @@ -22,7 +22,7 @@ import java.util.ArrayList; /** * Implementation of {@link AnnotatedOutput} which stores the written data - * into a byte[]. + * into a {@code byte[]}. * *

      Note: As per the {@link Output} interface, multi-byte * writes all use little-endian order.

      @@ -38,26 +38,26 @@ public final class ByteArrayAnnotatedOutput */ private final boolean stretchy; - /** non-null; the data itself */ + /** {@code non-null;} the data itself */ private byte[] data; - /** >= 0; current output cursor */ + /** {@code >= 0;} current output cursor */ private int cursor; /** whether annotations are to be verbose */ private boolean verbose; /** - * null-ok; list of annotations, or null if this instance + * {@code null-ok;} list of annotations, or {@code null} if this instance * isn't keeping them */ private ArrayList annotations; - /** >= 40 (if used); the desired maximum annotation width */ + /** {@code >= 40 (if used);} the desired maximum annotation width */ private int annotationWidth; /** - * >= 8 (if used); the number of bytes of hex output to use + * {@code >= 8 (if used);} the number of bytes of hex output to use * in annotations */ private int hexCols; @@ -69,7 +69,7 @@ public final class ByteArrayAnnotatedOutput * capacity of the resulting instance. Also, the constructed * instance does not keep annotations by default. * - * @param data non-null; data array to use for output + * @param data {@code non-null;} data array to use for output */ public ByteArrayAnnotatedOutput(byte[] data) { this(data, false); @@ -87,7 +87,7 @@ public final class ByteArrayAnnotatedOutput /** * Internal constructor. * - * @param data non-null; data array to use for output + * @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) { @@ -105,25 +105,25 @@ public final class ByteArrayAnnotatedOutput } /** - * Gets the underlying byte[] of this instance, which + * Gets the underlying {@code byte[]} of this instance, which * may be larger than the number of bytes written * * @see #toByteArray * - * @return non-null; the byte[] + * @return {@code non-null;} the {@code byte[]} */ public byte[] getArray() { return data; } /** - * Constructs and returns a new byte[] that contains + * 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 non-null; an appropriately-constructed array + * @return {@code non-null;} an appropriately-constructed array */ public byte[] toByteArray() { byte[] result = new byte[cursor]; @@ -419,7 +419,7 @@ public final class ByteArrayAnnotatedOutput * be called only once per instance, and only before any data has been * written to the it. * - * @param annotationWidth >= 40; the desired maximum annotation width + * @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) { @@ -474,7 +474,7 @@ public final class ByteArrayAnnotatedOutput /** * Writes the annotated content of this instance to the given writer. * - * @param out non-null; where to write to + * @param out {@code non-null;} where to write to */ public void writeAnnotationsTo(Writer out) throws IOException { int width2 = getAnnotationWidth(); @@ -538,7 +538,7 @@ public final class ByteArrayAnnotatedOutput * Reallocates the underlying array if necessary. Calls to this method * should be guarded by a test of {@link #stretchy}. * - * @param desiredSize >= 0; the desired minimum total size of the array + * @param desiredSize {@code >= 0;} the desired minimum total size of the array */ private void ensureCapacity(int desiredSize) { if (data.length < desiredSize) { @@ -552,25 +552,25 @@ public final class ByteArrayAnnotatedOutput * Annotation on output. */ private static class Annotation { - /** >= 0; start of annotated range (inclusive) */ + /** {@code >= 0;} start of annotated range (inclusive) */ private final int start; /** - * >= 0; end of annotated range (exclusive); - * Integer.MAX_VALUE if unclosed + * {@code >= 0;} end of annotated range (exclusive); + * {@code Integer.MAX_VALUE} if unclosed */ private int end; - /** non-null; annotation text */ + /** {@code non-null;} annotation text */ private final String text; /** * Constructs an instance. * - * @param start >= 0; start of annotated range - * @param end >= start; end of annotated range (exclusive) or - * Integer.MAX_VALUE if unclosed - * @param text non-null; annotation text + * @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; @@ -581,8 +581,8 @@ public final class ByteArrayAnnotatedOutput /** * Constructs an instance. It is initally unclosed. * - * @param start >= 0; start of annotated range - * @param text non-null; annotation text + * @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); @@ -592,7 +592,7 @@ public final class ByteArrayAnnotatedOutput * Sets the end as given, but only if the instance is unclosed; * otherwise, do nothing. * - * @param end >= start; the end + * @param end {@code >= start;} the end */ public void setEndIfUnset(int end) { if (this.end == Integer.MAX_VALUE) { @@ -603,7 +603,7 @@ public final class ByteArrayAnnotatedOutput /** * Sets the end as given. * - * @param end >= start; the end + * @param end {@code >= start;} the end */ public void setEnd(int end) { this.end = end; @@ -630,7 +630,7 @@ public final class ByteArrayAnnotatedOutput /** * Gets the text. * - * @return non-null; the text + * @return {@code non-null;} the text */ public String getText() { return text; diff --git a/dx/src/com/android/dx/util/ExceptionWithContext.java b/dx/src/com/android/dx/util/ExceptionWithContext.java index 035546ed9..7f8523c15 100644 --- a/dx/src/com/android/dx/util/ExceptionWithContext.java +++ b/dx/src/com/android/dx/util/ExceptionWithContext.java @@ -24,7 +24,7 @@ import java.io.PrintWriter; */ public class ExceptionWithContext extends RuntimeException { - /** non-null; human-oriented context of the exception */ + /** {@code non-null;} human-oriented context of the exception */ private StringBuffer context; /** @@ -33,9 +33,9 @@ public class ExceptionWithContext * {@link ExceptionWithContext}, or a newly-constructed exception if it * was not. * - * @param ex non-null; the exception to augment - * @param str non-null; context to add - * @return non-null; an appropriate instance + * @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; @@ -62,7 +62,7 @@ public class ExceptionWithContext /** * Constructs an instance. * - * @param cause null-ok; exception that caused this one + * @param cause {@code null-ok;} exception that caused this one */ public ExceptionWithContext(Throwable cause) { this(null, cause); @@ -72,7 +72,7 @@ public class ExceptionWithContext * Constructs an instance. * * @param message human-oriented message - * @param cause null-ok; exception that caused this one + * @param cause {@code null-ok;} exception that caused this one */ public ExceptionWithContext(String message, Throwable cause) { super((message != null) ? message : @@ -105,7 +105,7 @@ public class ExceptionWithContext /** * Adds a line of context to this instance. * - * @param str non-null; new context + * @param str {@code non-null;} new context */ public void addContext(String str) { if (str == null) { @@ -121,7 +121,7 @@ public class ExceptionWithContext /** * Gets the context. * - * @return non-null; the context + * @return {@code non-null;} the context */ public String getContext() { return context.toString(); @@ -130,7 +130,7 @@ public class ExceptionWithContext /** * Prints the message and context. * - * @param out non-null; where to print to + * @param out {@code non-null;} where to print to */ public void printContext(PrintStream out) { out.println(getMessage()); @@ -140,7 +140,7 @@ public class ExceptionWithContext /** * Prints the message and context. * - * @param out non-null; where to print to + * @param out {@code non-null;} where to print to */ public void printContext(PrintWriter out) { out.println(getMessage()); diff --git a/dx/src/com/android/dx/util/FileUtils.java b/dx/src/com/android/dx/util/FileUtils.java index 07a7c7e8d..3f51207e0 100644 --- a/dx/src/com/android/dx/util/FileUtils.java +++ b/dx/src/com/android/dx/util/FileUtils.java @@ -35,8 +35,8 @@ public final class FileUtils { * Reads the named file, translating {@link IOException} to a * {@link RuntimeException} of some sort. * - * @param fileName non-null; name of the file to read - * @return non-null; contents of the file + * @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); @@ -47,8 +47,8 @@ public final class FileUtils { * Reads the given file, translating {@link IOException} to a * {@link RuntimeException} of some sort. * - * @param file non-null; the file to read - * @return non-null; contents of the file + * @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()) { diff --git a/dx/src/com/android/dx/util/FixedSizeList.java b/dx/src/com/android/dx/util/FixedSizeList.java index 7b7d32521..17d773c05 100644 --- a/dx/src/com/android/dx/util/FixedSizeList.java +++ b/dx/src/com/android/dx/util/FixedSizeList.java @@ -23,11 +23,11 @@ import java.util.Arrays; */ public class FixedSizeList extends MutabilityControl implements ToHuman { - /** non-null; array of elements */ + /** {@code non-null;} array of elements */ private Object[] arr; /** - * Constructs an instance. All indices initially contain null. + * Constructs an instance. All indices initially contain {@code null}. * * @param size the size of the list */ @@ -94,10 +94,10 @@ public class FixedSizeList /** * Gets a customized string form for this instance. * - * @param prefix null-ok; prefix for the start of the result - * @param separator null-ok; separator to insert between each item - * @param suffix null-ok; suffix for the end of the result - * @return non-null; the custom string + * @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); @@ -108,10 +108,10 @@ public class FixedSizeList * only work if every element of the list implements {@link * ToHuman}. * - * @param prefix null-ok; prefix for the start of the result - * @param separator null-ok; separator to insert between each item - * @param suffix null-ok; suffix for the end of the result - * @return non-null; the custom string + * @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); @@ -126,7 +126,7 @@ public class FixedSizeList /** * Shrinks this instance to fit, by removing any unset - * (null) elements, leaving the remaining elements in + * ({@code null}) elements, leaving the remaining elements in * their original order. */ public void shrinkToFit() { @@ -165,12 +165,12 @@ public class FixedSizeList /** * 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 NullPointerException. This method is + * will throw {@code NullPointerException}. This method is * protected so that subclasses may offer a safe type-checked * public interface to their clients. * - * @param n >= 0, < size(); which element - * @return non-null; the indicated element + * @param n {@code >= 0, < size();} which element + * @return {@code non-null;} the indicated element */ protected final Object get0(int n) { try { @@ -188,13 +188,13 @@ public class FixedSizeList } /** - * Gets the indicated element, allowing nulls to be + * 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 >= 0, < size(); which element - * @return null-ok; the indicated element + * @param n {@code >= 0, < size();} which element + * @return {@code null-ok;} the indicated element */ protected final Object getOrNull0(int n) { return arr[n]; @@ -206,8 +206,8 @@ public class FixedSizeList * subclasses may offer a safe type-checked public interface to * their clients. * - * @param n >= 0, < size(); which element - * @param obj null-ok; the value to store + * @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(); @@ -239,11 +239,11 @@ public class FixedSizeList * Helper for {@link #toString} and {@link #toHuman}, which both of * those call to pretty much do everything. * - * @param prefix null-ok; prefix for the start of the result - * @param separator null-ok; separator to insert between each item - * @param suffix null-ok; suffix for the end of the result + * @param 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 non-null; the custom string + * @return {@code non-null;} the custom string */ private String toString0(String prefix, String separator, String suffix, boolean human) { diff --git a/dx/src/com/android/dx/util/Hex.java b/dx/src/com/android/dx/util/Hex.java index cf4c130d2..cb71e5e20 100644 --- a/dx/src/com/android/dx/util/Hex.java +++ b/dx/src/com/android/dx/util/Hex.java @@ -28,10 +28,10 @@ public final class Hex { } /** - * Formats a long as an 8-byte unsigned hex value. + * Formats a {@code long} as an 8-byte unsigned hex value. * * @param v value to format - * @return non-null; formatted form + * @return {@code non-null;} formatted form */ public static String u8(long v) { char[] result = new char[16]; @@ -44,10 +44,10 @@ public final class Hex { } /** - * Formats an int as a 4-byte unsigned hex value. + * Formats an {@code int} as a 4-byte unsigned hex value. * * @param v value to format - * @return non-null; formatted form + * @return {@code non-null;} formatted form */ public static String u4(int v) { char[] result = new char[8]; @@ -60,10 +60,10 @@ public final class Hex { } /** - * Formats an int as a 3-byte unsigned hex value. + * Formats an {@code int} as a 3-byte unsigned hex value. * * @param v value to format - * @return non-null; formatted form + * @return {@code non-null;} formatted form */ public static String u3(int v) { char[] result = new char[6]; @@ -76,10 +76,10 @@ public final class Hex { } /** - * Formats an int as a 2-byte unsigned hex value. + * Formats an {@code int} as a 2-byte unsigned hex value. * * @param v value to format - * @return non-null; formatted form + * @return {@code non-null;} formatted form */ public static String u2(int v) { char[] result = new char[4]; @@ -92,12 +92,12 @@ public final class Hex { } /** - * Formats an int as either a 2-byte unsigned hex value + * 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 non-null; formatted form + * @return {@code non-null;} formatted form */ public static String u2or4(int v) { if (v == (char) v) { @@ -108,10 +108,10 @@ public final class Hex { } /** - * Formats an int as a 1-byte unsigned hex value. + * Formats an {@code int} as a 1-byte unsigned hex value. * * @param v value to format - * @return non-null; formatted form + * @return {@code non-null;} formatted form */ public static String u1(int v) { char[] result = new char[2]; @@ -124,10 +124,10 @@ public final class Hex { } /** - * Formats an int as a 4-bit unsigned hex nibble. + * Formats an {@code int} as a 4-bit unsigned hex nibble. * * @param v value to format - * @return non-null; formatted form + * @return {@code non-null;} formatted form */ public static String uNibble(int v) { char[] result = new char[1]; @@ -137,10 +137,10 @@ public final class Hex { } /** - * Formats a long as an 8-byte signed hex value. + * Formats a {@code long} as an 8-byte signed hex value. * * @param v value to format - * @return non-null; formatted form + * @return {@code non-null;} formatted form */ public static String s8(long v) { char[] result = new char[17]; @@ -161,10 +161,10 @@ public final class Hex { } /** - * Formats an int as a 4-byte signed hex value. + * Formats an {@code int} as a 4-byte signed hex value. * * @param v value to format - * @return non-null; formatted form + * @return {@code non-null;} formatted form */ public static String s4(int v) { char[] result = new char[9]; @@ -185,10 +185,10 @@ public final class Hex { } /** - * Formats an int as a 2-byte signed hex value. + * Formats an {@code int} as a 2-byte signed hex value. * * @param v value to format - * @return non-null; formatted form + * @return {@code non-null;} formatted form */ public static String s2(int v) { char[] result = new char[5]; @@ -209,10 +209,10 @@ public final class Hex { } /** - * Formats an int as a 1-byte signed hex value. + * Formats an {@code int} as a 1-byte signed hex value. * * @param v value to format - * @return non-null; formatted form + * @return {@code non-null;} formatted form */ public static String s1(int v) { char[] result = new char[3]; @@ -233,18 +233,18 @@ public final class Hex { } /** - * Formats a hex dump of a portion of a byte[]. The 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 (""). + * in which case the result is always the empty string ({@code ""}). * - * @param arr non-null; array to format - * @param offset >= 0; offset to the part to dump - * @param length >= 0; number of bytes to dump - * @param outOffset >= 0; first output offset to print - * @param bpl >= 0; number of bytes of output per line - * @param addressLength {2,4,6,8}; number of characters for each address + * @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 non-null; a string of the dump + * @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) { diff --git a/dx/src/com/android/dx/util/HexParser.java b/dx/src/com/android/dx/util/HexParser.java index 4b6b7b2f7..3d0c99235 100644 --- a/dx/src/com/android/dx/util/HexParser.java +++ b/dx/src/com/android/dx/util/HexParser.java @@ -28,7 +28,7 @@ public final class HexParser { } /** - * Parses the given text as hex, returning a byte[] + * 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 @@ -38,8 +38,8 @@ public final class HexParser { * of the subsequent characters is used, until the next double * quote. Quoted strings may not span multiple lines. * - * @param src non-null; the source string - * @return non-null; the parsed form + * @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(); diff --git a/dx/src/com/android/dx/util/IndentingWriter.java b/dx/src/com/android/dx/util/IndentingWriter.java index db4e0a2ad..92f0b5566 100644 --- a/dx/src/com/android/dx/util/IndentingWriter.java +++ b/dx/src/com/android/dx/util/IndentingWriter.java @@ -27,31 +27,31 @@ import java.io.Writer; * line. */ public final class IndentingWriter extends FilterWriter { - /** null-ok; optional prefix for every line */ + /** {@code null-ok;} optional prefix for every line */ private final String prefix; - /** > 0; the maximum output width */ + /** {@code > 0;} the maximum output width */ private final int width; - /** > 0; the maximum indent */ + /** {@code > 0;} the maximum indent */ private final int maxIndent; - /** >= 0; current output column (zero-based) */ + /** {@code >= 0;} current output column (zero-based) */ private int column; /** whether indent spaces are currently being collected */ private boolean collectingIndent; - /** >= 0; current indent amount */ + /** {@code >= 0;} current indent amount */ private int indent; /** * Constructs an instance. * - * @param out non-null; writer to send final output to - * @param width >= 0; the maximum output width (not including - * prefix), or 0 for no maximum - * @param prefix non-null; the prefix for each line + * @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); @@ -78,9 +78,9 @@ public final class IndentingWriter extends FilterWriter { /** * Constructs a no-prefix instance. * - * @param out non-null; writer to send final output to - * @param width >= 0; the maximum output width (not including - * prefix), or 0 for no maximum + * @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, ""); diff --git a/dx/src/com/android/dx/util/IntIterator.java b/dx/src/com/android/dx/util/IntIterator.java index 88181b52e..4caa43946 100644 --- a/dx/src/com/android/dx/util/IntIterator.java +++ b/dx/src/com/android/dx/util/IntIterator.java @@ -35,10 +35,4 @@ public interface IntIterator { * @throws java.util.NoSuchElementException if no next element exists */ int next(); - - /** - * Removes a value from the collection underlying this iterator. - * May throw UnsupportedOperationException(). - */ -// void remove(); } diff --git a/dx/src/com/android/dx/util/IntList.java b/dx/src/com/android/dx/util/IntList.java index f60bbb5e2..c51c0282c 100644 --- a/dx/src/com/android/dx/util/IntList.java +++ b/dx/src/com/android/dx/util/IntList.java @@ -19,16 +19,16 @@ package com.android.dx.util; import java.util.Arrays; /** - * Simple list of ints. + * Simple list of {@code int}s. */ public final class IntList extends MutabilityControl { - /** non-null; immutable, no-element instance */ + /** {@code non-null;} immutable, no-element instance */ public static final IntList EMPTY = new IntList(0); - /** non-null; array of elements */ + /** {@code non-null;} array of elements */ private int[] values; - /** >= 0; current size of the list */ + /** {@code >= 0;} current size of the list */ private int size; /** whether the values are currently sorted */ @@ -78,7 +78,7 @@ public final class IntList extends MutabilityControl { /** * Constructs an empty instance. * - * @param initialCapacity >= 0; initial capacity of the list + * @param initialCapacity {@code >= 0;} initial capacity of the list */ public IntList(int initialCapacity) { super(true); @@ -165,7 +165,7 @@ public final class IntList extends MutabilityControl { /** * Gets the indicated value. * - * @param n >= 0, < size(); which element + * @param n {@code >= 0, < size();} which element * @return the indicated element's value */ public int get(int n) { @@ -184,7 +184,7 @@ public final class IntList extends MutabilityControl { /** * Sets the value at the given index. * - * @param n >= 0, < size(); which element + * @param n {@code >= 0, < size();} which element * @param value value to store */ public void set(int n, int value) { @@ -229,7 +229,7 @@ public final class IntList extends MutabilityControl { * current size (that is, insertion as a last element is legal but * no further). * - * @param n >=0 <=size(); index of where to insert + * @param n {@code >= 0, <=size();} index of where to insert * @param value value to insert */ public void insert(int n, int value) { @@ -252,7 +252,7 @@ public final class IntList extends MutabilityControl { * Removes an element at a given index, shifting elements at greater * indicies down one. * - * @param n >=0 < size(); index of element to remove + * @param n {@code >=0, < size();} index of element to remove */ public void removeIndex(int n) { if (n >= size) { @@ -307,7 +307,7 @@ public final class IntList extends MutabilityControl { /** * Pops N elements off the end of the list and decreasing the size by N. * - * @param n >= 0; number of elements to remove from end. + * @param n {@code >= 0;} number of elements to remove from end. * @exception IndexOutOfBoundsException if stack is smaller than N */ public void pop(int n) { @@ -319,7 +319,7 @@ public final class IntList extends MutabilityControl { /** * Shrinks the size of the list. * - * @param newSize >= 0; the new size + * @param newSize {@code >= 0;} the new size */ public void shrink(int newSize) { if (newSize < 0) { @@ -338,7 +338,7 @@ public final class IntList extends MutabilityControl { /** * Makes and returns a mutable copy of the list. * - * @return non-null; an appropriately-constructed instance + * @return {@code non-null;} an appropriately-constructed instance */ public IntList mutableCopy() { int sz = size; @@ -380,12 +380,12 @@ public final class IntList extends MutabilityControl { /** * Performs a binary search on a sorted list, returning the index of * the given value if it is present or - * (-(insertion point) - 1) if the value is not present. + * {@code (-(insertion point) - 1)} if the value is not present. * If the list is not sorted, then reverts to linear search and returns - * -size() if the element is not found. + * {@code -size()} if the element is not found. * * @param value value to find - * @return index of value or (-(insertion point) - 1) if the + * @return index of value or {@code (-(insertion point) - 1)} if the * value is not present */ public int binarysearch(int value) { diff --git a/dx/src/com/android/dx/util/IntSet.java b/dx/src/com/android/dx/util/IntSet.java index 10b6ee047..33b6bdd48 100644 --- a/dx/src/com/android/dx/util/IntSet.java +++ b/dx/src/com/android/dx/util/IntSet.java @@ -44,24 +44,24 @@ public interface IntSet { boolean has(int value); /** - * Merges other into this set, so this set becomes the + * Merges {@code other} into this set, so this set becomes the * union of the two. * - * @param other non-null; other set to merge with. + * @param other {@code non-null;} other set to merge with. */ void merge(IntSet other); /** * Returns the count of unique elements in this set. * - * @return > = 0; count of unique elements + * @return {@code > = 0;} count of unique elements */ int elements(); /** * Iterates the set * - * @return non-null; a set iterator + * @return {@code non-null;} a set iterator */ IntIterator iterator(); } diff --git a/dx/src/com/android/dx/util/LabeledItem.java b/dx/src/com/android/dx/util/LabeledItem.java index cc6a0d252..b4856cfc9 100644 --- a/dx/src/com/android/dx/util/LabeledItem.java +++ b/dx/src/com/android/dx/util/LabeledItem.java @@ -24,7 +24,7 @@ public interface LabeledItem { /* * Gets the label of this block. * - * @return >= 0; the label + * @return {@code >= 0;} the label */ public int getLabel(); } diff --git a/dx/src/com/android/dx/util/LabeledList.java b/dx/src/com/android/dx/util/LabeledList.java index 3168a38d0..28a148bd1 100644 --- a/dx/src/com/android/dx/util/LabeledList.java +++ b/dx/src/com/android/dx/util/LabeledList.java @@ -58,7 +58,7 @@ public class LabeledList extends FixedSizeList { /** * Gets the maximum label (exclusive) of any block added to this instance. * - * @return >= 0; the maximum label + * @return {@code >= 0;} the maximum label */ public int getMaxLabel() { int sz = labelToIndex.size(); @@ -102,8 +102,8 @@ public class LabeledList extends FixedSizeList { * Gets the index of the first item in the list with the given * label, if any. * - * @param label >= 0; the label to look for - * @return >= -1; the index of the so-labelled item, or -1 + * @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 int indexOfLabel(int label) { @@ -142,8 +142,8 @@ public class LabeledList extends FixedSizeList { /** * Sets the element at the given index. * - * @param n >= 0, < size(); which element - * @param item null-ok; the value to store + * @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); diff --git a/dx/src/com/android/dx/util/Leb128Utils.java b/dx/src/com/android/dx/util/Leb128Utils.java index dfd416f6b..6ed3a6181 100644 --- a/dx/src/com/android/dx/util/Leb128Utils.java +++ b/dx/src/com/android/dx/util/Leb128Utils.java @@ -41,7 +41,6 @@ public final class Leb128Utils { int count = 0; while (remaining != 0) { - value = remaining; remaining >>= 7; count++; } diff --git a/dx/src/com/android/dx/util/ListIntSet.java b/dx/src/com/android/dx/util/ListIntSet.java index a9f79afea..6d28a18e4 100644 --- a/dx/src/com/android/dx/util/ListIntSet.java +++ b/dx/src/com/android/dx/util/ListIntSet.java @@ -122,11 +122,6 @@ public class ListIntSet implements IntSet { return ints.get(idx++); } - - /** @inheritDoc */ - public void remove() { - throw new UnsupportedOperationException(); - } }; } diff --git a/dx/src/com/android/dx/util/MutabilityControl.java b/dx/src/com/android/dx/util/MutabilityControl.java index 8b3383b73..14e0f2ed6 100644 --- a/dx/src/com/android/dx/util/MutabilityControl.java +++ b/dx/src/com/android/dx/util/MutabilityControl.java @@ -36,7 +36,7 @@ public class MutabilityControl { /** * Constructs an instance, explicitly indicating the mutability. * - * @param mutable true iff this instance is mutable + * @param mutable {@code true} iff this instance is mutable */ public MutabilityControl(boolean mutable) { this.mutable = mutable; @@ -51,9 +51,9 @@ public class MutabilityControl { /** * Checks to see whether or not this instance is immutable. This is the - * same as calling !isMutable(). + * same as calling {@code !isMutable()}. * - * @return true iff this instance is immutable + * @return {@code true} iff this instance is immutable */ public final boolean isImmutable() { return !mutable; @@ -62,7 +62,7 @@ public class MutabilityControl { /** * Checks to see whether or not this instance is mutable. * - * @return true iff this instance is mutable + * @return {@code true} iff this instance is mutable */ public final boolean isMutable() { return mutable; diff --git a/dx/src/com/android/dx/util/Output.java b/dx/src/com/android/dx/util/Output.java index b3c37472b..5e737ae53 100644 --- a/dx/src/com/android/dx/util/Output.java +++ b/dx/src/com/android/dx/util/Output.java @@ -18,7 +18,7 @@ package com.android.dx.util; /** * Interface for a sink for binary output. This is similar to - * java.util.DataOutput, but no IOExceptions + * {@code java.util.DataOutput}, but no {@code IOExceptions} * are declared, and multibyte output is defined to be little-endian. */ public interface Output { @@ -26,7 +26,7 @@ public interface Output { * Gets the current cursor position. This is the same as the number of * bytes written to this instance. * - * @return >= 0; the cursor position + * @return {@code >= 0;} the cursor position */ public int getCursor(); @@ -34,34 +34,34 @@ public interface Output { * Asserts that the cursor is the given value. * * @param expectedCursor the expected cursor value - * @throws RuntimeException thrown if getCursor() != - * expectedCursor + * @throws RuntimeException thrown if {@code getCursor() != + * expectedCursor} */ public void assertCursor(int expectedCursor); /** - * Writes a byte to this instance. + * 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 short to this instance. + * 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 int to this instance. + * Writes an {@code int} to this instance. * * @param value the value to write */ public void writeInt(int value); /** - * Writes a long to this instance. + * Writes a {@code long} to this instance. * * @param value the value to write */ @@ -73,7 +73,7 @@ public interface Output { * 7.6. * * @param value value to write, treated as an unsigned value - * @return 1..5; the number of bytes actually written + * @return {@code 1..5;} the number of bytes actually written */ public int writeUnsignedLeb128(int value); @@ -83,47 +83,47 @@ public interface Output { * 7.6. * * @param value value to write - * @return 1..5; the number of bytes actually written + * @return {@code 1..5;} the number of bytes actually written */ public int writeSignedLeb128(int value); /** * Writes a {@link ByteArray} to this instance. * - * @param bytes non-null; the array to write + * @param bytes {@code non-null;} the array to write */ public void write(ByteArray bytes); /** - * Writes a portion of a byte[] to this instance. + * Writes a portion of a {@code byte[]} to this instance. * - * @param bytes non-null; the array to write - * @param offset >= 0; offset into bytes for the first + * @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 >= 0; number of bytes to write + * @param length {@code >= 0;} number of bytes to write */ public void write(byte[] bytes, int offset, int length); /** - * Writes a byte[] to this instance. This is just - * a convenient shorthand for write(bytes, 0, bytes.length). + * Writes a {@code byte[]} to this instance. This is just + * a convenient shorthand for {@code write(bytes, 0, bytes.length)}. * - * @param bytes non-null; the array to write + * @param bytes {@code non-null;} the array to write */ public void write(byte[] bytes); /** - * Writes the given number of 0 bytes. + * Writes the given number of {@code 0} bytes. * - * @param count >= 0; the number of zeroes to write + * @param count {@code >= 0;} the number of zeroes to write */ public void writeZeroes(int count); /** - * Adds extra bytes if necessary (with value 0) to + * Adds extra bytes if necessary (with value {@code 0}) to * force alignment of the output cursor as given. * - * @param alignment > 0; the alignment; must be a power of two + * @param alignment {@code > 0;} the alignment; must be a power of two */ public void alignTo(int alignment); } diff --git a/dx/src/com/android/dx/util/ToHuman.java b/dx/src/com/android/dx/util/ToHuman.java index 89bf4f735..b3a31a5c4 100644 --- a/dx/src/com/android/dx/util/ToHuman.java +++ b/dx/src/com/android/dx/util/ToHuman.java @@ -23,9 +23,9 @@ package com.android.dx.util; public interface ToHuman { /** * Return the "human" string form of this instance. This is - * generally less "debuggy" than toString(). + * generally less "debuggy" than {@code toString()}. * - * @return non-null; the human string form + * @return {@code non-null;} the human string form */ public String toHuman(); } diff --git a/dx/src/com/android/dx/util/TwoColumnOutput.java b/dx/src/com/android/dx/util/TwoColumnOutput.java index cc9f7d4c6..a155c152f 100644 --- a/dx/src/com/android/dx/util/TwoColumnOutput.java +++ b/dx/src/com/android/dx/util/TwoColumnOutput.java @@ -28,34 +28,34 @@ import java.io.Writer; * one which goes on the right. */ public final class TwoColumnOutput { - /** non-null; underlying writer for final output */ + /** {@code non-null;} underlying writer for final output */ private final Writer out; - /** > 0; the left column width */ + /** {@code > 0;} the left column width */ private final int leftWidth; - /** non-null; pending left column output */ + /** {@code non-null;} pending left column output */ private final StringBuffer leftBuf; - /** non-null; pending right column output */ + /** {@code non-null;} pending right column output */ private final StringBuffer rightBuf; - /** non-null; left column writer */ + /** {@code non-null;} left column writer */ private final IndentingWriter leftColumn; - /** non-null; right column writer */ + /** {@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 non-null; first string - * @param width1 > 0; width of the first column - * @param spacer non-null; spacer string - * @param s2 non-null; second string - * @param width2 > 0; width of the second column - * @return non-null; an appropriately-formatted 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) { @@ -80,10 +80,10 @@ public final class TwoColumnOutput { /** * Constructs an instance. * - * @param out non-null; writer to send final output to - * @param leftWidth > 0; width of the left column, in characters - * @param rightWidth > 0; width of the right column, in characters - * @param spacer non-null; spacer string to sit between the two columns + * @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) { @@ -118,10 +118,10 @@ public final class TwoColumnOutput { /** * Constructs an instance. * - * @param out non-null; stream to send final output to - * @param leftWidth >= 1; width of the left column, in characters - * @param rightWidth >= 1; width of the right column, in characters - * @param spacer non-null; spacer string to sit between the two columns + * @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) { @@ -131,7 +131,7 @@ public final class TwoColumnOutput { /** * Gets the writer to use to write to the left column. * - * @return non-null; the left column writer + * @return {@code non-null;} the left column writer */ public Writer getLeft() { return leftColumn; @@ -140,7 +140,7 @@ public final class TwoColumnOutput { /** * Gets the writer to use to write to the right column. * - * @return non-null; the right column writer + * @return {@code non-null;} the right column writer */ public Writer getRight() { return rightColumn; @@ -226,8 +226,8 @@ public final class TwoColumnOutput { * Appends a newline to the given buffer via the given writer, but * only if it isn't empty and doesn't already end with one. * - * @param buf non-null; the buffer in question - * @param out non-null; the writer to use + * @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) @@ -242,8 +242,8 @@ public final class TwoColumnOutput { /** * Writes the given number of spaces to the given writer. * - * @param out non-null; where to write - * @param amt >= 0; the number of spaces to write + * @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) { diff --git a/dx/src/com/android/dx/util/Writers.java b/dx/src/com/android/dx/util/Writers.java index f10e400af..632b0821e 100644 --- a/dx/src/com/android/dx/util/Writers.java +++ b/dx/src/com/android/dx/util/Writers.java @@ -20,7 +20,7 @@ import java.io.PrintWriter; import java.io.Writer; /** - * Utilities for dealing with Writers. + * Utilities for dealing with {@code Writer}s. */ public final class Writers { /** @@ -31,12 +31,12 @@ public final class Writers { } /** - * Makes a PrintWriter for the given 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 non-null; writer to (possibly) wrap - * @return non-null; an appropriate instance + * @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) { diff --git a/dx/src/com/android/dx/util/_tests/_Bits.java b/dx/src/com/android/dx/util/_tests/_Bits.java index e529b505f..a95fc14d7 100644 --- a/dx/src/com/android/dx/util/_tests/_Bits.java +++ b/dx/src/com/android/dx/util/_tests/_Bits.java @@ -21,7 +21,7 @@ import com.android.dx.util.Bits; import junit.framework.TestCase; /** - * Test the class com.android.dx.util.Bits. + * Test the class {@code com.android.dx.util.Bits}. */ public class _Bits extends TestCase { diff --git a/dx/src/com/android/dx/util/_tests/_IntList.java b/dx/src/com/android/dx/util/_tests/_IntList.java index 241e8be78..dadbd54fb 100644 --- a/dx/src/com/android/dx/util/_tests/_IntList.java +++ b/dx/src/com/android/dx/util/_tests/_IntList.java @@ -21,7 +21,7 @@ import com.android.dx.util.IntList; import junit.framework.TestCase; /** - * Test the class com.android.dx.util.IntList. + * Test the class {@code com.android.dx.util.IntList}. */ public class _IntList extends TestCase { diff --git a/dx/tests/111-use-null-as-array/Blort.java b/dx/tests/111-use-null-as-array/Blort.java new file mode 100644 index 000000000..c16684fbf --- /dev/null +++ b/dx/tests/111-use-null-as-array/Blort.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Blort { + public static boolean test_getBooleanArray() { + boolean[] arr = null; + return arr[1]; + } + + public static byte test_getByteArray() { + byte[] arr = null; + return arr[2]; + } + + public static char test_getCharArray() { + char[] arr = null; + return arr[3]; + } + + public static double test_getDoubleArray() { + double[] arr = null; + return arr[4]; + } + + public static float test_getFloatArray() { + float[] arr = null; + return arr[5]; + } + + public static int test_getIntArray() { + int[] arr = null; + return arr[6]; + } + + public static long test_getLongArray() { + long[] arr = null; + return arr[7]; + } + + public static Object test_getObjectArray() { + Object[] arr = null; + return arr[8]; + } + + public static short test_getShortArray() { + short[] arr = null; + return arr[9]; + } + + public static void test_setBooleanArray() { + boolean[] arr = null; + arr[1] = true; + } + + public static void test_setByteArray() { + byte[] arr = null; + arr[2] = (byte) 3; + } + + public static void test_setCharArray() { + char[] arr = null; + arr[4] = (char) 5; + } + + public static void test_setDoubleArray() { + double[] arr = null; + arr[6] = 7.0F; + } + + public static void test_setFloatArray() { + float[] arr = null; + arr[8] = 9.0F; + } + + public static void test_setIntArray() { + int[] arr = null; + arr[10] = 11; + } + + public static void test_setLongArray() { + long[] arr = null; + arr[12] = 13; + } + + public static void test_setObjectArray() { + Object[] arr = null; + arr[14] = "blort"; + } + + public static void test_setShortArray() { + short[] arr = null; + arr[15] = (short) 16; + } +} + diff --git a/dx/tests/111-use-null-as-array/expected.txt b/dx/tests/111-use-null-as-array/expected.txt new file mode 100644 index 000000000..7e2116b92 --- /dev/null +++ b/dx/tests/111-use-null-as-array/expected.txt @@ -0,0 +1,116 @@ +Blort.test_getBooleanArray:()Z: +regs: 0002; ins: 0000; outs: 0000 + 0000: const/4 v0, #null // #0 + 0001: const/4 v1, #int 1 // #1 + 0002: aget-byte v0, v0, v1 + 0004: return v0 +Blort.test_getByteArray:()B: +regs: 0002; ins: 0000; outs: 0000 + 0000: const/4 v0, #null // #0 + 0001: const/4 v1, #int 2 // #2 + 0002: aget-byte v0, v0, v1 + 0004: return v0 +Blort.test_getCharArray:()C: +regs: 0002; ins: 0000; outs: 0000 + 0000: const/4 v0, #null // #0 + 0001: const/4 v1, #int 3 // #3 + 0002: aget-char v0, v0, v1 + 0004: return v0 +Blort.test_getDoubleArray:()D: +regs: 0002; ins: 0000; outs: 0000 + 0000: const/4 v0, #null // #0 + 0001: const/4 v1, #int 4 // #4 + 0002: aget-wide v0, v0, v1 + 0004: return-wide v0 +Blort.test_getFloatArray:()F: +regs: 0002; ins: 0000; outs: 0000 + 0000: const/4 v0, #null // #0 + 0001: const/4 v1, #int 5 // #5 + 0002: aget v0, v0, v1 + 0004: return v0 +Blort.test_getIntArray:()I: +regs: 0002; ins: 0000; outs: 0000 + 0000: const/4 v0, #null // #0 + 0001: const/4 v1, #int 6 // #6 + 0002: aget v0, v0, v1 + 0004: return v0 +Blort.test_getLongArray:()J: +regs: 0002; ins: 0000; outs: 0000 + 0000: const/4 v0, #null // #0 + 0001: const/4 v1, #int 7 // #7 + 0002: aget-wide v0, v0, v1 + 0004: return-wide v0 +Blort.test_getObjectArray:()Ljava/lang/Object;: +regs: 0002; ins: 0000; outs: 0000 + 0000: const/4 v0, #null // #0 + 0001: const/16 v1, #int 8 // #0008 + 0003: aget-object v0, v0, v1 + 0005: return-object v0 +Blort.test_getShortArray:()S: +regs: 0002; ins: 0000; outs: 0000 + 0000: const/4 v0, #null // #0 + 0001: const/16 v1, #int 9 // #0009 + 0003: aget-short v0, v0, v1 + 0005: return v0 +Blort.test_setBooleanArray:()V: +regs: 0002; ins: 0000; outs: 0000 + 0000: const/4 v1, #int 1 // #1 + 0001: const/4 v0, #null // #0 + 0002: aput v1, v0, v1 + 0004: return-void +Blort.test_setByteArray:()V: +regs: 0003; ins: 0000; outs: 0000 + 0000: const/4 v0, #null // #0 + 0001: const/4 v1, #int 2 // #2 + 0002: const/4 v2, #int 3 // #3 + 0003: aput v2, v0, v1 + 0005: return-void +Blort.test_setCharArray:()V: +regs: 0003; ins: 0000; outs: 0000 + 0000: const/4 v0, #null // #0 + 0001: const/4 v1, #int 4 // #4 + 0002: const/4 v2, #int 5 // #5 + 0003: aput v2, v0, v1 + 0005: return-void +Blort.test_setDoubleArray:()V: +regs: 0004; ins: 0000; outs: 0000 + 0000: const/4 v0, #null // #0 + 0001: const/4 v1, #int 6 // #6 + 0002: const-wide/high16 v2, #double 7.0 // #401c000000000000 + 0004: aput-wide v2, v0, v1 + 0006: return-void +Blort.test_setFloatArray:()V: +regs: 0003; ins: 0000; outs: 0000 + 0000: const/4 v0, #null // #0 + 0001: const/16 v1, #int 8 // #0008 + 0003: const/high16 v2, #float 9.0 // #41100000 + 0005: aput v2, v0, v1 + 0007: return-void +Blort.test_setIntArray:()V: +regs: 0003; ins: 0000; outs: 0000 + 0000: const/4 v0, #null // #0 + 0001: const/16 v1, #int 10 // #000a + 0003: const/16 v2, #int 11 // #000b + 0005: aput v2, v0, v1 + 0007: return-void +Blort.test_setLongArray:()V: +regs: 0004; ins: 0000; outs: 0000 + 0000: const/4 v0, #null // #0 + 0001: const/16 v1, #int 12 // #000c + 0003: const-wide/16 v2, #long 13 // #000d + 0005: aput-wide v2, v0, v1 + 0007: return-void +Blort.test_setObjectArray:()V: +regs: 0003; ins: 0000; outs: 0000 + 0000: const/4 v0, #null // #0 + 0001: const/16 v1, #int 14 // #000e + 0003: const-string v2, "blort" + 0005: aput-object v2, v0, v1 + 0007: return-void +Blort.test_setShortArray:()V: +regs: 0003; ins: 0000; outs: 0000 + 0000: const/4 v0, #null // #0 + 0001: const/16 v1, #int 15 // #000f + 0003: const/16 v2, #int 16 // #0010 + 0005: aput v2, v0, v1 + 0007: return-void diff --git a/dx/tests/111-use-null-as-array/info.txt b/dx/tests/111-use-null-as-array/info.txt new file mode 100644 index 000000000..624386d1e --- /dev/null +++ b/dx/tests/111-use-null-as-array/info.txt @@ -0,0 +1,18 @@ +This is a smoke test of dex conversion, which checks to see that uses +of a known-null in contexts that require a specific type end up getting +converted to the type in question. When executed, this sort of code +will inevitably throw a NullPointerException, but if the opcode weren't +correct, they would instead incorrectly fail verification. + +If you inspect the expected output of this test, you will see that +there are some surprising instructions in there, such as using +aget-byte for what was a boolean[] in the source code. In these cases, +the resulting output is still correct (passes verification and will +throw a NullPointerException if ever executed). However, it happens +that during translation there simply wasn't enough information to +recover the "true" original meaning at the level of actual opcode +selection. + +This test compares emitted code against a known-good (via eyeballing) +version, so it is possible for this test to spuriously fail if other +aspects of conversion end up altering the output in innocuous ways. diff --git a/dx/tests/111-use-null-as-array/run b/dx/tests/111-use-null-as-array/run new file mode 100644 index 000000000..7e4e1e8a1 --- /dev/null +++ b/dx/tests/111-use-null-as-array/run @@ -0,0 +1,19 @@ +#!/bin/bash +# +# Copyright (C) 2009 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +$JAVAC -g -d . Blort.java +dx --debug --dex --positions=none --no-locals \ + --dump-to=- --dump-method="Blort.test*" *.class diff --git a/libcore/Android.mk b/libcore/Android.mk index b22b4c04c..7de73be1b 100644 --- a/libcore/Android.mk +++ b/libcore/Android.mk @@ -51,7 +51,7 @@ include $(BUILD_JAVA_LIBRARY) # resource with a "#" in its name, but Perforce doesn't # allow us to submit such a file. So we create it here # on-the-fly. -TMP_RESOURCE_DIR := /tmp/ +TMP_RESOURCE_DIR := $(OUT_DIR)/tmp/ TMP_RESOURCE_FILE := org/apache/harmony/luni/tests/java/lang/test\#.properties $(TMP_RESOURCE_DIR)$(TMP_RESOURCE_FILE): diff --git a/libcore/annotation/src/main/java/java/lang/annotation/Annotation.java b/libcore/annotation/src/main/java/java/lang/annotation/Annotation.java index 43ad1bb37..00707b1d5 100644 --- a/libcore/annotation/src/main/java/java/lang/annotation/Annotation.java +++ b/libcore/annotation/src/main/java/java/lang/annotation/Annotation.java @@ -22,17 +22,15 @@ package java.lang.annotation; * itself is not an annotation, and neither is an interface that simply * extends this one. Only the compiler is able to create proper annotation * types. - * - * @since Android 1.0 + * + * @since 1.5 */ public interface Annotation { /** * Returns the type of this annotation. - * + * * @return A {@code Class} instance representing the annotation type. - * - * @since Android 1.0 */ Class annotationType(); @@ -70,14 +68,12 @@ public interface Annotation { * calling their {@code equals()} method. *
    5. * - * + * * @param obj * The object to compare to. - * - * @return {@code true} if {@code obj} is equal to this annotation, - * {@code false} otherwise. - * - * @since Android 1.0 + * + * @return {@code true} if {@code obj} is equal to this annotation, + * {@code false} otherwise. */ boolean equals(Object obj); @@ -111,10 +107,8 @@ public interface Annotation { * calling their {@code hashCode} method. * * - * + * * @return the hash code. - * - * @since Android 1.0 */ int hashCode(); @@ -123,11 +117,9 @@ public interface Annotation { * strictly defined what the representation has to look like, but it usually * consists of the name of the annotation, preceded by a "@". If the * annotation contains field members, their names and values are also - * included in the result. + * included in the result. * * @return the {@code String} that represents this annotation. - * - * @since Android 1.0 */ String toString(); } diff --git a/libcore/annotation/src/main/java/java/lang/annotation/AnnotationFormatError.java b/libcore/annotation/src/main/java/java/lang/annotation/AnnotationFormatError.java index ce5c3a0d8..67775c732 100644 --- a/libcore/annotation/src/main/java/java/lang/annotation/AnnotationFormatError.java +++ b/libcore/annotation/src/main/java/java/lang/annotation/AnnotationFormatError.java @@ -22,8 +22,8 @@ package java.lang.annotation; * syntactically incorrect and the annotation parser is unable to process it. * This exception is unlikely to ever occur, given that the code has been * compiled by an ordinary Java compiler. - * - * @since Android 1.0 + * + * @since 1.5 */ public class AnnotationFormatError extends Error { @@ -31,11 +31,9 @@ public class AnnotationFormatError extends Error { /** * Constructs an instance with the message provided. - * + * * @param message * the details of the error. - * - * @since Android 1.0 */ public AnnotationFormatError(String message) { super(message); @@ -43,14 +41,11 @@ public class AnnotationFormatError extends Error { /** * Constructs an instance with a message and a cause. - * + * * @param message * the details of the error. - * * @param cause * the cause of the error or {@code null} if none. - * - * @since Android 1.0 */ public AnnotationFormatError(String message, Throwable cause) { super(message, cause); @@ -60,11 +55,9 @@ public class AnnotationFormatError extends Error { * Constructs an instance with a cause. If the cause is not * {@code null}, then {@code cause.toString()} is used as the * error's message. - * + * * @param cause * the cause of the error or {@code null} if none. - * - * @since Android 1.0 */ public AnnotationFormatError(Throwable cause) { super(cause == null ? null : cause.toString(), cause); diff --git a/libcore/annotation/src/main/java/java/lang/annotation/AnnotationTypeMismatchException.java b/libcore/annotation/src/main/java/java/lang/annotation/AnnotationTypeMismatchException.java index 5bb3cbf75..0ff79ec8f 100644 --- a/libcore/annotation/src/main/java/java/lang/annotation/AnnotationTypeMismatchException.java +++ b/libcore/annotation/src/main/java/java/lang/annotation/AnnotationTypeMismatchException.java @@ -24,8 +24,8 @@ import org.apache.harmony.annotation.internal.nls.Messages; /** * Indicates that an annotation type has changed since it was compiled or * serialized. - * - * @since Android 1.0 + * + * @since 1.5 */ public class AnnotationTypeMismatchException extends RuntimeException { @@ -37,16 +37,13 @@ public class AnnotationTypeMismatchException extends RuntimeException { /** * Constructs an instance for the given type element and the type found. - * + * * @param element * the annotation type element. - * * @param foundType * the invalid type that was found. This is actually the textual * type description found in the binary class representation, * so it may not be human-readable. - * - * @since Android 1.0 */ public AnnotationTypeMismatchException(Method element, String foundType) { super(Messages.getString("annotation.1", element, foundType)); //$NON-NLS-1$ @@ -56,10 +53,8 @@ public class AnnotationTypeMismatchException extends RuntimeException { /** * Returns the method object for the invalid type. - * + * * @return a {@link Method} instance. - * - * @since Android 1.0 */ public Method element() { return element; @@ -67,10 +62,8 @@ public class AnnotationTypeMismatchException extends RuntimeException { /** * Returns the invalid type. - * + * * @return a string describing the invalid data. - * - * @since Android 1.0 */ public String foundType() { return foundType; diff --git a/libcore/annotation/src/main/java/java/lang/annotation/Documented.java b/libcore/annotation/src/main/java/java/lang/annotation/Documented.java index 2849fd2a8..7e7f72f8f 100644 --- a/libcore/annotation/src/main/java/java/lang/annotation/Documented.java +++ b/libcore/annotation/src/main/java/java/lang/annotation/Documented.java @@ -20,8 +20,8 @@ package java.lang.annotation; /** * Defines a meta-annotation for indicating that an annotation is documented and * considered part of the public API. - * - * @since Android 1.0 + * + * @since 1.5 */ @Documented @Retention(RetentionPolicy.RUNTIME) diff --git a/libcore/annotation/src/main/java/java/lang/annotation/ElementType.java b/libcore/annotation/src/main/java/java/lang/annotation/ElementType.java index 92f5109cb..f0f52aacf 100644 --- a/libcore/annotation/src/main/java/java/lang/annotation/ElementType.java +++ b/libcore/annotation/src/main/java/java/lang/annotation/ElementType.java @@ -21,8 +21,8 @@ package java.lang.annotation; * Defines an enumeration for Java program elements. It is used in conjunction * with the {@link Target} meta-annotation to restrict the use of an annotation * to certain program elements. - * - * @since Android 1.0 + * + * @since 1.5 */ public enum ElementType { /** diff --git a/libcore/annotation/src/main/java/java/lang/annotation/IncompleteAnnotationException.java b/libcore/annotation/src/main/java/java/lang/annotation/IncompleteAnnotationException.java index 3a3155191..a5d20688d 100644 --- a/libcore/annotation/src/main/java/java/lang/annotation/IncompleteAnnotationException.java +++ b/libcore/annotation/src/main/java/java/lang/annotation/IncompleteAnnotationException.java @@ -23,8 +23,8 @@ import org.apache.harmony.annotation.internal.nls.Messages; * Indicates that an element of an annotation type was accessed that was added * after the type was compiled or serialized. This does not apply to new * elements that have default values. - * - * @since Android 1.0 + * + * @since 1.5 */ public class IncompleteAnnotationException extends RuntimeException { @@ -37,13 +37,11 @@ public class IncompleteAnnotationException extends RuntimeException { /** * Constructs an instance with the incomplete annotation type and the name * of the element that's missing. - * + * * @param annotationType * the annotation type. * @param elementName * the name of the incomplete element. - * - * @since Android 1.0 */ public IncompleteAnnotationException( Class annotationType, String elementName) { @@ -54,10 +52,8 @@ public class IncompleteAnnotationException extends RuntimeException { /** * Returns the annotation type. - * + * * @return a Class instance. - * - * @since Android 1.0 */ public Class annotationType() { return annotationType; @@ -65,10 +61,8 @@ public class IncompleteAnnotationException extends RuntimeException { /** * Returns the incomplete element's name. - * + * * @return the name of the element. - * - * @since Android 1.0 */ public String elementName() { return elementName; diff --git a/libcore/annotation/src/main/java/java/lang/annotation/Inherited.java b/libcore/annotation/src/main/java/java/lang/annotation/Inherited.java index cf16928da..730d30abc 100644 --- a/libcore/annotation/src/main/java/java/lang/annotation/Inherited.java +++ b/libcore/annotation/src/main/java/java/lang/annotation/Inherited.java @@ -20,8 +20,8 @@ package java.lang.annotation; /** * Defines a meta-annotation for indicating that an annotation is automatically * inherited. - * - * @since Android 1.0 + * + * @since 1.5 */ @Documented @Retention(RetentionPolicy.RUNTIME) diff --git a/libcore/annotation/src/main/java/java/lang/annotation/Retention.java b/libcore/annotation/src/main/java/java/lang/annotation/Retention.java index 198fccc15..275739e8d 100644 --- a/libcore/annotation/src/main/java/java/lang/annotation/Retention.java +++ b/libcore/annotation/src/main/java/java/lang/annotation/Retention.java @@ -21,15 +21,12 @@ package java.lang.annotation; * Defines a meta-annotation for determining the scope of retention for an * annotation. If the retention annotation is not set {@code * RetentionPolicy.CLASS} is used as default retention. - * - * @since Android 1.0 + * + * @since 1.5 */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Retention { - // BEGIN android-changed - // copied from newer version of harmony RetentionPolicy value(); - // END android-changed } diff --git a/libcore/annotation/src/main/java/java/lang/annotation/RetentionPolicy.java b/libcore/annotation/src/main/java/java/lang/annotation/RetentionPolicy.java index 014b9100a..70de3b0e4 100644 --- a/libcore/annotation/src/main/java/java/lang/annotation/RetentionPolicy.java +++ b/libcore/annotation/src/main/java/java/lang/annotation/RetentionPolicy.java @@ -21,8 +21,8 @@ package java.lang.annotation; * Defines an enumeration for annotation retention policies. Used in conjunction * with the {@link Retention} annotation to specify an annotation's time-to-live * in the overall development life cycle. - * - * @since Android 1.0 + * + * @since 1.5 */ public enum RetentionPolicy { /** diff --git a/libcore/annotation/src/main/java/java/lang/annotation/Target.java b/libcore/annotation/src/main/java/java/lang/annotation/Target.java index 1f53fa06c..4ba0938b1 100644 --- a/libcore/annotation/src/main/java/java/lang/annotation/Target.java +++ b/libcore/annotation/src/main/java/java/lang/annotation/Target.java @@ -20,8 +20,8 @@ package java.lang.annotation; /** * Defines a meta-annotation for determining what {@link ElementType}s an * annotation can be applied to. - * - * @since Android 1.0 + * + * @since 1.5 */ @Documented @Retention(RetentionPolicy.RUNTIME) diff --git a/libcore/annotation/src/test/java/org/apache/harmony/annotation/tests/java/lang/annotation/AnnotationTypeMismatchExceptionTest.java b/libcore/annotation/src/test/java/org/apache/harmony/annotation/tests/java/lang/annotation/AnnotationTypeMismatchExceptionTest.java index 11348870e..5430286f4 100644 --- a/libcore/annotation/src/test/java/org/apache/harmony/annotation/tests/java/lang/annotation/AnnotationTypeMismatchExceptionTest.java +++ b/libcore/annotation/src/test/java/org/apache/harmony/annotation/tests/java/lang/annotation/AnnotationTypeMismatchExceptionTest.java @@ -65,7 +65,7 @@ public class AnnotationTypeMismatchExceptionTest extends TestCase { Method m = methods[0]; AnnotationTypeMismatchException e = new AnnotationTypeMismatchException( m, "some type"); - assertNotNull("can not instanciate AnnotationTypeMismatchException", e); + assertNotNull("can not instantiate AnnotationTypeMismatchException", e); assertSame("wrong method name", m, e.element()); assertEquals("wrong found type", "some type", e.foundType()); } diff --git a/libcore/annotation/src/test/java/org/apache/harmony/annotation/tests/java/lang/annotation/IncompleteAnnotationExceptionTest.java b/libcore/annotation/src/test/java/org/apache/harmony/annotation/tests/java/lang/annotation/IncompleteAnnotationExceptionTest.java index de56330d1..5c718ed86 100644 --- a/libcore/annotation/src/test/java/org/apache/harmony/annotation/tests/java/lang/annotation/IncompleteAnnotationExceptionTest.java +++ b/libcore/annotation/src/test/java/org/apache/harmony/annotation/tests/java/lang/annotation/IncompleteAnnotationExceptionTest.java @@ -80,7 +80,7 @@ public class IncompleteAnnotationExceptionTest extends TestCase { String elementName = "some element"; IncompleteAnnotationException e = new IncompleteAnnotationException( clazz, elementName); - assertNotNull("can not instanciate IncompleteAnnotationException", e); + assertNotNull("can not instantiate IncompleteAnnotationException", e); assertSame("wrong annotation type", clazz, e.annotationType()); assertSame("wrong element name", elementName, e.elementName()); } diff --git a/libcore/archive/src/main/java/java/util/jar/Attributes.java b/libcore/archive/src/main/java/java/util/jar/Attributes.java index 5a4d923c4..4ee94dfdd 100644 --- a/libcore/archive/src/main/java/java/util/jar/Attributes.java +++ b/libcore/archive/src/main/java/java/util/jar/Attributes.java @@ -17,6 +17,7 @@ package java.util.jar; +import java.io.UnsupportedEncodingException; import java.util.Collection; import java.util.HashMap; import java.util.Map; @@ -28,7 +29,6 @@ import org.apache.harmony.archive.util.Util; * The {@code Attributes} class is used to store values for manifest entries. * Attribute keys are generally instances of {@code Attributes.Name}. Values * associated with attribute keys are of type {@code String}. - * @since Android 1.0 */ public class Attributes implements Cloneable, Map { @@ -37,8 +37,6 @@ public class Attributes implements Cloneable, Map { * {@link Attributes.Name}) of a JAR file manifest to arbitrary values. The * attribute names thus are obtained from the {@link Manifest} for * convenience. - * - * @since Android 1.0 */ protected Map map; @@ -46,46 +44,35 @@ public class Attributes implements Cloneable, Map { * The name part of the name/value pairs constituting an attribute as * defined by the specification of the JAR manifest. May be composed of the * following ASCII signs as defined in the EBNF below: - * + * *
            * name       = alphanum *headerchar
            * headerchar = alphanum | - | _
      -     * alphanum   = {A-Z} | {a-z} | {0-9} 
      +     * alphanum   = {A-Z} | {a-z} | {0-9}
            * 
      - * - * @since Android 1.0 */ public static class Name { - private final String name; + private final byte[] name; private int hashCode; /** * The class path (a main attribute). - * - * @since Android 1.0 */ public static final Name CLASS_PATH = new Name("Class-Path"); //$NON-NLS-1$ /** * The version of the manifest file (a main attribute). - * - * @since Android 1.0 */ - public static final Name MANIFEST_VERSION = new Name( - "Manifest-Version"); //$NON-NLS-1$ + public static final Name MANIFEST_VERSION = new Name("Manifest-Version"); //$NON-NLS-1$ /** * The main class's name (for stand-alone applications). - * - * @since Android 1.0 */ public static final Name MAIN_CLASS = new Name("Main-Class"); //$NON-NLS-1$ /** * Defines the signature version of the JAR file. - * - * @since Android 1.0 */ public static final Name SIGNATURE_VERSION = new Name( "Signature-Version"); //$NON-NLS-1$ @@ -98,16 +85,12 @@ public class Attributes implements Cloneable, Map { /** * The {@code Sealed} manifest attribute which may have the value * {@code true} for sealed archives. - * - * @since Android 1.0 */ public static final Name SEALED = new Name("Sealed"); //$NON-NLS-1$ /** * The {@code Implementation-Title} attribute whose value is a string * that defines the title of the extension implementation. - * - * @since Android 1.0 */ public static final Name IMPLEMENTATION_TITLE = new Name( "Implementation-Title"); //$NON-NLS-1$ @@ -115,8 +98,6 @@ public class Attributes implements Cloneable, Map { /** * The {@code Implementation-Version} attribute defining the version of * the extension implementation. - * - * @since Android 1.0 */ public static final Name IMPLEMENTATION_VERSION = new Name( "Implementation-Version"); //$NON-NLS-1$ @@ -124,8 +105,6 @@ public class Attributes implements Cloneable, Map { /** * The {@code Implementation-Vendor} attribute defining the organization * that maintains the extension implementation. - * - * @since Android 1.0 */ public static final Name IMPLEMENTATION_VENDOR = new Name( "Implementation-Vendor"); //$NON-NLS-1$ @@ -133,8 +112,6 @@ public class Attributes implements Cloneable, Map { /** * The {@code Specification-Title} attribute defining the title of the * extension specification. - * - * @since Android 1.0 */ public static final Name SPECIFICATION_TITLE = new Name( "Specification-Title"); //$NON-NLS-1$ @@ -142,8 +119,6 @@ public class Attributes implements Cloneable, Map { /** * The {@code Specification-Version} attribute defining the version of * the extension specification. - * - * @since Android 1.0 */ public static final Name SPECIFICATION_VERSION = new Name( "Specification-Version"); //$NON-NLS-1$ @@ -151,8 +126,6 @@ public class Attributes implements Cloneable, Map { /** * The {@code Specification-Vendor} attribute defining the organization * that maintains the extension specification. - * - * @since Android 1.0 */ public static final Name SPECIFICATION_VENDOR = new Name( "Specification-Vendor"); //$NON-NLS-1$ @@ -160,23 +133,17 @@ public class Attributes implements Cloneable, Map { /** * The {@code Extension-List} attribute defining the extensions that are * needed by the applet. - * - * @since Android 1.0 */ public static final Name EXTENSION_LIST = new Name("Extension-List"); //$NON-NLS-1$ /** * The {@code Extension-Name} attribute which defines the unique name of * the extension. - * - * @since Android 1.0 */ public static final Name EXTENSION_NAME = new Name("Extension-Name"); //$NON-NLS-1$ /** * The {@code Extension-Installation} attribute. - * - * @since Android 1.0 */ public static final Name EXTENSION_INSTALLATION = new Name( "Extension-Installation"); //$NON-NLS-1$ @@ -185,8 +152,6 @@ public class Attributes implements Cloneable, Map { * The {@code Implementation-Vendor-Id} attribute specifies the vendor * of an extension implementation if the applet requires an * implementation from a specific vendor. - * - * @since Android 1.0 */ public static final Name IMPLEMENTATION_VENDOR_ID = new Name( "Implementation-Vendor-Id"); //$NON-NLS-1$ @@ -195,91 +160,112 @@ public class Attributes implements Cloneable, Map { * The {@code Implementation-URL} attribute specifying a URL that can be * used to obtain the most recent version of the extension if the * required version is not already installed. - * - * @since Android 1.0 */ public static final Name IMPLEMENTATION_URL = new Name( "Implementation-URL"); //$NON-NLS-1$ + static final Name NAME = new Name("Name"); + /** * A String which must satisfy the following EBNF grammar to specify an * additional attribute: - * + * *
                * name       = alphanum *headerchar
                * headerchar = alphanum | - | _
                * alphanum   = {A-Z} | {a-z} | {0-9}
                * 
      - * + * * @param s * The Attribute string. * @exception IllegalArgumentException * if the string does not satisfy the EBNF grammar. - * @since Android 1.0 */ public Name(String s) { int i = s.length(); - if (i == 0 || i > 70) { + if (i == 0 || i > Manifest.LINE_LENGTH_LIMIT - 2) { throw new IllegalArgumentException(); } + + name = new byte[i]; + for (; --i >= 0;) { char ch = s.charAt(i); if (!((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_' || ch == '-' || (ch >= '0' && ch <= '9'))) { throw new IllegalArgumentException(s); } + name[i] = (byte) ch; } - name = s; + } + + /** + * A private constructor for a trusted attribute name. + */ + Name(byte[] buf) { + name = buf; + } + + byte[] getBytes() { + return name; } /** * Returns this attribute name. - * + * * @return the attribute name. - * @since Android 1.0 */ @Override public String toString() { - return name; + try { + return new String(name, "ISO-8859-1"); + } catch (UnsupportedEncodingException iee) { + throw new InternalError(iee.getLocalizedMessage()); + } } /** * returns whether the argument provided is the same as the attribute * name. - * + * * @return if the attribute names correspond. - * @param an + * @param object * An attribute name to be compared with this name. - * @since Android 1.0 */ @Override - public boolean equals(Object an) { - if (an == null) { + public boolean equals(Object object) { + if (object == null || object.getClass() != getClass() + || object.hashCode() != hashCode()) { return false; } - return an.getClass() == this.getClass() - && name.equalsIgnoreCase(((Name) an).name); + + return Util.equalsIgnoreCase(name, ((Name) object).name); } /** * Computes a hash code of the name. - * + * * @return the hash value computed from the name. - * @since Android 1.0 */ @Override public int hashCode() { if (hashCode == 0) { - hashCode = Util.toASCIILowerCase("name").hashCode(); + int hash = 0, multiplier = 1; + for (int i = name.length - 1; i >= 0; i--) { + // 'A' & 0xDF == 'a' & 0xDF, ..., 'Z' & 0xDF == 'z' & 0xDF + hash += (name[i] & 0xDF) * multiplier; + int shifted = multiplier << 5; + multiplier = shifted - multiplier; + } + hashCode = hash; } return hashCode; } + } /** * Constructs an {@code Attributes} instance. - * - * @since Android 1.0 */ public Attributes() { map = new HashMap(); @@ -288,23 +274,21 @@ public class Attributes implements Cloneable, Map { /** * Constructs an {@code Attributes} instance obtaining keys and values from * the parameter {@code attrib}. - * + * * @param attrib * The attributes to obtain entries from. - * @since Android 1.0 */ @SuppressWarnings("unchecked") public Attributes(Attributes attrib) { - map = (Map)((HashMap) attrib.map).clone(); + map = (Map) ((HashMap) attrib.map).clone(); } /** * Constructs an {@code Attributes} instance with initial capacity of size * {@code size}. - * + * * @param size * Initial size of this {@code Attributes} instance. - * @since Android 1.0 */ public Attributes(int size) { map = new HashMap(size); @@ -312,8 +296,6 @@ public class Attributes implements Cloneable, Map { /** * Removes all key/value pairs from this {@code Attributes}. - * - * @since Android 1.0 */ public void clear() { map.clear(); @@ -321,11 +303,10 @@ public class Attributes implements Cloneable, Map { /** * Determines whether this {@code Attributes} contains the specified key. - * + * * @param key * The key to search for. * @return {@code true} if the key is found, {@code false} otherwise. - * @since Android 1.0 */ public boolean containsKey(Object key) { return map.containsKey(key); @@ -333,11 +314,10 @@ public class Attributes implements Cloneable, Map { /** * Determines whether this {@code Attributes} contains the specified value. - * + * * @param value * the value to search for. * @return {@code true} if the value is found, {@code false} otherwise. - * @since Android 1.0 */ public boolean containsValue(Object value) { return map.containsValue(value); @@ -346,9 +326,8 @@ public class Attributes implements Cloneable, Map { /** * Returns a set containing map entries for each of the key/value pair * contained in this {@code Attributes}. - * + * * @return a set of Map.Entry's - * @since Android 1.0 */ public Set> entrySet() { return map.entrySet(); @@ -356,12 +335,11 @@ public class Attributes implements Cloneable, Map { /** * Returns the value associated with the parameter key. - * + * * @param key * the key to search for. * @return Object associated with key, or {@code null} if key does not * exist. - * @since Android 1.0 */ public Object get(Object key) { return map.get(key); @@ -369,9 +347,8 @@ public class Attributes implements Cloneable, Map { /** * Determines whether this {@code Attributes} contains any keys. - * + * * @return {@code true} if one or more keys exist, {@code false} otherwise. - * @since Android 1.0 */ public boolean isEmpty() { return map.isEmpty(); @@ -380,9 +357,8 @@ public class Attributes implements Cloneable, Map { /** * Returns a {@code Set} containing all the keys found in this {@code * Attributes}. - * + * * @return a {@code Set} of all keys. - * @since Android 1.0 */ public Set keySet() { return map.keySet(); @@ -390,7 +366,7 @@ public class Attributes implements Cloneable, Map { /** * Stores key/value pairs in this {@code Attributes}. - * + * * @param key * the key to associate with value. * @param value @@ -399,21 +375,20 @@ public class Attributes implements Cloneable, Map { * @exception ClassCastException * when key is not an {@code Attributes.Name} or value is not * a {@code String}. - *@since Android 1.0 */ - @SuppressWarnings("cast") // Require cast to force ClassCastException + @SuppressWarnings("cast") + // Require cast to force ClassCastException public Object put(Object key, Object value) { - return map.put((Name)key, (String)value); + return map.put((Name) key, (String) value); } /** - * Stores all the key/value pairs in the argument in this {@code Attributes} - * . - * + * Stores all the key/value pairs in the argument in this {@code + * Attributes}. + * * @param attrib - * the associations to store (must be of type {@code Attributes} - * ). - * @since Android 1.0 + * the associations to store (must be of type {@code + * Attributes}). */ public void putAll(Map attrib) { if (attrib == null || !(attrib instanceof Attributes)) { @@ -425,12 +400,11 @@ public class Attributes implements Cloneable, Map { /** * Deletes the key/value pair with key {@code key} from this {@code * Attributes}. - * + * * @param key * the key to remove. * @return the values associated with the removed key, {@code null} if not * present. - * @since Android 1.0 */ public Object remove(Object key) { return map.remove(key); @@ -439,20 +413,18 @@ public class Attributes implements Cloneable, Map { /** * Returns the number of key/value pairs associated with this {@code * Attributes}. - * + * * @return the size of this {@code Attributes}. - * @since Android 1.0 */ public int size() { return map.size(); } /** - * Returns a collection of all the values present in this {@code Attributes} - * . - * + * Returns a collection of all the values present in this {@code + * Attributes}. + * * @return a collection of all values present. - * @since Android 1.0 */ public Collection values() { return map.values(); @@ -473,9 +445,8 @@ public class Attributes implements Cloneable, Map { /** * Returns the hash code of this {@code Attributes}. - * + * * @return the hash code of this object. - * @since Android 1.0 */ @Override public int hashCode() { @@ -486,12 +457,11 @@ public class Attributes implements Cloneable, Map { * Determines if this {@code Attributes} and the parameter {@code * Attributes} are equal. Two {@code Attributes} instances are equal if they * contain the same keys and values. - * + * * @param obj * the object with which this {@code Attributes} is compared. * @return {@code true} if the {@code Attributes} are equal, {@code false} * otherwise. - * @since Android 1.0 */ @Override public boolean equals(Object obj) { @@ -507,12 +477,11 @@ public class Attributes implements Cloneable, Map { /** * Returns the value associated with the parameter {@code Attributes.Name} * key. - * + * * @param name * the key to obtain the value for. * @return the {@code String} associated with name, or {@code null} if name * is not a valid key. - * @since Android 1.0 */ public String getValue(Attributes.Name name) { return (String) map.get(name); @@ -520,12 +489,11 @@ public class Attributes implements Cloneable, Map { /** * Returns the string associated with the parameter name. - * + * * @param name * the key to obtain the value for. * @return the string associated with name, or {@code null} if name is not a * valid key. - * @since Android 1.0 */ public String getValue(String name) { return (String) map.get(new Attributes.Name(name)); @@ -534,13 +502,12 @@ public class Attributes implements Cloneable, Map { /** * Stores the value {@code val} associated with the key {@code name} in this * {@code Attributes}. - * + * * @param name * the key to store. * @param val * the value to store in this {@code Attributes}. * @return the value being stored. - * @since Android 1.0 */ public String putValue(String name, String val) { return (String) map.put(new Attributes.Name(name), val); diff --git a/libcore/archive/src/main/java/java/util/jar/InitManifest.java b/libcore/archive/src/main/java/java/util/jar/InitManifest.java index 47fdf1ca2..bf9c397a6 100644 --- a/libcore/archive/src/main/java/java/util/jar/InitManifest.java +++ b/libcore/archive/src/main/java/java/util/jar/InitManifest.java @@ -17,279 +17,206 @@ package java.util.jar; -import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.InputStream; -import java.io.UTFDataFormatException; -import java.security.AccessController; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CoderResult; import java.util.Map; import org.apache.harmony.archive.internal.nls.Messages; -import org.apache.harmony.luni.util.PriviAction; -import org.apache.harmony.luni.util.Util; +import org.apache.harmony.luni.util.ThreadLocalCache; class InitManifest { - private final byte[] inbuf = new byte[1024]; - private int inbufCount = 0, inbufPos = 0; + private byte[] buf; - private byte[] buffer = new byte[5]; + private int pos; - private char[] charbuf = new char[0]; + Attributes.Name name; - private final ByteArrayOutputStream out = new ByteArrayOutputStream(256); + String value; - private String encoding; + CharsetDecoder decoder = ThreadLocalCache.utf8Decoder.get(); + CharBuffer cBuf = ThreadLocalCache.charBuffer.get(); - private boolean usingUTF8 = true; - - private final Map attributeNames = new HashMap(); + InitManifest(byte[] buf, Attributes main, Attributes.Name ver) + throws IOException { - private final byte[] mainAttributesChunk; + this.buf = buf; - InitManifest(InputStream is, Attributes main, Map entries, Map chunks, - String verString) throws IOException { - encoding = AccessController.doPrivileged(new PriviAction( - "manifest.read.encoding")); //$NON-NLS-1$ - if ("".equals(encoding)) { //$NON-NLS-1$ - encoding = null; + // check a version attribute + if (!readHeader() || (ver != null && !name.equals(ver))) { + throw new IOException(Messages.getString( + "archive.2D", ver)); //$NON-NLS-1$ } - Attributes current = main; - ArrayList list = new ArrayList(); - - // Return the chunk of main attributes in the manifest. - mainAttributesChunk = nextChunk(is, list); - - Iterator it = list.iterator(); - while (it.hasNext()) { - addAttribute(it.next(), current); + main.put(name, value); + while (readHeader()) { + main.put(name, value); } + } - // Check for version attribute - if (verString != null && main.getValue(verString) == null) { - throw new IOException(Messages.getString("archive.2D", verString)); //$NON-NLS-1$ - } + void initEntries(Map entries, + Map chunks) throws IOException { - list.clear(); - byte[] chunk = null; - while (chunks == null ? readLines(is, list) : (chunk = nextChunk(is, - list)) != null) { - it = list.iterator(); - String line = it.next(); - if (line.length() < 7 - || !Util.toASCIILowerCase(line.substring(0, 5)).equals("name:")) { //$NON-NLS-1$ + int mark = pos; + while (readHeader()) { + if (!Attributes.Name.NAME.equals(name)) { throw new IOException(Messages.getString("archive.23")); //$NON-NLS-1$ } - // Name: length required space char - String name = line.substring(6, line.length()); - current = new Attributes(12); - if (chunks != null) { - chunks.put(name, chunk); + String entryNameValue = value; + + Attributes entry = entries.get(entryNameValue); + if (entry == null) { + entry = new Attributes(12); } - entries.put(name, current); - while (it.hasNext()) { - addAttribute(it.next(), current); + + while (readHeader()) { + entry.put(name, value); + } + + if (chunks != null) { + if (chunks.get(entryNameValue) != null) { + // TODO A bug: there might be several verification chunks for + // the same name. I believe they should be used to update + // signature in order of appearance; there are two ways to fix + // this: either use a list of chunks, or decide on used + // signature algorithm in advance and reread the chunks while + // updating the signature; for now a defensive error is thrown + throw new IOException(Messages.getString("archive.34")); //$NON-NLS-1$ + } + chunks.put(entryNameValue, new Manifest.Chunk(mark, pos)); + mark = pos; } - list.clear(); + + entries.put(entryNameValue, entry); } + } + + int getPos() { + return pos; + } + /** + * Number of subsequent line breaks. + */ + int linebreak = 0; + + /** + * Read a single line from the manifest buffer. + */ + private boolean readHeader() throws IOException { + if (linebreak > 1) { + // break a section on an empty line + linebreak = 0; + return false; + } + readName(); + linebreak = 0; + readValue(); + // if the last line break is missed, the line + // is ignored by the reference implementation + return linebreak > 0; } - byte[] getMainAttributesChunk() { - return mainAttributesChunk; + private byte[] wrap(int mark, int pos) { + byte[] buffer = new byte[pos - mark]; + System.arraycopy(buf, mark, buffer, 0, pos - mark); + return buffer; } - private void addLine(int length, List lines) throws IOException { - if (encoding != null) { - lines.add(new String(buffer, 0, length, encoding)); - } else { - if (usingUTF8) { - try { - if (charbuf.length < length) { - charbuf = new char[length]; - } - lines.add(Util.convertUTF8WithBuf(buffer, charbuf, 0, - length)); - } catch (UTFDataFormatException e) { - usingUTF8 = false; + private void readName() throws IOException { + int i = 0; + int mark = pos; + + while (pos < buf.length) { + byte b = buf[pos++]; + + if (b == ':') { + byte[] nameBuffer = wrap(mark, pos - 1); + + if (buf[pos++] != ' ') { + throw new IOException(Messages.getString( + "archive.30", nameBuffer)); //$NON-NLS-1$ } + + name = new Attributes.Name(nameBuffer); + return; } - if (!usingUTF8) { - if (charbuf.length < length) { - charbuf = new char[length]; - } - // If invalid UTF8, convert bytes to chars setting the upper - // bytes to zeros - int charOffset = 0; - int offset = 0; - for (int i = length; --i >= 0;) { - charbuf[charOffset++] = (char) (buffer[offset++] & 0xff); - } - lines.add(new String(charbuf, 0, length)); + + if (!((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || b == '_' + || b == '-' || (b >= '0' && b <= '9'))) { + throw new IOException(Messages.getString("archive.30", b)); //$NON-NLS-1$ } } + if (i > 0) { + throw new IOException(Messages.getString( + "archive.30", wrap(mark, buf.length))); //$NON-NLS-1$ + } } - private byte[] nextChunk(InputStream in, List lines) - throws IOException { - if (inbufCount == -1) { - return null; - } + private void readValue() throws IOException { byte next; - int pos = 0; - boolean blankline = false, lastCr = false; - out.reset(); - while (true) { - if (inbufPos == inbufCount) { - if ((inbufCount = in.read(inbuf)) == -1) { - if (out.size() == 0) { - return null; - } - if (blankline) { - addLine(pos, lines); - } - return out.toByteArray(); - } - if (inbufCount == inbuf.length && in.available() == 0) { - /* archive.2E = "line too long" */ - throw new IOException(Messages.getString("archive.2E")); //$NON-NLS-1$ - } - inbufPos = 0; - } - next = inbuf[inbufPos++]; - if (lastCr) { - if (next != '\n') { - inbufPos--; - next = '\r'; + boolean lastCr = false; + int mark = pos; + int last = pos; + + decoder.reset(); + cBuf.clear(); + + while (pos < buf.length) { + next = buf[pos++]; + + switch (next) { + case 0: + throw new IOException(Messages.getString("archive.2F")); //$NON-NLS-1$ + case '\n': + if (lastCr) { + lastCr = false; } else { - if (out.size() == 0) { - continue; - } - out.write('\r'); + linebreak++; } - lastCr = false; - } else if (next == '\r') { + continue; + case '\r': lastCr = true; + linebreak++; continue; - } - if (blankline) { - if (next == ' ') { - out.write(next); - blankline = false; - continue; - } - addLine(pos, lines); - if (next == '\n') { - out.write(next); - return out.toByteArray(); - } - pos = 0; - } else if (next == '\n') { - if (out.size() == 0) { + case ' ': + if (linebreak == 1) { + decode(mark, last, false); + mark = pos; + linebreak = 0; continue; } - out.write(next); - blankline = true; - continue; } - blankline = false; - out.write(next); - if (pos == buffer.length) { - byte[] newBuf = new byte[buffer.length * 2]; - System.arraycopy(buffer, 0, newBuf, 0, buffer.length); - buffer = newBuf; + + if (linebreak >= 1) { + pos--; + break; } - buffer[pos++] = next; + last = pos; } - } - private boolean readLines(InputStream in, List lines) - throws IOException { - if (inbufCount == -1) { - return false; - } - byte next; - int pos = 0; - boolean blankline = false, lastCr = false; - while (true) { - if (inbufPos == inbufCount) { - if ((inbufCount = in.read(inbuf)) == -1) { - if (blankline) { - addLine(pos, lines); - } - return lines.size() != 0; - } - if (inbufCount == inbuf.length && in.available() == 0) { - /* archive.2E = "line too long" */ - throw new IOException(Messages.getString("archive.2E")); //$NON-NLS-1$ - } - inbufPos = 0; - } - next = inbuf[inbufPos++]; - if (lastCr) { - if (next != '\n') { - inbufPos--; - next = '\r'; - } - lastCr = false; - } else if (next == '\r') { - lastCr = true; - continue; - } - if (blankline) { - if (next == ' ') { - blankline = false; - continue; - } - addLine(pos, lines); - if (next == '\n') { - return true; - } - pos = 0; - } else if (next == '\n') { - if (pos == 0 && lines.size() == 0) { - continue; - } - blankline = true; - continue; - } - blankline = false; - if (pos == buffer.length) { - byte[] newBuf = new byte[buffer.length * 2]; - System.arraycopy(buffer, 0, newBuf, 0, buffer.length); - buffer = newBuf; - } - buffer[pos++] = next; + decode(mark, last, true); + while (CoderResult.OVERFLOW == decoder.flush(cBuf)) { + enlargeBuffer(); } + value = new String(cBuf.array(), cBuf.arrayOffset(), cBuf.position()); } - /* Get the next attribute and add it */ - private void addAttribute(String line, Attributes current) + private void decode(int mark, int pos, boolean endOfInput) throws IOException { - String header; - int hdrIdx = line.indexOf(':'); - if (hdrIdx < 1) { - throw new IOException(Messages.getString("archive.2F", line)); //$NON-NLS-1$ - } - header = line.substring(0, hdrIdx); - Attributes.Name name = attributeNames.get(header); - if (name == null) { - try { - name = new Attributes.Name(header); - } catch (IllegalArgumentException e) { - throw new IOException(e.toString()); - } - attributeNames.put(header, name); - } - if (hdrIdx + 1 >= line.length() || line.charAt(hdrIdx + 1) != ' ') { - throw new IOException(Messages.getString("archive.2F", line)); //$NON-NLS-1$ + ByteBuffer bBuf = ByteBuffer.wrap(buf, mark, pos - mark); + while (CoderResult.OVERFLOW == decoder.decode(bBuf, cBuf, endOfInput)) { + enlargeBuffer(); } - // +2 due to required SPACE char - current.put(name, line.substring(hdrIdx + 2, line.length())); + } + + private void enlargeBuffer() { + CharBuffer newBuf = CharBuffer.allocate(cBuf.capacity() * 2); + newBuf.put(cBuf.array(), cBuf.arrayOffset(), cBuf.position()); + cBuf = newBuf; + ThreadLocalCache.charBuffer.set(cBuf); } } diff --git a/libcore/archive/src/main/java/java/util/jar/JarEntry.java b/libcore/archive/src/main/java/java/util/jar/JarEntry.java index b8dabee34..869e4b4fb 100644 --- a/libcore/archive/src/main/java/java/util/jar/JarEntry.java +++ b/libcore/archive/src/main/java/java/util/jar/JarEntry.java @@ -33,27 +33,27 @@ import javax.security.auth.x500.X500Principal; /** * Represents a single file in a JAR archive together with the manifest * attributes and digital signatures associated with it. - * - * @since Android 1.0 + * + * @see JarFile + * @see JarInputStream */ public class JarEntry extends ZipEntry { private Attributes attributes; JarFile parentJar; - + CodeSigner signers[]; // Cached factory used to build CertPath-s in getCodeSigners(). private CertificateFactory factory; - private boolean isFactoryChecked = false; + private boolean isFactoryChecked = false; /** * Creates a new {@code JarEntry} named name. * * @param name * The name of the new {@code JarEntry}. - * @since Android 1.0 */ public JarEntry(String name) { super(name); @@ -64,7 +64,6 @@ public class JarEntry extends ZipEntry { * * @param entry * The ZipEntry to obtain values from. - * @since Android 1.0 */ public JarEntry(ZipEntry entry) { super(entry); @@ -78,7 +77,6 @@ public class JarEntry extends ZipEntry { * @exception IOException * If an error occurs obtaining the {@code Attributes}. * @see Attributes - * @since Android 1.0 */ public Attributes getAttributes() throws IOException { if (attributes != null || parentJar == null) { @@ -99,7 +97,6 @@ public class JarEntry extends ZipEntry { * * @return the certificate for this entry. * @see java.security.cert.Certificate - * @since Android 1.0 */ public Certificate[] getCertificates() { if (null == parentJar) { @@ -122,12 +119,11 @@ public class JarEntry extends ZipEntry { * * @param je * The {@code JarEntry} to obtain values from. - * @since Android 1.0 */ public JarEntry(JarEntry je) { super(je); parentJar = je.parentJar; - attributes = je.attributes; + attributes = je.attributes; signers = je.signers; } @@ -139,7 +135,6 @@ public class JarEntry extends ZipEntry { * * @return the code signers for the JAR entry. * @see CodeSigner - * @since Android 1.0 */ public CodeSigner[] getCodeSigners() { if (null == signers) { @@ -155,7 +150,7 @@ public class JarEntry extends ZipEntry { } private CodeSigner[] getCodeSigners(Certificate[] certs) { - if(null == certs) { + if (null == certs) { return null; } diff --git a/libcore/archive/src/main/java/java/util/jar/JarException.java b/libcore/archive/src/main/java/java/util/jar/JarException.java index f18d63949..d6943c475 100644 --- a/libcore/archive/src/main/java/java/util/jar/JarException.java +++ b/libcore/archive/src/main/java/java/util/jar/JarException.java @@ -22,8 +22,6 @@ import java.util.zip.ZipException; /** * This runtime exception is thrown when a problem occurs while reading a JAR * file. - * - * @since Android 1.0 */ public class JarException extends ZipException { @@ -31,8 +29,6 @@ public class JarException extends ZipException { /** * Constructs a new {@code JarException} instance. - * - * @since Android 1.0 */ public JarException() { super(); @@ -41,10 +37,9 @@ public class JarException extends ZipException { /** * Constructs a new {@code JarException} instance with the specified * message. - * + * * @param detailMessage * the detail message for the exception. - * @since Android 1.0 */ public JarException(String detailMessage) { super(detailMessage); diff --git a/libcore/archive/src/main/java/java/util/jar/JarFile.java b/libcore/archive/src/main/java/java/util/jar/JarFile.java index 9af9056b4..6f4eb836e 100644 --- a/libcore/archive/src/main/java/java/util/jar/JarFile.java +++ b/libcore/archive/src/main/java/java/util/jar/JarFile.java @@ -29,7 +29,6 @@ import java.io.File; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; -import java.security.MessageDigest; import java.util.Enumeration; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; @@ -39,17 +38,14 @@ import org.apache.harmony.archive.util.Util; /** * {@code JarFile} is used to read jar entries and their associated data from * jar files. - * + * * @see JarInputStream * @see JarEntry - * @since Android 1.0 */ public class JarFile extends ZipFile { /** * The MANIFEST file name. - * - * @since Android 1.0 */ public static final String MANIFEST_NAME = "META-INF/MANIFEST.MF"; //$NON-NLS-1$ @@ -70,63 +66,88 @@ public class JarFile extends ZipFile { private ZipEntry zipEntry; - private JarVerifier verifier; - private JarVerifier.VerifierEntry entry; - private MessageDigest digest; + // BEGIN android-added + private boolean done = false; + // END android-added - JarFileInputStream(InputStream is, ZipEntry ze, JarVerifier ver) { + JarFileInputStream(InputStream is, ZipEntry ze, + JarVerifier.VerifierEntry e) { super(is); - if (ver != null) { - zipEntry = ze; - verifier = ver; - count = zipEntry.getSize(); - entry = verifier.initEntry(ze.getName()); - if (entry != null) { - digest = entry.digest; - } - } + zipEntry = ze; + count = zipEntry.getSize(); + entry = e; } @Override public int read() throws IOException { - int r = super.read(); - if (entry != null) { + // BEGIN android-changed + if (done) { + return -1; + } + if (count > 0) { + int r = super.read(); if (r != -1) { - digest.update((byte) r); + entry.write(r); count--; + } else { + count = 0; } - if (r == -1 || count <= 0) { - JarVerifier.VerifierEntry temp = entry; - entry = null; - verifier.verifySignatures(temp, zipEntry); + if (count == 0) { + done = true; + entry.verify(); } + return r; + } else { + done = true; + entry.verify(); + return -1; } - return r; + // END android-changed } @Override public int read(byte[] buf, int off, int nbytes) throws IOException { - int r = super.read(buf, off, nbytes); - if (entry != null) { + // BEGIN android-changed + if (done) { + return -1; + } + if (count > 0) { + int r = super.read(buf, off, nbytes); if (r != -1) { int size = r; if (count < size) { size = (int) count; } - digest.update(buf, off, size); - count -= r; + entry.write(buf, off, size); + count -= size; + } else { + count = 0; } - if (r == -1 || count <= 0) { - JarVerifier.VerifierEntry temp = entry; - entry = null; - verifier.verifySignatures(temp, zipEntry); + if (count == 0) { + done = true; + entry.verify(); } + return r; + } else { + done = true; + entry.verify(); + return -1; } - return r; + // END android-changed } + // BEGIN android-added + @Override + public int available() throws IOException { + if (done) { + return 0; + } + return super.available(); + } + // END android-added + @Override public long skip(long nbytes) throws IOException { long cnt = 0, rem = 0; @@ -146,12 +167,11 @@ public class JarFile extends ZipFile { /** * Create a new {@code JarFile} using the contents of the specified file. - * + * * @param file * the JAR file as {@link File}. * @throws IOException * If the file cannot be read. - * @since Android 1.0 */ public JarFile(File file) throws IOException { this(file, true); @@ -159,14 +179,13 @@ public class JarFile extends ZipFile { /** * Create a new {@code JarFile} using the contents of the specified file. - * + * * @param file * the JAR file as {@link File}. * @param verify * if this JAR file is signed whether it must be verified. * @throws IOException * If the file cannot be read. - * @since Android 1.0 */ public JarFile(File file, boolean verify) throws IOException { super(file); @@ -178,7 +197,7 @@ public class JarFile extends ZipFile { /** * Create a new {@code JarFile} using the contents of file. - * + * * @param file * the JAR file as {@link File}. * @param verify @@ -188,7 +207,6 @@ public class JarFile extends ZipFile { * {@link ZipFile#OPEN_DELETE OPEN_DELETE}. * @throws IOException * If the file cannot be read. - * @since Android 1.0 */ public JarFile(File file, boolean verify, int mode) throws IOException { super(file, mode); @@ -201,12 +219,11 @@ public class JarFile extends ZipFile { /** * Create a new {@code JarFile} from the contents of the file specified by * filename. - * + * * @param filename * the file name referring to the JAR file. * @throws IOException * if file name cannot be opened for reading. - * @since Android 1.0 */ public JarFile(String filename) throws IOException { this(filename, true); @@ -216,14 +233,13 @@ public class JarFile extends ZipFile { /** * Create a new {@code JarFile} from the contents of the file specified by * {@code filename}. - * + * * @param filename * the file name referring to the JAR file. * @param verify * if this JAR filed is signed whether it must be verified. * @throws IOException * If file cannot be opened or read. - * @since Android 1.0 */ public JarFile(String filename, boolean verify) throws IOException { super(filename); @@ -236,11 +252,10 @@ public class JarFile extends ZipFile { /** * Return an enumeration containing the {@code JarEntrys} contained in this * {@code JarFile}. - * + * * @return the {@code Enumeration} containing the JAR entries. * @throws IllegalStateException * if this {@code JarFile} is closed. - * @since Android 1.0 */ @Override public Enumeration entries() { @@ -260,7 +275,7 @@ public class JarFile extends ZipFile { public JarEntry nextElement() { JarEntry je = new JarEntry(ze.nextElement()); - je.parentJar = jf; + je.parentJar = jf; return je; } } @@ -270,11 +285,10 @@ public class JarFile extends ZipFile { /** * Return the {@code JarEntry} specified by its name or {@code null} if no * such entry exists. - * + * * @param name * the name of the entry in the JAR file. * @return the JAR entry defined by the name. - * @since Android 1.0 */ public JarEntry getJarEntry(String name) { return (JarEntry) getEntry(name); @@ -302,14 +316,13 @@ public class JarFile extends ZipFile { /** * Returns the {@code Manifest} object associated with this {@code JarFile} * or {@code null} if no MANIFEST entry exists. - * + * * @return the MANIFEST. * @throws IOException * if an error occurs reading the MANIFEST file. * @throws IllegalStateException * if the jar file is closed. * @see Manifest - * @since Android 1.0 */ public Manifest getManifest() throws IOException { // BEGIN android-added @@ -358,10 +371,14 @@ public class JarFile extends ZipFile { if (verifier == null) { break; } - } else if (verifier != null && entryName.length() > dirLength - && (Util.ASCIIIgnoreCaseRegionMatches(entryName, entryName.length() - 3, ".SF", 0 ,3) //$NON-NLS-1$ - || Util.ASCIIIgnoreCaseRegionMatches(entryName, entryName.length() - 4, ".DSA", 0 ,4) //$NON-NLS-1$ - || Util.ASCIIIgnoreCaseRegionMatches(entryName, entryName.length() - 4, ".RSA", 0 ,4))){ //$NON-NLS-1$ + } else if (verifier != null + && entryName.length() > dirLength + && (Util.ASCIIIgnoreCaseRegionMatches(entryName, + entryName.length() - 3, ".SF", 0, 3) //$NON-NLS-1$ + || Util.ASCIIIgnoreCaseRegionMatches(entryName, + entryName.length() - 4, ".DSA", 0, 4) //$NON-NLS-1$ + || Util.ASCIIIgnoreCaseRegionMatches(entryName, + entryName.length() - 4, ".RSA", 0, 4))) { //$NON-NLS-1$ signed = true; InputStream is = super.getInputStream(entry); // BEGIN android-modified @@ -379,13 +396,12 @@ public class JarFile extends ZipFile { /** * Return an {@code InputStream} for reading the decompressed contents of * ZIP entry. - * + * * @param ze * the ZIP entry to be read. * @return the input stream to read from. * @throws IOException * if an error occurred while creating the input stream. - * @since Android 1.0 */ @Override public InputStream getInputStream(ZipEntry ze) throws IOException { @@ -395,8 +411,7 @@ public class JarFile extends ZipFile { if (verifier != null) { verifier.setManifest(getManifest()); if (manifest != null) { - verifier.mainAttributesChunk = manifest - .getMainAttributesChunk(); + verifier.mainAttributesEnd = manifest.getMainAttributesEnd(); } if (verifier.readCertificates()) { verifier.removeMetaEntries(); @@ -412,19 +427,24 @@ public class JarFile extends ZipFile { if (in == null) { return null; } - return new JarFileInputStream(in, ze, ze.getSize() >= 0 ? verifier - : null); + if (verifier == null || ze.getSize() == -1) { + return in; + } + JarVerifier.VerifierEntry entry = verifier.initEntry(ze.getName()); + if (entry == null) { + return in; + } + return new JarFileInputStream(in, ze, entry); } /** * Return the {@code JarEntry} specified by name or {@code null} if no such * entry exists. - * + * * @param name * the name of the entry in the JAR file. * @return the ZIP entry extracted. - * @since Android 1.0 - */ + */ @Override public ZipEntry getEntry(String name) { ZipEntry ze = super.getEntry(name); @@ -432,16 +452,16 @@ public class JarFile extends ZipFile { return ze; } JarEntry je = new JarEntry(ze); - je.parentJar = this; + je.parentJar = this; return je; } // BEGIN android-modified private ZipEntry[] getMetaEntriesImpl(byte[] buf) { int n = 0; - + List list = new ArrayList(); - + Enumeration allEntries = entries(); while (allEntries.hasMoreElements()) { ZipEntry ze = allEntries.nextElement(); @@ -463,10 +483,9 @@ public class JarFile extends ZipFile { // BEGIN android-added /** * Closes this {@code JarFile}. - * + * * @throws IOException * if an error occurs. - * @since Android 1.0 */ @Override public void close() throws IOException { diff --git a/libcore/archive/src/main/java/java/util/jar/JarInputStream.java b/libcore/archive/src/main/java/java/util/jar/JarInputStream.java index ef353ab9c..c803183c7 100644 --- a/libcore/archive/src/main/java/java/util/jar/JarInputStream.java +++ b/libcore/archive/src/main/java/java/util/jar/JarInputStream.java @@ -24,14 +24,13 @@ import java.io.OutputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; -import org.apache.harmony.archive.util.Util; +import org.apache.harmony.luni.util.Util; /** * The input stream from which the JAR file to be read may be fetched. It is * used like the {@code ZipInputStream}. - * + * * @see ZipInputStream - * @since Android 1.0 */ public class JarInputStream extends ZipInputStream { @@ -51,7 +50,7 @@ public class JarInputStream extends ZipInputStream { /** * Constructs a new {@code JarInputStream} from an input stream. - * + * * @param stream * the input stream containing the JAR file. * @param verify @@ -59,7 +58,6 @@ public class JarInputStream extends ZipInputStream { * @throws IOException * If an error occurs reading entries from the input stream. * @see ZipInputStream#ZipInputStream(InputStream) - * @since Android 1.0 */ public JarInputStream(InputStream stream, boolean verify) throws IOException { @@ -84,8 +82,8 @@ public class JarInputStream extends ZipInputStream { if (verify) { verifier.setManifest(manifest); if (manifest != null) { - verifier.mainAttributesChunk = manifest - .getMainAttributesChunk(); + verifier.mainAttributesEnd = manifest + .getMainAttributesEnd(); } } @@ -103,13 +101,12 @@ public class JarInputStream extends ZipInputStream { /** * Constructs a new {@code JarInputStream} from an input stream. - * + * * @param stream * the input stream containing the JAR file. * @throws IOException * If an error occurs reading entries from the input stream. * @see ZipInputStream#ZipInputStream(InputStream) - * @since Android 1.0 */ public JarInputStream(InputStream stream) throws IOException { this(stream, true); @@ -120,7 +117,6 @@ public class JarInputStream extends ZipInputStream { * JarInputStream} or {@code null} if no manifest entry exists. * * @return the MANIFEST specifying the contents of the JAR file. - * @since Android 1.0 */ public Manifest getManifest() { return manifest; @@ -133,7 +129,6 @@ public class JarInputStream extends ZipInputStream { * @return the next JAR entry. * @throws IOException * if an error occurs while reading the entry. - * @since Android 1.0 */ public JarEntry getNextJarEntry() throws IOException { return (JarEntry) getNextEntry(); @@ -142,7 +137,7 @@ public class JarInputStream extends ZipInputStream { /** * Reads up to {@code length} of decompressed data and stores it in * {@code buffer} starting at {@code offset}. - * + * * @param buffer * Buffer to store into * @param offset @@ -152,7 +147,6 @@ public class JarInputStream extends ZipInputStream { * @return Number of uncompressed bytes read * @throws IOException * if an IOException occurs. - * @since Android 1.0 */ @Override public int read(byte[] buffer, int offset, int length) throws IOException { @@ -175,9 +169,7 @@ public class JarInputStream extends ZipInputStream { throw e; } } else { - verifier.verifySignatures( - (JarVerifier.VerifierEntry) verStream, - jarEntry); + ((JarVerifier.VerifierEntry) verStream).verify(); } } } else { @@ -194,7 +186,6 @@ public class JarInputStream extends ZipInputStream { * @return the next extracted ZIP entry. * @throws IOException * if an error occurs while reading the entry. - * @since Android 1.0 */ @Override public ZipEntry getNextEntry() throws IOException { diff --git a/libcore/archive/src/main/java/java/util/jar/JarOutputStream.java b/libcore/archive/src/main/java/java/util/jar/JarOutputStream.java index 640f4eff2..e901d87e4 100644 --- a/libcore/archive/src/main/java/java/util/jar/JarOutputStream.java +++ b/libcore/archive/src/main/java/java/util/jar/JarOutputStream.java @@ -25,8 +25,6 @@ import java.util.zip.ZipOutputStream; /** * The {@code JarOutputStream} is used to write data in the {@code JarFile} * format to an arbitrary output stream - * - * @since Android 1.0 */ public class JarOutputStream extends ZipOutputStream { @@ -79,7 +77,6 @@ public class JarOutputStream extends ZipOutputStream { * @throws IOException * if an error occurs writing to the entry. * @see ZipEntry - * @since Android 1.0 */ @Override public void putNextEntry(ZipEntry ze) throws IOException { diff --git a/libcore/archive/src/main/java/java/util/jar/JarVerifier.java b/libcore/archive/src/main/java/java/util/jar/JarVerifier.java index b9173f291..6c1ee93c6 100644 --- a/libcore/archive/src/main/java/java/util/jar/JarVerifier.java +++ b/libcore/archive/src/main/java/java/util/jar/JarVerifier.java @@ -31,13 +31,12 @@ import java.util.Iterator; import java.util.Map; import java.util.StringTokenizer; import java.util.Vector; -import java.util.zip.ZipEntry; import org.apache.harmony.archive.internal.nls.Messages; import org.apache.harmony.luni.util.Base64; import org.apache.harmony.security.utils.JarUtils; -import org.apache.harmony.archive.util.Util; +import org.apache.harmony.luni.util.Util; // BEGIN android-added import org.apache.harmony.xnet.provider.jsse.OpenSSLMessageDigestJDK; @@ -65,65 +64,81 @@ class JarVerifier { private HashMap metaEntries = new HashMap(5); - private final Hashtable> signatures = - new Hashtable>(5); + private final Hashtable> signatures = new Hashtable>( + 5); - private final Hashtable certificates = - new Hashtable(5); + private final Hashtable certificates = new Hashtable( + 5); - private final Hashtable verifiedEntries = - new Hashtable(); + private final Hashtable verifiedEntries = new Hashtable(); - byte[] mainAttributesChunk; + int mainAttributesEnd; - // BEGIN android-added - private static long measureCount = 0; - - private static long averageTime = 0; - // END android-added - /** - * TODO Type description + * Stores and a hash and a message digest and verifies that massage digest + * matches the hash. */ - static class VerifierEntry extends OutputStream { + class VerifierEntry extends OutputStream { - MessageDigest digest; + private String name; - byte[] hash; + private MessageDigest digest; - Certificate[] certificates; + private byte[] hash; - VerifierEntry(MessageDigest digest, byte[] hash, + private Certificate[] certificates; + + VerifierEntry(String name, MessageDigest digest, byte[] hash, Certificate[] certificates) { + this.name = name; this.digest = digest; this.hash = hash; this.certificates = certificates; } - /* - * (non-Javadoc) - * - * @see java.io.OutputStream#write(int) + /** + * Updates a digest with one byte. */ @Override public void write(int value) { digest.update((byte) value); } - /* - * (non-Javadoc) - * - * @see java.io.OutputStream#write(byte[], int, int) + /** + * Updates a digest with byte array. */ @Override public void write(byte[] buf, int off, int nbytes) { digest.update(buf, off, nbytes); } + + /** + * Verifies that the digests stored in the manifest match the decrypted + * digests from the .SF file. This indicates the validity of the + * signing, not the integrity of the file, as it's digest must be + * calculated and verified when its contents are read. + * + * @throws SecurityException + * if the digest value stored in the manifest does not + * agree with the decrypted digest as recovered from the + * .SF file. + * @see #initEntry(String) + */ + void verify() { + byte[] d = digest.digest(); + if (!MessageDigest.isEqual(d, Base64.decode(hash))) { + throw new SecurityException(Messages.getString( + "archive.32", new Object[] { //$NON-NLS-1$ + JarFile.MANIFEST_NAME, name, jarName })); + } + verifiedEntries.put(name, certificates); + } + } /** * Constructs and returns a new instance of {@code JarVerifier}. - * + * * @param name * the name of the JAR file being verified. */ @@ -136,13 +151,12 @@ class JarVerifier { * stream. This method constructs and returns a new {@link VerifierEntry} * which contains the certificates used to sign the entry and its hash value * as specified in the JAR MANIFEST format. - * + * * @param name * the name of an entry in a JAR file which is not in the * {@code META-INF} directory. * @return a new instance of {@link VerifierEntry} which can be used by * callers as an {@link OutputStream}. - * @since Android 1.0 */ VerifierEntry initEntry(String name) { // If no manifest is present by the time an entry is found, @@ -159,8 +173,8 @@ class JarVerifier { } Vector certs = new Vector(); - Iterator>> it = - signatures.entrySet().iterator(); + Iterator>> it = signatures + .entrySet().iterator(); while (it.hasNext()) { Map.Entry> entry = it.next(); HashMap hm = entry.getValue(); @@ -197,18 +211,18 @@ class JarVerifier { } byte[] hashBytes; try { - hashBytes = hash.getBytes("ISO8859_1"); //$NON-NLS-1$ + hashBytes = hash.getBytes("ISO-8859-1"); //$NON-NLS-1$ } catch (UnsupportedEncodingException e) { throw new RuntimeException(e.toString()); } try { // BEGIN android-changed - return new VerifierEntry(OpenSSLMessageDigestJDK.getInstance(algorithm), + return new VerifierEntry(name, OpenSSLMessageDigestJDK.getInstance(algorithm), hashBytes, certificatesArray); // END android-changed } catch (NoSuchAlgorithmException e) { - // Ignored + // ignored } } return null; @@ -219,7 +233,7 @@ class JarVerifier { * entry in the {@code META-INF} directory including the manifest * file itself. Files associated with the signing of a JAR would also be * added to this collection. - * + * * @param name * the name of the file located in the {@code META-INF} * directory. @@ -234,7 +248,7 @@ class JarVerifier { /** * If the associated JAR file is signed, check on the validity of all of the * known signatures. - * + * * @return {@code true} if the associated JAR is signed and an internal * check verifies the validity of the signature(s). {@code false} if * the associated JAR file has no entries at all in its {@code @@ -243,12 +257,10 @@ class JarVerifier { *

      * Will also return {@code true} if the JAR file is not * signed. - *

      * @throws SecurityException * if the JAR file is signed and it is determined that a * signature block file contains an invalid signature for the * corresponding signature file. - * @since Android 1.0 */ synchronized boolean readCertificates() { if (metaEntries == null) { @@ -281,6 +293,12 @@ class JarVerifier { return; } + byte[] manifest = metaEntries.get(JarFile.MANIFEST_NAME); + // Manifest entry is required for any verifications. + if (manifest == null) { + return; + } + byte[] sBlockBytes = metaEntries.get(certFile); try { Certificate[] signerCertChain = JarUtils.verifySignature( @@ -288,7 +306,7 @@ class JarVerifier { new ByteArrayInputStream(sBlockBytes)); /* * Recursive call in loading security provider related class which - * is in a signed JAR. + * is in a signed JAR. */ if (null == metaEntries) { return; @@ -299,74 +317,70 @@ class JarVerifier { } catch (IOException e) { return; } catch (GeneralSecurityException e) { - /* [MSG "archive.30", "{0} failed verification of {1}"] */ - throw new SecurityException( - Messages.getString("archive.30", jarName, signatureFile)); //$NON-NLS-1$ + /* [MSG "archive.31", "{0} failed verification of {1}"] */ + throw new SecurityException(Messages.getString( + "archive.31", jarName, signatureFile)); //$NON-NLS-1$ } // Verify manifest hash in .sf file Attributes attributes = new Attributes(); - HashMap hm = new HashMap(); + HashMap entries = new HashMap(); try { - new InitManifest(new ByteArrayInputStream(sfBytes), attributes, hm, - null, "Signature-Version"); //$NON-NLS-1$ + InitManifest im = new InitManifest(sfBytes, attributes, Attributes.Name.SIGNATURE_VERSION); + im.initEntries(entries, null); } catch (IOException e) { return; } boolean createdBySigntool = false; - String createdByValue = attributes.getValue("Created-By"); //$NON-NLS-1$ - if (createdByValue != null) { - createdBySigntool = createdByValue.indexOf("signtool") != -1; //$NON-NLS-1$ + String createdBy = attributes.getValue("Created-By"); //$NON-NLS-1$ + if (createdBy != null) { + createdBySigntool = createdBy.indexOf("signtool") != -1; //$NON-NLS-1$ } // Use .SF to verify the mainAttributes of the manifest // If there is no -Digest-Manifest-Main-Attributes entry in .SF // file, such as those created before java 1.5, then we ignore // such verification. - // FIXME: The meaning of createdBySigntool - if (mainAttributesChunk != null && !createdBySigntool) { + if (mainAttributesEnd > 0 && !createdBySigntool) { String digestAttribute = "-Digest-Manifest-Main-Attributes"; //$NON-NLS-1$ - if (!verify(attributes, digestAttribute, mainAttributesChunk, - false, true)) { - /* [MSG "archive.30", "{0} failed verification of {1}"] */ - throw new SecurityException( - Messages.getString("archive.30", jarName, signatureFile)); //$NON-NLS-1$ + if (!verify(attributes, digestAttribute, manifest, 0, + mainAttributesEnd, false, true)) { + /* [MSG "archive.31", "{0} failed verification of {1}"] */ + throw new SecurityException(Messages.getString( + "archive.31", jarName, signatureFile)); //$NON-NLS-1$ } } - byte[] manifest = metaEntries.get(JarFile.MANIFEST_NAME); - if (manifest == null) { - return; - } - // Use .SF to verify the whole manifest + // Use .SF to verify the whole manifest. String digestAttribute = createdBySigntool ? "-Digest" //$NON-NLS-1$ : "-Digest-Manifest"; //$NON-NLS-1$ - if (!verify(attributes, digestAttribute, manifest, false, false)) { - Iterator> it = hm.entrySet() + if (!verify(attributes, digestAttribute, manifest, 0, manifest.length, + false, false)) { + Iterator> it = entries.entrySet() .iterator(); while (it.hasNext()) { Map.Entry entry = it.next(); - byte[] chunk = man.getChunk(entry.getKey()); + Manifest.Chunk chunk = man.getChunk(entry.getKey()); if (chunk == null) { return; } - if (!verify(entry.getValue(), "-Digest", chunk, //$NON-NLS-1$ - createdBySigntool, false)) { - /* [MSG "archive.31", "{0} has invalid digest for {1} in {2}"] */ - throw new SecurityException( - Messages.getString("archive.31", //$NON-NLS-1$ - new Object[] { signatureFile, entry.getKey(), jarName })); + if (!verify(entry.getValue(), "-Digest", manifest, //$NON-NLS-1$ + chunk.start, chunk.end, createdBySigntool, false)) { + throw new SecurityException(Messages.getString( + "archive.32", //$NON-NLS-1$ + new Object[] { signatureFile, entry.getKey(), + jarName })); } } } metaEntries.put(signatureFile, null); - signatures.put(signatureFile, hm); + signatures.put(signatureFile, entries); } /** * Associate this verifier with the specified {@link Manifest} object. - * + * * @param mf * a {@code java.util.jar.Manifest} object. */ @@ -375,36 +389,9 @@ class JarVerifier { } /** - * Verifies that the digests stored in the manifest match the decrypted - * digests from the .SF file. This indicates the validity of the signing, - * not the integrity of the file, as it's digest must be calculated and - * verified when its contents are read. - * - * @param entry - * the {@link VerifierEntry} associated with the specified - * {@code zipEntry}. - * @param zipEntry - * an entry in the JAR file - * @throws SecurityException - * if the digest value stored in the manifest does not - * agree with the decrypted digest as recovered from the - * {@code .SF} file. - * @see #initEntry(String) - */ - void verifySignatures(VerifierEntry entry, ZipEntry zipEntry) { - byte[] digest = entry.digest.digest(); - if (!MessageDigest.isEqual(digest, Base64.decode(entry.hash))) { - /* [MSG "archive.31", "{0} has invalid digest for {1} in {2}"] */ - throw new SecurityException(Messages.getString("archive.31", new Object[] { //$NON-NLS-1$ - JarFile.MANIFEST_NAME, zipEntry.getName(), jarName })); - } - verifiedEntries.put(zipEntry.getName(), entry.certificates); - } - - /** - * Returns a {@code boolean} indication of whether or not the - * associated JAR file is signed. - * + * Returns a boolean indication of whether or not the + * associated jar file is signed. + * * @return {@code true} if the JAR is signed, {@code false} * otherwise. */ @@ -413,7 +400,7 @@ class JarVerifier { } private boolean verify(Attributes attributes, String entry, byte[] data, - boolean ignoreSecondEndline, boolean ignorable) { + int start, int end, boolean ignoreSecondEndline, boolean ignorable) { String algorithms = attributes.getValue("Digest-Algorithms"); //$NON-NLS-1$ if (algorithms == null) { algorithms = "SHA SHA1"; //$NON-NLS-1$ @@ -434,16 +421,16 @@ class JarVerifier { } catch (NoSuchAlgorithmException e) { continue; } - if (ignoreSecondEndline && data[data.length - 1] == '\n' - && data[data.length - 2] == '\n') { - md.update(data, 0, data.length - 1); + if (ignoreSecondEndline && data[end - 1] == '\n' + && data[end - 2] == '\n') { + md.update(data, start, end - 1 - start); } else { - md.update(data, 0, data.length); + md.update(data, start, end - start); } byte[] b = md.digest(); byte[] hashBytes; try { - hashBytes = hash.getBytes("ISO8859_1"); //$NON-NLS-1$ + hashBytes = hash.getBytes("ISO-8859-1"); //$NON-NLS-1$ } catch (UnsupportedEncodingException e) { throw new RuntimeException(e.toString()); } @@ -456,7 +443,7 @@ class JarVerifier { * Returns all of the {@link java.security.cert.Certificate} instances that * were used to verify the signature on the JAR entry called * {@code name}. - * + * * @param name * the name of a JAR entry. * @return an array of {@link java.security.cert.Certificate}. @@ -472,7 +459,7 @@ class JarVerifier { /** * Remove all entries from the internal collection of data held about each * JAR entry in the {@code META-INF} directory. - * + * * @see #addMetaEntry(String, byte[]) */ void removeMetaEntries() { @@ -483,7 +470,7 @@ class JarVerifier { * Returns a {@code Vector} of all of the * {@link java.security.cert.Certificate}s that are associated with the * signing of the named signature file. - * + * * @param signatureFileName * the name of a signature file. * @param certificates diff --git a/libcore/archive/src/main/java/java/util/jar/Manifest.java b/libcore/archive/src/main/java/java/util/jar/Manifest.java index 3b0d89a6f..b28f3fb67 100644 --- a/libcore/archive/src/main/java/java/util/jar/Manifest.java +++ b/libcore/archive/src/main/java/java/util/jar/Manifest.java @@ -17,47 +17,65 @@ package java.util.jar; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.nio.ByteBuffer; import java.nio.CharBuffer; -import java.nio.charset.Charset; -import java.security.AccessController; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; import java.util.HashMap; import java.util.Iterator; import java.util.Map; -import org.apache.harmony.luni.util.PriviAction; +import org.apache.harmony.archive.internal.nls.Messages; +import org.apache.harmony.luni.util.InputStreamExposer; +import org.apache.harmony.luni.util.ThreadLocalCache; /** * The {@code Manifest} class is used to obtain attribute information for a * {@code JarFile} and its entries. - * - * @since Android 1.0 */ public class Manifest implements Cloneable { - private static final int LINE_LENGTH_LIMIT = 70; + static final int LINE_LENGTH_LIMIT = 72; private static final byte[] LINE_SEPARATOR = new byte[] { '\r', '\n' }; - private static final Attributes.Name NAME_ATTRIBUTE = new Attributes.Name("Name"); //$NON-NLS-1$ + private static final byte[] VALUE_SEPARATOR = new byte[] { ':', ' ' }; + + private static final Attributes.Name NAME_ATTRIBUTE = new Attributes.Name( + "Name"); //$NON-NLS-1$ private Attributes mainAttributes = new Attributes(); - private HashMap entryAttributes = new HashMap(); + private HashMap entries = new HashMap(); + + static class Chunk { + int start; + int end; + + Chunk(int start, int end) { + this.start = start; + this.end = end; + } + } + + private HashMap chunks; - private HashMap chunks; + /** + * Manifest bytes are used for delayed entry parsing. + */ + private InitManifest im; /** - * The data chunk of main attributes in the manifest is needed in + * The end of the main attributes section in the manifest is needed in * verification. */ - private byte[] mainAttributesChunk; + private int mainEnd; /** * Creates a new {@code Manifest} instance. - * - * @since Android 1.0 */ public Manifest() { super(); @@ -66,12 +84,11 @@ public class Manifest implements Cloneable { /** * Creates a new {@code Manifest} instance using the attributes obtained * from the input stream. - * + * * @param is * {@code InputStream} to parse for attributes. * @throws IOException * if an IO error occurs while creating this {@code Manifest} - * @since Android 1.0 */ public Manifest(InputStream is) throws IOException { super(); @@ -81,20 +98,20 @@ public class Manifest implements Cloneable { /** * Creates a new {@code Manifest} instance. The new instance will have the * same attributes as those found in the parameter {@code Manifest}. - * + * * @param man * {@code Manifest} instance to obtain attributes from. - * @since Android 1.0 */ @SuppressWarnings("unchecked") public Manifest(Manifest man) { mainAttributes = (Attributes) man.mainAttributes.clone(); - entryAttributes = (HashMap) man.entryAttributes.clone(); + entries = (HashMap) ((HashMap) man + .getEntries()).clone(); } Manifest(InputStream is, boolean readChunks) throws IOException { if (readChunks) { - chunks = new HashMap(); + chunks = new HashMap(); } read(is); } @@ -102,23 +119,21 @@ public class Manifest implements Cloneable { /** * Resets the both the main attributes as well as the entry attributes * associated with this {@code Manifest}. - * - * @since Android 1.0 */ public void clear() { - entryAttributes.clear(); + im = null; + entries.clear(); mainAttributes.clear(); } /** * Returns the {@code Attributes} associated with the parameter entry * {@code name}. - * + * * @param name * the name of the entry to obtain {@code Attributes} from. * @return the Attributes for the entry or {@code null} if the entry does * not exist. - * @since Android 1.0 */ public Attributes getAttributes(String name) { return getEntries().get(name); @@ -127,20 +142,31 @@ public class Manifest implements Cloneable { /** * Returns a map containing the {@code Attributes} for each entry in the * {@code Manifest}. - * + * * @return the map of entry attributes. - * @since Android 1.0 */ public Map getEntries() { - return entryAttributes; + initEntries(); + return entries; + } + + private void initEntries() { + if (im == null) { + return; + } + // try { + // im.initEntries(entries, chunks); + // } catch (IOException ioe) { + // throw new RuntimeException(ioe); + // } + // im = null; } /** * Returns the main {@code Attributes} of the {@code JarFile}. - * + * * @return main {@code Attributes} associated with the source {@code * JarFile}. - * @since Android 1.0 */ public Attributes getMainAttributes() { return mainAttributes; @@ -149,9 +175,8 @@ public class Manifest implements Cloneable { /** * Creates a copy of this {@code Manifest}. The returned {@code Manifest} * will equal the {@code Manifest} from which it was cloned. - * + * * @return a copy of this instance. - * @since Android 1.0 */ @Override public Object clone() { @@ -161,12 +186,11 @@ public class Manifest implements Cloneable { /** * Writes out the attribute information of the receiver to the specified * {@code OutputStream}. - * + * * @param os * The {@code OutputStream} to write to. * @throws IOException * If an error occurs writing the {@code Manifest}. - * @since Android 1.0 */ public void write(OutputStream os) throws IOException { write(this, os); @@ -175,39 +199,98 @@ public class Manifest implements Cloneable { /** * Constructs a new {@code Manifest} instance obtaining attribute * information from the specified input stream. - * + * * @param is * The {@code InputStream} to read from. * @throws IOException * If an error occurs reading the {@code Manifest}. - * @since Android 1.0 */ public void read(InputStream is) throws IOException { - InitManifest initManifest = new InitManifest(is, mainAttributes, entryAttributes, - chunks, null); - mainAttributesChunk = initManifest.getMainAttributesChunk(); + byte[] buf; + // Try to read get a reference to the bytes directly + try { + buf = InputStreamExposer.expose(is); + } catch (UnsupportedOperationException uoe) { + buf = readFully(is); + } + + if (buf.length == 0) { + return; + } + + // a workaround for HARMONY-5662 + // replace EOF and NUL with another new line + // which does not trigger an error + byte b = buf[buf.length - 1]; + if (0 == b || 26 == b) { + buf[buf.length - 1] = '\n'; + } + + // Attributes.Name.MANIFEST_VERSION is not used for + // the second parameter for RI compatibility + im = new InitManifest(buf, mainAttributes, null); + mainEnd = im.getPos(); + // FIXME + im.initEntries(entries, chunks); + im = null; + } + + /* + * Helper to read the entire contents of the manifest from the + * given input stream. Usually we can do this in a single read + * but we need to account for 'infinite' streams, by ensuring we + * have a line feed within a reasonable number of characters. + */ + private byte[] readFully(InputStream is) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buffer = new byte[8192]; + + while (true) { + int count = is.read(buffer); + if (count == -1) { + // TODO: Do we need to copy this, or can we live with junk at the end? + return baos.toByteArray(); + } + baos.write(buffer, 0, count); + + if (!containsLine(buffer, count)) { + throw new IOException(Messages.getString("archive.2E")); //$NON-NLS-1$ + } + } + } + + /* + * Check to see if the buffer contains a newline or carriage + * return character within the first 'length' bytes. Used to + * check the validity of the manifest input stream. + */ + private boolean containsLine(byte[] buffer, int length) { + for (int i = 0; i < length; i++) { + if (buffer[i] == 0x0A || buffer[i] == 0x0D) { + return true; + } + } + return false; } /** * Returns the hash code for this instance. - * + * * @return this {@code Manifest}'s hashCode. - * @since Android 1.0 */ @Override public int hashCode() { - return mainAttributes.hashCode() ^ entryAttributes.hashCode(); + return mainAttributes.hashCode() ^ getEntries().hashCode(); } /** * Determines if the receiver is equal to the parameter object. Two {@code * Manifest}s are equal if they have identical main attributes as well as * identical entry attributes. - * + * * @param o * the object to compare against. * @return {@code true} if the manifests are equal, {@code false} otherwise - * @since Android 1.0 */ @Override public boolean equals(Object o) { @@ -220,10 +303,10 @@ public class Manifest implements Cloneable { if (!mainAttributes.equals(((Manifest) o).mainAttributes)) { return false; } - return entryAttributes.equals(((Manifest) o).entryAttributes); + return getEntries().equals(((Manifest) o).getEntries()); } - byte[] getChunk(String name) { + Chunk getChunk(String name) { return chunks.get(name); } @@ -231,114 +314,84 @@ public class Manifest implements Cloneable { chunks = null; } - byte[] getMainAttributesChunk() { - return mainAttributesChunk; + int getMainAttributesEnd() { + return mainEnd; } /** * Writes out the attribute information of the specified manifest to the * specified {@code OutputStream} - * + * * @param manifest * the manifest to write out. * @param out * The {@code OutputStream} to write to. * @throws IOException * If an error occurs writing the {@code Manifest}. - * @since Android 1.0 */ static void write(Manifest manifest, OutputStream out) throws IOException { - Charset charset = null; - String encoding = AccessController.doPrivileged(new PriviAction( - "manifest.write.encoding")); //$NON-NLS-1$ - if (encoding != null) { - if (encoding.length() == 0) { - encoding = "UTF8"; //$NON-NLS-1$ - } - charset = Charset.forName(encoding); - } - String version = manifest.mainAttributes.getValue(Attributes.Name.MANIFEST_VERSION); + CharsetEncoder encoder = ThreadLocalCache.utf8Encoder.get(); + ByteBuffer buffer = ThreadLocalCache.byteBuffer.get(); + + String version = manifest.mainAttributes + .getValue(Attributes.Name.MANIFEST_VERSION); if (version != null) { - writeEntry(out, charset, Attributes.Name.MANIFEST_VERSION, version); + writeEntry(out, Attributes.Name.MANIFEST_VERSION, version, encoder, + buffer); Iterator entries = manifest.mainAttributes.keySet().iterator(); while (entries.hasNext()) { Attributes.Name name = (Attributes.Name) entries.next(); if (!name.equals(Attributes.Name.MANIFEST_VERSION)) { - writeEntry(out, charset, name, manifest.mainAttributes.getValue(name)); + writeEntry(out, name, manifest.mainAttributes + .getValue(name), encoder, buffer); } } } out.write(LINE_SEPARATOR); - Iterator i = manifest.entryAttributes.keySet().iterator(); + Iterator i = manifest.getEntries().keySet().iterator(); while (i.hasNext()) { String key = i.next(); - writeEntry(out, charset, NAME_ATTRIBUTE, key); - Attributes attrib = manifest.entryAttributes.get(key); + writeEntry(out, NAME_ATTRIBUTE, key, encoder, buffer); + Attributes attrib = manifest.entries.get(key); Iterator entries = attrib.keySet().iterator(); while (entries.hasNext()) { Attributes.Name name = (Attributes.Name) entries.next(); - writeEntry(out, charset, name, attrib.getValue(name)); + writeEntry(out, name, attrib.getValue(name), encoder, buffer); } out.write(LINE_SEPARATOR); } } - private static void writeEntry(OutputStream os, Charset charset, Attributes.Name name, - String value) throws IOException { - int offset = 0; - int limit = LINE_LENGTH_LIMIT; - byte[] out = (name.toString() + ": ").getBytes("ISO8859_1"); //$NON-NLS-1$ //$NON-NLS-2$ - if (out.length > limit) { - while (out.length - offset >= limit) { - int len = out.length - offset; - if (len > limit) { - len = limit; - } - if (offset > 0) { - os.write(' '); - } - os.write(out, offset, len); - os.write(LINE_SEPARATOR); - offset += len; - limit = LINE_LENGTH_LIMIT - 1; - } - } - int size = out.length - offset; - final byte[] outBuf = new byte[LINE_LENGTH_LIMIT]; - System.arraycopy(out, offset, outBuf, 0, size); - for (int i = 0; i < value.length(); i++) { - char[] oneChar = new char[1]; - oneChar[0] = value.charAt(i); - byte[] buf; - if (oneChar[0] < 128 || charset == null) { - byte[] oneByte = new byte[1]; - oneByte[0] = (byte) oneChar[0]; - buf = oneByte; - } else { - buf = charset.encode(CharBuffer.wrap(oneChar, 0, 1)).array(); - } - if (size + buf.length > limit) { - if (limit != LINE_LENGTH_LIMIT) { - os.write(' '); - } - os.write(outBuf, 0, size); - os.write(LINE_SEPARATOR); - limit = LINE_LENGTH_LIMIT - 1; - size = 0; - } - if (buf.length == 1) { - outBuf[size] = buf[0]; - } else { - System.arraycopy(buf, 0, outBuf, size, buf.length); - } - size += buf.length; + private static void writeEntry(OutputStream os, Attributes.Name name, + String value, CharsetEncoder encoder, ByteBuffer bBuf) + throws IOException { + byte[] out = name.getBytes(); + if (out.length > LINE_LENGTH_LIMIT) { + throw new IOException(Messages.getString( + "archive.33", name, Integer.valueOf(LINE_LENGTH_LIMIT))); //$NON-NLS-1$ } - if (size > 0) { - if (limit != LINE_LENGTH_LIMIT) { - os.write(' '); + + os.write(out); + os.write(VALUE_SEPARATOR); + + encoder.reset(); + bBuf.clear().limit(LINE_LENGTH_LIMIT - out.length - 2); + + CharBuffer cBuf = CharBuffer.wrap(value); + CoderResult r; + + while (true) { + r = encoder.encode(cBuf, bBuf, true); + if (CoderResult.UNDERFLOW == r) { + r = encoder.flush(bBuf); } - os.write(outBuf, 0, size); + os.write(bBuf.array(), bBuf.arrayOffset(), bBuf.position()); os.write(LINE_SEPARATOR); + if (CoderResult.UNDERFLOW == r) { + break; + } + os.write(' '); + bBuf.clear().limit(LINE_LENGTH_LIMIT - 1); } } } diff --git a/libcore/archive/src/main/java/java/util/jar/Pack200.java b/libcore/archive/src/main/java/java/util/jar/Pack200.java index e0689e073..3e7081d2e 100644 --- a/libcore/archive/src/main/java/java/util/jar/Pack200.java +++ b/libcore/archive/src/main/java/java/util/jar/Pack200.java @@ -27,8 +27,6 @@ import java.util.SortedMap; /** * Class factory for {@link Pack200.Packer} and {@link Pack200.Unpacker}. - * - * @since Android 1.0 */ public abstract class Pack200 { @@ -39,8 +37,8 @@ public abstract class Pack200 { /** * Prevent this class from being instantiated. */ - private Pack200(){ - //do nothing + private Pack200() { + // do nothing } /** @@ -50,10 +48,8 @@ public abstract class Pack200 { * {@code 'java.util.jar.Pack200.Packer'}. If this system property is * defined an instance of the specified class is returned, otherwise the * system's default implementation is returned. - *

      * * @return an instance of {@code Packer} - * @since Android 1.0 */ public static Pack200.Packer newPacker() { return (Packer) AccessController @@ -61,7 +57,7 @@ public abstract class Pack200 { public Object run() { String className = System .getProperty(SYSTEM_PROPERTY_PACKER, - "org.apache.harmony.archive.internal.pack200.Pack200PackerAdapter"); //$NON-NLS-1$ + "org.apache.harmony.pack200.Pack200PackerAdapter"); //$NON-NLS-1$ try { // TODO Not sure if this will cause problems with // loading the packer @@ -82,10 +78,8 @@ public abstract class Pack200 { * property {@code 'java.util.jar.Pack200.Unpacker'}. If this system * property is defined an instance of the specified class is returned, * otherwise the system's default implementation is returned. - *

      * * @return a instance of {@code Unpacker}. - * @since Android 1.0 */ public static Pack200.Unpacker newUnpacker() { return (Unpacker) AccessController @@ -93,7 +87,7 @@ public abstract class Pack200 { public Object run() { String className = System .getProperty(SYSTEM_PROPERTY_UNPACKER, - "org.apache.harmony.archive.internal.pack200.Pack200UnpackerAdapter");//$NON-NLS-1$ + "org.apache.harmony.unpack200.Pack200UnpackerAdapter");//$NON-NLS-1$ try { return ClassLoader.getSystemClassLoader() .loadClass(className).newInstance(); @@ -107,150 +101,109 @@ public abstract class Pack200 { /** * The interface defining the API for converting a JAR file to an output * stream in the Pack200 format. - * - * @since Android 1.0 */ public static interface Packer { /** * the format of a class attribute name. - * - * @since Android 1.0 */ static final String CLASS_ATTRIBUTE_PFX = "pack.class.attribute."; //$NON-NLS-1$ /** * the format of a code attribute name. - * - * @since Android 1.0 */ static final String CODE_ATTRIBUTE_PFX = "pack.code.attribute."; //$NON-NLS-1$ /** * the deflation hint to set in the output archive. - * - * @since Android 1.0 */ static final String DEFLATE_HINT = "pack.deflate.hint";//$NON-NLS-1$ /** * the indicated amount of effort to use in compressing the archive. - * - * @since Android 1.0 */ static final String EFFORT = "pack.effort";//$NON-NLS-1$ /** * a String representation for {@code error}. - * - * @since Android 1.0 */ static final String ERROR = "error";//$NON-NLS-1$ /** * a String representation of {@code false}. - * - * @since Android 1.0 */ static final String FALSE = "false";//$NON-NLS-1$ /** * the format of a field attribute name. - * - * @since Android 1.0 */ static final String FIELD_ATTRIBUTE_PFX = "pack.field.attribute.";//$NON-NLS-1$ /** * a String representation for {@code keep}. - * - * @since Android 1.0 */ static final String KEEP = "keep";//$NON-NLS-1$ /** * decide if all elements shall transmit in their original order. - * - * @since Android 1.0 */ static final String KEEP_FILE_ORDER = "pack.keep.file.order";//$NON-NLS-1$ /** * a String representation for {@code latest}. - * - * @since Android 1.0 */ static final String LATEST = "latest";//$NON-NLS-1$ /** * the format of a method attribute name. - * - * @since Android 1.0 */ static final String METHOD_ATTRIBUTE_PFX = "pack.method.attribute.";//$NON-NLS-1$ /** * if it shall attempt to determine the latest modification time if this * is set to {@code LATEST}. - * - * @since Android 1.0 */ static final String MODIFICATION_TIME = "pack.modification.time";//$NON-NLS-1$ /** * a String representation of {@code pass}. - * - * @since Android 1.0 */ static final String PASS = "pass";//$NON-NLS-1$ /** * the file that will not be compressed. - * - * @since Android 1.0 */ static final String PASS_FILE_PFX = "pack.pass.file.";//$NON-NLS-1$ /** * packer progress as a percentage. - * - * @since Android 1.0 */ static final String PROGRESS = "pack.progress";//$NON-NLS-1$ /** * The number of bytes of each archive segment. - * - * @since Android 1.0 */ static final String SEGMENT_LIMIT = "pack.segment.limit";//$NON-NLS-1$ /** * a String representation of {@code strip}. - * - * @since Android 1.0 */ static final String STRIP = "strip";//$NON-NLS-1$ /** * a String representation of {@code true}. - * - * @since Android 1.0 */ static final String TRUE = "true";//$NON-NLS-1$ /** * the action to take if an unknown attribute is encountered. - * - * @since Android 1.0 */ static final String UNKNOWN_ATTRIBUTE = "pack.unknown.attribute";//$NON-NLS-1$ /** * Returns a sorted map of the properties of this packer. - * + * * @return the properties of the packer. - * @since Android 1.0 */ SortedMap properties(); @@ -263,7 +216,6 @@ public abstract class Pack200 { * stream of compressed data. * @throws IOException * if I/O exception occurs. - * @since Android 1.0 */ void pack(JarFile in, OutputStream out) throws IOException; @@ -277,7 +229,6 @@ public abstract class Pack200 { * stream of compressed data. * @throws IOException * if I/O exception occurs. - * @since Android 1.0 */ void pack(JarInputStream in, OutputStream out) throws IOException; @@ -301,52 +252,39 @@ public abstract class Pack200 { /** * The interface defining the API for converting a packed stream in the * Pack200 format to a JAR file. - * - * @since Android 1.0 */ public static interface Unpacker { /** * The String indicating if the unpacker should ignore all transmitted * values,can be replaced by either {@code true} or {@code false}. - * - * @since Android 1.0 */ static final String DEFLATE_HINT = "unpack.deflate.hint";//$NON-NLS-1$ /** * a String representation of {@code false}. - * - * @since Android 1.0 */ static final String FALSE = "false";//$NON-NLS-1$ /** * a String representation of {@code keep}. - * - * @since Android 1.0 */ static final String KEEP = "keep";//$NON-NLS-1$ /** * the progress as a {@code percentage}. - * - * @since Android 1.0 */ static final String PROGRESS = "unpack.progress";//$NON-NLS-1$ /** * a String representation of {@code true}. - * - * @since Android 1.0 */ static final String TRUE = "true";//$NON-NLS-1$ /** * Returns a sorted map of the properties of this unpacker. - * + * * @return the properties of unpacker. - * @since Android 1.0 */ SortedMap properties(); @@ -359,7 +297,6 @@ public abstract class Pack200 { * JAR output stream of uncompressed data. * @throws IOException * if I/O exception occurs. - * @since Android 1.0 */ void unpack(InputStream in, JarOutputStream out) throws IOException; @@ -373,7 +310,6 @@ public abstract class Pack200 { * JAR output stream of uncompressed data. * @throws IOException * if I/O exception occurs. - * @since Android 1.0 */ void unpack(File in, JarOutputStream out) throws IOException; diff --git a/libcore/archive/src/main/java/java/util/zip/Adler32.java b/libcore/archive/src/main/java/java/util/zip/Adler32.java index 8eaa18e7d..a5f77d734 100644 --- a/libcore/archive/src/main/java/java/util/zip/Adler32.java +++ b/libcore/archive/src/main/java/java/util/zip/Adler32.java @@ -17,14 +17,12 @@ package java.util.zip; - /** * The Adler-32 class is used to compute the {@code Adler32} checksum from a set * of data. Compared to the CRC-32 algorithm it trades reliabilty for speed. * Refer to RFC 1950 for the specification. - * + * * @see CRC32 - * @since Android 1.0 */ public class Adler32 implements java.util.zip.Checksum { @@ -34,7 +32,6 @@ public class Adler32 implements java.util.zip.Checksum { * Returns the {@code Adler32} checksum for all input received. * * @return The checksum for this instance. - * @since Android 1.0 */ public long getValue() { return adler; @@ -42,8 +39,6 @@ public class Adler32 implements java.util.zip.Checksum { /** * Reset this instance to its initial checksum. - * - * @since Android 1.0 */ public void reset() { adler = 1; @@ -55,7 +50,6 @@ public class Adler32 implements java.util.zip.Checksum { * * @param i * the byte to update checksum with. - * @since Android 1.0 */ public void update(int i) { adler = updateByteImpl(i, adler); @@ -66,7 +60,6 @@ public class Adler32 implements java.util.zip.Checksum { * * @param buf * bytes to update checksum with. - * @since Android 1.0 */ public void update(byte[] buf) { update(buf, 0, buf.length); @@ -85,7 +78,6 @@ public class Adler32 implements java.util.zip.Checksum { * @throws ArrayIndexOutOfBoundsException * if {@code offset > buf.length} or {@code nbytes} is negative * or {@code offset + nbytes > buf.length}. - * @since Android 1.0 */ public void update(byte[] buf, int off, int nbytes) { // avoid int overflow, check null buf diff --git a/libcore/archive/src/main/java/java/util/zip/CRC32.java b/libcore/archive/src/main/java/java/util/zip/CRC32.java index 36dc376bc..59e8f81fc 100644 --- a/libcore/archive/src/main/java/java/util/zip/CRC32.java +++ b/libcore/archive/src/main/java/java/util/zip/CRC32.java @@ -17,12 +17,9 @@ package java.util.zip; - /** * The CRC32 class is used to compute a CRC32 checksum from data provided as * input value. - * - * @since Android 1.0 */ public class CRC32 implements java.util.zip.Checksum { @@ -34,7 +31,6 @@ public class CRC32 implements java.util.zip.Checksum { * Returns the CRC32 checksum for all input received. * * @return The checksum for this instance. - * @since Android 1.0 */ public long getValue() { return crc; @@ -42,8 +38,6 @@ public class CRC32 implements java.util.zip.Checksum { /** * Resets the CRC32 checksum to it initial state. - * - * @since Android 1.0 */ public void reset() { tbytes = crc = 0; @@ -52,10 +46,9 @@ public class CRC32 implements java.util.zip.Checksum { /** * Updates this checksum with the byte value provided as integer. - * + * * @param val * represents the byte to update the checksum. - * @since Android 1.0 */ public void update(int val) { crc = updateByteImpl((byte) val, crc); @@ -66,7 +59,6 @@ public class CRC32 implements java.util.zip.Checksum { * * @param buf * the buffer holding the data to update the checksum with. - * @since Android 1.0 */ public void update(byte[] buf) { update(buf, 0, buf.length); @@ -82,7 +74,6 @@ public class CRC32 implements java.util.zip.Checksum { * the offset in {@code buf} to obtain data from. * @param nbytes * the number of bytes to read from {@code buf}. - * @since Android 1.0 */ public void update(byte[] buf, int off, int nbytes) { // avoid int overflow, check null buf diff --git a/libcore/archive/src/main/java/java/util/zip/CheckedInputStream.java b/libcore/archive/src/main/java/java/util/zip/CheckedInputStream.java index 659125ac2..6513e66f7 100644 --- a/libcore/archive/src/main/java/java/util/zip/CheckedInputStream.java +++ b/libcore/archive/src/main/java/java/util/zip/CheckedInputStream.java @@ -17,7 +17,6 @@ package java.util.zip; - import java.io.IOException; import java.io.InputStream; @@ -26,8 +25,6 @@ import java.io.InputStream; * same time as the data, on which the checksum is computed, is read from a * stream. The purpose of this checksum is to establish data integrity, * comparing the computed checksum against a published checksum value. - * - * @since Android 1.0 */ public class CheckedInputStream extends java.io.FilterInputStream { @@ -42,7 +39,6 @@ public class CheckedInputStream extends java.io.FilterInputStream { * the input stream to calculate checksum from. * @param csum * an entity implementing the checksum algorithm. - * @since Android 1.0 */ public CheckedInputStream(InputStream is, Checksum csum) { super(is); @@ -57,7 +53,6 @@ public class CheckedInputStream extends java.io.FilterInputStream { * otherwise. * @throws IOException * if an {@code IOException} occurs. - * @since Android 1.0 */ @Override public int read() throws IOException { @@ -84,7 +79,6 @@ public class CheckedInputStream extends java.io.FilterInputStream { * end of the filtered stream while reading the data. * @throws IOException * if this stream is closed or some I/O error occurs. - * @since Android 1.0 */ @Override public int read(byte[] buf, int off, int nbytes) throws IOException { @@ -99,7 +93,6 @@ public class CheckedInputStream extends java.io.FilterInputStream { * Returns the checksum calculated on the stream read so far. * * @return the updated checksum. - * @since Android 1.0 */ public Checksum getChecksum() { return check; @@ -114,7 +107,6 @@ public class CheckedInputStream extends java.io.FilterInputStream { * @throws IOException * if this stream is closed or another I/O error occurs. * @return the number of bytes skipped. - * @since Android 1.0 */ @Override public long skip(long nbytes) throws IOException { diff --git a/libcore/archive/src/main/java/java/util/zip/CheckedOutputStream.java b/libcore/archive/src/main/java/java/util/zip/CheckedOutputStream.java index 969949280..08fe799fd 100644 --- a/libcore/archive/src/main/java/java/util/zip/CheckedOutputStream.java +++ b/libcore/archive/src/main/java/java/util/zip/CheckedOutputStream.java @@ -17,7 +17,6 @@ package java.util.zip; - import java.io.IOException; import java.io.OutputStream; @@ -26,8 +25,6 @@ import java.io.OutputStream; * of all data written to a stream. The purpose of this checksum is to establish * data integrity, by publishing the checksum to other parties wanting to read * the non corrupted data. - * - * @since Android 1.0 */ public class CheckedOutputStream extends java.io.FilterOutputStream { @@ -42,7 +39,6 @@ public class CheckedOutputStream extends java.io.FilterOutputStream { * the output stream to calculate checksum for. * @param cs * an entity implementing the checksum algorithm. - * @since Android 1.0 */ public CheckedOutputStream(OutputStream os, Checksum cs) { super(os); @@ -53,7 +49,6 @@ public class CheckedOutputStream extends java.io.FilterOutputStream { * Returns the checksum calculated on the stream read so far. * * @return the updated checksum. - * @since Android 1.0 */ public Checksum getChecksum() { return check; @@ -67,7 +62,6 @@ public class CheckedOutputStream extends java.io.FilterOutputStream { * the data value to written to the output stream. * @throws IOException * if an IO error has occurred. - * @since Android 1.0 */ @Override public void write(int val) throws IOException { @@ -88,7 +82,6 @@ public class CheckedOutputStream extends java.io.FilterOutputStream { * number of bytes to write to the output stream. * @throws IOException * if an IO error has occurred. - * @since Android 1.0 */ @Override public void write(byte[] buf, int off, int nbytes) throws IOException { diff --git a/libcore/archive/src/main/java/java/util/zip/Checksum.java b/libcore/archive/src/main/java/java/util/zip/Checksum.java index 0405c0831..901ff7a8d 100644 --- a/libcore/archive/src/main/java/java/util/zip/Checksum.java +++ b/libcore/archive/src/main/java/java/util/zip/Checksum.java @@ -20,8 +20,6 @@ package java.util.zip; /** * Holds information about a checksum which was computed with the methods * implementing a checksum algorithm. - * - * @since Android 1.0 */ public interface Checksum { @@ -29,37 +27,32 @@ public interface Checksum { * Returns the current calculated checksum value. * * @return the checksum. - * @since Android 1.0 */ public long getValue(); /** * Resets the checksum value applied before beginning calculations on a new * stream of data. - * - * @since Android 1.0 */ public void reset(); - /** - * Updates the checksum value with the given byte. - * - * @param val - * the byte to update the checksum with. - * @since Android 1.0 - */ - public void update(int val); - /** * Updates the checksum with the given bytes. - * + * * @param buf * the byte array from which to read the bytes. * @param off * the initial position in {@code buf} to read the bytes from. * @param nbytes * the number of bytes to read from {@code buf}. - * @since Android 1.0 */ public void update(byte[] buf, int off, int nbytes); + + /** + * Updates the checksum value with the given byte. + * + * @param val + * the byte to update the checksum with. + */ + public void update(int val); } diff --git a/libcore/archive/src/main/java/java/util/zip/DataFormatException.java b/libcore/archive/src/main/java/java/util/zip/DataFormatException.java index 9ffe2ab2c..1e9c5a28a 100644 --- a/libcore/archive/src/main/java/java/util/zip/DataFormatException.java +++ b/libcore/archive/src/main/java/java/util/zip/DataFormatException.java @@ -20,8 +20,6 @@ package java.util.zip; /** * {@code DataFormatException} is used to indicate an error in the format of a * particular data stream which is to be uncompressed. - * - * @since Android 1.0 */ public class DataFormatException extends Exception { @@ -29,8 +27,6 @@ public class DataFormatException extends Exception { /** * Constructs a new {@code DataFormatException} instance. - * - * @since Android 1.0 */ public DataFormatException() { super(); @@ -39,10 +35,9 @@ public class DataFormatException extends Exception { /** * Constructs a new {@code DataFormatException} instance with the specified * message. - * + * * @param detailMessage * the detail message for the exception. - * @since Android 1.0 */ public DataFormatException(String detailMessage) { super(detailMessage); diff --git a/libcore/archive/src/main/java/java/util/zip/Deflater.java b/libcore/archive/src/main/java/java/util/zip/Deflater.java index f91f1ca5a..38771a87c 100644 --- a/libcore/archive/src/main/java/java/util/zip/Deflater.java +++ b/libcore/archive/src/main/java/java/util/zip/Deflater.java @@ -17,6 +17,10 @@ package java.util.zip; +// BEGIN android-changed +// import org.apache.harmony.luni.platform.OSResourcesMonitor; +// END android-changed + /** * This class compresses data using the DEFLATE algorithm (see specification). @@ -24,83 +28,65 @@ package java.util.zip; * Basically this class is part of the API to the stream based ZLIB compression * library and is used as such by {@code DeflaterOutputStream} and its * descendants. - *

      *

      * The typical usage of a {@code Deflater} instance outside this package * consists of a specific call to one of its constructors before being passed to * an instance of {@code DeflaterOutputStream}. - *

      - * + * * @see DeflaterOutputStream * @see Inflater - * @since Android 1.0 */ public class Deflater { /** * Upper bound for the compression level range. - * - * @since Android 1.0 */ public static final int BEST_COMPRESSION = 9; /** * Lower bound for compression level range. - * - * @since Android 1.0 */ public static final int BEST_SPEED = 1; - + /** * Usage of the default compression level. - * - * @since Android 1.0 */ public static final int DEFAULT_COMPRESSION = -1; - + /** * Default value for compression strategy. - * - * @since Android 1.0 */ public static final int DEFAULT_STRATEGY = 0; - + /** * Default value for compression method. - * - * @since Android 1.0 */ public static final int DEFLATED = 8; - + /** * Possible value for compression strategy. - * - * @since Android 1.0 */ public static final int FILTERED = 1; - + /** * Possible value for compression strategy. - * - * @since Android 1.0 */ public static final int HUFFMAN_ONLY = 2; - + /** * Possible value for compression level. - * - * @since Android 1.0 */ public static final int NO_COMPRESSION = 0; private static final int Z_NO_FLUSH = 0; private static final int Z_FINISH = 4; - + // Fill in the JNI id caches private static native void oneTimeInitialization(); - - // A stub buffer used when deflate() called while inputBuffer has not been set. + + // A stub buffer used when deflate() called while inputBuffer has not been + // set. private static final byte[] STUB_INPUT_BUFFER = new byte[0]; static { @@ -120,21 +106,19 @@ public class Deflater { private byte[] inputBuffer; private int inRead; - + private int inLength; - + /** * Constructs a new {@code Deflater} instance with default compression * level. The strategy can be specified with {@link #setStrategy}, only. A * header is added to the output by default; use constructor {@code * Deflater(level, boolean)} if you need to omit the header. - * - * @since Android 1.0 */ public Deflater() { this(DEFAULT_COMPRESSION, false); } - + /** * Constructs a new {@code Deflater} instance with a specific compression * level. The strategy can be specified with {@code setStrategy}, only. A @@ -143,7 +127,6 @@ public class Deflater { * * @param level * the compression level in the range between 0 and 9. - * @since Android 1.0 */ public Deflater(int level) { this(level, false); @@ -159,7 +142,6 @@ public class Deflater { * the compression level in the range between 0 and 9. * @param noHeader * {@code true} indicates that no ZLIB header should be written. - * @since Android 1.0 */ public Deflater(int level, boolean noHeader) { super(); @@ -167,7 +149,8 @@ public class Deflater { throw new IllegalArgumentException(); } compressLevel = level; - streamHandle = createStream(compressLevel, strategy, noHeader); + streamHandle = createStreamWithMemoryEnsurance(compressLevel, strategy, + noHeader); } /** @@ -178,7 +161,6 @@ public class Deflater { * buffer to write compressed data to. * @return number of bytes of compressed data written to {@code buf}. * @see #deflate(byte[], int, int) - * @since Android 1.0 */ public int deflate(byte[] buf) { return deflate(buf, 0, buf.length); @@ -195,7 +177,6 @@ public class Deflater { * @param nbytes * maximum number of bytes of compressed data to be written. * @return the number of bytes of compressed data written to {@code buf}. - * @since Android 1.0 */ public synchronized int deflate(byte[] buf, int off, int nbytes) { if (streamHandle == -1) { @@ -224,8 +205,6 @@ public class Deflater { * finalize()}, it can be called explicitly in order to free native * resources before the next GC cycle. After {@code end()} was called other * methods will typically throw an {@code IllegalStateException}. - * - * @since Android 1.0 */ public synchronized void end() { if (streamHandle != -1) { @@ -245,7 +224,6 @@ public class Deflater { * to it. * * @see #finished - * @since Android 1.0 */ public synchronized void finish() { flushParm = Z_FINISH; @@ -256,7 +234,6 @@ public class Deflater { * compressed. * * @return true if all data has been compressed, false otherwise. - * @since Android 1.0 */ public synchronized boolean finished() { return finished; @@ -271,7 +248,6 @@ public class Deflater { * used. * @see #setDictionary(byte[]) * @see #setDictionary(byte[], int, int) - * @since Android 1.0 */ public synchronized int getAdler() { if (streamHandle == -1) { @@ -287,14 +263,13 @@ public class Deflater { * Returns the total number of bytes of input consumed by the {@code Deflater}. * * @return number of bytes of input read. - * @since Android 1.0 */ public synchronized int getTotalIn() { if (streamHandle == -1) { throw new IllegalStateException(); } - return (int)getTotalInImpl(streamHandle); + return (int) getTotalInImpl(streamHandle); } private synchronized native long getTotalInImpl(long handle); @@ -303,14 +278,13 @@ public class Deflater { * Returns the total number of compressed bytes output by this {@code Deflater}. * * @return number of compressed bytes output. - * @since Android 1.0 */ public synchronized int getTotalOut() { if (streamHandle == -1) { throw new IllegalStateException(); } - return (int)getTotalOutImpl(streamHandle); + return (int) getTotalOutImpl(streamHandle); } private synchronized native long getTotalOutImpl(long handle); @@ -327,7 +301,6 @@ public class Deflater { * @see #finished() * @see #setInput(byte[]) * @see #setInput(byte[], int, int) - * @since Android 1.0 */ public synchronized boolean needsInput() { if (inputBuffer == null) { @@ -343,7 +316,6 @@ public class Deflater { * {@code true} if the {@code Deflater} is to be reused. * * @see #finished - * @since Android 1.0 */ public synchronized void reset() { if (streamHandle == -1) { @@ -367,7 +339,6 @@ public class Deflater { * @param buf * the buffer containing the dictionary data bytes. * @see Deflater#Deflater(int, boolean) - * @since Android 1.0 */ public void setDictionary(byte[] buf) { setDictionary(buf, 0, buf.length); @@ -378,7 +349,7 @@ public class Deflater { * setDictionary() can only be called if this {@code Deflater} supports the writing * of ZLIB headers. This is the default behaviour but can be overridden * using {@code Deflater(int, boolean)}. - * + * * @param buf * the buffer containing the dictionary data bytes. * @param off @@ -386,7 +357,6 @@ public class Deflater { * @param nbytes * the length of the data. * @see Deflater#Deflater(int, boolean) - * @since Android 1.0 */ public synchronized void setDictionary(byte[] buf, int off, int nbytes) { if (streamHandle == -1) { @@ -410,7 +380,6 @@ public class Deflater { * * @param buf * the buffer. - * @since Android 1.0 */ public void setInput(byte[] buf) { setInput(buf, 0, buf.length); @@ -427,7 +396,6 @@ public class Deflater { * the offset of the data. * @param nbytes * the length of the data. - * @since Android 1.0 */ public synchronized void setInput(byte[] buf, int off, int nbytes) { if (streamHandle == -1) { @@ -463,7 +431,6 @@ public class Deflater { * compression level to use * @exception IllegalArgumentException * If the compression level is invalid. - * @since Android 1.0 */ public synchronized void setLevel(int level) { if (level < DEFAULT_COMPRESSION || level > BEST_COMPRESSION) { @@ -485,7 +452,6 @@ public class Deflater { * @exception IllegalArgumentException * If the strategy specified is not one of FILTERED, * HUFFMAN_ONLY or DEFAULT_STRATEGY. - * @since Android 1.0 */ public synchronized void setStrategy(int strategy) { if (strategy < DEFAULT_STRATEGY || strategy > HUFFMAN_ONLY) { @@ -496,14 +462,14 @@ public class Deflater { } this.strategy = strategy; } - + /** * Returns a long int of total number of bytes read by the {@code Deflater}. This * method performs the same as {@code getTotalIn} except it returns a long value * instead of an integer * + * @see #getTotalIn() * @return total number of bytes read by {@code Deflater}. - * @since Android 1.0 */ public synchronized long getBytesRead() { // Throw NPE here @@ -518,8 +484,8 @@ public class Deflater { * method performs the same as {@code getTotalOut} except it returns a long * value instead of an integer * + * @see #getTotalOut() * @return bytes exactly write by {@code Deflater} - * @since Android 1.0 */ public synchronized long getBytesWritten() { // Throw NPE here @@ -529,5 +495,13 @@ public class Deflater { return getTotalOutImpl(streamHandle); } + private long createStreamWithMemoryEnsurance(int level, int strategy1, + boolean noHeader1) { + // BEGIN android-changed + // OSResourcesMonitor.ensurePhysicalMemoryCapacity(); + // END android-changed + return createStream(level, strategy1, noHeader1); + } + private native long createStream(int level, int strategy1, boolean noHeader1); } diff --git a/libcore/archive/src/main/java/java/util/zip/DeflaterOutputStream.java b/libcore/archive/src/main/java/java/util/zip/DeflaterOutputStream.java index 773e4c488..03769fb7b 100644 --- a/libcore/archive/src/main/java/java/util/zip/DeflaterOutputStream.java +++ b/libcore/archive/src/main/java/java/util/zip/DeflaterOutputStream.java @@ -17,7 +17,6 @@ package java.util.zip; - import java.io.FilterOutputStream; import java.io.IOException; import java.io.OutputStream; @@ -28,24 +27,19 @@ import org.apache.harmony.archive.internal.nls.Messages; * This class provides an implementation of {@code FilterOutputStream} that * compresses data using the DEFLATE algorithm. Basically it wraps the * {@code Deflater} class and takes care of the buffering. - * + * * @see Deflater - * @since Android 1.0 */ public class DeflaterOutputStream extends FilterOutputStream { static final int BUF_SIZE = 512; /** * The buffer for the data to be written to. - * - * @since Android 1.0 */ protected byte[] buf; /** * The deflater used. - * - * @since Android 1.0 */ protected Deflater def; @@ -61,7 +55,6 @@ public class DeflaterOutputStream extends FilterOutputStream { * @param def * is the specific {@code Deflater} that is used to compress * data. - * @since Android 1.0 */ public DeflaterOutputStream(OutputStream os, Deflater def) { this(os, def, BUF_SIZE); @@ -76,7 +69,6 @@ public class DeflaterOutputStream extends FilterOutputStream { * * @param os * is the OutputStream where to write the compressed data to. - * @since Android 1.0 */ public DeflaterOutputStream(OutputStream os) { this(os, new Deflater()); @@ -94,7 +86,6 @@ public class DeflaterOutputStream extends FilterOutputStream { * data. * @param bsize * is the size to be used for the internal buffer. - * @since Android 1.0 */ public DeflaterOutputStream(OutputStream os, Deflater def, int bsize) { super(os); @@ -114,7 +105,6 @@ public class DeflaterOutputStream extends FilterOutputStream { * * @throws IOException * If an error occurs during deflation. - * @since Android 1.0 */ protected void deflate() throws IOException { int x = 0; @@ -132,7 +122,6 @@ public class DeflaterOutputStream extends FilterOutputStream { * @throws IOException * If an error occurs while closing the data compression * process. - * @since Android 1.0 */ @Override public void close() throws IOException { @@ -149,7 +138,6 @@ public class DeflaterOutputStream extends FilterOutputStream { * * @throws IOException * If an error occurs. - * @since Android 1.0 */ public void finish() throws IOException { if (done) { @@ -186,7 +174,6 @@ public class DeflaterOutputStream extends FilterOutputStream { * the number of bytes of data to read from the buffer. * @throws IOException * If an error occurs during writing. - * @since Android 1.0 */ @Override public void write(byte[] buffer, int off, int nbytes) throws IOException { diff --git a/libcore/archive/src/main/java/java/util/zip/GZIPInputStream.java b/libcore/archive/src/main/java/java/util/zip/GZIPInputStream.java index fc70d6283..cc7a019a8 100644 --- a/libcore/archive/src/main/java/java/util/zip/GZIPInputStream.java +++ b/libcore/archive/src/main/java/java/util/zip/GZIPInputStream.java @@ -17,7 +17,6 @@ package java.util.zip; - import java.io.EOFException; import java.io.IOException; import java.io.InputStream; @@ -28,49 +27,40 @@ import org.apache.harmony.archive.internal.nls.Messages; * The {@code GZIPInputStream} class is used to read data stored in the GZIP * format, reading and decompressing GZIP data from the underlying stream into * its buffer. - * - * @since Android 1.0 */ -public class GZIPInputStream extends java.util.zip.InflaterInputStream { +public class GZIPInputStream extends InflaterInputStream { + + private static final int FCOMMENT = 16; + + private static final int FEXTRA = 4; + + private static final int FHCRC = 2; + + private static final int FNAME = 8; + + /** + * The magic header for the GZIP format. + */ + public final static int GZIP_MAGIC = 0x8b1f; /** * The checksum algorithm used when handling uncompressed data. - * - * @since Android 1.0 */ protected CRC32 crc = new CRC32(); /** * Indicates the end of the input stream. - * - * @since Android 1.0 */ protected boolean eos = false; - /** - * The magic header for the GZIP format. - * - * @since Android 1.0 - */ - public final static int GZIP_MAGIC = 0x8b1f; - - private static final int FHCRC = 2; - - private static final int FEXTRA = 4; - - private static final int FNAME = 8; - - private static final int FCOMMENT = 16; - /** * Construct a {@code GZIPInputStream} to read from GZIP data from the * underlying stream. - * + * * @param is * the {@code InputStream} to read data from. * @throws IOException * if an {@code IOException} occurs. - * @since Android 1.0 */ public GZIPInputStream(InputStream is) throws IOException { this(is, BUF_SIZE); @@ -86,7 +76,6 @@ public class GZIPInputStream extends java.util.zip.InflaterInputStream { * the internal read buffer size. * @throws IOException * if an {@code IOException} occurs. - * @since Android 1.0 */ public GZIPInputStream(InputStream is, int size) throws IOException { super(is, new Inflater(true), size); @@ -134,6 +123,15 @@ public class GZIPInputStream extends java.util.zip.InflaterInputStream { } } + /** + * Closes this stream and any underlying streams. + */ + @Override + public void close() throws IOException { + eos = true; + super.close(); + } + private long getLong(byte[] buffer, int off) { long l = 0; l |= (buffer[off] & 0xFF); @@ -147,51 +145,70 @@ public class GZIPInputStream extends java.util.zip.InflaterInputStream { return (buffer[off] & 0xFF) | ((buffer[off + 1] & 0xFF) << 8); } + /** + * Reads and decompresses GZIP data from the underlying stream into the + * given buffer. + * + * @param buffer + * Buffer to receive data + * @param off + * Offset in buffer to store data + * @param nbytes + * Number of bytes to read + */ @Override public int read(byte[] buffer, int off, int nbytes) throws IOException { if (closed) { throw new IOException(Messages.getString("archive.1E")); //$NON-NLS-1$ } - if(eof){ + // BEGIN android-changed + if (eos) { return -1; } // avoid int overflow, check null buffer - if (off <= buffer.length && nbytes >= 0 && off >= 0 - && buffer.length - off >= nbytes) { - int val = super.read(buffer, off, nbytes); - if (val != -1) { - crc.update(buffer, off, val); - } else if (!eos) { - eos = true; - // Get non-compressed bytes read by fill - // BEGIN android-changed - // copied from newer version of harmony - int size = inf.getRemaining(); - final int trailerSize = 8; // crc (4 bytes) + total out (4 - // bytes) - byte[] b = new byte[trailerSize]; - int copySize = (size > trailerSize) ? trailerSize : size; - - System.arraycopy(buf, len - size, b, 0, copySize); - readFully(b, copySize, trailerSize - copySize); - // END android-changed - if (getLong(b, 0) != crc.getValue()) { - throw new IOException(Messages.getString("archive.20")); //$NON-NLS-1$ - } - if ((int) getLong(b, 4) != inf.getTotalOut()) { - throw new IOException(Messages.getString("archive.21")); //$NON-NLS-1$ - } - } - return val; + if (off > buffer.length || nbytes < 0 || off < 0 + || buffer.length - off < nbytes) { + throw new ArrayIndexOutOfBoundsException(); + } + + int bytesRead; + try { + bytesRead = super.read(buffer, off, nbytes); + } finally { + eos = eof; // update eos after every read(), even when it throws + } + + if (bytesRead != -1) { + crc.update(buffer, off, bytesRead); + } + + if (eos) { + verifyCrc(); } - throw new ArrayIndexOutOfBoundsException(); + + return bytesRead; + // END android-changed } - - @Override - public void close() throws IOException { - eos = true; - super.close(); + + // BEGIN android-added + private void verifyCrc() throws IOException { + // Get non-compressed bytes read by fill + int size = inf.getRemaining(); + final int trailerSize = 8; // crc (4 bytes) + total out (4 bytes) + byte[] b = new byte[trailerSize]; + int copySize = (size > trailerSize) ? trailerSize : size; + + System.arraycopy(buf, len - size, b, 0, copySize); + readFully(b, copySize, trailerSize - copySize); + + if (getLong(b, 0) != crc.getValue()) { + throw new IOException(Messages.getString("archive.20")); //$NON-NLS-1$ + } + if ((int) getLong(b, 4) != inf.getTotalOut()) { + throw new IOException(Messages.getString("archive.21")); //$NON-NLS-1$ + } } + // END android-added private void readFully(byte[] buffer, int offset, int length) throws IOException { diff --git a/libcore/archive/src/main/java/java/util/zip/GZIPOutputStream.java b/libcore/archive/src/main/java/java/util/zip/GZIPOutputStream.java index fa41e1997..f146da18c 100644 --- a/libcore/archive/src/main/java/java/util/zip/GZIPOutputStream.java +++ b/libcore/archive/src/main/java/java/util/zip/GZIPOutputStream.java @@ -17,22 +17,17 @@ package java.util.zip; - import java.io.IOException; import java.io.OutputStream; /** * The {@code GZIPOutputStream} class is used to write data to a stream in the * GZIP storage format. - * - * @since Android 1.0 */ public class GZIPOutputStream extends DeflaterOutputStream { /** * The checksum algorithm used when treating uncompressed data. - * - * @since Android 1.0 */ protected CRC32 crc = new CRC32(); @@ -44,7 +39,6 @@ public class GZIPOutputStream extends DeflaterOutputStream { * the {@code OutputStream} to write data to. * @throws IOException * if an {@code IOException} occurs. - * @since Android 1.0 */ public GZIPOutputStream(OutputStream os) throws IOException { this(os, BUF_SIZE); @@ -61,7 +55,6 @@ public class GZIPOutputStream extends DeflaterOutputStream { * the internal buffer size. * @throws IOException * if an {@code IOException} occurs. - * @since Android 1.0 */ public GZIPOutputStream(OutputStream os, int size) throws IOException { super(os, new Deflater(Deflater.DEFAULT_COMPRESSION, true), size); @@ -76,10 +69,9 @@ public class GZIPOutputStream extends DeflaterOutputStream { /** * Indicates to the stream that all data has been written out, and any GZIP * terminal data can now be written. - * + * * @throws IOException * if an {@code IOException} occurs. - * @since Android 1.0 */ @Override public void finish() throws IOException { @@ -88,24 +80,29 @@ public class GZIPOutputStream extends DeflaterOutputStream { writeLong(crc.tbytes); } + /** + * Write up to nbytes of data from the given buffer, starting at offset off, + * to the underlying stream in GZIP format. + */ @Override public void write(byte[] buffer, int off, int nbytes) throws IOException { super.write(buffer, off, nbytes); crc.update(buffer, off, nbytes); } - private int writeShort(int i) throws IOException { - out.write(i & 0xFF); - out.write((i >> 8) & 0xFF); + private long writeLong(long i) throws IOException { + // Write out the long value as an unsigned int + int unsigned = (int) i; + out.write(unsigned & 0xFF); + out.write((unsigned >> 8) & 0xFF); + out.write((unsigned >> 16) & 0xFF); + out.write((unsigned >> 24) & 0xFF); return i; } - private long writeLong(long i) throws IOException { - // Write out the long value as an unsigned int - out.write((int) (i & 0xFF)); - out.write((int) (i >> 8) & 0xFF); - out.write((int) (i >> 16) & 0xFF); - out.write((int) (i >> 24) & 0xFF); + private int writeShort(int i) throws IOException { + out.write(i & 0xFF); + out.write((i >> 8) & 0xFF); return i; } } diff --git a/libcore/archive/src/main/java/java/util/zip/Inflater.java b/libcore/archive/src/main/java/java/util/zip/Inflater.java index 9b93e549f..048d95979 100644 --- a/libcore/archive/src/main/java/java/util/zip/Inflater.java +++ b/libcore/archive/src/main/java/java/util/zip/Inflater.java @@ -30,45 +30,72 @@ import java.io.FileDescriptor; * Basically this class is part of the API to the stream based ZLIB compression * library and is used as such by {@code InflaterInputStream} and its * descendants. - *

      *

      * The typical usage of a {@code Inflater} outside this package consists of a * specific call to one of its constructors before being passed to an instance * of {@code InflaterInputStream}. - *

      - * + * * @see InflaterInputStream * @see Deflater - * @since Android 1.0 */ public class Inflater { + private static final byte MAGIC_NUMBER = 120; + + static { + oneTimeInitialization(); + } + + // Fill in the JNI id caches + private static native void oneTimeInitialization(); + private boolean finished; // Set by the inflateImpl native - private boolean needsDictionary; // Set by the inflateImpl native + // BEGIN android-removed + // private boolean gotFirstHeaderByte; + // END android-removed - private long streamHandle = -1; + int inLength; int inRead; - - int inLength; - // Fill in the JNI id caches - private static native void oneTimeInitialization(); + private boolean needsDictionary; // Set by the inflateImpl native - static { - oneTimeInitialization(); + // BEGIN android-removed + // private boolean pass_magic_number_check = true; + // END android-removed + + private long streamHandle = -1; + + /** + * This constructor creates an inflater that expects a header from the input + * stream. Use {@code Inflater(boolean)} if the input comes without a ZLIB + * header. + */ + public Inflater() { + this(false); } - - private static final byte MAGIC_NUMBER = 120; - private boolean gotFirstByte = false; - private boolean pass_magic_number_check = true; - + + /** + * This constructor allows to create an inflater that expects no header from + * the input stream. + * + * @param noHeader + * {@code true} indicates that no ZLIB header comes with the + * input. + */ + public Inflater(boolean noHeader) { + streamHandle = createStream(noHeader); + // BEGIN android-removed + // gotFirstHeaderByte = noHeader; + // END android-removed + } + + private native long createStream(boolean noHeader1); + /** * Release any resources associated with this {@code Inflater}. Any unused * input/output is discarded. This is also called by the finalize method. - * - * @since Android 1.0 */ public synchronized void end() { if (streamHandle != -1) { @@ -91,10 +118,9 @@ public class Inflater { * stream. If deflated bytes remain and {@code needsInput()} returns {@code * true} this method will return {@code false}. This method should be * called after all deflated input is supplied to the {@code Inflater}. - * + * * @return {@code true} if all input has been inflated, {@code false} * otherwise. - * @since Android 1.0 */ public synchronized boolean finished() { return finished; @@ -103,10 +129,9 @@ public class Inflater { /** * Returns the Adler32 checksum of either all bytes inflated, or the * checksum of the preset dictionary if one has been supplied. - * + * * @return The Adler32 checksum associated with this * {@code Inflater}. - * @since Android 1.0 . */ public synchronized int getAdler() { if (streamHandle == -1) { @@ -117,12 +142,41 @@ public class Inflater { private native synchronized int getAdlerImpl(long handle); + /** + * Returns the total number of bytes read by the {@code Inflater}. This + * method performs the same as {@code getTotalIn()} except that it returns a + * {@code long} value instead of an integer. + * + * @return the total number of bytes read. + */ + public synchronized long getBytesRead() { + // Throw NPE here + if (streamHandle == -1) { + throw new NullPointerException(); + } + return getTotalInImpl(streamHandle); + } + + /** + * Returns a the total number of bytes read by the {@code Inflater}. This + * method performs the same as {@code getTotalOut} except it returns a + * {@code long} value instead of an integer. + * + * @return the total bytes written to the output buffer. + */ + public synchronized long getBytesWritten() { + // Throw NPE here + if (streamHandle == -1) { + throw new NullPointerException(); + } + return getTotalOutImpl(streamHandle); + } + /** * Returns the number of bytes of current input remaining to be read by the * inflater. - * + * * @return the number of bytes of unread input. - * @since Android 1.0 */ public synchronized int getRemaining() { return inLength - inRead; @@ -131,9 +185,8 @@ public class Inflater { /** * Returns total number of bytes of input read by the {@code Inflater}. The * result value is limited by {@code Integer.MAX_VALUE}. - * + * * @return the total number of bytes read. - * @since Android 1.0 */ public synchronized int getTotalIn() { if (streamHandle == -1) { @@ -149,9 +202,8 @@ public class Inflater { /** * Returns total number of bytes written to the output buffer by the {@code * Inflater}. The result value is limited by {@code Integer.MAX_VALUE}. - * + * * @return the total bytes of output data written. - * @since Android 1.0 */ public synchronized int getTotalOut() { if (streamHandle == -1) { @@ -166,14 +218,13 @@ public class Inflater { /** * Inflates bytes from current input and stores them in {@code buf}. - * + * * @param buf * the buffer where decompressed data bytes are written. * @return the number of bytes inflated. * @throws DataFormatException * if the underlying stream is corrupted or was not compressed * using a {@code Deflater}. - * @since Android 1.0 */ public int inflate(byte[] buf) throws DataFormatException { return inflate(buf, 0, buf.length); @@ -182,7 +233,7 @@ public class Inflater { /** * Inflates up to n bytes from the current input and stores them in {@code * buf} starting at {@code off}. - * + * * @param buf * the buffer to write inflated bytes to. * @param off @@ -205,15 +256,17 @@ public class Inflater { if (streamHandle == -1) { throw new IllegalStateException(); } - - if (!pass_magic_number_check) { - throw new DataFormatException(); - } + + // BEGIN android-removed + // if (!pass_magic_number_check) { + // throw new DataFormatException(); + // } + // END android-removed if (needsInput()) { return 0; } - + boolean neededDict = needsDictionary; needsDictionary = false; int result = inflateImpl(buf, off, nbytes, streamHandle); @@ -228,42 +281,16 @@ public class Inflater { private native synchronized int inflateImpl(byte[] buf, int off, int nbytes, long handle); - /** - * This constructor creates an inflater that expects a header from the input - * stream. Use {@code Inflater(boolean)} if the input comes without a ZLIB - * header. - * - * @since Android 1.0 - * @since Android 1.0 - */ - public Inflater() { - this(false); - } - - /** - * This constructor allows to create an inflater that expects no header from - * the input stream. - * - * @param noHeader - * {@code true} indicates that no ZLIB header comes with the - * input. - * @since Android 1.0 - */ - public Inflater(boolean noHeader) { - streamHandle = createStream(noHeader); - } - /** * Indicates whether the input bytes were compressed with a preset * dictionary. This method should be called prior to {@code inflate()} to * determine whether a dictionary is required. If so {@code setDictionary()} * should be called with the appropriate dictionary prior to calling {@code * inflate()}. - * + * * @return {@code true} if a preset dictionary is required for inflation. * @see #setDictionary(byte[]) * @see #setDictionary(byte[], int, int) - * @since Android 1.0 */ public synchronized boolean needsDictionary() { return needsDictionary; @@ -271,11 +298,10 @@ public class Inflater { /** * Indicates that input has to be passed to the inflater. - * + * * @return {@code true} if {@code setInput} has to be called before * inflation can proceed. * @see #setInput(byte[]) - * @since Android 1.0 */ public synchronized boolean needsInput() { return inRead == inLength; @@ -284,8 +310,6 @@ public class Inflater { /** * Resets the {@code Inflater}. Should be called prior to inflating a new * set of data. - * - * @since Android 1.0 */ public synchronized void reset() { if (streamHandle == -1) { @@ -303,11 +327,10 @@ public class Inflater { * Sets the preset dictionary to be used for inflation to {@code buf}. * {@code needsDictionary()} can be called to determine whether the current * input was deflated using a preset dictionary. - * + * * @param buf * The buffer containing the dictionary bytes. * @see #needsDictionary - * @since Android 1.0 */ public synchronized void setDictionary(byte[] buf) { setDictionary(buf, 0, buf.length); @@ -316,7 +339,11 @@ public class Inflater { /** * Like {@code setDictionary(byte[])}, allowing to define a specific region * inside {@code buf} to be used as a dictionary. - * + *

      + * The dictionary should be set if the {@link #inflate(byte[])} returned + * zero bytes inflated and {@link #needsDictionary()} returns + * true. + * * @param buf * the buffer containing the dictionary data bytes. * @param off @@ -324,7 +351,6 @@ public class Inflater { * @param nbytes * the length of the data. * @see #needsDictionary - * @since Android 1.0 */ public synchronized void setDictionary(byte[] buf, int off, int nbytes) { if (streamHandle == -1) { @@ -345,11 +371,10 @@ public class Inflater { /** * Sets the current input to to be decrompressed. This method should only be * called if {@code needsInput()} returns {@code true}. - * + * * @param buf * the input buffer. * @see #needsInput - * @since Android 1.0 */ public synchronized void setInput(byte[] buf) { setInput(buf, 0, buf.length); @@ -360,7 +385,7 @@ public class Inflater { * {@code off} and ending at {@code nbytes - 1} where data is written after * decompression. This method should only be called if {@code needsInput()} * returns {@code true}. - * + * * @param buf * the input buffer. * @param off @@ -368,7 +393,6 @@ public class Inflater { * @param nbytes * the number of bytes to read. * @see #needsInput - * @since Android 1.0 */ public synchronized void setInput(byte[] buf, int off, int nbytes) { if (streamHandle == -1) { @@ -383,22 +407,13 @@ public class Inflater { } else { throw new ArrayIndexOutOfBoundsException(); } - // BEGIN android-note - // Note: pass_magic_number_check is set to false when setInput is - // called for the first time and for a single byte. - // Since setInput is called only by InflaterInputStream.fill - // with an arbitrary byte len this check seems quite useless. - // FIXME: We should find out whether the first byte has to be the magic - // number in all cases and correct the check as well as place it - // in setFileInput accordingly. - // And at a first glance it doesn't look like the first byte has - // to be 120. - // END android-note - if(!gotFirstByte && nbytes>0) - { - pass_magic_number_check = (buf[off] == MAGIC_NUMBER || nbytes > 1); - gotFirstByte = true; - } + + // BEGIN android-removed + // if (!gotFirstHeaderByte && nbytes > 0) { + // pass_magic_number_check = (buf[off] == MAGIC_NUMBER); + // gotFirstHeaderByte = true; + //} + // END android-removed } // BEGIN android-added @@ -407,14 +422,13 @@ public class Inflater { * off} and ending at {@code nbytes - 1}. This method should only be called * if {@code needsInput()} returns {@code true}. * - * @param file + * @param fd * the input file. * @param off * the offset to read from in buffer. * @param nbytes * the number of bytes to read. * @see #needsInput - * @since Android 1.0 */ synchronized int setFileInput(FileDescriptor fd, long off, int nbytes) { if (streamHandle == -1) { @@ -426,38 +440,6 @@ public class Inflater { } // END android-added - /** - * Returns the total number of bytes read by the {@code Inflater}. This - * method performs the same as {@code getTotalIn()} except that it returns a - * {@code long} value instead of an integer. - * - * @return the total number of bytes read. - * @since Android 1.0 - */ - public synchronized long getBytesRead() { - // Throw NPE here - if (streamHandle == -1) { - throw new NullPointerException(); - } - return getTotalInImpl(streamHandle); - } - - /** - * Returns a the total number of bytes read by the {@code Inflater}. This - * method performs the same as {@code getTotalOut} except it returns a - * {@code long} value instead of an integer. - * - * @return the total bytes written to the output buffer. - * @since Android 1.0 - */ - public synchronized long getBytesWritten() { - // Throw NPE here - if (streamHandle == -1) { - throw new NullPointerException(); - } - return getTotalOutImpl(streamHandle); - } - private native synchronized void setInputImpl(byte[] buf, int off, int nbytes, long handle); @@ -465,6 +447,4 @@ public class Inflater { private native synchronized int setFileInputImpl(FileDescriptor fd, long off, int nbytes, long handle); // END android-added - - private native long createStream(boolean noHeader1); } diff --git a/libcore/archive/src/main/java/java/util/zip/InflaterInputStream.java b/libcore/archive/src/main/java/java/util/zip/InflaterInputStream.java index 5d3bda037..8a7c86b48 100644 --- a/libcore/archive/src/main/java/java/util/zip/InflaterInputStream.java +++ b/libcore/archive/src/main/java/java/util/zip/InflaterInputStream.java @@ -17,7 +17,6 @@ package java.util.zip; - import java.io.EOFException; import java.io.FilterInputStream; import java.io.IOException; @@ -31,36 +30,34 @@ import org.apache.harmony.archive.internal.nls.Messages; * (see specification). * Basically it wraps the {@code Inflater} class and takes care of the * buffering. - * + * * @see Inflater * @see DeflaterOutputStream - * @since Android 1.0 */ public class InflaterInputStream extends FilterInputStream { /** * The inflater used for this stream. - * - * @since Android 1.0 */ protected Inflater inf; /** * The input buffer used for decompression. - * - * @since Android 1.0 */ protected byte[] buf; /** * The length of the buffer. - * - * @since Android 1.0 */ protected int len; boolean closed; + /** + * True if this stream's last byte has been returned to the user. This + * could be because the underlying stream has been exhausted, or if errors + * were encountered while inflating that stream. + */ boolean eof; static final int BUF_SIZE = 512; @@ -74,10 +71,9 @@ public class InflaterInputStream extends FilterInputStream { * InputStream} from which the compressed data is to be read from. Default * settings for the {@code Inflater} and internal buffer are be used. In * particular the Inflater expects a ZLIB header from the input stream. - * + * * @param is * the {@code InputStream} to read data from. - * @since Android 1.0 */ public InflaterInputStream(InputStream is) { this(is, new Inflater(), BUF_SIZE); @@ -86,12 +82,11 @@ public class InflaterInputStream extends FilterInputStream { /** * This constructor lets you pass a specifically initialized Inflater, * for example one that expects no ZLIB header. - * + * * @param is * the {@code InputStream} to read data from. * @param inf * the specific {@code Inflater} for uncompressing data. - * @since Android 1.0 */ public InflaterInputStream(InputStream is, Inflater inf) { this(is, inf, BUF_SIZE); @@ -100,14 +95,13 @@ public class InflaterInputStream extends FilterInputStream { /** * This constructor lets you specify both the {@code Inflater} as well as * the internal buffer size to be used. - * + * * @param is * the {@code InputStream} to read data from. * @param inf * the specific {@code Inflater} for uncompressing data. * @param bsize * the size to be used for the internal buffer. - * @since Android 1.0 */ public InflaterInputStream(InputStream is, Inflater inf, int bsize) { super(is); @@ -129,11 +123,10 @@ public class InflaterInputStream extends FilterInputStream { /** * Reads a single byte of decompressed data. - * + * * @return the byte read. * @throws IOException * if an error occurs reading the byte. - * @since Android 1.0 */ @Override public int read() throws IOException { @@ -147,7 +140,7 @@ public class InflaterInputStream extends FilterInputStream { /** * Reads up to {@code nbytes} of decompressed data and stores it in * {@code buffer} starting at {@code off}. - * + * * @param buffer * the buffer to write data to. * @param off @@ -157,7 +150,6 @@ public class InflaterInputStream extends FilterInputStream { * @return Number of uncompressed bytes read * @throws IOException * if an IOException occurs. - * @since Android 1.0 */ @Override public int read(byte[] buffer, int off, int nbytes) throws IOException { @@ -178,50 +170,54 @@ public class InflaterInputStream extends FilterInputStream { return 0; } - if (inf.finished()) { - eof = true; + // BEGIN android-changed + if (eof) { return -1; } // avoid int overflow, check null buffer - if (off <= buffer.length && nbytes >= 0 && off >= 0 - && buffer.length - off >= nbytes) { - do { - if (inf.needsInput()) { - fill(); - } - int result; - try { - result = inf.inflate(buffer, off, nbytes); - } catch (DataFormatException e) { - if (len == -1) { - throw new EOFException(); - } - throw (IOException)(new IOException().initCause(e)); - } + if (off > buffer.length || nbytes < 0 || off < 0 + || buffer.length - off < nbytes) { + throw new ArrayIndexOutOfBoundsException(); + } + + do { + if (inf.needsInput()) { + fill(); + } + // Invariant: if reading returns -1 or throws, eof must be true. + // It may also be true if the next read() should return -1. + try { + int result = inf.inflate(buffer, off, nbytes); + eof = inf.finished(); if (result > 0) { return result; - } else if (inf.finished()) { - eof = true; + } else if (eof) { return -1; } else if (inf.needsDictionary()) { + eof = true; return -1; } else if (len == -1) { + eof = true; throw new EOFException(); - // If result == 0, fill() and try again + // If result == 0, fill() and try again } - } while (true); - } - throw new ArrayIndexOutOfBoundsException(); + } catch (DataFormatException e) { + eof = true; + if (len == -1) { + throw new EOFException(); + } + throw (IOException) (new IOException().initCause(e)); + } + } while (true); + // END android-changed } - /** * Fills the input buffer with data to be decompressed. - * + * * @throws IOException * if an {@code IOException} occurs. - * @since Android 1.0 */ protected void fill() throws IOException { if (closed) { @@ -246,24 +242,30 @@ public class InflaterInputStream extends FilterInputStream { /** * Skips up to n bytes of uncompressed data. - * + * * @param nbytes * the number of bytes to skip. * @return the number of uncompressed bytes skipped. * @throws IOException * if an error occurs skipping. - * @since Android 1.0 */ @Override public long skip(long nbytes) throws IOException { if (nbytes >= 0) { + // BEGIN android-changed + if (buf == null) { + buf = new byte[BUF_SIZE]; + } + // END android-changed long count = 0, rem = 0; while (count < nbytes) { int x = read(buf, 0, (rem = nbytes - count) > buf.length ? buf.length : (int) rem); if (x == -1) { - eof = true; + // BEGIN android-removed + // eof = true; + // END android-removed return count; } count += x; @@ -274,12 +276,20 @@ public class InflaterInputStream extends FilterInputStream { } /** - * Returns whether data can be read from this stream. - * - * @return 0 if this stream has been closed, 1 otherwise. + * Returns 0 when when this stream has exhausted its input; and 1 otherwise. + * A result of 1 does not guarantee that further bytes can be returned, + * with or without blocking. + * + *

      Although consistent with the RI, this behavior is inconsistent with + * {@link InputStream#available()}, and violates the Liskov + * Substitution Principle. This method should not be used. + * + * @return 0 if no further bytes are available. Otherwise returns 1, + * which suggests (but does not guarantee) that additional bytes are + * available. * @throws IOException * If an error occurs. - * @since Android 1.0 */ @Override public int available() throws IOException { @@ -295,10 +305,9 @@ public class InflaterInputStream extends FilterInputStream { /** * Closes the input stream. - * + * * @throws IOException * If an error occurs closing the input stream. - * @since Android 1.0 */ @Override public void close() throws IOException { @@ -309,17 +318,15 @@ public class InflaterInputStream extends FilterInputStream { super.close(); } } - + /** - * This implementation overrides the super type implementation to do nothing - * at all. - * + * Marks the current position in the stream. This implementation overrides + * the super type implementation to do nothing at all. + * * @param readlimit * of no use. - * @since Android 1.0 */ @Override - @SuppressWarnings("unused") public void mark(int readlimit) { // do nothing } @@ -328,22 +335,20 @@ public class InflaterInputStream extends FilterInputStream { * Reset the position of the stream to the last marked position. This * implementation overrides the supertype implementation and always throws * an {@link IOException IOException} when called. - * + * * @throws IOException * if the method is called - * @since Android 1.0 */ @Override - public void reset() throws IOException{ + public void reset() throws IOException { throw new IOException(); } - + /** * Returns whether the receiver implements {@code mark} semantics. This type * does not support {@code mark()}, so always responds {@code false}. - * + * * @return false, always - * @since Android 1.0 */ @Override public boolean markSupported() { diff --git a/libcore/archive/src/main/java/java/util/zip/ZipConstants.java b/libcore/archive/src/main/java/java/util/zip/ZipConstants.java index d804b0e8c..d00adc92a 100644 --- a/libcore/archive/src/main/java/java/util/zip/ZipConstants.java +++ b/libcore/archive/src/main/java/java/util/zip/ZipConstants.java @@ -17,7 +17,6 @@ package java.util.zip; - interface ZipConstants { public static final long LOCSIG = 0x4034b50, EXTSIG = 0x8074b50, diff --git a/libcore/archive/src/main/java/java/util/zip/ZipEntry.java b/libcore/archive/src/main/java/java/util/zip/ZipEntry.java index 2cc7a9c13..9774b6ac5 100644 --- a/libcore/archive/src/main/java/java/util/zip/ZipEntry.java +++ b/libcore/archive/src/main/java/java/util/zip/ZipEntry.java @@ -36,10 +36,9 @@ import java.util.GregorianCalendar; * itself. For example when reading a ZIP-file you will first retrieve * all its entries in a collection and then read the data for a specific entry * through an input stream. - * + * * @see ZipFile * @see ZipOutputStream - * @since Android 1.0 */ public class ZipEntry implements ZipConstants, Cloneable { String name, comment; @@ -94,26 +93,21 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Zip entry state: Deflated. - * - * @since Android 1.0 */ public static final int DEFLATED = 8; /** * Zip entry state: Stored. - * - * @since Android 1.0 */ public static final int STORED = 0; /** * Constructs a new {@code ZipEntry} with the specified name. - * + * * @param name * the name of the ZIP entry. * @throws IllegalArgumentException * if the name length is outside the range (> 0xFFFF). - * @since Android 1.0 */ public ZipEntry(String name) { if (name == null) { @@ -147,11 +141,10 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Gets the comment for this {@code ZipEntry}. - * + * * @return the comment for this {@code ZipEntry}, or {@code null} if there * is no comment. If we're reading an archive with * {@code ZipInputStream} the comment is not available. - * @since Android 1.0 */ public String getComment() { return comment; @@ -159,10 +152,9 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Gets the compressed size of this {@code ZipEntry}. - * + * * @return the compressed size, or -1 if the compressed size has not been * set. - * @since Android 1.0 */ public long getCompressedSize() { return compressedSize; @@ -170,9 +162,8 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Gets the checksum for this {@code ZipEntry}. - * + * * @return the checksum, or -1 if the checksum has not been set. - * @since Android 1.0 */ public long getCrc() { return crc; @@ -180,10 +171,9 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Gets the extra information for this {@code ZipEntry}. - * + * * @return a byte array containing the extra information, or {@code null} if * there is none. - * @since Android 1.0 */ public byte[] getExtra() { return extra; @@ -191,10 +181,9 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Gets the compression method for this {@code ZipEntry}. - * + * * @return the compression method, either {@code DEFLATED}, {@code STORED} * or -1 if the compression method has not been set. - * @since Android 1.0 */ public int getMethod() { return compressionMethod; @@ -202,9 +191,8 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Gets the name of this {@code ZipEntry}. - * + * * @return the entry name. - * @since Android 1.0 */ public String getName() { return name; @@ -212,10 +200,9 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Gets the uncompressed size of this {@code ZipEntry}. - * + * * @return the uncompressed size, or {@code -1} if the size has not been * set. - * @since Android 1.0 */ public long getSize() { return size; @@ -223,10 +210,9 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Gets the last modification time of this {@code ZipEntry}. - * + * * @return the last modification time as the number of milliseconds since * Jan. 1, 1970. - * @since Android 1.0 */ public long getTime() { if (time != -1) { @@ -242,10 +228,9 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Determine whether or not this {@code ZipEntry} is a directory. - * + * * @return {@code true} when this {@code ZipEntry} is a directory, {@code * false} otherwise. - * @since Android 1.0 */ public boolean isDirectory() { return name.charAt(name.length() - 1) == '/'; @@ -253,10 +238,9 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Sets the comment for this {@code ZipEntry}. - * + * * @param string * the comment for this entry. - * @since Android 1.0 */ public void setComment(String string) { if (string == null || string.length() <= 0xFFFF) { @@ -268,10 +252,9 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Sets the compressed size for this {@code ZipEntry}. - * + * * @param value * the compressed size (in bytes). - * @since Android 1.0 */ public void setCompressedSize(long value) { compressedSize = value; @@ -279,12 +262,11 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Sets the checksum for this {@code ZipEntry}. - * + * * @param value * the checksum for this entry. * @throws IllegalArgumentException * if {@code value} is < 0 or > 0xFFFFFFFFL. - * @since Android 1.0 */ public void setCrc(long value) { if (value >= 0 && value <= 0xFFFFFFFFL) { @@ -296,12 +278,11 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Sets the extra information for this {@code ZipEntry}. - * + * * @param data * a byte array containing the extra information. * @throws IllegalArgumentException * when the length of data is greater than 0xFFFF bytes. - * @since Android 1.0 */ public void setExtra(byte[] data) { if (data == null || data.length <= 0xFFFF) { @@ -313,13 +294,12 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Sets the compression method for this {@code ZipEntry}. - * + * * @param value * the compression method, either {@code DEFLATED} or {@code * STORED}. * @throws IllegalArgumentException * when value is not {@code DEFLATED} or {@code STORED}. - * @since Android 1.0 */ public void setMethod(int value) { if (value != STORED && value != DEFLATED) { @@ -330,12 +310,11 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Sets the uncompressed size of this {@code ZipEntry}. - * + * * @param value * the uncompressed size for this entry. * @throws IllegalArgumentException * if {@code value} < 0 or {@code value} > 0xFFFFFFFFL. - * @since Android 1.0 */ public void setSize(long value) { if (value >= 0 && value <= 0xFFFFFFFFL) { @@ -347,11 +326,10 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Sets the modification time of this {@code ZipEntry}. - * + * * @param value * the modification time as the number of milliseconds since Jan. * 1, 1970. - * @since Android 1.0 */ public void setTime(long value) { GregorianCalendar cal = new GregorianCalendar(); @@ -372,9 +350,8 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Returns the string representation of this {@code ZipEntry}. - * + * * @return the string representation of this {@code ZipEntry}. - * @since Android 1.0 */ @Override public String toString() { @@ -401,10 +378,9 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Constructs a new {@code ZipEntry} using the values obtained from {@code * ze}. - * + * * @param ze * the {@code ZipEntry} from which to obtain values. - * @since Android 1.0 */ public ZipEntry(ZipEntry ze) { name = ze.name; @@ -434,9 +410,8 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Returns a shallow copy of this entry. - * + * * @return a copy of this entry. - * @since Android 1.0 */ @Override public Object clone() { @@ -445,9 +420,8 @@ public class ZipEntry implements ZipConstants, Cloneable { /** * Returns the hash code for this {@code ZipEntry}. - * + * * @return the hash code of the entry. - * @since Android 1.0 */ @Override public int hashCode() { @@ -471,7 +445,7 @@ public class ZipEntry implements ZipConstants, Cloneable { * readIntLE, so we're going to read the entire header at once * and then parse the results out without using any function calls. * Uglier, but should be much faster. - * + * * Note that some lines look a bit different, because the corresponding * fields or locals are long and so we need to do & 0xffffffffl to avoid * problems induced by sign extension. @@ -549,7 +523,7 @@ public class ZipEntry implements ZipConstants, Cloneable { int count; int len = b.length; int off = 0; - + while (len > 0) { count = in.read(b, off, len); if (count <= 0) diff --git a/libcore/archive/src/main/java/java/util/zip/ZipException.java b/libcore/archive/src/main/java/java/util/zip/ZipException.java index 590117bb7..6dab26f8c 100644 --- a/libcore/archive/src/main/java/java/util/zip/ZipException.java +++ b/libcore/archive/src/main/java/java/util/zip/ZipException.java @@ -17,7 +17,6 @@ package java.util.zip; - import java.io.IOException; /** @@ -26,7 +25,6 @@ import java.io.IOException; * * @see ZipFile * @see ZipInputStream - * @since Android 1.0 */ public class ZipException extends IOException { @@ -34,8 +32,6 @@ public class ZipException extends IOException { /** * Constructs a new {@code ZipException} instance. - * - * @since Android 1.0 */ public ZipException() { super(); @@ -44,10 +40,9 @@ public class ZipException extends IOException { /** * Constructs a new {@code ZipException} instance with the specified * message. - * + * * @param detailMessage * the detail message for the exception. - * @since Android 1.0 */ public ZipException(String detailMessage) { super(detailMessage); diff --git a/libcore/archive/src/main/java/java/util/zip/ZipFile.java b/libcore/archive/src/main/java/java/util/zip/ZipFile.java index f1415d971..653b2c963 100644 --- a/libcore/archive/src/main/java/java/util/zip/ZipFile.java +++ b/libcore/archive/src/main/java/java/util/zip/ZipFile.java @@ -37,17 +37,13 @@ import java.util.NoSuchElementException; * While {@code ZipInputStream} provides stream based read access to a * ZIP-archive, this class implements more efficient (file based) access * and makes use of the central directory within a ZIP-archive. - *

      *

      * Use {@code ZipOutputStream} if you want to create an archive. - *

      *

      * A temporary ZIP file can be marked for automatic deletion upon closing it. - *

      - * + * * @see ZipEntry * @see ZipOutputStream - * @since Android 1.0 */ public class ZipFile implements ZipConstants { @@ -57,28 +53,23 @@ public class ZipFile implements ZipConstants { /** * Open zip file for read. - * - * @since Android 1.0 */ public static final int OPEN_READ = 1; /** * Delete zip file when closed. - * - * @since Android 1.0 */ public static final int OPEN_DELETE = 4; /** * Constructs a new {@code ZipFile} with the specified file. - * + * * @param file * the file to read from. * @throws ZipException * if a ZIP error occurs. * @throws IOException * if an {@code IOException} occurs. - * @since Android 1.0 */ public ZipFile(File file) throws ZipException, IOException { this(file, OPEN_READ); @@ -88,22 +79,31 @@ public class ZipFile implements ZipConstants { * Opens a file as ZIP-archive. "mode" must be {@code OPEN_READ} or * {@code OPEN_DELETE} . The latter sets the "delete on exit" flag through a * file. - * + * * @param file * the ZIP file to read. * @param mode * the mode of the file open operation. * @throws IOException * if an {@code IOException} occurs. - * @since Android 1.0 */ public ZipFile(File file, int mode) throws IOException { - if (mode == (OPEN_READ | OPEN_DELETE)) - fileToDeleteOnClose = file; // file.deleteOnExit(); - else if (mode != OPEN_READ) - throw new IllegalArgumentException("invalid mode"); - fileName = file.getPath(); + if (mode == OPEN_READ || mode == (OPEN_READ | OPEN_DELETE)) { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkRead(fileName); + } + if ((mode & OPEN_DELETE) != 0) { + if (security != null) { + security.checkDelete(fileName); + } + fileToDeleteOnClose = file; // file.deleteOnExit(); + } + } else { + throw new IllegalArgumentException(); + } + mRaf = new RandomAccessFile(fileName, "r"); mEntryList = new ArrayList(); @@ -124,12 +124,11 @@ public class ZipFile implements ZipConstants { /** * Opens a ZIP archived file. - * + * * @param name * the name of the ZIP file. * @throws IOException * if an IOException occurs. - * @since Android 1.0 */ public ZipFile(String name) throws IOException { this(new File(name), OPEN_READ); @@ -142,10 +141,9 @@ public class ZipFile implements ZipConstants { /** * Closes this ZIP file. - * + * * @throws IOException * if an IOException occurs. - * @since Android 1.0 */ public void close() throws IOException { RandomAccessFile raf = mRaf; @@ -171,9 +169,8 @@ public class ZipFile implements ZipConstants { /** * Returns an enumeration of the entries. The entries are listed in the * order in which they appear in the ZIP archive. - * + * * @return the enumeration of the entries. - * @since Android 1.0 */ public Enumeration entries() { return new Enumeration() { @@ -195,12 +192,11 @@ public class ZipFile implements ZipConstants { /** * Gets the ZIP entry with the specified name from this {@code ZipFile}. - * + * * @param entryName * the name of the entry in the ZIP file. * @return a {@code ZipEntry} or {@code null} if the entry name does not * exist in the ZIP file. - * @since Android 1.0 */ public ZipEntry getEntry(String entryName) { if (entryName != null) { @@ -213,13 +209,12 @@ public class ZipFile implements ZipConstants { /** * Returns an input stream on the data of the specified {@code ZipEntry}. - * + * * @param entry * the ZipEntry. * @return an input stream of the data contained in the {@code ZipEntry}. * @throws IOException * if an {@code IOException} occurs. - * @since Android 1.0 */ public InputStream getInputStream(ZipEntry entry) throws IOException { /* @@ -259,9 +254,8 @@ public class ZipFile implements ZipConstants { /** * Gets the file name of this {@code ZipFile}. - * + * * @return the file name of this {@code ZipFile}. - * @since Android 1.0 */ public String getName() { return fileName; @@ -269,9 +263,8 @@ public class ZipFile implements ZipConstants { /** * Returns the number of {@code ZipEntries} in this {@code ZipFile}. - * + * * @return the number of entries in this file. - * @since Android 1.0 */ public int size() { return mEntryList.size(); diff --git a/libcore/archive/src/main/java/java/util/zip/ZipInputStream.java b/libcore/archive/src/main/java/java/util/zip/ZipInputStream.java index 262fa3f88..f86cbe03c 100644 --- a/libcore/archive/src/main/java/java/util/zip/ZipInputStream.java +++ b/libcore/archive/src/main/java/java/util/zip/ZipInputStream.java @@ -17,7 +17,6 @@ package java.util.zip; - import java.io.EOFException; import java.io.IOException; import java.io.InputStream; @@ -36,18 +35,14 @@ import org.apache.harmony.luni.util.Util; * the so called ZIP entries. Therefore when reading from a {@code * ZipInputStream} first the entry's attributes will be retrieved with {@code * getNextEntry} before its data is read. - *

      *

      * While {@code InflaterInputStream} can read a compressed ZIP-archive * entry, this extension can read uncompressed entries as well. - *

      *

      * Use {@code ZipFile} if you can access the archive as a file directly. - *

      - * + * * @see ZipEntry * @see ZipFile - * @since Android 1.0 */ public class ZipInputStream extends InflaterInputStream implements ZipConstants { static final int DEFLATED = 8; @@ -58,7 +53,7 @@ public class ZipInputStream extends InflaterInputStream implements ZipConstants static final int ZIPLocalHeaderVersionNeeded = 20; - // BEGI android-removed + // BEGIN android-removed // private boolean zipClosed = false; // END android-removed @@ -82,10 +77,9 @@ public class ZipInputStream extends InflaterInputStream implements ZipConstants /** * Constructs a new {@code ZipInputStream} from the specified input stream. - * + * * @param stream * the input stream to representing a ZIP archive. - * @since Android 1.0 */ public ZipInputStream(InputStream stream) { super(new PushbackInputStream(stream, BUF_SIZE), new Inflater(true)); @@ -96,10 +90,9 @@ public class ZipInputStream extends InflaterInputStream implements ZipConstants /** * Closes this {@code ZipInputStream}. - * + * * @throws IOException * if an {@code IOException} occurs. - * @since Android 1.0 */ @Override public void close() throws IOException { @@ -113,10 +106,9 @@ public class ZipInputStream extends InflaterInputStream implements ZipConstants /** * Closes the current ZIP entry and positions to read the next entry. - * + * * @throws IOException * if an {@code IOException} occurs. - * @since Android 1.0 */ public void closeEntry() throws IOException { // BEGIN android-changed @@ -133,8 +125,24 @@ public class ZipInputStream extends InflaterInputStream implements ZipConstants return; } } + + /* + * The following code is careful to leave the ZipInputStream in a + * consistent state, even when close() results in an exception. It does + * so by: + * - pushing bytes back into the source stream + * - reading a data descriptor footer from the source stream + * - resetting fields that manage the entry being closed + */ + // Ensure all entry bytes are read - skip(Long.MAX_VALUE); + Exception failure = null; + try { + skip(Long.MAX_VALUE); + } catch (Exception e) { + failure = e; + } + int inB, out; if (currentEntry.compressionMethod == DEFLATED) { inB = inf.getTotalIn(); @@ -143,12 +151,38 @@ public class ZipInputStream extends InflaterInputStream implements ZipConstants inB = inRead; out = inRead; } - int diff = 0; + int diff = entryIn - inB; // Pushback any required bytes - if ((diff = entryIn - inB) != 0) { + if (diff != 0) { ((PushbackInputStream) in).unread(buf, len - diff, diff); } + try { + readAndVerifyDataDescriptor(inB, out); + } catch (Exception e) { + if (failure == null) { // otherwise we're already going to throw + failure = e; + } + } + + inf.reset(); + lastRead = inRead = entryIn = len = 0; + crc.reset(); + currentEntry = null; + + if (failure != null) { + if (failure instanceof IOException) { + throw (IOException) failure; + } else if (failure instanceof RuntimeException) { + throw (RuntimeException) failure; + } + AssertionError error = new AssertionError(); + error.initCause(failure); + throw error; + } + } + + private void readAndVerifyDataDescriptor(int inB, int out) throws IOException { if (hasDD) { in.read(hdrBuf, 0, EXTHDR); if (getLong(hdrBuf, 0) != EXTSIG) { @@ -164,27 +198,19 @@ public class ZipInputStream extends InflaterInputStream implements ZipConstants if (currentEntry.compressedSize != inB || currentEntry.size != out) { throw new ZipException(Messages.getString("archive.21")); //$NON-NLS-1$ } - - inf.reset(); - lastRead = inRead = entryIn = len = 0; - crc.reset(); - currentEntry = null; } /** - * Reads the next entry from this {@code ZipInputStream}. - * + * Reads the next entry from this {@code ZipInputStream} or {@code null} if + * no more entries are present. + * * @return the next {@code ZipEntry} contained in the input stream. * @throws IOException - * if the stream is not positioned at the beginning of an entry - * or if an other {@code IOException} occurs. + * if an {@code IOException} occurs. * @see ZipEntry - * @since Android 1.0 */ public ZipEntry getNextEntry() throws IOException { - if (currentEntry != null) { - closeEntry(); - } + closeEntry(); if (entriesEnd) { return null; } @@ -266,87 +292,89 @@ public class ZipInputStream extends InflaterInputStream implements ZipConstants } currentEntry.setExtra(e); } - // BEGIN android-added - eof = false; - // END android-added return currentEntry; } /* Read 4 bytes from the buffer and store it as an int */ + /** + * Reads up to the specified number of uncompressed bytes into the buffer + * starting at the offset. + * + * @param buffer + * a byte array + * @param start + * the starting offset into the buffer + * @param length + * the number of bytes to read + * @return the number of bytes read + */ @Override public int read(byte[] buffer, int start, int length) throws IOException { // BEGIN android-changed if (closed) { throw new IOException(Messages.getString("archive.1E")); //$NON-NLS-1$ } - // END android-changed if (inf.finished() || currentEntry == null) { return -1; } // avoid int overflow, check null buffer - if (start <= buffer.length && length >= 0 && start >= 0 - && buffer.length - start >= length) { - if (currentEntry.compressionMethod == STORED) { - int csize = (int) currentEntry.size; - if (inRead >= csize) { - // BEGIN android-added + if (start > buffer.length || length < 0 || start < 0 + || buffer.length - start < length) { + throw new ArrayIndexOutOfBoundsException(); + } + + if (currentEntry.compressionMethod == STORED) { + int csize = (int) currentEntry.size; + if (inRead >= csize) { + return -1; + } + if (lastRead >= len) { + lastRead = 0; + if ((len = in.read(buf)) == -1) { eof = true; - // END android-added return -1; } - if (lastRead >= len) { - lastRead = 0; - if ((len = in.read(buf)) == -1) { - // BEGIN android-added - eof = true; - // END android-added - return -1; - } - entryIn += len; - } - // BEGIN android-changed - int toRead = length > (len - lastRead) ? len - lastRead : length; - // END android-changed - if ((csize - inRead) < toRead) { - toRead = csize - inRead; - } - System.arraycopy(buf, lastRead, buffer, start, toRead); - lastRead += toRead; - inRead += toRead; - crc.update(buffer, start, toRead); - return toRead; + entryIn += len; } - if (inf.needsInput()) { - fill(); - if (len > 0) { - entryIn += len; - } + int toRead = length > (len - lastRead) ? len - lastRead : length; + if ((csize - inRead) < toRead) { + toRead = csize - inRead; } - int read = 0; - try { - read = inf.inflate(buffer, start, length); - } catch (DataFormatException e) { - throw new ZipException(e.getMessage()); - } - if (read == 0 && inf.finished()) { - return -1; + System.arraycopy(buf, lastRead, buffer, start, toRead); + lastRead += toRead; + inRead += toRead; + crc.update(buffer, start, toRead); + return toRead; + } + if (inf.needsInput()) { + fill(); + if (len > 0) { + entryIn += len; } - crc.update(buffer, start, read); - return read; } - throw new ArrayIndexOutOfBoundsException(); + int read; + try { + read = inf.inflate(buffer, start, length); + } catch (DataFormatException e) { + throw new ZipException(e.getMessage()); + } + if (read == 0 && inf.finished()) { + return -1; + } + crc.update(buffer, start, read); + return read; + // END android-changed } /** * Skips up to the specified number of bytes in the current ZIP entry. - * + * * @param value * the number of bytes to skip. * @return the number of bytes skipped. * @throws IOException * if an {@code IOException} occurs. - * @since Android 1.0 */ @Override public long skip(long value) throws IOException { @@ -364,15 +392,14 @@ public class ZipInputStream extends InflaterInputStream implements ZipConstants return skipped; } throw new IllegalArgumentException(); -} + } /** * Returns 0 if the {@code EOF} has been reached, otherwise returns 1. - * + * * @return 0 after {@code EOF} of current entry, 1 otherwise. * @throws IOException * if an IOException occurs. - * @since Android 1.0 */ @Override public int available() throws IOException { @@ -380,20 +407,16 @@ public class ZipInputStream extends InflaterInputStream implements ZipConstants if (closed) { throw new IOException(Messages.getString("archive.1E")); //$NON-NLS-1$ } - if (currentEntry == null) { - return 1; - } - return super.available(); + return (currentEntry == null || inRead < currentEntry.size) ? 1 : 0; // END android-changed } /** * creates a {@link ZipEntry } with the given name. - * + * * @param name * the name of the entry. * @return the created {@code ZipEntry}. - * @since Android 1.0 */ protected ZipEntry createZipEntry(String name) { return new ZipEntry(name); diff --git a/libcore/archive/src/main/java/java/util/zip/ZipOutputStream.java b/libcore/archive/src/main/java/java/util/zip/ZipOutputStream.java index 4ddf643ef..58e781f67 100644 --- a/libcore/archive/src/main/java/java/util/zip/ZipOutputStream.java +++ b/libcore/archive/src/main/java/java/util/zip/ZipOutputStream.java @@ -17,7 +17,6 @@ package java.util.zip; - import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; @@ -32,33 +31,26 @@ import org.apache.harmony.archive.internal.nls.Messages; * {@code ZipOutputStream} is used to write {@code ZipEntries} to the underlying * stream. Output from {@code ZipOutputStream} conforms to the {@code ZipFile} * file format. - *

      *

      * While {@code DeflaterOutputStream} can write a compressed ZIP-archive * entry, this extension can write uncompressed entries as well. In this case * special rules apply, for this purpose refer to the file format * specification. - *

      - * + * * @see ZipEntry * @see ZipFile - * @since Android 1.0 */ public class ZipOutputStream extends DeflaterOutputStream implements ZipConstants { /** * Indicates deflated entries. - * - * @since Android 1.0 */ public static final int DEFLATED = 8; /** * Indicates uncompressed entries. - * - * @since Android 1.0 */ public static final int STORED = 0; @@ -90,7 +82,6 @@ public class ZipOutputStream extends DeflaterOutputStream implements * * @param p1 * the {@code OutputStream} to write the data to. - * @since Android 1.0 */ public ZipOutputStream(OutputStream p1) { super(p1, new Deflater(Deflater.DEFAULT_COMPRESSION, true)); @@ -102,7 +93,6 @@ public class ZipOutputStream extends DeflaterOutputStream implements * * @throws IOException * If an error occurs closing the stream. - * @since Android 1.0 */ @Override public void close() throws IOException { @@ -119,7 +109,6 @@ public class ZipOutputStream extends DeflaterOutputStream implements * * @throws IOException * If an error occurs closing the entry. - * @since Android 1.0 */ public void closeEntry() throws IOException { if (cDir == null) { @@ -205,7 +194,6 @@ public class ZipOutputStream extends DeflaterOutputStream implements * * @throws IOException * if an error occurs while terminating the stream. - * @since Android 1.0 */ @Override public void finish() throws IOException { @@ -253,7 +241,6 @@ public class ZipOutputStream extends DeflaterOutputStream implements * @throws IOException * If an error occurs storing the entry. * @see #write - * @since Android 1.0 */ public void putNextEntry(ZipEntry ze) throws java.io.IOException { if (currentEntry != null) { @@ -286,7 +273,8 @@ public class ZipOutputStream extends DeflaterOutputStream implements nameLength = utf8Count(ze.name); if (nameLength > 0xffff) { /* [MSG "archive.2A", "Name too long: {0}"] */ - throw new IllegalArgumentException(Messages.getString("archive.2A", ze.name)); //$NON-NLS-1$ + throw new IllegalArgumentException(Messages.getString( + "archive.2A", ze.name)); //$NON-NLS-1$ } def.setLevel(compressLevel); @@ -338,7 +326,6 @@ public class ZipOutputStream extends DeflaterOutputStream implements * * @param comment * the comment associated with the file. - * @since Android 1.0 */ public void setComment(String comment) { if (comment.length() > 0xFFFF) { @@ -355,7 +342,6 @@ public class ZipOutputStream extends DeflaterOutputStream implements * @param level * the compression level (ranging from -1 to 8). * @see Deflater - * @since Android 1.0 */ public void setLevel(int level) { if (level < Deflater.DEFAULT_COMPRESSION @@ -372,7 +358,6 @@ public class ZipOutputStream extends DeflaterOutputStream implements * * @param method * the compression method to use. - * @since Android 1.0 */ public void setMethod(int method) { if (method != STORED && method != DEFLATED) { @@ -398,11 +383,17 @@ public class ZipOutputStream extends DeflaterOutputStream implements } + /** + * Writes data for the current entry to the underlying stream. + * + * @exception IOException + * If an error occurs writing to the stream + */ @Override public void write(byte[] buffer, int off, int nbytes) throws java.io.IOException { // avoid int overflow, check null buf - if ((off > buffer.length) || (nbytes < 0) || (off < 0) + if ((off < 0 || (nbytes < 0) || off > buffer.length) || (buffer.length - off < nbytes)) { throw new IndexOutOfBoundsException(); } diff --git a/libcore/archive/src/main/java/org/apache/harmony/archive/internal/nls/Messages.java b/libcore/archive/src/main/java/org/apache/harmony/archive/internal/nls/Messages.java index 764f34d2d..3ba50fa5a 100644 --- a/libcore/archive/src/main/java/org/apache/harmony/archive/internal/nls/Messages.java +++ b/libcore/archive/src/main/java/org/apache/harmony/archive/internal/nls/Messages.java @@ -27,7 +27,6 @@ package org.apache.harmony.archive.internal.nls; - import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Locale; @@ -50,7 +49,7 @@ import org.apache.harmony.luni.util.MsgHelp; * is looked up, or resource bundle support is not available, the key itself * will be returned as the associated message. This means that the KEY * should a reasonable human-readable (english) string. - * + * */ public class Messages { @@ -61,7 +60,7 @@ public class Messages { /** * Retrieves a message which has no arguments. - * + * * @param msg * String the key to look up. * @return String the message for that key in the system message bundle. @@ -74,7 +73,7 @@ public class Messages { /** * Retrieves a message which takes 1 argument. - * + * * @param msg * String the key to look up. * @param arg @@ -87,7 +86,7 @@ public class Messages { /** * Retrieves a message which takes 1 integer argument. - * + * * @param msg * String the key to look up. * @param arg @@ -100,7 +99,7 @@ public class Messages { /** * Retrieves a message which takes 1 character argument. - * + * * @param msg * String the key to look up. * @param arg @@ -113,7 +112,7 @@ public class Messages { /** * Retrieves a message which takes 2 arguments. - * + * * @param msg * String the key to look up. * @param arg1 @@ -128,7 +127,7 @@ public class Messages { /** * Retrieves a message which takes several arguments. - * + * * @param msg * String the key to look up. * @param args diff --git a/libcore/archive/src/main/java/org/apache/harmony/archive/internal/nls/messages.properties b/libcore/archive/src/main/java/org/apache/harmony/archive/internal/nls/messages.properties index 1ae5c5eb1..e909af038 100644 --- a/libcore/archive/src/main/java/org/apache/harmony/archive/internal/nls/messages.properties +++ b/libcore/archive/src/main/java/org/apache/harmony/archive/internal/nls/messages.properties @@ -60,8 +60,11 @@ archive.29=Entry already exists: {0} archive.2A=Name too long: {0} archive.2B=String is too long archive.2C=No active entry -archive.2D=Missing version string\: {0} -archive.2E=Line too long -archive.2F=Invalid attribute {0} -archive.30={0} failed verification of {1} -archive.31={0} has invalid digest for {1} in {2} +archive.2D=Missing version attribute\: {0} +archive.2E=Manifest is too long +archive.2F=NUL character in a manifest +archive.30=Invalid attribute {0} +archive.31={0} failed verification of {1} +archive.32={0} has invalid digest for {1} in {2} +archive.33=A length of the encoded header name "{1}" exceeded maximum length {2} +archive.34=A jar verifier does not support more than one entry with the same name diff --git a/libcore/archive/src/main/java/org/apache/harmony/archive/util/Util.java b/libcore/archive/src/main/java/org/apache/harmony/archive/util/Util.java index bed3e9132..b15108a38 100644 --- a/libcore/archive/src/main/java/org/apache/harmony/archive/util/Util.java +++ b/libcore/archive/src/main/java/org/apache/harmony/archive/util/Util.java @@ -30,39 +30,49 @@ public class Util { return false; } - s1 = s1.substring(start1, start1 + length); - s2 = s2.substring(start2, start2 + length); - - return toASCIILowerCase(s1).equals(toASCIILowerCase(s2)); + char c1, c2; + for (int i = 0; i < length; i++) { + if ((c1 = s1.charAt(start1++)) != (c2 = s2.charAt(start2++)) + && toASCIIUpperCase(c1) != toASCIIUpperCase(c2)) { + return false; + } + } + return true; } throw new NullPointerException(); } - public static String toASCIILowerCase(String s) { - int len = s.length(); - StringBuilder buffer = new StringBuilder(len); - for (int i = 0; i < len; i++) { - char c = s.charAt(i); - if ('A' <= c && c <= 'Z') { - buffer.append((char) (c + ('a' - 'A'))); - } else { - buffer.append(c); + public static final boolean equalsIgnoreCase(byte[] buf1, byte[] buf2) { + if (buf1 == buf2) { + return true; + } + + if (buf1 == null || buf2 == null || buf1.length != buf2.length) { + return false; + } + + byte b1, b2; + + for (int i = 0; i < buf1.length; i++) { + if ((b1 = buf1[i]) != (b2 = buf2[i]) + && toASCIIUpperCase(b1) != toASCIIUpperCase(b2)) { + return false; } } - return buffer.toString(); + return true; } - public static String toASCIIUpperCase(String s) { - int len = s.length(); - StringBuilder buffer = new StringBuilder(len); - for (int i = 0; i < len; i++) { - char c = s.charAt(i); - if ('a' <= c && c <= 'z') { - buffer.append((char) (c - ('a' - 'A'))); - } else { - buffer.append(c); - } + static final char toASCIIUpperCase(char c) { + if ('a' <= c && c <= 'z') { + return (char) (c - ('a' - 'A')); + } + return c; + } + + static final byte toASCIIUpperCase(byte b) { + if ('a' <= b && b <= 'z') { + return (byte) (b - ('a' - 'A')); } - return buffer.toString(); + return b; } } diff --git a/libcore/archive/src/main/native/java_util_zip_Adler32.c b/libcore/archive/src/main/native/java_util_zip_Adler32.c index a7a182a49..1b02a11aa 100644 --- a/libcore/archive/src/main/native/java_util_zip_Adler32.c +++ b/libcore/archive/src/main/native/java_util_zip_Adler32.c @@ -15,23 +15,25 @@ * limitations under the License. */ +#include "jni.h" #include "hy2sie.h" - #include "zlib.h" - +#include "sieb.h" JNIEXPORT jlong JNICALL Java_java_util_zip_Adler32_updateImpl (JNIEnv * env, jobject recv, jbyteArray buf, int off, int len, jlong crc) { - PORT_ACCESS_FROM_ENV (env); - jbyte *b; jboolean isCopy; jlong result; b = (*env)->GetPrimitiveArrayCritical (env, buf, &isCopy); + if (b == NULL) { + throwNewOutOfMemoryError(env, ""); + return 0; + } result = (jlong) adler32 ((uLong) crc, (Bytef *) (b + off), (uInt) len); (*env)->ReleasePrimitiveArrayCritical (env, buf, b, JNI_ABORT); @@ -42,9 +44,8 @@ JNIEXPORT jlong JNICALL Java_java_util_zip_Adler32_updateByteImpl (JNIEnv * env, jobject recv, jint val, jlong crc) { - PORT_ACCESS_FROM_ENV (env); - - return adler32 ((uLong) crc, (Bytef *) (&val), 1); + Bytef bytefVal = val; + return adler32 ((uLong) crc, (Bytef *) (&bytefVal), 1); } diff --git a/libcore/archive/src/main/native/java_util_zip_CRC32.c b/libcore/archive/src/main/native/java_util_zip_CRC32.c index 068886888..cee25e5e9 100644 --- a/libcore/archive/src/main/native/java_util_zip_CRC32.c +++ b/libcore/archive/src/main/native/java_util_zip_CRC32.c @@ -16,6 +16,7 @@ */ #include "hy2sie.h" +#include "sieb.h" #include "zlib.h" @@ -28,8 +29,10 @@ Java_java_util_zip_CRC32_updateImpl (JNIEnv * env, jobject recv, jlong result; b = ((*env)->GetPrimitiveArrayCritical (env, buf, 0)); - if (b == NULL) + if (b == NULL) { + throwNewOutOfMemoryError(env, ""); return -1; + } result = crc32 ((uLong) crc, (Bytef *) (b + off), (uInt) len); ((*env)->ReleasePrimitiveArrayCritical (env, buf, b, JNI_ABORT)); return result; diff --git a/libcore/archive/src/main/native/java_util_zip_Deflater.c b/libcore/archive/src/main/native/java_util_zip_Deflater.c index c8bd1990c..2e0e268da 100644 --- a/libcore/archive/src/main/native/java_util_zip_Deflater.c +++ b/libcore/archive/src/main/native/java_util_zip_Deflater.c @@ -18,11 +18,13 @@ #include "hy2sie.h" #include "zlib.h" -#include "zipsup.h" - +#include "zip.h" +#include "jni.h" +#ifndef HY_ZIP_API void zfree PROTOTYPE ((void *opaque, void *address)); void *zalloc PROTOTYPE ((void *opaque, U_32 items, U_32 size)); +#endif static struct { @@ -52,10 +54,10 @@ Java_java_util_zip_Deflater_setDictionaryImpl (JNIEnv * env, jobject recv, if (err != Z_OK) { jclmem_free_memory (env, dBytes); - throwNewIllegalArgumentException (env, ""); + THROW_ZIP_EXCEPTION(env, err, IllegalArgumentException); return; } - stream->dict = dBytes; + stream->dict = (U_8*) dBytes; } JNIEXPORT jlong JNICALL @@ -94,9 +96,8 @@ JNIEXPORT jlong JNICALL Java_java_util_zip_Deflater_createStream (JNIEnv * env, jobject recv, jint level, jint strategy, jboolean noHeader) -{ +{ PORT_ACCESS_FROM_ENV (env); - JCLZipStream *jstream; z_stream *stream; int err = 0; @@ -109,7 +110,12 @@ Java_java_util_zip_Deflater_createStream (JNIEnv * env, jobject recv, // results in 2 x 128K being allocated per Deflater, which is // not acceptable. // END android-changed - +#ifdef HY_ZIP_API + VMI_ACCESS_FROM_ENV (env); + VMIZipFunctionTable *zipFuncs; + zipFuncs = (*VMI)->GetZipFunctions(VMI); +#endif + /*Allocate mem for wrapped struct */ jstream = jclmem_allocate_memory (env, sizeof (JCLZipStream)); if (jstream == NULL) @@ -141,11 +147,10 @@ Java_java_util_zip_Deflater_createStream (JNIEnv * env, jobject recv, mlevel, /*Memory allocation for internal compression state. 9 uses the most. */ // END android-changed strategy); - if (err != Z_OK) - { - throwNewIllegalArgumentException (env, ""); - return -1; - } + if (err != Z_OK) { + THROW_ZIP_EXCEPTION(env, err, IllegalArgumentException); + return -1; + } return (jlong) ((IDATA) jstream); } @@ -170,8 +175,10 @@ Java_java_util_zip_Deflater_setInputImpl (JNIEnv * env, jobject recv, return; } in = ((*env)->GetPrimitiveArrayCritical (env, buf, 0)); - if (in == NULL) + if (in == NULL) { + throwNewOutOfMemoryError(env, ""); return; + } memcpy (stream->inaddr, (in + off), len); ((*env)->ReleasePrimitiveArrayCritical (env, buf, in, JNI_ABORT)); stream->stream->next_in = (Bytef *) stream->inaddr; @@ -185,8 +192,6 @@ Java_java_util_zip_Deflater_deflateImpl (JNIEnv * env, jobject recv, jbyteArray buf, int off, int len, jlong handle, int flushParm) { - PORT_ACCESS_FROM_ENV (env); - jbyte *out; JCLZipStream *stream; jint err = 0; @@ -203,29 +208,34 @@ Java_java_util_zip_Deflater_deflateImpl (JNIEnv * env, jobject recv, sin = stream->stream->total_in; sout = stream->stream->total_out; out = ((*env)->GetPrimitiveArrayCritical (env, buf, 0)); - if (out == NULL) + if (out == NULL) { + throwNewOutOfMemoryError(env, ""); return -1; + } stream->stream->next_out = (Bytef *) out + off; err = deflate (stream->stream, flushParm); ((*env)->ReleasePrimitiveArrayCritical (env, buf, out, 0)); - if (err != Z_OK) - { - if (err == Z_STREAM_END) - { - ((*env)-> - SetBooleanField (env, recv, - gCachedFields.finished, - JNI_TRUE)); - return stream->stream->total_out - sout; - } + if (err != Z_OK) { + if (err == Z_MEM_ERROR) { + throwNewOutOfMemoryError(env, ""); + return 0; } + if (err == Z_STREAM_END) + { + ((*env)-> + SetBooleanField (env, recv, + gCachedFields.finished, + JNI_TRUE)); + return stream->stream->total_out - sout; + } + } if (flushParm != Z_FINISH) { /* Need to update the number of input bytes read. */ ((*env)-> SetIntField (env, recv, - gCachedFields.inRead, - (jint) stream->stream->total_in - sin + inBytes)); + gCachedFields.inRead, + (jint) stream->stream->total_in - sin + inBytes)); } return stream->stream->total_out - sout; } @@ -262,8 +272,6 @@ Java_java_util_zip_Deflater_setLevelsImpl (JNIEnv * env, jobject recv, int level, int strategy, jlong handle) { - PORT_ACCESS_FROM_ENV (env); - JCLZipStream *stream; jbyte b = 0; int err = 0; @@ -276,8 +284,9 @@ Java_java_util_zip_Deflater_setLevelsImpl (JNIEnv * env, jobject recv, stream = (JCLZipStream *) ((IDATA) handle); stream->stream->next_out = (Bytef *) & b; err = deflateParams (stream->stream, level, strategy); - if (err != Z_OK) - throwNewIllegalStateException (env, ""); + if (err != Z_OK) { + THROW_ZIP_EXCEPTION(env, err, IllegalStateException); + } } JNIEXPORT void JNICALL diff --git a/libcore/archive/src/main/native/java_util_zip_Inflater.c b/libcore/archive/src/main/native/java_util_zip_Inflater.c index d3a7d7c39..4b30d4e35 100644 --- a/libcore/archive/src/main/native/java_util_zip_Inflater.c +++ b/libcore/archive/src/main/native/java_util_zip_Inflater.c @@ -16,6 +16,7 @@ */ #include "hy2sie.h" +#include "zip.h" #include "zlib.h" #include @@ -24,6 +25,7 @@ #include +void throwNewDataFormatException (JNIEnv * env, const char *message); void zfree PROTOTYPE ((void *opaque, void *address)); void *zalloc PROTOTYPE ((void *opaque, U_32 items, U_32 size)); @@ -36,27 +38,6 @@ static struct { } gCachedFields; -// Contents from Harmony's inflater.h was put here: -// -typedef struct JCLZipStream -{ - U_8 *inaddr; - int inCap; - U_8 *dict; - z_stream *stream; -} JCLZipStream; - - - -/** - * Throw java.util.zip.DataFormatException - */ -void -throwNewDataFormatException (JNIEnv * env, const char *message) -{ - jniThrowException(env, "java/util/zip/DataFormatException", message); -} - /* Create a new stream . This stream cannot be used until it has been properly initialized. */ JNIEXPORT jlong JNICALL @@ -69,6 +50,11 @@ Java_java_util_zip_Inflater_createStream (JNIEnv * env, jobject recv, z_stream *stream; int err = 0; int wbits = 15; /*Use MAX for fastest */ +#ifdef HY_ZIP_API + VMI_ACCESS_FROM_ENV (env); + VMIZipFunctionTable *zipFuncs; + zipFuncs = (*VMI)->GetZipFunctions(VMI); +#endif /*Allocate mem for wrapped struct */ jstream = jclmem_allocate_memory (env, sizeof (JCLZipStream)); @@ -104,7 +90,7 @@ Java_java_util_zip_Inflater_createStream (JNIEnv * env, jobject recv, { jclmem_free_memory (env, stream); jclmem_free_memory (env, jstream); - throwNewIllegalArgumentException (env, ""); + THROW_ZIP_EXCEPTION(env, err, IllegalArgumentException); return -1; } @@ -134,8 +120,10 @@ Java_java_util_zip_Inflater_setInputImpl (JNIEnv * env, jobject recv, stream->stream->next_in = (Bytef *) baseAddr; stream->stream->avail_in = len; in = ((*env)->GetPrimitiveArrayCritical (env, buf, 0)); - if (in == NULL) + if (in == NULL) { + throwNewOutOfMemoryError(env, ""); return; + } memcpy (baseAddr, (in + off), len); ((*env)->ReleasePrimitiveArrayCritical (env, buf, in, JNI_ABORT)); return; @@ -176,8 +164,6 @@ Java_java_util_zip_Inflater_inflateImpl (JNIEnv * env, jobject recv, jbyteArray buf, int off, int len, jlong handle) { - PORT_ACCESS_FROM_ENV (env); - jbyte *out; JCLZipStream *stream = (JCLZipStream *) ((IDATA) handle); jint err = 0; @@ -192,9 +178,10 @@ Java_java_util_zip_Inflater_inflateImpl (JNIEnv * env, jobject recv, sin = stream->stream->total_in; sout = stream->stream->total_out; out = ((*env)->GetPrimitiveArrayCritical (env, buf, 0)); - - if (out == NULL) + if (out == NULL) { + throwNewOutOfMemoryError(env, ""); return -1; + } stream->stream->next_out = (Bytef *) out + off; err = inflate (stream->stream, Z_SYNC_FLUSH); ((*env)->ReleasePrimitiveArrayCritical (env, buf, out, 0)); @@ -217,7 +204,7 @@ Java_java_util_zip_Inflater_inflateImpl (JNIEnv * env, jobject recv, } else { - throwNewDataFormatException (env, ""); + THROW_ZIP_EXCEPTION(env, err, DataFormatException); return -1; } } @@ -280,7 +267,7 @@ Java_java_util_zip_Inflater_setDictionaryImpl (JNIEnv * env, jobject recv, if (err != Z_OK) { jclmem_free_memory (env, dBytes); - throwNewIllegalArgumentException (env, ""); + THROW_ZIP_EXCEPTION(env, err, IllegalArgumentException); return; } stream->dict = dBytes; @@ -297,11 +284,19 @@ Java_java_util_zip_Inflater_resetImpl (JNIEnv * env, jobject recv, err = inflateReset (stream->stream); if (err != Z_OK) { - throwNewIllegalArgumentException (env, ""); + THROW_ZIP_EXCEPTION(env, err, IllegalArgumentException); return; } } +/** + * Throw java.util.zip.DataFormatException + */ +void +throwNewDataFormatException (JNIEnv * env, const char *message) +{ + jniThrowException(env, "java/util/zip/DataFormatException", message); +} JNIEXPORT jlong JNICALL Java_java_util_zip_Inflater_getTotalOutImpl (JNIEnv * env, jobject recv, @@ -311,6 +306,7 @@ Java_java_util_zip_Inflater_getTotalOutImpl (JNIEnv * env, jobject recv, stream = (JCLZipStream *) ((IDATA) handle); return stream->stream->total_out; + } JNIEXPORT jlong JNICALL diff --git a/libcore/archive/src/main/native/sieb.c b/libcore/archive/src/main/native/sieb.c index ab9430b80..6881cf661 100644 --- a/libcore/archive/src/main/native/sieb.c +++ b/libcore/archive/src/main/native/sieb.c @@ -10,20 +10,6 @@ void throwNewOutOfMemoryError (JNIEnv * env, const char *message) jniThrowException(env, "java/lang/OutOfMemoryError", message); } -// Throw java.lang.IllegalStateException -void throwNewIllegalStateException (JNIEnv * env, const char *message) -{ - jniThrowException(env, "java/lang/IllegalStateException", message); -} - -// Throw java.lang.IllegalArgumentException -void throwNewIllegalArgumentException (JNIEnv * env, const char *message) -{ - jniThrowException(env, "java/lang/IllegalArgumentException", message); -} - - - void * sieb_malloc (JNIEnv * env, size_t byteCnt) { void * adr = malloc(byteCnt); if (adr == 0) { diff --git a/libcore/archive/src/main/native/sieb.h b/libcore/archive/src/main/native/sieb.h index 536c806cf..541ad90a3 100644 --- a/libcore/archive/src/main/native/sieb.h +++ b/libcore/archive/src/main/native/sieb.h @@ -7,9 +7,8 @@ -void throwNewOutOfMemoryError (JNIEnv * env, const char *message); -void throwNewIllegalArgumentException (JNIEnv * env, const char *message); -void throwNewIllegalStateException (JNIEnv * env, const char *message); +void throwNewOutOfMemoryError (JNIEnv * env, + const char *message); void * sieb_malloc (JNIEnv * env, size_t byteCnt); diff --git a/libcore/archive/src/main/native/sub.mk b/libcore/archive/src/main/native/sub.mk index 047c319e7..694c18575 100644 --- a/libcore/archive/src/main/native/sub.mk +++ b/libcore/archive/src/main/native/sub.mk @@ -7,7 +7,8 @@ LOCAL_SRC_FILES := \ java_util_zip_CRC32.c \ java_util_zip_Deflater.c \ java_util_zip_Inflater.c \ - zipalloc.c \ + zip.c \ + zipalloc.c \ sieb.c LOCAL_C_INCLUDES += \ diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/platform/ISystemComponent.java b/libcore/archive/src/main/native/zip.c similarity index 60% rename from libcore/luni/src/main/java/org/apache/harmony/luni/platform/ISystemComponent.java rename to libcore/archive/src/main/native/zip.c index 29cf55556..3d15d2a0b 100644 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/platform/ISystemComponent.java +++ b/libcore/archive/src/main/native/zip.c @@ -1,4 +1,5 @@ -/* Licensed to the Apache Software Foundation (ASF) under one or more +/* + * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 @@ -14,13 +15,23 @@ * limitations under the License. */ -package org.apache.harmony.luni.platform; +#include "zip.h" +#include "jni.h" +/** + * Throw java.lang.IllegalStateException + */ +void +throwNewIllegalStateException (JNIEnv * env, const char *message) +{ + jniThrowException(env, "java/lang/IllegalStateException", message); +} /** - * ISystemComponent - * - */ -public interface ISystemComponent extends IAdaptable { - /*empty*/ + * Throw java.lang.IllegalArgumentException + */ +void +throwNewIllegalArgumentException (JNIEnv * env, const char *message) +{ + jniThrowException(env, "java/lang/IllegalArgumentException", message); } diff --git a/libcore/archive/src/main/native/zip.h b/libcore/archive/src/main/native/zip.h new file mode 100644 index 000000000..145207324 --- /dev/null +++ b/libcore/archive/src/main/native/zip.h @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if !defined(zip_h) +#define zip_h + +#ifndef HY_ZIP_API +#include "zipsup.h" +#else /* HY_ZIP_API */ +#include "vmizip.h" +#endif /* HY_ZIP_API */ + +#include "hymutex.h" + +typedef struct JCLZipFile +{ + struct JCLZipFile *last; + struct JCLZipFile *next; +#ifndef HY_ZIP_API + HyZipFile hyZipFile; +#else + VMIZipFile hyZipFile; +#endif +} JCLZipFile; + +/* Fake JCLZipFile entry. last, next must be in the same position as JCLZipFile */ +typedef struct JCLZipFileLink +{ + JCLZipFile *last; + JCLZipFile *next; + MUTEX mutex; +} JCLZipFileLink; + +// Contents from Harmony's inflater.h was put here: +// +typedef struct JCLZipStream +{ + U_8 *inaddr; + int inCap; + U_8 *dict; + z_stream *stream; +} JCLZipStream; + +#define THROW_ZIP_EXCEPTION(env, err, type) \ + if (err == Z_MEM_ERROR) { \ + throwNewOutOfMemoryError(env, ""); \ + } else { \ + throwNew##type(env, (const char*) zError(err)); \ + } + +void throwNewIllegalStateException PROTOTYPE((JNIEnv* env, + const char* message)); +void throwNewIllegalArgumentException PROTOTYPE((JNIEnv* env, + const char* message)); + +#endif /* zip_h */ diff --git a/libcore/archive/src/main/native/zipsup.c b/libcore/archive/src/main/native/zipsup.c index 1bbe51feb..22ea7e9e1 100644 --- a/libcore/archive/src/main/native/zipsup.c +++ b/libcore/archive/src/main/native/zipsup.c @@ -1438,7 +1438,7 @@ retry: return ZIP_ERR_FILE_CORRUPT; /* should never happen! */ } result = zip_establishCache (portLib, zipFile); - if (result) + if (!result) { /* (silently start operating without a cache if we couldn't make a new one) */ } diff --git a/libcore/archive/src/main/native/zipsup.h b/libcore/archive/src/main/native/zipsup.h index adc086a1f..67a2eda2c 100644 --- a/libcore/archive/src/main/native/zipsup.h +++ b/libcore/archive/src/main/native/zipsup.h @@ -34,23 +34,17 @@ extern "C" #include "zlib.h" -// Contents from Harmony's inflater.h was put here: -// -typedef struct JCLZipStream -{ - U_8 *inaddr; - U_8 *dict; - z_stream *stream; -} JCLZipStream; - - typedef struct HyZipCachePool HyZipCachePool; HyZipCachePool * zipsup_GetZipCachePool(HyPortLibrary * portLib); +#if defined(HY_LOCAL_ZLIB) +#define HY_ZIP_DLL_NAME "z" +#else #define HY_ZIP_DLL_NAME "hyzlib" +#endif #define ZIP_INTERNAL_MAX 80 #define ZIP_CM_Reduced1 2 @@ -156,18 +150,6 @@ zipsup_GetZipCachePool(HyPortLibrary * portLib); -// Contents from Harmony's zip.h were put in java_util_zip_ZipFile.c -// and here: -typedef struct JCLZipFile -{ - struct JCLZipFile *last; - struct JCLZipFile *next; - HyZipFile hyZipFile; -} JCLZipFile; - - - - #include "hymutex.h" extern MUTEX zip_GlobalMutex; diff --git a/libcore/archive/src/test/java-internal/org/apache/harmony/archive/util/UtilTest.java b/libcore/archive/src/test/java-internal/org/apache/harmony/archive/util/UtilTest.java new file mode 100644 index 000000000..9c28dc2e0 --- /dev/null +++ b/libcore/archive/src/test/java-internal/org/apache/harmony/archive/util/UtilTest.java @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.archive.util; + +import junit.framework.TestCase; + +public class UtilTest extends TestCase { + private static final String ASCII_ALPHABET_LC = "abcdefghijklmnopqrstuvwxyz"; + private static final String ASCII_ALPHABET_UC = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + private static final byte[] ASCII_ALPHABET_LC_BYTES; + private static final byte[] ASCII_ALPHABET_UC_BYTES; + + static { + ASCII_ALPHABET_LC_BYTES = new byte[ASCII_ALPHABET_LC.length()]; + for (int i = 0; i < ASCII_ALPHABET_LC_BYTES.length; i++) { + final char c = ASCII_ALPHABET_LC.charAt(i); + final byte b = (byte) c; + assert ((char) b) == c; + ASCII_ALPHABET_LC_BYTES[i] = b; + } + + ASCII_ALPHABET_UC_BYTES = new byte[ASCII_ALPHABET_UC.length()]; + for (int i = 0; i < ASCII_ALPHABET_UC_BYTES.length; i++) { + final char c = ASCII_ALPHABET_UC.charAt(i); + final byte b = (byte) c; + assert ((char) b) == c; + ASCII_ALPHABET_UC_BYTES[i] = b; + } + } + + public void testASCIIIgnoreCaseRegionMatches() { + final String s1 = ASCII_ALPHABET_LC; + final String s2 = ASCII_ALPHABET_UC; + for (int i = 0; i < s1.length(); i++) { + assertTrue(Util.ASCIIIgnoreCaseRegionMatches(s1, i, s2, i, s1 + .length() + - i)); + } + } + + public void testToASCIIUpperCaseByte() { + for (int i = 0; i < ASCII_ALPHABET_LC_BYTES.length; i++) { + assertEquals(ASCII_ALPHABET_UC_BYTES[i], Util + .toASCIIUpperCase(ASCII_ALPHABET_LC_BYTES[i])); + } + for (int i = 0; i < ASCII_ALPHABET_UC_BYTES.length; i++) { + assertEquals(ASCII_ALPHABET_UC_BYTES[i], Util + .toASCIIUpperCase(ASCII_ALPHABET_UC_BYTES[i])); + } + } + + public void testToASCIIUpperCaseChar() { + for (int i = 0; i < ASCII_ALPHABET_LC.length(); i++) { + assertEquals(ASCII_ALPHABET_UC.charAt(i), Util + .toASCIIUpperCase(ASCII_ALPHABET_LC.charAt(i))); + } + for (int i = 0; i < ASCII_ALPHABET_UC.length(); i++) { + assertEquals(ASCII_ALPHABET_UC.charAt(i), Util + .toASCIIUpperCase(ASCII_ALPHABET_UC.charAt(i))); + } + } + + public void testEqualsIgnoreCaseByteArrayByteArray() { + assertTrue(Util.equalsIgnoreCase(ASCII_ALPHABET_LC_BYTES, + ASCII_ALPHABET_LC_BYTES)); + assertTrue(Util.equalsIgnoreCase(ASCII_ALPHABET_LC_BYTES, + ASCII_ALPHABET_UC_BYTES)); + assertTrue(Util.equalsIgnoreCase(ASCII_ALPHABET_UC_BYTES, + ASCII_ALPHABET_UC_BYTES)); + } + +} diff --git a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/AttributesTest.java b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/AttributesTest.java index 0a8b03729..0b3d2cf4e 100644 --- a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/AttributesTest.java +++ b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/AttributesTest.java @@ -421,6 +421,27 @@ public class AttributesTest extends TestCase { assertNull(attribute.get(name)); } + /** + * @tests java.util.jar.Attributes.hashCode() + */ + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "", + method = "hashCode", + args = {} + ) + public void test_hashCode_consistent_with_map() { + MockAttributes mockAttr = new MockAttributes(); + mockAttr.putValue("1", "one"); + assertEquals(mockAttr.getMap().hashCode(), mockAttr.hashCode()); + } + + private static class MockAttributes extends Attributes { + public Map getMap() { + return map; + } + } + @TestTargetNew( level = TestLevel.COMPLETE, notes = "", @@ -470,7 +491,7 @@ public class AttributesTest extends TestCase { } @TestTargetNew( - level = TestLevel.COMPLETE, + level = TestLevel.PARTIAL_COMPLETE, notes = "", method = "hashCode", args = {} diff --git a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarEntryTest.java b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarEntryTest.java index 40eff3bf6..90144be21 100644 --- a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarEntryTest.java +++ b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarEntryTest.java @@ -71,6 +71,29 @@ public class JarEntryTest extends TestCase { } } + /** + * @throws IOException + * @tests java.util.jar.JarEntry#JarEntry(java.util.jar.JarEntry) + */ + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "", + method = "JarEntry", + args = {java.util.jar.JarEntry.class} + ) + public void test_ConstructorLjava_util_jar_JarEntry_on_null() throws IOException { + JarEntry newJarEntry = new JarEntry(jarFile.getJarEntry(entryName)); + assertNotNull(newJarEntry); + + jarEntry = null; + try { + newJarEntry = new JarEntry(jarEntry); + fail("Should throw NullPointerException"); + } catch (NullPointerException e) { + // Expected + } + } + /** * @tests java.util.jar.JarEntry#JarEntry(java.util.zip.ZipEntry) */ @@ -163,10 +186,21 @@ public class JarEntryTest extends TestCase { JarEntry jarEntry2 = jarFile.getJarEntry("Test.class"); InputStream in = jarFile.getInputStream(jarEntry1); byte[] buffer = new byte[1024]; + // BEGIN android-changed + // the certificates are non-null too early and in.available() fails + // while (in.available() > 0) { + // assertNull("getCertificates() should be null until the entry is read", + // jarEntry1.getCertificates()); + // assertNull(jarEntry2.getCertificates()); + // in.read(buffer); + // } while (in.read(buffer) >= 0); in.close(); + // END android-changed + assertEquals("the file is fully read", -1, in.read()); assertNotNull(jarEntry1.getCertificates()); assertNotNull(jarEntry2.getCertificates()); + in.close(); } /** @@ -187,8 +221,14 @@ public class JarEntryTest extends TestCase { InputStream in = jarFile.getInputStream(jarEntry); byte[] buffer = new byte[1024]; while (in.available() > 0) { + // BEGIN android-changed + // the code signers are non-null too early + // assertNull("getCodeSigners() should be null until the entry is read", + // jarEntry.getCodeSigners()); + // END android-changed in.read(buffer); } + assertEquals("the file is fully read", -1, in.read()); CodeSigner[] codeSigners = jarEntry.getCodeSigners(); assertEquals(2, codeSigners.length); List certs_bob = codeSigners[0].getSignerCertPath() @@ -240,7 +280,7 @@ public class JarEntryTest extends TestCase { } @TestTargetNew( - level = TestLevel.COMPLETE, + level = TestLevel.PARTIAL_COMPLETE, notes = "", method = "JarEntry", args = {java.util.jar.JarEntry.class} diff --git a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarFileTest.java b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarFileTest.java index 720f78d3a..d2a51109f 100644 --- a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarFileTest.java +++ b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarFileTest.java @@ -14,16 +14,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.apache.harmony.archive.tests.java.util.jar; import dalvik.annotation.AndroidOnly; -import dalvik.annotation.TestTargetClass; -import dalvik.annotation.TestTargets; import dalvik.annotation.TestLevel; +import dalvik.annotation.TestTargetClass; import dalvik.annotation.TestTargetNew; +import junit.framework.TestCase; + +import tests.support.Support_PlatformFile; +import tests.support.resource.Support_Resources; + import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; @@ -43,10 +46,6 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipException; import java.util.zip.ZipFile; -import junit.framework.TestCase; -import tests.support.Support_PlatformFile; -import tests.support.resource.Support_Resources; - @TestTargetClass(JarFile.class) public class JarFileTest extends TestCase { @@ -73,13 +72,25 @@ public class JarFileTest extends TestCase { private final String jarName3 = "hyts_manifest1.jar"; private final String jarName4 = "hyts_signed.jar"; - + private final String jarName5 = "hyts_signed_inc.jar"; private final String entryName = "foo/bar/A.class"; private final String entryName3 = "coucou/FileAccess.class"; + private final String integrateJar = "Integrate.jar"; + + private final String integrateJarEntry = "Test.class"; + + private final String emptyEntryJar = "EmptyEntries_signed.jar"; + + private final String emptyEntry1 = "subfolder/internalSubset01.js"; + + private final String emptyEntry2 = "svgtest.js"; + + private final String emptyEntry3 = "svgunit.js"; + private File resources; // custom security manager @@ -102,7 +113,7 @@ public class JarFileTest extends TestCase { * @tests java.util.jar.JarFile#JarFile(java.io.File) */ @TestTargetNew( - level = TestLevel.COMPLETE, + level = TestLevel.PARTIAL_COMPLETE, notes = "", method = "JarFile", args = {java.io.File.class} @@ -299,6 +310,27 @@ public class JarFileTest extends TestCase { } } + /** + * Constructs JarFile object. + * + * @tests java.util.jar.JarFile#JarFile(java.io.File) + * @tests java.util.jar.JarFile#JarFile(java.lang.String) + */ + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "", + method = "JarFile", + args = {java.io.File.class} + ) + public void testConstructor_file() throws IOException { + File f = new File(resources, jarName); + Support_Resources.copyFile(resources, null, jarName); + assertTrue(new JarFile(f).getEntry(entryName).getName().equals( + entryName)); + assertTrue(new JarFile(f.getPath()).getEntry(entryName).getName() + .equals(entryName)); + } + /** * @tests java.util.jar.JarFile#entries() */ @@ -316,11 +348,11 @@ public class JarFileTest extends TestCase { Support_Resources.copyFile(resources, null, jarName); JarFile jarFile = new JarFile(new File(resources, jarName)); Enumeration e = jarFile.entries(); - int i = 0; - while (e.hasMoreElements()) { - i++; + int i; + for (i = 0; e.hasMoreElements(); i++) { e.nextElement(); } + assertEquals(jarFile.size(), i); jarFile.close(); assertEquals(6, i); } @@ -336,24 +368,20 @@ public class JarFileTest extends TestCase { JarFile jarFile = new JarFile(new File(resources, jarName)); Enumeration enumeration = jarFile.entries(); jarFile.close(); - boolean pass = false; try { enumeration.hasMoreElements(); + fail("hasMoreElements() did not detect a closed jar file"); } catch (IllegalStateException e) { - pass = true; } - assertTrue("hasMoreElements did not detect closed jar file", pass); Support_Resources.copyFile(resources, null, jarName); jarFile = new JarFile(new File(resources, jarName)); enumeration = jarFile.entries(); jarFile.close(); - pass = false; try { enumeration.nextElement(); + fail("nextElement() did not detect closed jar file"); } catch (IllegalStateException e) { - pass = true; } - assertTrue("nextElement did not detect closed jar file", pass); } /** @@ -361,7 +389,7 @@ public class JarFileTest extends TestCase { * @tests java.util.jar.JarFile#getJarEntry(java.lang.String) */ @TestTargetNew( - level = TestLevel.COMPLETE, + level = TestLevel.PARTIAL_COMPLETE, notes = "", method = "getEntry", args = {java.lang.String.class} @@ -442,6 +470,92 @@ public class JarFileTest extends TestCase { } } + + /** + * @tests java.util.jar.JarFile#getJarEntry(java.lang.String) + */ + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "", + method = "getEntry", + args = {java.lang.String.class} + ) + public void testGetJarEntry() throws Exception { + Support_Resources.copyFile(resources, null, jarName); + JarFile jarFile = new JarFile(new File(resources, jarName)); + assertEquals("Error in returned entry", 311, jarFile.getEntry( + entryName).getSize()); + jarFile.close(); + + // tests for signed jars + // test all signed jars in the /Testres/Internal/SignedJars directory + String jarDirUrl = Support_Resources + .getResourceURL("/../internalres/signedjars"); + Vector signedJars = new Vector(); + try { + InputStream is = new URL(jarDirUrl + "/jarlist.txt").openStream(); + while (is.available() > 0) { + StringBuilder linebuff = new StringBuilder(80); // Typical line + // length + done: while (true) { + int nextByte = is.read(); + switch (nextByte) { + case -1: + break done; + case (byte) '\r': + if (linebuff.length() == 0) { + // ignore + } + break done; + case (byte) '\n': + if (linebuff.length() == 0) { + // ignore + } + break done; + default: + linebuff.append((char) nextByte); + } + } + if (linebuff.length() == 0) { + break; + } + String line = linebuff.toString(); + signedJars.add(line); + } + is.close(); + } catch (IOException e) { + // no list of jars found + } + + for (int i = 0; i < signedJars.size(); i++) { + String jarName = signedJars.get(i); + try { + File file = Support_Resources.getExternalLocalFile(jarDirUrl + + "/" + jarName); + jarFile = new JarFile(file, true); + boolean foundCerts = false; + Enumeration e = jarFile.entries(); + while (e.hasMoreElements()) { + JarEntry entry = e.nextElement(); + InputStream is = jarFile.getInputStream(entry); + is.skip(100000); + is.close(); + Certificate[] certs = entry.getCertificates(); + if (certs != null && certs.length > 0) { + foundCerts = true; + break; + } + } + assertTrue( + "No certificates found during signed jar test for jar \"" + + jarName + "\"", foundCerts); + } catch (IOException e) { + fail("Exception during signed jar test for jar \"" + jarName + + "\": " + e.toString()); + } + } + } + /** * @tests java.util.jar.JarFile#getManifest() */ @@ -539,85 +653,6 @@ public class JarFileTest extends TestCase { } } - /** - * @throws IOException - * @tests java.util.jar.JarFile#getInputStream(java.util.zip.ZipEntry) - */ - @TestTargetNew( - level = TestLevel.COMPLETE, - notes = "", - method = "getInputStream", - args = {java.util.zip.ZipEntry.class} - ) - public void test_getInputStreamLjava_util_jar_JarEntry() throws IOException { - File localFile = null; - try { - Support_Resources.copyFile(resources, null, jarName); - localFile = new File(resources, jarName); - } catch (Exception e) { - fail("Failed to create local file: " + e); - } - - byte[] b = new byte[1024]; - try { - JarFile jf = new JarFile(localFile); - java.io.InputStream is = jf.getInputStream(jf.getEntry(entryName)); - // BEGIN android-removed - // jf.close(); - // END android-removed - assertTrue("Returned invalid stream", is.available() > 0); - int r = is.read(b, 0, 1024); - is.close(); - StringBuffer sb = new StringBuffer(r); - for (int i = 0; i < r; i++) { - sb.append((char) (b[i] & 0xff)); - } - String contents = sb.toString(); - assertTrue("Incorrect stream read", contents.indexOf("bar") > 0); - // BEGIN android-added - jf.close(); - // END android-added - } catch (Exception e) { - fail("Exception during test: " + e.toString()); - } - - try { - JarFile jf = new JarFile(localFile); - InputStream in = jf.getInputStream(new JarEntry("invalid")); - assertNull("Got stream for non-existent entry", in); - } catch (Exception e) { - fail("Exception during test 2: " + e); - } - - try { - Support_Resources.copyFile(resources, null, jarName); - File signedFile = new File(resources, jarName); - JarFile jf = new JarFile(signedFile); - JarEntry jre = new JarEntry("foo/bar/A.class"); - jf.getInputStream(jre); - // InputStream returned in any way, exception can be thrown in case - // of reading from this stream only. - // fail("Should throw ZipException"); - } catch (ZipException ee) { - // expected - } - - try { - Support_Resources.copyFile(resources, null, jarName); - File signedFile = new File(resources, jarName); - JarFile jf = new JarFile(signedFile); - JarEntry jre = new JarEntry("foo/bar/A.class"); - jf.close(); - jf.getInputStream(jre); - // InputStream returned in any way, exception can be thrown in case - // of reading from this stream only. - // The same for IOException - fail("Should throw IllegalStateException"); - } catch (IllegalStateException ee) { - // expected - } - } - /** * @tests java.util.jar.JarFile#getInputStream(java.util.zip.ZipEntry) */ @@ -660,7 +695,7 @@ public class JarFileTest extends TestCase { } catch (Exception e) { fail("Exception during test 4: " + e); } - + try { JarFile jar = new JarFile(signedFile); JarEntry entry = new JarEntry(entryName3); @@ -682,7 +717,7 @@ public class JarFileTest extends TestCase { } catch (Exception e) { fail("Failed to create local file 5: " + e); } - + try { JarFile jar = new JarFile(signedFile); JarEntry entry = new JarEntry(entryName3); @@ -732,7 +767,37 @@ public class JarFileTest extends TestCase { Enumeration entries = jarFile.entries(); while (entries.hasMoreElements()) { ZipEntry zipEntry = entries.nextElement(); - jarFile.getInputStream(zipEntry); + jarFile.getInputStream(zipEntry).skip(Long.MAX_VALUE); + } + } + + /** + * The jar is intact, but the entry object is modified. + */ + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "", + method = "getInputStream", + args = {ZipEntry.class} + ) + public void testJarVerificationModifiedEntry() throws IOException { + Support_Resources.copyFile(resources, null, integrateJar); + File f = new File(resources, integrateJar); + + JarFile jarFile = new JarFile(f); + ZipEntry zipEntry = jarFile.getJarEntry(integrateJarEntry); + zipEntry.setSize(zipEntry.getSize() + 1); + jarFile.getInputStream(zipEntry).skip(Long.MAX_VALUE); + + jarFile = new JarFile(f); + zipEntry = jarFile.getJarEntry(integrateJarEntry); + zipEntry.setSize(zipEntry.getSize() - 1); + try { + //jarFile.getInputStream(zipEntry).skip(Long.MAX_VALUE); + jarFile.getInputStream(zipEntry).read(new byte[5000], 0, 5000); + fail("SecurityException expected"); + } catch (SecurityException e) { + // desired } } @@ -781,7 +846,6 @@ public class JarFileTest extends TestCase { Enumeration entries = jarFile.entries(); int count = 0; while (entries.hasMoreElements()) { - ZipEntry zipEntry = entries.nextElement(); jarFile.getInputStream(zipEntry); count++; @@ -818,7 +882,7 @@ public class JarFileTest extends TestCase { while (in.available() > 0) { in.read(buffer); } - fail("should throw Security Exception"); + fail("SecurityException expected"); } catch (SecurityException e) { // desired } @@ -827,7 +891,7 @@ public class JarFileTest extends TestCase { /* * In the Modified.jar, the main attributes of META-INF/MANIFEST.MF is * tampered manually. Hence the RI 5.0 JarFile.getInputStream of any - * JarEntry will throw security exception, but the apache harmony will not. + * JarEntry will throw security exception. */ @TestTargetNew( level = TestLevel.PARTIAL_COMPLETE, @@ -846,7 +910,7 @@ public class JarFileTest extends TestCase { ZipEntry zipEntry = entries.nextElement(); try { jarFile.getInputStream(zipEntry); - fail("should throw Security Exception"); + fail("SecurityException expected"); } catch (SecurityException e) { // desired } @@ -927,4 +991,112 @@ public class JarFileTest extends TestCase { // Can not check IOException } + + /** + * @throws IOException + * @tests java.util.jar.JarFile#getInputStream(java.util.zip.ZipEntry) + */ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "getInputStream", + args = {java.util.zip.ZipEntry.class} + ) + public void test_getInputStreamLjava_util_jar_JarEntry() throws IOException { + File localFile = null; + try { + Support_Resources.copyFile(resources, null, jarName); + localFile = new File(resources, jarName); + } catch (Exception e) { + fail("Failed to create local file: " + e); + } + + byte[] b = new byte[1024]; + try { + JarFile jf = new JarFile(localFile); + java.io.InputStream is = jf.getInputStream(jf.getEntry(entryName)); + // BEGIN android-removed + // jf.close(); + // END android-removed + assertTrue("Returned invalid stream", is.available() > 0); + int r = is.read(b, 0, 1024); + is.close(); + StringBuffer sb = new StringBuffer(r); + for (int i = 0; i < r; i++) { + sb.append((char) (b[i] & 0xff)); + } + String contents = sb.toString(); + assertTrue("Incorrect stream read", contents.indexOf("bar") > 0); + // BEGIN android-added + jf.close(); + // END android-added + } catch (Exception e) { + fail("Exception during test: " + e.toString()); + } + + try { + JarFile jf = new JarFile(localFile); + InputStream in = jf.getInputStream(new JarEntry("invalid")); + assertNull("Got stream for non-existent entry", in); + } catch (Exception e) { + fail("Exception during test 2: " + e); + } + + try { + Support_Resources.copyFile(resources, null, jarName); + File signedFile = new File(resources, jarName); + JarFile jf = new JarFile(signedFile); + JarEntry jre = new JarEntry("foo/bar/A.class"); + jf.getInputStream(jre); + // InputStream returned in any way, exception can be thrown in case + // of reading from this stream only. + // fail("Should throw ZipException"); + } catch (ZipException ee) { + // expected + } + + try { + Support_Resources.copyFile(resources, null, jarName); + File signedFile = new File(resources, jarName); + JarFile jf = new JarFile(signedFile); + JarEntry jre = new JarEntry("foo/bar/A.class"); + jf.close(); + jf.getInputStream(jre); + // InputStream returned in any way, exception can be thrown in case + // of reading from this stream only. + // The same for IOException + fail("Should throw IllegalStateException"); + } catch (IllegalStateException ee) { + // expected + } + } + + /** + * The jar is intact, but the entry object is modified. + */ + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "Regression test for issue introduced by HAROMNY-4569. " + + "signed archives containing files with size 0 could not get verified", + method = "getInputStream", + args = {ZipEntry.class} + ) + public void testJarVerificationEmptyEntry() throws IOException { + Support_Resources.copyFile(resources, null, emptyEntryJar); + File f = new File(resources, emptyEntryJar); + + JarFile jarFile = new JarFile(f); + + ZipEntry zipEntry = jarFile.getJarEntry(emptyEntry1); + int res = jarFile.getInputStream(zipEntry).read(new byte[100], 0, 100); + assertEquals("Wrong length of empty jar entry", -1, res); + + zipEntry = jarFile.getJarEntry(emptyEntry2); + res = jarFile.getInputStream(zipEntry).read(new byte[100], 0, 100); + assertEquals("Wrong length of empty jar entry", -1, res); + + zipEntry = jarFile.getJarEntry(emptyEntry3); + res = jarFile.getInputStream(zipEntry).read(); + assertEquals("Wrong length of empty jar entry", -1, res); + } } diff --git a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarInputStreamTest.java b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarInputStreamTest.java index 64e0e1aa1..5befa77c7 100644 --- a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarInputStreamTest.java +++ b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarInputStreamTest.java @@ -154,12 +154,12 @@ public class JarInputStreamTest extends junit.framework.TestCase { assertEquals(actual, desired); jis.close(); -// try { -// jis.getNextJarEntry(); //Android implementation does not throw exception -// fail("IOException expected"); -// } catch (IOException ee) { -// // expected -// } + try { + jis.getNextJarEntry(); + fail("IOException expected"); + } catch (IOException ee) { + // expected + } File resources = Support_Resources.createTempFolder(); Support_Resources.copyFile(resources, null, "Broken_entry.jar"); @@ -179,11 +179,9 @@ public class JarInputStreamTest extends junit.framework.TestCase { method = "getNextJarEntry", args = {} ) - @KnownFailure("IOException not thrown when using getNextJarEntry() after close().") public void test_getNextJarEntry_Ex() throws Exception { final Set desired = new HashSet(Arrays - .asList(new String[] { - "foo/", "foo/bar/", "foo/bar/A.class", "Blah.txt"})); + .asList("foo/", "foo/bar/", "foo/bar/A.class", "Blah.txt")); Set actual = new HashSet(); InputStream is = new URL(jarName).openConnection().getInputStream(); JarInputStream jis = new JarInputStream(is); @@ -196,7 +194,7 @@ public class JarInputStreamTest extends junit.framework.TestCase { jis.close(); try { - jis.getNextJarEntry(); //Android implementation does not throw exception + jis.getNextJarEntry(); fail("IOException expected"); } catch (IOException ee) { // expected @@ -276,29 +274,21 @@ public class JarInputStreamTest extends junit.framework.TestCase { ) public void test_JarInputStream_Modified_Manifest_MainAttributes_getNextEntry() throws IOException { - String modJarName = Support_Resources - .getURL("Modified_Manifest_MainAttributes.jar"); - InputStream is = new URL(modJarName).openConnection().getInputStream(); + String modJarName = Support_Resources.getURL("Modified_Manifest_MainAttributes.jar"); + InputStream is = new URL(modJarName).openConnection() + .getInputStream(); JarInputStream jin = new JarInputStream(is, true); - ZipEntry zipEntry = null; - final int indexofDSA = 2; - final int totalEntries = 4; - int count = 0; - while (count == 0 || zipEntry != null) { - count++; - try { - zipEntry = jin.getNextEntry(); - if (count == indexofDSA + 1) { - fail("Should throw Security Exception"); - } - } catch (SecurityException e) { - if (count != indexofDSA + 1) { - throw e; - } - } + assertEquals("META-INF/TESTROOT.SF", jin.getNextEntry().getName()); + assertEquals("META-INF/TESTROOT.DSA", jin.getNextEntry().getName()); + try { + jin.getNextEntry(); + fail(); + } catch (SecurityException expected) { } - assertEquals(totalEntries + 2, count); + assertEquals("META-INF/", jin.getNextEntry().getName()); + assertEquals("Test.class", jin.getNextEntry().getName()); + assertNull(jin.getNextEntry()); jin.close(); } @@ -542,7 +532,6 @@ public class JarInputStreamTest extends junit.framework.TestCase { method = "close", args = {} ) - @KnownFailure("The behaviour is different from RI, but not neccessarily wrong. However a strange exception message is given anyway!") public void test_closeAfterException() throws Exception { File resources = Support_Resources.createTempFolder(); Support_Resources.copyFile(resources, null, "Broken_entry.jar"); @@ -555,7 +544,7 @@ public class JarInputStreamTest extends junit.framework.TestCase { } catch (ZipException ee) { // expected } - jis.close(); // Android throws exception here, but RI throws when getNextEntry/read/skip are called. + jis.close(); try { jis.getNextEntry(); fail("IOException expected"); diff --git a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarOutputStreamTest.java b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarOutputStreamTest.java index e6521374e..acdad71d4 100644 --- a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarOutputStreamTest.java +++ b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarOutputStreamTest.java @@ -48,7 +48,7 @@ public class JarOutputStreamTest extends junit.framework.TestCase { method = "putNextEntry", args = {java.util.zip.ZipEntry.class} ) - public void test_putNextEntryLjava_util_zip_ZipEntry() { + public void test_putNextEntryLjava_util_zip_ZipEntry() throws Exception { // testClass file`s actual extension is .class, since having .class // extension files in source dir causes // problems on eclipse, the extension is changed into .ser or it can be @@ -76,35 +76,30 @@ public class JarOutputStreamTest extends junit.framework.TestCase { File outputJar = null; JarOutputStream jout = null; - try { - // open the output jarfile - outputJar = File.createTempFile("hyts_", ".jar"); - jout = new JarOutputStream(new FileOutputStream(outputJar), - newman); - jout.putNextEntry(new JarEntry(entryName)); - } catch (Exception e) { - fail("Error creating JarOutputStream: " + e); - } + // open the output jarfile + outputJar = File.createTempFile("hyts_", ".jar"); + jout = new JarOutputStream(new FileOutputStream(outputJar), + newman); + jout.putNextEntry(new JarEntry(entryName)); + File resources = Support_Resources.createTempFolder(); - try { - // read in the class file, and output it to the jar - Support_Resources.copyFile(resources, null, testClass); - URL jarURL = new URL((new File(resources, testClass)).toURL() - .toString()); - InputStream jis = jarURL.openStream(); - - byte[] bytes = new byte[1024]; - int len; - while ((len = jis.read(bytes)) != -1) { - jout.write(bytes, 0, len); - } - - jout.flush(); - jout.close(); - jis.close(); - } catch (Exception e) { - fail("Error writing JAR file for testing: " + e); + + // read in the class file, and output it to the jar + Support_Resources.copyFile(resources, null, testClass); + URL jarURL = new URL((new File(resources, testClass)).toURL() + .toString()); + InputStream jis = jarURL.openStream(); + + byte[] bytes = new byte[1024]; + int len; + while ((len = jis.read(bytes)) != -1) { + jout.write(bytes, 0, len); } + + jout.flush(); + jout.close(); + jis.close(); + String res = null; // set up the VM parameters String[] args = new String[2]; diff --git a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/ManifestTest.java b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/ManifestTest.java index 57e4744ff..42b254365 100644 --- a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/ManifestTest.java +++ b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/ManifestTest.java @@ -14,12 +14,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.apache.harmony.archive.tests.java.util.jar; -import dalvik.annotation.TestTargetClass; -import dalvik.annotation.TestTargets; +import dalvik.annotation.KnownFailure; import dalvik.annotation.TestLevel; +import dalvik.annotation.TestTargetClass; import dalvik.annotation.TestTargetNew; import java.io.ByteArrayInputStream; @@ -28,14 +27,15 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.net.MalformedURLException; import java.net.URL; +import java.net.MalformedURLException; import java.util.Map; import java.util.jar.Attributes; import java.util.jar.JarFile; import java.util.jar.Manifest; import junit.framework.TestCase; + import tests.support.resource.Support_Resources; @TestTargetClass(Manifest.class) @@ -49,6 +49,10 @@ public class ManifestTest extends TestCase { private Manifest m2; + private final String ATT_ENTRY_NAME = "HasAttributes.txt"; + + private final String MANIFEST_NAME = "manifest/hyts_MANIFEST.MF"; + private File resources; @Override @@ -68,6 +72,19 @@ public class ManifestTest extends TestCase { } } + private Manifest getManifest(String fileName) { + try { + Support_Resources.copyFile(resources, null, fileName); + JarFile jarFile = new JarFile(new File(resources, fileName)); + Manifest m = jarFile.getManifest(); + jarFile.close(); + return m; + } catch (Exception e) { + fail("Exception during setup: " + e.toString()); + return null; + } + } + /** * @tests java.util.jar.Manifest#Manifest() */ @@ -87,264 +104,136 @@ public class ManifestTest extends TestCase { } /** - * @tests java.util.jar.Manifest#Manifest(java.io.InputStream) - */ - @TestTargetNew( - level = TestLevel.COMPLETE, - notes = "IOException checking missed.", - method = "Manifest", - args = {java.io.InputStream.class} - ) - public void test_ConstructorLjava_io_InputStream() { - // Test for method java.util.jar.Manifest(java.io.InputStream) - /* - * ByteArrayOutputStream baos = new ByteArrayOutputStream(); - * m2.write(baos); InputSteam is = new ByteArrayInputStream - * (baos.toByteArray()); Manifest myManifest = new Manifest (is); - * assertTrue("Manifests should be equal", myManifest.equals(m2)); - */ - - Manifest manifest = null; - InputStream is = null; - try { - is = new URL(Support_Resources.getURL("manifest/hyts_MANIFEST.MF")) - .openStream(); - } catch (MalformedURLException e1) { - fail("Failed to create InputStream object"); - } catch (IOException e1) { - fail("Failed to create InputStream object"); - } - try { - manifest = new Manifest(is); - } catch (MalformedURLException e) { - fail("Malformed URL"); - } catch (IOException e) { - fail("IOException"); - } - Attributes main = manifest.getMainAttributes(); - assertEquals("Bundle-Name not correct", "ClientSupport", main - .getValue("Bundle-Name")); - assertEquals( - "Bundle-Description not correct", - - "Provides SessionService, AuthenticationService. Extends RegistryService.", - main.getValue("Bundle-Description")); - assertEquals("Bundle-Activator not correct", - "com.ibm.ive.eccomm.client.support.ClientSupportActivator", - main.getValue("Bundle-Activator")); - assertEquals( - "Import-Package not correct", - - "com.ibm.ive.eccomm.client.services.log,com.ibm.ive.eccomm.client.services.registry,com.ibm.ive.eccomm.service.registry; specification-version=1.0.0,com.ibm.ive.eccomm.service.session; specification-version=1.0.0,com.ibm.ive.eccomm.service.framework; specification-version=1.2.0,org.osgi.framework; specification-version=1.0.0,org.osgi.service.log; specification-version=1.0.0,com.ibm.ive.eccomm.flash; specification-version=1.2.0,com.ibm.ive.eccomm.client.xml,com.ibm.ive.eccomm.client.http.common,com.ibm.ive.eccomm.client.http.client", - main.getValue("Import-Package")); - assertEquals( - "Import-Service not correct", - - "org.osgi.service.log.LogReaderServiceorg.osgi.service.log.LogService,com.ibm.ive.eccomm.service.registry.RegistryService", - main.getValue("Import-Service")); - assertEquals( - "Export-Package not correct", - - "com.ibm.ive.eccomm.client.services.authentication; specification-version=1.0.0,com.ibm.ive.eccomm.service.authentication; specification-version=1.0.0,com.ibm.ive.eccomm.common; specification-version=1.0.0,com.ibm.ive.eccomm.client.services.registry.store; specification-version=1.0.0", - main.getValue("Export-Package")); - assertEquals( - "Export-Service not correct", - - "com.ibm.ive.eccomm.service.authentication.AuthenticationService,com.ibm.ive.eccomm.service.session.SessionService", - main.getValue("Export-Service")); - assertEquals("Bundle-Vendor not correct", "IBM", main - .getValue("Bundle-Vendor")); - assertEquals("Bundle-Version not correct", "1.2.0", main - .getValue("Bundle-Version")); - try { - is.close(); - } catch (IOException e1) { - fail("Failed to close InputStream object"); - } - try { - manifest = new Manifest(is); - fail("IOException expected"); - } catch (MalformedURLException e) { - fail("IOException expected"); - } catch (IOException e) { - // expected - } - } - - /** - * @tests java.util.jar.Manifest#clear() + * @tests java.util.jar.Manifest#Manifest(java.util.jar.Manifest) */ @TestTargetNew( - level = TestLevel.COMPLETE, - notes = "", - method = "clear", - args = {} + level = TestLevel.PARTIAL_COMPLETE, + notes = "", + method = "Manifest", + args = {java.util.jar.Manifest.class} ) - public void test_clear() { - // Test for method void java.util.jar.Manifest.clear() - m2.clear(); - assertTrue("Should have no entries", m2.getEntries().isEmpty()); - assertTrue("Should have no main attributes", m2.getMainAttributes() - .isEmpty()); + public void testCopyingConstructor() throws IOException { + Manifest firstManifest = new Manifest(new URL(Support_Resources + .getURL(MANIFEST_NAME)).openStream()); + Manifest secondManifest = new Manifest(firstManifest); + assertEquals(firstManifest, secondManifest); } /** - * @tests java.util.jar.Manifest#getAttributes(java.lang.String) + * @tests java.util.jar.Manifest#Manifest(Manifest) */ @TestTargetNew( - level = TestLevel.COMPLETE, - notes = "", - method = "getAttributes", - args = {java.lang.String.class} + level = TestLevel.PARTIAL_COMPLETE, + notes = "", + method = "Manifest", + args = {java.util.jar.Manifest.class} ) - public void test_getAttributesLjava_lang_String() { - // Test for method java.util.jar.Attributes - // java.util.jar.Manifest.getAttributes(java.lang.String) - assertNull("Should not exist", m2.getAttributes("Doesn't Exist")); - assertEquals("Should exist", "OK", m2 - .getAttributes("HasAttributes.txt").get( - new Attributes.Name("MyAttribute"))); + public void test_ConstructorLjava_util_jar_Manifest() { + // Test for method java.util.jar.Manifest() + Manifest emptyManifest = new Manifest(); + Manifest emptyClone = new Manifest(emptyManifest); + assertTrue("Should have no entries", emptyClone.getEntries().isEmpty()); + assertTrue("Should have no main attributes", emptyClone + .getMainAttributes().isEmpty()); + assertEquals(emptyClone, emptyManifest); + assertEquals(emptyClone, emptyManifest.clone()); } - /** - * @tests java.util.jar.Manifest#getEntries() - */ - @TestTargetNew( - level = TestLevel.COMPLETE, - notes = "", - method = "getEntries", - args = {} - ) - public void test_getEntries() { - // Test for method java.util.Map java.util.jar.Manifest.getEntries() - Map myMap = m2.getEntries(); - assertNull("Shouldn't exist", myMap.get("Doesn't exist")); - assertEquals("Should exist", "OK", myMap.get("HasAttributes.txt").get( - new Attributes.Name("MyAttribute"))); - + private void assertAttribute(Attributes attr, String name, String value) { + assertEquals("Incorrect " + name, value, attr.getValue(name)); } - /** - * @tests java.util.jar.Manifest#getMainAttributes() - */ - @TestTargetNew( - level = TestLevel.COMPLETE, - notes = "", - method = "getMainAttributes", - args = {} - ) - public void test_getMainAttributes() { - // Test for method java.util.jar.Attributes - // java.util.jar.Manifest.getMainAttributes() - Attributes a = m.getMainAttributes(); - assertEquals("Manifest_Version should return 1.0", "1.0", a - .get(Attributes.Name.MANIFEST_VERSION)); + private void checkManifest(Manifest manifest) { + Attributes main = manifest.getMainAttributes(); + assertAttribute(main, "Bundle-Name", "ClientSupport"); + assertAttribute(main, "Bundle-Description", + "Provides SessionService, AuthenticationService. Extends RegistryService."); + assertAttribute(main, "Bundle-Activator", + "com.ibm.ive.eccomm.client.support.ClientSupportActivator"); + assertAttribute( + main, + "Import-Package", + "com.ibm.ive.eccomm.client.services.log,com.ibm.ive.eccomm.client.services.registry,com.ibm.ive.eccomm.service.registry; specification-version=1.0.0,com.ibm.ive.eccomm.service.session; specification-version=1.0.0,com.ibm.ive.eccomm.service.framework; specification-version=1.2.0,org.osgi.framework; specification-version=1.0.0,org.osgi.service.log; specification-version=1.0.0,com.ibm.ive.eccomm.flash; specification-version=1.2.0,com.ibm.ive.eccomm.client.xml,com.ibm.ive.eccomm.client.http.common,com.ibm.ive.eccomm.client.http.client"); + assertAttribute( + main, + "Import-Service", + "org.osgi.service.log.LogReaderServiceorg.osgi.service.log.LogService,com.ibm.ive.eccomm.service.registry.RegistryService"); + assertAttribute( + main, + "Export-Package", + "com.ibm.ive.eccomm.client.services.authentication; specification-version=1.0.0,com.ibm.ive.eccomm.service.authentication; specification-version=1.0.0,com.ibm.ive.eccomm.common; specification-version=1.0.0,com.ibm.ive.eccomm.client.services.registry.store; specification-version=1.0.0"); + assertAttribute( + main, + "Export-Service", + "com.ibm.ive.eccomm.service.authentication.AuthenticationService,com.ibm.ive.eccomm.service.session.SessionService"); + assertAttribute(main, "Bundle-Vendor", "IBM"); + assertAttribute(main, "Bundle-Version", "1.2.0"); } /** - * @tests {@link java.util.jar.Manifest#read(java.io.InputStream) + * @tests java.util.jar.Manifest#Manifest(java.io.InputStream) */ @TestTargetNew( level = TestLevel.COMPLETE, - notes = "", - method = "read", + notes = "IOException checking missed.", + method = "Manifest", args = {java.io.InputStream.class} ) - public void test_readLjava_io_InputStream() { - // Regression for HARMONY-89 - InputStream is = new InputStreamImpl(); - try { - new Manifest().read(is); - fail("Assert 0: Should have thrown IOException"); - } catch (IOException e) { - // expected - } + public void test_ConstructorLjava_io_InputStream() throws IOException { + Manifest m = getManifest(attJarName); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + m.write(baos); + InputStream is = new ByteArrayInputStream(baos.toByteArray()); + Manifest mCopy = new Manifest(is); + assertEquals(m, mCopy); - Manifest manifest = new Manifest(); - try { - manifest.read(new URL(Support_Resources - .getURL("manifest/hyts_MANIFEST.MF")).openStream()); - } catch (MalformedURLException e) { - fail("Can nor read manifest"); - } catch (IOException e) { - fail("Can nor read manifest"); - } - Attributes main = manifest.getMainAttributes(); - assertEquals("Bundle-Name not correct", "ClientSupport", main - .getValue("Bundle-Name")); - assertEquals( - "Bundle-Description not correct", - - "Provides SessionService, AuthenticationService. Extends RegistryService.", - main.getValue("Bundle-Description")); - assertEquals("Bundle-Activator not correct", - "com.ibm.ive.eccomm.client.support.ClientSupportActivator", - main.getValue("Bundle-Activator")); - assertEquals( - "Import-Package not correct", - - "com.ibm.ive.eccomm.client.services.log,com.ibm.ive.eccomm.client.services.registry,com.ibm.ive.eccomm.service.registry; specification-version=1.0.0,com.ibm.ive.eccomm.service.session; specification-version=1.0.0,com.ibm.ive.eccomm.service.framework; specification-version=1.2.0,org.osgi.framework; specification-version=1.0.0,org.osgi.service.log; specification-version=1.0.0,com.ibm.ive.eccomm.flash; specification-version=1.2.0,com.ibm.ive.eccomm.client.xml,com.ibm.ive.eccomm.client.http.common,com.ibm.ive.eccomm.client.http.client", - main.getValue("Import-Package")); - assertEquals( - "Import-Service not correct", - - "org.osgi.service.log.LogReaderServiceorg.osgi.service.log.LogService,com.ibm.ive.eccomm.service.registry.RegistryService", - main.getValue("Import-Service")); - assertEquals( - "Export-Package not correct", - - "com.ibm.ive.eccomm.client.services.authentication; specification-version=1.0.0,com.ibm.ive.eccomm.service.authentication; specification-version=1.0.0,com.ibm.ive.eccomm.common; specification-version=1.0.0,com.ibm.ive.eccomm.client.services.registry.store; specification-version=1.0.0", - main.getValue("Export-Package")); - assertEquals( - "Export-Service not correct", - - "com.ibm.ive.eccomm.service.authentication.AuthenticationService,com.ibm.ive.eccomm.service.session.SessionService", - main.getValue("Export-Service")); - assertEquals("Bundle-Vendor not correct", "IBM", main - .getValue("Bundle-Vendor")); - assertEquals("Bundle-Version not correct", "1.2.0", main - .getValue("Bundle-Version")); - } + Manifest manifest = new Manifest(new URL(Support_Resources + .getURL(MANIFEST_NAME)).openStream()); + checkManifest(manifest); - // helper class - class InputStreamImpl extends InputStream { - public InputStreamImpl() { - super(); - } + // regression test for HARMONY-5424 + String manifestContent = "Manifest-Version: 1.0\nCreated-By: Apache\nPackage: \nBuild-Jdk: 1.4.1_01\n\n" + + "Name: \nSpecification-Title: foo\nSpecification-Version: 1.0\nSpecification-Vendor: \n" + + "Implementation-Title: \nImplementation-Version: 1.0\nImplementation-Vendor: \n\n"; + ByteArrayInputStream bis = new ByteArrayInputStream(manifestContent + .getBytes("ISO-8859-1")); - @Override - public int read() { - return 0; - } + + Manifest mf = new Manifest(bis); + assertEquals("Should be 4 main attributes", 4, mf.getMainAttributes() + .size()); + + Map entries = mf.getEntries(); + assertEquals("Should be one named entry", 1, entries.size()); + + Attributes namedEntryAttributes = (Attributes) (entries.get("")); + assertEquals("Should be 6 named entry attributes", 6, + namedEntryAttributes.size()); } /** - * @tests java.util.jar.Manifest#Manifest(Manifest) + * @tests java.util.jar.Manifest#clear() */ @TestTargetNew( - level = TestLevel.COMPLETE, - notes = "", - method = "Manifest", - args = {java.util.jar.Manifest.class} + level = TestLevel.COMPLETE, + notes = "", + method = "clear", + args = {} ) - public void test_ConstructorLjava_util_jar_Manifest() { - // Test for method java.util.jar.Manifest() - Manifest emptyManifest = new Manifest(); - Manifest emptyClone = new Manifest(emptyManifest); - assertTrue("Should have no entries", emptyClone.getEntries().isEmpty()); - assertTrue("Should have no main attributes", emptyClone - .getMainAttributes().isEmpty()); - assertEquals(emptyClone, emptyManifest); - assertEquals(emptyClone, emptyManifest.clone()); + public void test_clear() { + m2.clear(); + assertTrue("Should have no entries", m2.getEntries().isEmpty()); + assertTrue("Should have no main attributes", m2.getMainAttributes() + .isEmpty()); } @TestTargetNew( - level = TestLevel.COMPLETE, - notes = "", - method = "clone", - args = {} + level = TestLevel.COMPLETE, + notes = "", + method = "clone", + args = {} ) - public void test_clone() { + public void test_clone() throws IOException { Manifest emptyManifest = new Manifest(); Manifest emptyClone = (Manifest) emptyManifest.clone(); assertTrue("Should have no entries", emptyClone.getEntries().isEmpty()); @@ -354,88 +243,25 @@ public class ManifestTest extends TestCase { assertEquals(emptyManifest.clone().getClass().getName(), "java.util.jar.Manifest"); - Manifest manifest = null; - try { - manifest = new Manifest(new URL(Support_Resources - .getURL("manifest/hyts_MANIFEST.MF")).openStream()); - } catch (MalformedURLException e) { - fail("Malformed URL"); - } catch (IOException e) { - fail("IOException"); - } + Manifest manifest = new Manifest(new URL(Support_Resources + .getURL("manifest/hyts_MANIFEST.MF")).openStream()); Manifest manifestClone = (Manifest) manifest.clone(); - Attributes main = manifestClone.getMainAttributes(); - assertEquals("Bundle-Name not correct", "ClientSupport", main - .getValue("Bundle-Name")); - assertEquals( - "Bundle-Description not correct", - - "Provides SessionService, AuthenticationService. Extends RegistryService.", - main.getValue("Bundle-Description")); - assertEquals("Bundle-Activator not correct", - "com.ibm.ive.eccomm.client.support.ClientSupportActivator", - main.getValue("Bundle-Activator")); - assertEquals( - "Import-Package not correct", - - "com.ibm.ive.eccomm.client.services.log,com.ibm.ive.eccomm.client.services.registry,com.ibm.ive.eccomm.service.registry; specification-version=1.0.0,com.ibm.ive.eccomm.service.session; specification-version=1.0.0,com.ibm.ive.eccomm.service.framework; specification-version=1.2.0,org.osgi.framework; specification-version=1.0.0,org.osgi.service.log; specification-version=1.0.0,com.ibm.ive.eccomm.flash; specification-version=1.2.0,com.ibm.ive.eccomm.client.xml,com.ibm.ive.eccomm.client.http.common,com.ibm.ive.eccomm.client.http.client", - main.getValue("Import-Package")); - assertEquals( - "Import-Service not correct", - - "org.osgi.service.log.LogReaderServiceorg.osgi.service.log.LogService,com.ibm.ive.eccomm.service.registry.RegistryService", - main.getValue("Import-Service")); - assertEquals( - "Export-Package not correct", - - "com.ibm.ive.eccomm.client.services.authentication; specification-version=1.0.0,com.ibm.ive.eccomm.service.authentication; specification-version=1.0.0,com.ibm.ive.eccomm.common; specification-version=1.0.0,com.ibm.ive.eccomm.client.services.registry.store; specification-version=1.0.0", - main.getValue("Export-Package")); - assertEquals( - "Export-Service not correct", - - "com.ibm.ive.eccomm.service.authentication.AuthenticationService,com.ibm.ive.eccomm.service.session.SessionService", - main.getValue("Export-Service")); - assertEquals("Bundle-Vendor not correct", "IBM", main - .getValue("Bundle-Vendor")); - assertEquals("Bundle-Version not correct", "1.2.0", main - .getValue("Bundle-Version")); + manifestClone.getMainAttributes(); + checkManifest(manifestClone); } @TestTargetNew( - level = TestLevel.COMPLETE, - notes = "", - method = "equals", - args = {java.lang.Object.class} + level = TestLevel.COMPLETE, + notes = "", + method = "equals", + args = {java.lang.Object.class} ) - public void test_equals() { - Manifest manifest1 = null; - Manifest manifest2 = null; + public void test_equals() throws IOException { + Manifest manifest1 = new Manifest(new URL(Support_Resources.getURL( + "manifest/hyts_MANIFEST.MF")).openStream()); + Manifest manifest2 = new Manifest(new URL(Support_Resources.getURL( + "manifest/hyts_MANIFEST.MF")).openStream()); Manifest manifest3 = new Manifest(); - InputStream is = null; - try { - is = new URL(Support_Resources.getURL("manifest/hyts_MANIFEST.MF")) - .openStream(); - } catch (MalformedURLException e1) { - fail("Failed to create InputStream object"); - } catch (IOException e1) { - fail("Failed to create InputStream object"); - } - try { - manifest1 = new Manifest(is); - } catch (MalformedURLException e) { - fail("Malformed URL"); - } catch (IOException e) { - fail("IOException"); - } - - try { - manifest2 = new Manifest(new URL(Support_Resources - .getURL("manifest/hyts_MANIFEST.MF")).openStream()); - } catch (MalformedURLException e) { - fail("Malformed URL"); - } catch (IOException e) { - fail("IOException"); - } assertTrue(manifest1.equals(manifest1)); assertTrue(manifest1.equals(manifest2)); @@ -444,27 +270,69 @@ public class ManifestTest extends TestCase { } @TestTargetNew( - level = TestLevel.COMPLETE, - notes = "", - method = "hashCode", - args = {} + level = TestLevel.COMPLETE, + notes = "", + method = "hashCode", + args = {} ) - public void test_hashCode() { - Manifest manifest1 = null; + public void test_hashCode() throws IOException { + Manifest manifest1 = new Manifest(new URL(Support_Resources + .getURL("manifest/hyts_MANIFEST.MF")).openStream()); Manifest manifest2 = new Manifest(); - InputStream is = null; - try { - manifest1 = new Manifest(new URL(Support_Resources - .getURL("manifest/hyts_MANIFEST.MF")).openStream()); - } catch (MalformedURLException e) { - fail("Malformed URL"); - } catch (IOException e) { - fail("IOException"); - } assertEquals(manifest1.hashCode(), manifest1.hashCode()); assertNotSame(manifest1.hashCode(), manifest2.hashCode()); } + /** + * @tests java.util.jar.Manifest#getAttributes(java.lang.String) + */ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "getAttributes", + args = {String.class} + ) + public void test_getAttributesLjava_lang_String() { + assertNull("Should not exist", + m2.getAttributes("Doesn't Exist")); + assertEquals("Should exist", "OK", m2.getAttributes("HasAttributes.txt").get( + new Attributes.Name("MyAttribute"))); + } + + /** + * @tests java.util.jar.Manifest#getEntries() + */ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "getEntries", + args = {} + ) + public void test_getEntries() { + Map myMap = m2.getEntries(); + assertNull("Shouldn't exist", myMap.get("Doesn't exist")); + assertEquals("Should exist", + "OK", myMap.get("HasAttributes.txt").get( + new Attributes.Name("MyAttribute"))); + } + + /** + * @tests java.util.jar.Manifest#getMainAttributes() + */ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "getMainAttributes", + args = {} + ) + public void test_getMainAttributes() { + // Test for method java.util.jar.Attributes + // java.util.jar.Manifest.getMainAttributes() + Attributes a = m.getMainAttributes(); + assertEquals("Manifest_Version should return 1.0", "1.0", a.get( + Attributes.Name.MANIFEST_VERSION)); + } + @TestTargetNew( level = TestLevel.COMPLETE, notes = "", @@ -510,4 +378,219 @@ public class ManifestTest extends TestCase { assertTrue(manifest1.equals(manifest2)); } + + /** + * Ensures compatibility with manifests produced by gcc. + * + * @see HARMONY-5662 + */ + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "", + method = "Manifest", + args = {InputStream.class} + ) + public void testNul() throws IOException { + String manifestContent = + "Manifest-Version: 1.0\nCreated-By: nasty gcc tool\n\n\0"; + + byte[] bytes = manifestContent.getBytes("ISO-8859-1"); + new Manifest(new ByteArrayInputStream(bytes)); // the last NUL is ok + + bytes[bytes.length - 1] = 26; + new Manifest(new ByteArrayInputStream(bytes)); // the last EOF is ok + + bytes[bytes.length - 1] = 'A'; // the last line ignored + new Manifest(new ByteArrayInputStream(bytes)); + + bytes[2] = 0; // NUL char in Manifest + try { + new Manifest(new ByteArrayInputStream(bytes)); + fail("IOException expected"); + } catch (IOException e) { + // desired + } + } + + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + method = "Manifest", + args = {InputStream.class} + ) + @KnownFailure("CharsetDecoder fails with an IllegalStateException") + public void testDecoding() throws IOException { + Manifest m = getManifest(attJarName); + final byte[] bVendor = new byte[] { (byte) 0xd0, (byte) 0x9C, + (byte) 0xd0, (byte) 0xb8, (byte) 0xd0, (byte) 0xbb, + (byte) 0xd0, (byte) 0xb0, (byte) 0xd1, (byte) 0x8f, ' ', + (byte) 0xd0, (byte) 0xb4, (byte) 0xd0, (byte) 0xbe, + (byte) 0xd1, (byte) 0x87, (byte) 0xd1, (byte) 0x83, + (byte) 0xd0, (byte) 0xbd, (byte) 0xd1, (byte) 0x8C, + (byte) 0xd0, (byte) 0xba, (byte) 0xd0, (byte) 0xb0, ' ', + (byte) 0xd0, (byte) 0x9C, (byte) 0xd0, (byte) 0xb0, + (byte) 0xd1, (byte) 0x88, (byte) 0xd0, (byte) 0xb0 }; + + final byte[] bSpec = new byte[] { (byte) 0xe1, (byte) 0x88, + (byte) 0xb0, (byte) 0xe1, (byte) 0x88, (byte) 0x8b, + (byte) 0xe1, (byte) 0x88, (byte) 0x9d, ' ', (byte) 0xe1, + (byte) 0x9a, (byte) 0xa0, (byte) 0xe1, (byte) 0x9a, + (byte) 0xb1, (byte) 0xe1, (byte) 0x9b, (byte) 0x81, + (byte) 0xe1, (byte) 0x9a, (byte) 0xa6, ' ', (byte) 0xd8, + (byte) 0xb3, (byte) 0xd9, (byte) 0x84, (byte) 0xd8, + (byte) 0xa7, (byte) 0xd9, (byte) 0x85, ' ', (byte) 0xd8, + (byte) 0xb9, (byte) 0xd8, (byte) 0xb3, (byte) 0xd9, + (byte) 0x84, (byte) 0xd8, (byte) 0xa7, (byte) 0xd9, + (byte) 0x85, (byte) 0xd8, (byte) 0xa9, ' ', (byte) 0xdc, + (byte) 0xab, (byte) 0xdc, (byte) 0xa0, (byte) 0xdc, + (byte) 0xa1, (byte) 0xdc, (byte) 0x90, ' ', (byte) 0xe0, + (byte) 0xa6, (byte) 0xb6, (byte) 0xe0, (byte) 0xa6, + (byte) 0xbe, (byte) 0xe0, (byte) 0xa6, (byte) 0xa8, + (byte) 0xe0, (byte) 0xa7, (byte) 0x8d, (byte) 0xe0, + (byte) 0xa6, (byte) 0xa4, (byte) 0xe0, (byte) 0xa6, + (byte) 0xbf, ' ', (byte) 0xd0, (byte) 0xa0, (byte) 0xd0, + (byte) 0xb5, (byte) 0xd0, (byte) 0xba, (byte) 0xd1, + (byte) 0x8a, (byte) 0xd0, (byte) 0xb5, (byte) 0xd0, + (byte) 0xbb, ' ', (byte) 0xd0, (byte) 0x9c, (byte) 0xd0, + (byte) 0xb8, (byte) 0xd1, (byte) 0x80, ' ', (byte) 0xe0, + (byte) 0xa6, (byte) 0xb6, (byte) 0xe0, (byte) 0xa6, + (byte) 0xbe, (byte) 0xe0, (byte) 0xa6, (byte) 0xa8, + (byte) 0xe0, (byte) 0xa7, (byte) 0x8d, (byte) 0xe0, + (byte) 0xa6, (byte) 0xa4, (byte) 0xe0, (byte) 0xa6, + (byte) 0xbf, ' ', (byte) 0xe0, (byte) 0xbd, (byte) 0x9e, + (byte) 0xe0, (byte) 0xbd, (byte) 0xb2, (byte) 0xe0, + (byte) 0xbc, (byte) 0x8b, (byte) 0xe0, (byte) 0xbd, + (byte) 0x96, (byte) 0xe0, (byte) 0xbd, (byte) 0x91, + (byte) 0xe0, (byte) 0xbd, (byte) 0xba, ' ', (byte) 0xd0, + (byte) 0x9c, (byte) 0xd0, (byte) 0xb0, (byte) 0xd1, + (byte) 0x88, (byte) 0xd0, (byte) 0xb0, (byte) 0xd1, + (byte) 0x80, ' ', (byte) 0xe1, (byte) 0x8f, (byte) 0x99, + (byte) 0xe1, (byte) 0x8e, (byte) 0xaf, (byte) 0xe1, + (byte) 0x8f, (byte) 0xb1, ' ', (byte) 0xcf, (byte) 0xa8, + (byte) 0xce, (byte) 0xb9, (byte) 0xcf, (byte) 0x81, + (byte) 0xce, (byte) 0xb7, (byte) 0xce, (byte) 0xbd, + (byte) 0xce, (byte) 0xb7, ' ', (byte) 0xde, (byte) 0x90, + (byte) 0xde, (byte) 0xaa, (byte) 0xde, (byte) 0x85, + (byte) 0xde, (byte) 0xa6, ' ', (byte) 0xe0, (byte) 0xbd, + (byte) 0x82, (byte) 0xe0, (byte) 0xbd, (byte) 0x9e, + (byte) 0xe0, (byte) 0xbd, (byte) 0xb2, (byte) 0xe0, + (byte) 0xbc, (byte) 0x8b, (byte) 0xe0, (byte) 0xbd, + (byte) 0x96, (byte) 0xe0, (byte) 0xbd, (byte) 0x91, + (byte) 0xe0, (byte) 0xbd, (byte) 0xba, ' ', (byte) 0xce, + (byte) 0x95, (byte) 0xce, (byte) 0xb9, (byte) 0xcf, + (byte) 0x81, (byte) 0xce, (byte) 0xae, (byte) 0xce, + (byte) 0xbd, (byte) 0xce, (byte) 0xb7, ' ', (byte) 0xd8, + (byte) 0xb5, (byte) 0xd9, (byte) 0x84, (byte) 0xd8, + (byte) 0xad, ' ', (byte) 0xe0, (byte) 0xaa, (byte) 0xb6, + (byte) 0xe0, (byte) 0xaa, (byte) 0xbe, (byte) 0xe0, + (byte) 0xaa, (byte) 0x82, (byte) 0xe0, (byte) 0xaa, + (byte) 0xa4, (byte) 0xe0, (byte) 0xaa, (byte) 0xbf, ' ', + (byte) 0xe5, (byte) 0xb9, (byte) 0xb3, (byte) 0xe5, + (byte) 0x92, (byte) 0x8c, ' ', (byte) 0xd7, (byte) 0xa9, + (byte) 0xd7, (byte) 0x9c, (byte) 0xd7, (byte) 0x95, + (byte) 0xd7, (byte) 0x9d, ' ', (byte) 0xd7, (byte) 0xa4, + (byte) 0xd7, (byte) 0xa8, (byte) 0xd7, (byte) 0x99, + (byte) 0xd7, (byte) 0x93, (byte) 0xd7, (byte) 0x9f, ' ', + (byte) 0xe5, (byte) 0x92, (byte) 0x8c, (byte) 0xe5, + (byte) 0xb9, (byte) 0xb3, ' ', (byte) 0xe5, (byte) 0x92, + (byte) 0x8c, (byte) 0xe5, (byte) 0xb9, (byte) 0xb3, ' ', + (byte) 0xd8, (byte) 0xaa, (byte) 0xd9, (byte) 0x89, + (byte) 0xd9, (byte) 0x86, (byte) 0xda, (byte) 0x86, + (byte) 0xd9, (byte) 0x84, (byte) 0xd9, (byte) 0x89, + (byte) 0xd9, (byte) 0x82, ' ', (byte) 0xe0, (byte) 0xae, + (byte) 0x85, (byte) 0xe0, (byte) 0xae, (byte) 0xae, + (byte) 0xe0, (byte) 0xaf, (byte) 0x88, (byte) 0xe0, + (byte) 0xae, (byte) 0xa4, (byte) 0xe0, (byte) 0xae, + (byte) 0xbf, ' ', (byte) 0xe0, (byte) 0xb0, (byte) 0xb6, + (byte) 0xe0, (byte) 0xb0, (byte) 0xbe, (byte) 0xe0, + (byte) 0xb0, (byte) 0x82, (byte) 0xe0, (byte) 0xb0, + (byte) 0xa4, (byte) 0xe0, (byte) 0xb0, (byte) 0xbf, ' ', + (byte) 0xe0, (byte) 0xb8, (byte) 0xaa, (byte) 0xe0, + (byte) 0xb8, (byte) 0xb1, (byte) 0xe0, (byte) 0xb8, + (byte) 0x99, (byte) 0xe0, (byte) 0xb8, (byte) 0x95, + (byte) 0xe0, (byte) 0xb8, (byte) 0xb4, (byte) 0xe0, + (byte) 0xb8, (byte) 0xa0, (byte) 0xe0, (byte) 0xb8, + (byte) 0xb2, (byte) 0xe0, (byte) 0xb8, (byte) 0x9e, ' ', + (byte) 0xe1, (byte) 0x88, (byte) 0xb0, (byte) 0xe1, + (byte) 0x88, (byte) 0x8b, (byte) 0xe1, (byte) 0x88, + (byte) 0x9d, ' ', (byte) 0xe0, (byte) 0xb7, (byte) 0x83, + (byte) 0xe0, (byte) 0xb7, (byte) 0x8f, (byte) 0xe0, + (byte) 0xb6, (byte) 0xb8, (byte) 0xe0, (byte) 0xb6, + (byte) 0xba, ' ', (byte) 0xe0, (byte) 0xa4, (byte) 0xb6, + (byte) 0xe0, (byte) 0xa4, (byte) 0xbe, (byte) 0xe0, + (byte) 0xa4, (byte) 0xa8, (byte) 0xe0, (byte) 0xa5, + (byte) 0x8d, (byte) 0xe0, (byte) 0xa4, (byte) 0xa4, + (byte) 0xe0, (byte) 0xa4, (byte) 0xbf, (byte) 0xe0, + (byte) 0xa4, (byte) 0x83, ' ', (byte) 0xe1, (byte) 0x83, + (byte) 0x9b, (byte) 0xe1, (byte) 0x83, (byte) 0xa8, + (byte) 0xe1, (byte) 0x83, (byte) 0x95, (byte) 0xe1, + (byte) 0x83, (byte) 0x98, (byte) 0xe1, (byte) 0x83, + (byte) 0x93, (byte) 0xe1, (byte) 0x83, (byte) 0x9d, + (byte) 0xe1, (byte) 0x83, (byte) 0x91, (byte) 0xe1, + (byte) 0x83, (byte) 0x90 }; + // TODO Cannot make the following word work, encoder changes needed + // (byte) 0xed, (byte) 0xa0, (byte) 0x80, + // (byte) 0xed, (byte) 0xbc, (byte) 0xb2, (byte) 0xed, + // (byte) 0xa0, (byte) 0x80, (byte) 0xed, (byte) 0xbc, + // (byte) 0xb0, (byte) 0xed, (byte) 0xa0, (byte) 0x80, + // (byte) 0xed, (byte) 0xbd, (byte) 0x85, (byte) 0xed, + // (byte) 0xa0, (byte) 0x80, (byte) 0xed, (byte) 0xbc, + // (byte) 0xb0, (byte) 0xed, (byte) 0xa0, (byte) 0x80, + // (byte) 0xed, (byte) 0xbc, (byte) 0xb9, (byte) 0xed, + // (byte) 0xa0, (byte) 0x80, (byte) 0xed, (byte) 0xbc, + // (byte) 0xb8, (byte) 0xed, (byte) 0xa0, (byte) 0x80, + // (byte) 0xed, (byte) 0xbc, (byte) 0xb9, ' ' + + final String vendor = new String(bVendor, "UTF-8"); + final String spec = new String(bSpec, "UTF-8"); + m.getMainAttributes() + .put(Attributes.Name.IMPLEMENTATION_VENDOR, vendor); + m.getAttributes(ATT_ENTRY_NAME).put( + Attributes.Name.IMPLEMENTATION_VENDOR, vendor); + m.getEntries().get(ATT_ENTRY_NAME).put( + Attributes.Name.SPECIFICATION_TITLE, spec); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + m.write(baos); + m = new Manifest(new ByteArrayInputStream(baos.toByteArray())); + + assertEquals(vendor, m.getMainAttributes().get( + Attributes.Name.IMPLEMENTATION_VENDOR)); + assertEquals(vendor, m.getEntries().get(ATT_ENTRY_NAME).get( + Attributes.Name.IMPLEMENTATION_VENDOR)); + assertEquals(spec, m.getAttributes(ATT_ENTRY_NAME).get( + Attributes.Name.SPECIFICATION_TITLE)); + } + + /** + * @tests {@link java.util.jar.Manifest#read(java.io.InputStream) + */ + @TestTargetNew( + level = TestLevel.PARTIAL, + notes = "", + method = "read", + args = {InputStream.class} + ) + public void testRead() { + // Regression for HARMONY-89 + InputStream is = new InputStreamImpl(); + try { + new Manifest().read(is); + fail("IOException expected"); + } catch (IOException e) { + // desired + } + } + + // helper class + private class InputStreamImpl extends InputStream { + public InputStreamImpl() { + super(); + } + + @Override + public int read() { + return 0; + } + } } diff --git a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/DeflaterOutputStreamTest.java b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/DeflaterOutputStreamTest.java index ed7238cc6..738f61020 100644 --- a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/DeflaterOutputStreamTest.java +++ b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/DeflaterOutputStreamTest.java @@ -211,21 +211,22 @@ public class DeflaterOutputStreamTest extends TestCase { ) public void test_close() throws Exception { File f1 = File.createTempFile("close", ".tst"); - FileOutputStream fos = new FileOutputStream(f1); - DeflaterOutputStream dos = new DeflaterOutputStream(fos); - byte byteArray[] = {1, 3, 4, 6}; - dos.write(byteArray); - FileInputStream fis = new FileInputStream(f1); - InflaterInputStream iis = new InflaterInputStream(fis); + InflaterInputStream iis = new InflaterInputStream(new FileInputStream(f1)); try { iis.read(); fail("EOFException Not Thrown"); } catch (EOFException e) { } + FileOutputStream fos = new FileOutputStream(f1); + DeflaterOutputStream dos = new DeflaterOutputStream(fos); + byte byteArray[] = {1, 3, 4, 6}; + dos.write(byteArray); dos.close(); + iis = new InflaterInputStream(new FileInputStream(f1)); + // Test to see if the finish method wrote the bytes to the file. assertEquals("Incorrect Byte Returned.", 1, iis.read()); assertEquals("Incorrect Byte Returned.", 3, iis.read()); diff --git a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/GZIPInputStreamTest.java b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/GZIPInputStreamTest.java index 75060bd4a..1e8ddb430 100644 --- a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/GZIPInputStreamTest.java +++ b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/GZIPInputStreamTest.java @@ -25,6 +25,8 @@ import dalvik.annotation.TestTargetNew; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; @@ -310,7 +312,43 @@ public class GZIPInputStreamTest extends junit.framework.TestCase { } } - @Override + /** + * Regression test for HARMONY-3703. + * @tests java.util.zip.GZIPInputStream#read() + */ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "read", + args = {byte[].class} + ) + public void test_read() throws IOException { + GZIPInputStream gis = null; + int result = 0; + byte[] buffer = new byte[] {1,2,3,4,5,6,7,8,9,10}; + File f = new File(resources.getAbsolutePath() + "test.gz"); + FileOutputStream out = new FileOutputStream(f); + GZIPOutputStream gout = new GZIPOutputStream(out); + + // write 100 bytes to the stream + for(int i = 0; i < 10; i++) { + gout.write(buffer); + } + gout.finish(); + out.write(1); + out.close(); + + gis = new GZIPInputStream(new FileInputStream(f)); + buffer = new byte[100]; + gis.read(buffer); + result = gis.read(); + gis.close(); + f.delete(); + + assertEquals("Incorrect value returned at the end of the file", -1, result); + } + + @Override protected void setUp() { resources = Support_Resources.createTempFolder(); } diff --git a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/GZIPOutputStreamTest.java b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/GZIPOutputStreamTest.java index 9b23b5682..b71ce63b3 100644 --- a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/GZIPOutputStreamTest.java +++ b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/GZIPOutputStreamTest.java @@ -201,8 +201,6 @@ public class GZIPOutputStreamTest extends junit.framework.TestCase { int r = 0; try { outGZIP.write(byteArray, 0, 11); - } catch (ArrayIndexOutOfBoundsException e) { - r = 1; } catch (IndexOutOfBoundsException ee) { r = 1; } diff --git a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/InflaterInputStreamTest.java b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/InflaterInputStreamTest.java index 2de996e22..707f13bbf 100644 --- a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/InflaterInputStreamTest.java +++ b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/InflaterInputStreamTest.java @@ -32,6 +32,8 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.io.FileOutputStream; +import java.io.EOFException; import java.util.zip.DeflaterOutputStream; import java.util.zip.Inflater; import java.util.zip.InflaterInputStream; @@ -208,6 +210,42 @@ public class InflaterInputStreamTest extends TestCase { } } + public void testAvailableNonEmptySource() throws Exception { + // this byte[] is a deflation of these bytes: { 1, 3, 4, 6 } + byte[] deflated = { 72, -119, 99, 100, 102, 97, 3, 0, 0, 31, 0, 15, 0 }; + InputStream in = new InflaterInputStream(new ByteArrayInputStream(deflated)); + // InflaterInputStream.available() returns either 1 or 0, even though + // that contradicts the behavior defined in InputStream.available() + assertEquals(1, in.read()); + assertEquals(1, in.available()); + assertEquals(3, in.read()); + assertEquals(1, in.available()); + assertEquals(4, in.read()); + assertEquals(1, in.available()); + assertEquals(6, in.read()); + assertEquals(0, in.available()); + assertEquals(-1, in.read()); + assertEquals(-1, in.read()); + } + + public void testAvailableSkip() throws Exception { + // this byte[] is a deflation of these bytes: { 1, 3, 4, 6 } + byte[] deflated = { 72, -119, 99, 100, 102, 97, 3, 0, 0, 31, 0, 15, 0 }; + InputStream in = new InflaterInputStream(new ByteArrayInputStream(deflated)); + assertEquals(1, in.available()); + assertEquals(4, in.skip(4)); + assertEquals(0, in.available()); + } + + public void testAvailableEmptySource() throws Exception { + // this byte[] is a deflation of the empty file + byte[] deflated = { 120, -100, 3, 0, 0, 0, 0, 1 }; + InputStream in = new InflaterInputStream(new ByteArrayInputStream(deflated)); + assertEquals(-1, in.read()); + assertEquals(-1, in.read()); + assertEquals(0, in.available()); + } + /** * @tests java.util.zip.InflaterInputStream#read(byte[], int, int) */ @@ -471,12 +509,11 @@ public class InflaterInputStreamTest extends TestCase { InflaterInputStream iis = new InflaterInputStream(is); int available; - int read; for (int i = 0; i < 11; i++) { - read = iis.read(); + iis.read(); available = iis.available(); - if (read == -1) { - assertEquals("Bytes Available Should Return 0 ", 0, available); + if (available == 0) { + assertEquals("Expected no more bytes to read", -1, iis.read()); } else { assertEquals("Bytes Available Should Return 1.", 1, available); } diff --git a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/InflaterTest.java b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/InflaterTest.java index 8b8918041..cd5d538fa 100644 --- a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/InflaterTest.java +++ b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/InflaterTest.java @@ -408,6 +408,22 @@ public class InflaterTest extends junit.framework.TestCase { infl2.end(); } + /** + * @tests java.util.zip.Inflater#Inflater() + */ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "Inflater", + args = {} + ) + public void test_Constructor() { + // test method of java.util.zip.inflater.Inflater() + Inflater inflate = new Inflater(); + assertNotNull("failed to create the instance of inflater", + inflate); + } + /** * @tests java.util.zip.Inflater#inflate(byte[], int, int) */ @@ -503,27 +519,6 @@ public class InflaterTest extends junit.framework.TestCase { infl2.end(); } - /** - * @tests java.util.zip.Inflater#Inflater() - */ - @TestTargetNew( - level = TestLevel.COMPLETE, - notes = "", - method = "Inflater", - args = {} - ) - public void test_Constructor() { - // test method of java.util.zip.inflater.Inflater() - try { - Inflater inflate = new Inflater(); - assertNotNull("failed to create the instance of inflater", inflate); - - } catch (Exception e) { - - assertTrue("Inflate () constructor threw an exception", true); - } - } - /** * @tests java.util.zip.Inflater#Inflater(boolean) */ @@ -867,17 +862,6 @@ public class InflaterTest extends junit.framework.TestCase { byte[] b = new byte[1024]; assertEquals(0, inflater.inflate(b)); inflater.end(); - - // Regression for HARMONY-2510 - inflater = new Inflater(); - byte[] input = new byte[] {-1}; - inflater.setInput(input); - try { - inflater.inflate(b); - fail("should throw DataFormateException"); - } catch (DataFormatException e) { - // expected - } } /** diff --git a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipFileTest.java b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipFileTest.java index 5530a2ec7..b025e1192 100644 --- a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipFileTest.java +++ b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipFileTest.java @@ -18,7 +18,6 @@ package org.apache.harmony.archive.tests.java.util.zip; import dalvik.annotation.KnownFailure; -import dalvik.annotation.BrokenTest; import dalvik.annotation.TestLevel; import dalvik.annotation.TestTargetClass; import dalvik.annotation.TestTargetNew; @@ -67,7 +66,7 @@ public class ZipFileTest extends junit.framework.TestCase { public void checkPermission(Permission perm) { // only check if it's a FilePermission because Locale checks // for a PropertyPermission with action"read" to get system props. - if (perm instanceof FilePermission + if (perm instanceof FilePermission && perm.getActions().equals(forbidenPermissionAction)) { throw new SecurityException(); } @@ -145,7 +144,7 @@ public class ZipFileTest extends junit.framework.TestCase { public void test_ConstructorLjava_lang_String() throws IOException { String oldUserDir = System.getProperty("user.dir"); System.setProperty("user.dir", System.getProperty("java.io.tmpdir")); - + zfile.close(); // about to reopen the same temp file ZipFile zip = new ZipFile(tempFileName); zip.close(); @@ -260,7 +259,7 @@ public class ZipFileTest extends junit.framework.TestCase { method = "entries", args = {} ) - public void test_entries() { + public void test_entries() throws Exception { // Test for method java.util.Enumeration java.util.zip.ZipFile.entries() Enumeration enumer = zfile.entries(); int c = 0; @@ -270,20 +269,16 @@ public class ZipFileTest extends junit.framework.TestCase { } assertTrue("Incorrect number of entries returned: " + c, c == 6); + Enumeration enumeration = zfile.entries(); + zfile.close(); + zfile = null; + boolean pass = false; try { - Enumeration enumeration = zfile.entries(); - zfile.close(); - zfile = null; - boolean pass = false; - try { - enumeration.hasMoreElements(); - } catch (IllegalStateException e) { - pass = true; - } - assertTrue("did not detect closed jar file", pass); - } catch (Exception e) { - fail("Exception during entries test: " + e.toString()); + enumeration.hasMoreElements(); + } catch (IllegalStateException e) { + pass = true; } + assertTrue("did not detect closed jar file", pass); } /** @@ -454,6 +449,99 @@ public class ZipFileTest extends junit.framework.TestCase { } /** + * @tests java.io.InputStream#reset() + */ + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + method = "getInputStream", + args = {java.util.zip.ZipEntry.class} + ) + @KnownFailure("ZipEntry.getInputStream().reset() fails with an IOException") + public void test_reset() throws IOException { + // read an uncompressed entry + ZipEntry zentry = zfile.getEntry("File1.txt"); + InputStream is = zfile.getInputStream(zentry); + byte[] rbuf1 = new byte[6]; + byte[] rbuf2 = new byte[6]; + int r1, r2; + r1 = is.read(rbuf1); + assertEquals(rbuf1.length, r1); + r2 = is.read(rbuf2); + assertEquals(rbuf2.length, r2); + + is.reset(); + r2 = is.read(rbuf2); + assertEquals(rbuf2.length, r2); + is.close(); + + // read a compressed entry + byte[] rbuf3 = new byte[4185]; + ZipEntry zentry2 = zfile.getEntry("File3.txt"); + is = zfile.getInputStream(zentry2); + r1 = is.read(rbuf3); + assertEquals(4183, r1); + is.reset(); + + r1 = is.read(rbuf3); + assertEquals(4183, r1); + is.close(); + + is = zfile.getInputStream(zentry2); + r1 = is.read(rbuf3, 0, 3000); + assertEquals(3000, r1); + is.reset(); + r1 = is.read(rbuf3, 0, 3000); + assertEquals(3000, r1); + is.close(); + } + + /** + * @tests java.io.InputStream#reset() + */ + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + method = "getInputStream", + args = {java.util.zip.ZipEntry.class} + ) + @KnownFailure("ZipEntry.getInputStream().reset() fails with an IOException") + public void test_reset_subtest0() throws IOException { + // read an uncompressed entry + ZipEntry zentry = zfile.getEntry("File1.txt"); + InputStream is = zfile.getInputStream(zentry); + byte[] rbuf1 = new byte[12]; + byte[] rbuf2 = new byte[12]; + int r = is.read(rbuf1, 0, 4); + assertEquals(4, r); + is.mark(0); + r = is.read(rbuf1); + assertEquals(8, r); + assertEquals(-1, is.read()); + + is.reset(); + r = is.read(rbuf2); + assertEquals(8, r); + assertEquals(-1, is.read()); + is.close(); + + // read a compressed entry + byte[] rbuf3 = new byte[4185]; + ZipEntry zentry2 = zfile.getEntry("File3.txt"); + is = zfile.getInputStream(zentry2); + r = is.read(rbuf3, 0, 3000); + assertEquals(3000, r); + is.mark(0); + r = is.read(rbuf3); + assertEquals(1183, r); + assertEquals(-1, is.read()); + + is.reset(); + r = is.read(rbuf3); + assertEquals(1183, r); + assertEquals(-1, is.read()); + is.close(); + } + + /** * Sets up the fixture, for example, open a network connection. This method * is called before a test is executed. */ diff --git a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipInputStreamTest.java b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipInputStreamTest.java index ac332faad..c5efedcd5 100644 --- a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipInputStreamTest.java +++ b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipInputStreamTest.java @@ -17,7 +17,6 @@ package org.apache.harmony.archive.tests.java.util.zip; -import dalvik.annotation.KnownFailure; import dalvik.annotation.TestLevel; import dalvik.annotation.TestTargetClass; import dalvik.annotation.TestTargetNew; @@ -30,6 +29,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; +import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.util.zip.ZipEntry; @@ -174,8 +174,6 @@ public class ZipInputStreamTest extends TestCase { method = "close", args = {} ) - @KnownFailure("after an exception has been thrown wile reading a " - + "call to close also throws an exception.") public void test_closeAfterException() throws Exception { File resources = Support_Resources.createTempFolder(); Support_Resources.copyFile(resources, null, "Broken_manifest.jar"); @@ -281,6 +279,31 @@ public class ZipInputStreamTest extends TestCase { } } + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + method = "read", + args = {byte[].class, int.class, int.class} + ) + public void testReadOneByteAtATime() throws IOException { + InputStream in = new FilterInputStream(Support_Resources.getStream("hyts_ZipFile.zip")) { + @Override + public int read(byte[] buffer, int offset, int count) throws IOException { + return super.read(buffer, offset, 1); // one byte at a time + } + + @Override + public int read(byte[] buffer) throws IOException { + return super.read(buffer, 0, 1); // one byte at a time + } + }; + + zis = new ZipInputStream(in); + while ((zentry = zis.getNextEntry()) != null) { + zentry.getName(); + } + zis.close(); + } + /** * @tests java.util.zip.ZipInputStream#skip(long) */ @@ -360,19 +383,19 @@ public class ZipInputStreamTest extends TestCase { long entrySize = entry.getSize(); assertTrue("Entry size was < 1", entrySize > 0); int i = 0; - for (i = 0; i < entrySize; i++) { + while (zis1.available() > 0) { zis1.skip(1); - if (zis1.available() == 0) break; + i++; } if (i != entrySize) { fail("ZipInputStream.available or ZipInputStream.skip does not " + "working properly. Only skipped " + i + " bytes instead of " + entrySize); } - zis1.skip(1); - assertTrue(zis1.available() == 0); + assertEquals(0, zis1.skip(1)); + assertEquals(0, zis1.available()); zis1.closeEntry(); - assertFalse(zis.available() == 0); + assertEquals(1, zis.available()); zis1.close(); try { zis1.available(); diff --git a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipOutputStreamTest.java b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipOutputStreamTest.java index 9a5f63afe..0398f03fd 100644 --- a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipOutputStreamTest.java +++ b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipOutputStreamTest.java @@ -86,7 +86,7 @@ public class ZipOutputStreamTest extends junit.framework.TestCase { ZipEntry ze = new ZipEntry("testEntry"); ze.setTime(System.currentTimeMillis()); zos.putNextEntry(ze); - zos.write("Hello World".getBytes()); + zos.write("Hello World".getBytes("UTF-8")); zos.closeEntry(); assertTrue("closeEntry failed to update required fields", ze.getSize() == 11 && ze.getCompressedSize() == 13); @@ -170,11 +170,8 @@ public class ZipOutputStreamTest extends junit.framework.TestCase { public void test_setCommentLjava_lang_String() { // There is no way to get the comment back, so no way to determine if // the comment is set correct - try { - zos.setComment("test setComment"); - } catch (Exception e) { - fail("Trying to set comment failed"); - } + zos.setComment("test setComment"); + try { zos.setComment(new String(new byte[0xFFFF + 1])); fail("Comment over 0xFFFF in length should throw exception"); @@ -301,6 +298,17 @@ public class ZipOutputStreamTest extends junit.framework.TestCase { } catch (IndexOutOfBoundsException e) { // expected } + + // Regression for HARMONY-4405 + try { + zip.write(null, 0, -2); + fail("Should throw IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + // Close stream because ZIP is invalid + stream.close(); } /** @@ -337,6 +345,8 @@ public class ZipOutputStreamTest extends junit.framework.TestCase { } catch (IOException e2) { // expected } + + zip1.close(); } @Override diff --git a/libcore/auth/src/main/java/javax/security/auth/AuthPermission.java b/libcore/auth/src/main/java/javax/security/auth/AuthPermission.java index 06ea3fb06..697d5c7bd 100644 --- a/libcore/auth/src/main/java/javax/security/auth/AuthPermission.java +++ b/libcore/auth/src/main/java/javax/security/auth/AuthPermission.java @@ -26,33 +26,31 @@ import org.apache.harmony.auth.internal.nls.Messages; * target name of the permission specifies which methods are allowed * without specifying the concrete action lists. Possible target names and * associated authentication permissions are: - * + * *
        *    doAs                      invoke Subject.doAs methods.
        *    doAsPrivileged            invoke the Subject.doAsPrivileged methods.
        *    getSubject                invoke Subject.getSubject().
        *    getSubjectFromDomainCombiner    invoke SubjectDomainCombiner.getSubject().
        *    setReadOnly               invoke Subject.setReadonly().
      - *    modifyPrincipals          modify the set of principals 
      + *    modifyPrincipals          modify the set of principals
        *                              associated with a Subject.
        *    modifyPublicCredentials   modify the set of public credentials
        *                              associated with a Subject.
        *    modifyPrivateCredentials  modify the set of private credentials
        *                              associated with a Subject.
      - *    refreshCredential         invoke the refresh method on a credential of a 
      + *    refreshCredential         invoke the refresh method on a credential of a
        *                              refreshable credential class.
        *    destroyCredential         invoke the destroy method on a credential of a
        *                              destroyable credential class.
        *    createLoginContext.name   instantiate a LoginContext with the
        *                              specified name. The wildcard name ('*')
        *                              allows to a LoginContext of any name.
      - *    getLoginConfiguration     invoke the getConfiguration method of 
      + *    getLoginConfiguration     invoke the getConfiguration method of
        *                              javax.security.auth.login.Configuration.
      - *    refreshLoginConfiguration Invoke the refresh method of 
      + *    refreshLoginConfiguration Invoke the refresh method of
        *                              javax.security.auth.login.Configuration.
        * 
      - * - * @since Android 1.0 */ public final class AuthPermission extends BasicPermission { @@ -77,7 +75,7 @@ public final class AuthPermission extends BasicPermission { /** * Creates an authentication permission with the specified target name. - * + * * @param name * the target name of this authentication permission. */ @@ -87,7 +85,7 @@ public final class AuthPermission extends BasicPermission { /** * Creates an authentication permission with the specified target name. - * + * * @param name * the target name of this authentication permission. * @param actions diff --git a/libcore/auth/src/main/java/javax/security/auth/DestroyFailedException.java b/libcore/auth/src/main/java/javax/security/auth/DestroyFailedException.java index a5438a6a4..27d4dfdcf 100644 --- a/libcore/auth/src/main/java/javax/security/auth/DestroyFailedException.java +++ b/libcore/auth/src/main/java/javax/security/auth/DestroyFailedException.java @@ -19,8 +19,6 @@ package javax.security.auth; /** * Signals that the {@link Destroyable#destroy()} method failed. - * - * @since Android 1.0 */ public class DestroyFailedException extends Exception { @@ -35,7 +33,7 @@ public class DestroyFailedException extends Exception { /** * Creates an exception of type {@code DestroyFailedException}. - * + * * @param message * A detail message that describes the reason for this exception. */ diff --git a/libcore/auth/src/main/java/javax/security/auth/Destroyable.java b/libcore/auth/src/main/java/javax/security/auth/Destroyable.java index 6194db662..b4d0fa2aa 100644 --- a/libcore/auth/src/main/java/javax/security/auth/Destroyable.java +++ b/libcore/auth/src/main/java/javax/security/auth/Destroyable.java @@ -20,8 +20,6 @@ package javax.security.auth; /** * Allows for special treatment of sensitive information, when it comes to * destroying or clearing of the data. - * - * @since Android 1.0 */ public interface Destroyable { @@ -29,7 +27,7 @@ public interface Destroyable { * Erases the sensitive information. Once an object is destroyed any calls * to its methods will throw an {@code IllegalStateException}. If it does * not succeed a DestroyFailedException is thrown. - * + * * @throws DestroyFailedException * if the information cannot be erased. */ @@ -37,7 +35,7 @@ public interface Destroyable { /** * Returns {@code true} once an object has been safely destroyed. - * + * * @return whether the object has been safely destroyed. */ boolean isDestroyed(); diff --git a/libcore/auth/src/main/java/javax/security/auth/PrivateCredentialPermission.java b/libcore/auth/src/main/java/javax/security/auth/PrivateCredentialPermission.java index d92ede5b2..35072b85a 100644 --- a/libcore/auth/src/main/java/javax/security/auth/PrivateCredentialPermission.java +++ b/libcore/auth/src/main/java/javax/security/auth/PrivateCredentialPermission.java @@ -31,11 +31,11 @@ import org.apache.harmony.auth.internal.nls.Messages; * Protects private credential objects belonging to a {@code Subject}. It has * only one action which is "read". The target name of this permission has a * special syntax: - * + * *
        * targetName = CredentialClass {PrincipalClass "PrincipalName"}*
        * 
      - * + * * First it states a credential class and is followed then by a list of one or * more principals identifying the subject. *

      @@ -43,12 +43,11 @@ import org.apache.harmony.auth.internal.nls.Messages; * Principal} class followed by the principal name in quotes. For example, the * following file may define permission to read the private credentials of a * principal named "Bob": "com.sun.PrivateCredential com.sun.Principal \"Bob\"" - *

      + *

      * The syntax also allows the use of the wildcard "*" in place of {@code * CredentialClass} or {@code PrincipalClass} and/or {@code PrincipalName}. - * + * * @see Principal - * @since Android 1.0 */ public final class PrivateCredentialPermission extends Permission { @@ -69,7 +68,7 @@ public final class PrivateCredentialPermission extends Permission { * Creates a new permission for private credentials specified by the target * name {@code name} and an {@code action}. The action is always * {@code "read"}. - * + * * @param name * the target name of the permission. * @param action @@ -197,13 +196,13 @@ public final class PrivateCredentialPermission extends Permission { * dimension of the array corresponds to the number of principals. The * second dimension defines either the name of the {@code PrincipalClass} * [x][0] or the value of {@code PrincipalName} [x][1]. - * + *

      * This corresponds to the the target name's syntax: - * + * *

            * targetName = CredentialClass {PrincipalClass "PrincipalName"}*
            * 
      - * + * * @return the principal classes and names associated with this {@code * PrivateCredentialPermission}. */ @@ -225,7 +224,7 @@ public final class PrivateCredentialPermission extends Permission { /** * Returns the class name of the credential associated with this permission. - * + * * @return the class name of the credential associated with this permission. */ public String getCredentialClass() { diff --git a/libcore/auth/src/main/java/javax/security/auth/Subject.java b/libcore/auth/src/main/java/javax/security/auth/Subject.java index 5a4ccebb8..5bf6bba25 100644 --- a/libcore/auth/src/main/java/javax/security/auth/Subject.java +++ b/libcore/auth/src/main/java/javax/security/auth/Subject.java @@ -51,8 +51,6 @@ import org.apache.harmony.auth.internal.nls.Messages; *
    6. Credentials (public and private) such as certificates, keys, or * authentication proofs such as tickets
    7. * - *

      - * @since Android 1.0 */ public final class Subject implements Serializable { @@ -104,7 +102,7 @@ public final class Subject implements Serializable { /** * The constructor for the subject, setting its public and private * credentials and principals according to the arguments. - * + * * @param readOnly * {@code true} if this {@code Subject} is read-only, thus * preventing any modifications to be done. @@ -135,7 +133,7 @@ public final class Subject implements Serializable { /** * Runs the code defined by {@code action} using the permissions granted to * the {@code Subject} itself and to the code as well. - * + * * @param subject * the distinguished {@code Subject}. * @param action @@ -154,7 +152,7 @@ public final class Subject implements Serializable { * Run the code defined by {@code action} using the permissions granted to * the {@code Subject} and to the code itself, additionally providing a more * specific context. - * + * * @param subject * the distinguished {@code Subject}. * @param action @@ -209,7 +207,7 @@ public final class Subject implements Serializable { /** * Runs the code defined by {@code action} using the permissions granted to * the subject and to the code itself. - * + * * @param subject * the distinguished {@code Subject}. * @param action @@ -231,7 +229,7 @@ public final class Subject implements Serializable { * Runs the code defined by {@code action} using the permissions granted to * the subject and to the code itself, additionally providing a more * specific context. - * + * * @param subject * the distinguished {@code Subject}. * @param action @@ -290,7 +288,7 @@ public final class Subject implements Serializable { * Checks two Subjects for equality. More specifically if the principals, * public and private credentials are equal, equality for two {@code * Subjects} is implied. - * + * * @param obj * the {@code Object} checked for equality with this {@code * Subject}. @@ -320,18 +318,18 @@ public final class Subject implements Serializable { /** * Returns this {@code Subject}'s {@link Principal}. - * + * * @return this {@code Subject}'s {@link Principal}. */ public Set getPrincipals() { return principals; } - + /** * Returns this {@code Subject}'s {@link Principal} which is a subclass of * the {@code Class} provided. - * + * * @param c * the {@code Class} as a criteria which the {@code Principal} * returned must satisfy. @@ -345,7 +343,7 @@ public final class Subject implements Serializable { /** * Returns the private credentials associated with this {@code Subject}. - * + * * @return the private credentials associated with this {@code Subject}. */ public Set getPrivateCredentials() { @@ -355,7 +353,7 @@ public final class Subject implements Serializable { /** * Returns this {@code Subject}'s private credentials which are a subclass * of the {@code Class} provided. - * + * * @param c * the {@code Class} as a criteria which the private credentials * returned must satisfy. @@ -369,18 +367,18 @@ public final class Subject implements Serializable { /** * Returns the public credentials associated with this {@code Subject}. - * + * * @return the public credentials associated with this {@code Subject}. */ public Set getPublicCredentials() { return publicCredentials; } - + /** * Returns this {@code Subject}'s public credentials which are a subclass of * the {@code Class} provided. - * + * * @param c * the {@code Class} as a criteria which the public credentials * returned must satisfy. @@ -394,7 +392,7 @@ public final class Subject implements Serializable { /** * Returns a hash code of this {@code Subject}. - * + * * @return a hash code of this {@code Subject}. */ @Override @@ -417,7 +415,7 @@ public final class Subject implements Serializable { /** * Returns whether this {@code Subject} is read-only or not. - * + * * @return whether this {@code Subject} is read-only or not. */ public boolean isReadOnly() { @@ -426,7 +424,7 @@ public final class Subject implements Serializable { /** * Returns a {@code String} representation of this {@code Subject}. - * + * * @return a {@code String} representation of this {@code Subject}. */ @Override @@ -479,7 +477,7 @@ public final class Subject implements Serializable { /** * Returns the {@code Subject} that was last associated with the {@code * context} provided as argument. - * + * * @param context * the {@code context} that was associated with the * {@code Subject}. @@ -781,4 +779,4 @@ public final class Subject implements Serializable { } } } -} +} \ No newline at end of file diff --git a/libcore/auth/src/main/java/javax/security/auth/SubjectDomainCombiner.java b/libcore/auth/src/main/java/javax/security/auth/SubjectDomainCombiner.java index 6a8f00b4b..4b9108415 100644 --- a/libcore/auth/src/main/java/javax/security/auth/SubjectDomainCombiner.java +++ b/libcore/auth/src/main/java/javax/security/auth/SubjectDomainCombiner.java @@ -25,8 +25,6 @@ import java.util.Set; /** * Merges permissions based on code source and code signers with permissions * granted to the specified {@link Subject}. - * - * @since Android 1.0 */ public class SubjectDomainCombiner implements DomainCombiner { @@ -39,7 +37,7 @@ public class SubjectDomainCombiner implements DomainCombiner { /** * Creates a domain combiner for the entity provided in {@code subject}. - * + * * @param subject * the entity to which this domain combiner is associated. */ @@ -53,7 +51,7 @@ public class SubjectDomainCombiner implements DomainCombiner { /** * Returns the entity to which this domain combiner is associated. - * + * * @return the entity to which this domain combiner is associated. */ public Subject getSubject() { @@ -68,7 +66,7 @@ public class SubjectDomainCombiner implements DomainCombiner { /** * Merges the {@code ProtectionDomain} with the {@code Principal}s * associated with the subject of this {@code SubjectDomainCombiner}. - * + * * @param currentDomains * the {@code ProtectionDomain}s associated with the context of * the current thread. The domains must be sorted according to diff --git a/libcore/auth/src/main/java/javax/security/auth/callback/Callback.java b/libcore/auth/src/main/java/javax/security/auth/callback/Callback.java index 6cf46b8fb..4854d3f4a 100644 --- a/libcore/auth/src/main/java/javax/security/auth/callback/Callback.java +++ b/libcore/auth/src/main/java/javax/security/auth/callback/Callback.java @@ -20,8 +20,6 @@ package javax.security.auth.callback; /** * Defines an empty base interface for all {@code Callback}s used during * authentication. - * - * @since Android 1.0 */ public interface Callback { } \ No newline at end of file diff --git a/libcore/auth/src/main/java/javax/security/auth/callback/CallbackHandler.java b/libcore/auth/src/main/java/javax/security/auth/callback/CallbackHandler.java index 952b81a2a..21bf30b0b 100644 --- a/libcore/auth/src/main/java/javax/security/auth/callback/CallbackHandler.java +++ b/libcore/auth/src/main/java/javax/security/auth/callback/CallbackHandler.java @@ -27,8 +27,6 @@ import java.io.IOException; * also possible to configure a system-default {@code CallbackHandler} by * setting the {@code auth.login.defaultCallbackHandler} property in the * standard {@code security.properties} file. - * - * @since Android 1.0 */ public interface CallbackHandler { @@ -42,7 +40,7 @@ public interface CallbackHandler { * values. If a {@code CallbackHandler} is not able to handle a specific * {@code Callback}, it needs to throw an * {@link UnsupportedCallbackException}. - * + * * @param callbacks * the array of {@code Callback}s that need handling * @throws IOException diff --git a/libcore/auth/src/main/java/javax/security/auth/callback/PasswordCallback.java b/libcore/auth/src/main/java/javax/security/auth/callback/PasswordCallback.java index 00020fe64..3617b75b1 100644 --- a/libcore/auth/src/main/java/javax/security/auth/callback/PasswordCallback.java +++ b/libcore/auth/src/main/java/javax/security/auth/callback/PasswordCallback.java @@ -25,8 +25,6 @@ import org.apache.harmony.auth.internal.nls.Messages; /** * Is used in conjunction with a {@link CallbackHandler} to retrieve a password * when needed. - * - * @since Android 1.0 */ public class PasswordCallback implements Callback, Serializable { @@ -47,7 +45,7 @@ public class PasswordCallback implements Callback, Serializable { /** * Creates a new {@code PasswordCallback} instance. - * + * * @param prompt * the message that should be displayed to the user * @param echoOn @@ -62,7 +60,7 @@ public class PasswordCallback implements Callback, Serializable { /** * Returns the prompt that was specified when creating this {@code * PasswordCallback} - * + * * @return the prompt */ public String getPrompt() { @@ -72,7 +70,7 @@ public class PasswordCallback implements Callback, Serializable { /** * Queries whether this {@code PasswordCallback} expects user input to be * echoed, which is specified during the creation of the object. - * + * * @return {@code true} if (and only if) user input should be echoed */ public boolean isEchoOn() { @@ -83,7 +81,7 @@ public class PasswordCallback implements Callback, Serializable { * Sets the password. The {@link CallbackHandler} that performs the actual * provisioning or input of the password needs to call this method to hand * back the password to the security service that requested it. - * + * * @param password * the password. A copy of this is stored, so subsequent changes * to the input array do not affect the {@code PasswordCallback}. @@ -101,7 +99,7 @@ public class PasswordCallback implements Callback, Serializable { * Returns the password. The security service that needs the password * usually calls this method once the {@link CallbackHandler} has finished * its work. - * + * * @return the password. A copy of the internal password is created and * returned, so subsequent changes to the internal password do not * affect the result. diff --git a/libcore/auth/src/main/java/javax/security/auth/callback/UnsupportedCallbackException.java b/libcore/auth/src/main/java/javax/security/auth/callback/UnsupportedCallbackException.java index d40ff45c7..bee7bd388 100644 --- a/libcore/auth/src/main/java/javax/security/auth/callback/UnsupportedCallbackException.java +++ b/libcore/auth/src/main/java/javax/security/auth/callback/UnsupportedCallbackException.java @@ -20,8 +20,6 @@ package javax.security.auth.callback; /** * Thrown when a {@link CallbackHandler} does not support a particular {@link * Callback}. - * - * @since Android 1.0 */ public class UnsupportedCallbackException extends Exception { @@ -32,7 +30,7 @@ public class UnsupportedCallbackException extends Exception { /** * Creates a new exception instance and initializes it with just the * unsupported {@code Callback}, but no error message. - * + * * @param callback * the {@code Callback} */ @@ -44,7 +42,7 @@ public class UnsupportedCallbackException extends Exception { /** * Creates a new exception instance and initializes it with both the * unsupported {@code Callback} and an error message. - * + * * @param callback * the {@code Callback} * @param message @@ -57,7 +55,7 @@ public class UnsupportedCallbackException extends Exception { /** * Returns the unsupported {@code Callback} that triggered this exception. - * + * * @return the {@code Callback} */ public Callback getCallback() { diff --git a/libcore/auth/src/main/java/javax/security/auth/login/LoginException.java b/libcore/auth/src/main/java/javax/security/auth/login/LoginException.java index a1d6ec06c..9433c43fe 100644 --- a/libcore/auth/src/main/java/javax/security/auth/login/LoginException.java +++ b/libcore/auth/src/main/java/javax/security/auth/login/LoginException.java @@ -21,8 +21,6 @@ import java.security.GeneralSecurityException; /** * Base class for exceptions that are thrown when a login error occurs. - * - * @since Android 1.0 */ public class LoginException extends GeneralSecurityException { @@ -37,7 +35,7 @@ public class LoginException extends GeneralSecurityException { /** * Creates a new exception instance and initializes it with a given message. - * + * * @param message the error message */ public LoginException(String message) { diff --git a/libcore/auth/src/main/java/javax/security/auth/x500/X500Principal.java b/libcore/auth/src/main/java/javax/security/auth/x500/X500Principal.java index efc57d1fa..19254a0d2 100644 --- a/libcore/auth/src/main/java/javax/security/auth/x500/X500Principal.java +++ b/libcore/auth/src/main/java/javax/security/auth/x500/X500Principal.java @@ -29,13 +29,11 @@ import org.apache.harmony.security.x501.Name; /** * Represents an X.500 principal, which holds the distinguished name of some - * network entity. An example of a distinguished name is {@code "O=Google, - * OU=Android, C=US"}. The class can be instantiated from a byte representation + * network entity. An example of a distinguished name is {@code "O=SomeOrg, + * OU=SomeOrgUnit, C=US"}. The class can be instantiated from a byte representation * of an object identifier (OID), an ASN.1 DER-encoded version, or a simple * string holding the distinguished name. The representations must follow either * RFC 2253, RFC 1779, or RFC2459. - * - * @since Android 1.0 */ public final class X500Principal implements Serializable, Principal { @@ -65,10 +63,10 @@ public final class X500Principal implements Serializable, Principal { /** * Creates a new X500Principal from a given ASN.1 DER encoding of a * distinguished name. - * + * * @param name * the ASN.1 DER-encoded distinguished name - * + * * @throws IllegalArgumentException * if the ASN.1 DER-encoded distinguished name is incorrect */ @@ -91,11 +89,11 @@ public final class X500Principal implements Serializable, Principal { /** * Creates a new X500Principal from a given ASN.1 DER encoding of a * distinguished name. - * + * * @param in * an {@code InputStream} holding the ASN.1 DER-encoded * distinguished name - * + * * @throws IllegalArgumentException * if the ASN.1 DER-encoded distinguished name is incorrect */ @@ -118,10 +116,10 @@ public final class X500Principal implements Serializable, Principal { /** * Creates a new X500Principal from a string representation of a * distinguished name. - * + * * @param name * the string representation of the distinguished name - * + * * @throws IllegalArgumentException * if the string representation of the distinguished name is * incorrect @@ -167,8 +165,8 @@ public final class X500Principal implements Serializable, Principal { /** * Returns an ASN.1 DER-encoded representation of the distinguished name - * contained in this X.500 principal. - * + * contained in this X.500 principal. + * * @return the ASN.1 DER-encoded representation */ public byte[] getEncoded() { @@ -179,9 +177,9 @@ public final class X500Principal implements Serializable, Principal { } /** - * Returns a human-readable string representation of the distinguished name + * Returns a human-readable string representation of the distinguished name * contained in this X.500 principal. - * + * * @return the string representation */ public String getName() { @@ -197,12 +195,12 @@ public final class X500Principal implements Serializable, Principal { * some canonicalizing operations like removing leading and trailing * whitespace, lower-casing the whole name, and bringing it into a * normalized Unicode representation. - * + * * @param format * the name of the format to use for the representation - * + * * @return the string representation - * + * * @throws IllegalArgumentException * if the {@code format} argument is not one of the three * mentioned above diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/AbstractExecutorService.java b/libcore/concurrent/src/main/java/java/util/concurrent/AbstractExecutorService.java index ed4ce4fa8..f7ed15cc3 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/AbstractExecutorService.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/AbstractExecutorService.java @@ -5,11 +5,10 @@ */ package java.util.concurrent; - import java.util.*; /** - * Provides default implementation of {@link ExecutorService} + * Provides default implementations of {@link ExecutorService} * execution methods. This class implements the submit, * invokeAny and invokeAll methods using the default * {@link FutureTask} class provided in this package. For example, @@ -24,6 +23,10 @@ import java.util.*; */ public abstract class AbstractExecutorService implements ExecutorService { + /** + * @throws RejectedExecutionException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + */ public Future submit(Runnable task) { if (task == null) throw new NullPointerException(); FutureTask ftask = new FutureTask(task, null); @@ -31,6 +34,10 @@ public abstract class AbstractExecutorService implements ExecutorService { return ftask; } + /** + * @throws RejectedExecutionException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + */ public Future submit(Runnable task, T result) { if (task == null) throw new NullPointerException(); FutureTask ftask = new FutureTask(task, result); @@ -38,6 +45,10 @@ public abstract class AbstractExecutorService implements ExecutorService { return ftask; } + /** + * @throws RejectedExecutionException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + */ public Future submit(Callable task) { if (task == null) throw new NullPointerException(); FutureTask ftask = new FutureTask(task); @@ -57,7 +68,7 @@ public abstract class AbstractExecutorService implements ExecutorService { if (ntasks == 0) throw new IllegalArgumentException(); List> futures= new ArrayList>(ntasks); - ExecutorCompletionService ecs = + ExecutorCompletionService ecs = new ExecutorCompletionService(this); // For efficiency, especially in executors with limited @@ -79,14 +90,14 @@ public abstract class AbstractExecutorService implements ExecutorService { int active = 1; for (;;) { - Future f = ecs.poll(); + Future f = ecs.poll(); if (f == null) { if (ntasks > 0) { --ntasks; futures.add(ecs.submit(it.next())); ++active; } - else if (active == 0) + else if (active == 0) break; else if (timed) { f = ecs.poll(nanos, TimeUnit.NANOSECONDS); @@ -96,29 +107,29 @@ public abstract class AbstractExecutorService implements ExecutorService { nanos -= now - lastTime; lastTime = now; } - else + else f = ecs.take(); } if (f != null) { --active; try { return f.get(); - } catch(InterruptedException ie) { + } catch (InterruptedException ie) { throw ie; - } catch(ExecutionException eex) { + } catch (ExecutionException eex) { ee = eex; - } catch(RuntimeException rex) { + } catch (RuntimeException rex) { ee = new ExecutionException(rex); } } - } + } if (ee == null) ee = new ExecutionException(); throw ee; } finally { - for (Future f : futures) + for (Future f : futures) f.cancel(true); } } @@ -133,8 +144,8 @@ public abstract class AbstractExecutorService implements ExecutorService { } } - public T invokeAny(Collection> tasks, - long timeout, TimeUnit unit) + public T invokeAny(Collection> tasks, + long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return doInvokeAny(tasks, true, unit.toNanos(timeout)); } @@ -153,10 +164,10 @@ public abstract class AbstractExecutorService implements ExecutorService { } for (Future f : futures) { if (!f.isDone()) { - try { - f.get(); - } catch(CancellationException ignore) { - } catch(ExecutionException ignore) { + try { + f.get(); + } catch (CancellationException ignore) { + } catch (ExecutionException ignore) { } } } @@ -164,13 +175,13 @@ public abstract class AbstractExecutorService implements ExecutorService { return futures; } finally { if (!done) - for (Future f : futures) + for (Future f : futures) f.cancel(true); } } - public List> invokeAll(Collection> tasks, - long timeout, TimeUnit unit) + public List> invokeAll(Collection> tasks, + long timeout, TimeUnit unit) throws InterruptedException { if (tasks == null || unit == null) throw new NullPointerException(); @@ -178,7 +189,7 @@ public abstract class AbstractExecutorService implements ExecutorService { List> futures = new ArrayList>(tasks.size()); boolean done = false; try { - for (Callable t : tasks) + for (Callable t : tasks) futures.add(new FutureTask(t)); long lastTime = System.nanoTime(); @@ -192,18 +203,18 @@ public abstract class AbstractExecutorService implements ExecutorService { nanos -= now - lastTime; lastTime = now; if (nanos <= 0) - return futures; + return futures; } for (Future f : futures) { if (!f.isDone()) { - if (nanos <= 0) - return futures; - try { - f.get(nanos, TimeUnit.NANOSECONDS); - } catch(CancellationException ignore) { - } catch(ExecutionException ignore) { - } catch(TimeoutException toe) { + if (nanos <= 0) + return futures; + try { + f.get(nanos, TimeUnit.NANOSECONDS); + } catch (CancellationException ignore) { + } catch (ExecutionException ignore) { + } catch (TimeoutException toe) { return futures; } long now = System.nanoTime(); @@ -215,7 +226,7 @@ public abstract class AbstractExecutorService implements ExecutorService { return futures; } finally { if (!done) - for (Future f : futures) + for (Future f : futures) f.cancel(true); } } diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/ArrayBlockingQueue.java b/libcore/concurrent/src/main/java/java/util/concurrent/ArrayBlockingQueue.java index 43b9fd09f..0082f0731 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/ArrayBlockingQueue.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/ArrayBlockingQueue.java @@ -24,8 +24,8 @@ import java.util.*; *

      This is a classic "bounded buffer", in which a * fixed-sized array holds elements inserted by producers and * extracted by consumers. Once created, the capacity cannot be - * increased. Attempts to offer an element to a full queue will - * result in the offer operation blocking; attempts to retrieve an + * increased. Attempts to put an element into a full queue + * will result in the operation blocking; attempts to take an * element from an empty queue will similarly block. * *

      This class supports an optional fairness policy for ordering @@ -35,8 +35,9 @@ import java.util.*; * generally decreases throughput but reduces variability and avoids * starvation. * - *

      This class implements all of the optional methods - * of the {@link Collection} and {@link Iterator} interfaces. + *

      This class and its iterator implement all of the + * optional methods of the {@link Collection} and {@link + * Iterator} interfaces. * * @since 1.5 * @author Doug Lea @@ -56,9 +57,9 @@ public class ArrayBlockingQueue extends AbstractQueue /** The queued items */ private final E[] items; /** items index for next take, poll or remove */ - private transient int takeIndex; + private int takeIndex; /** items index for next put, offer, or add. */ - private transient int putIndex; + private int putIndex; /** Number of items in the queue */ private int count; @@ -84,7 +85,7 @@ public class ArrayBlockingQueue extends AbstractQueue } /** - * Insert element at current put position, advance, and signal. + * Inserts element at current put position, advances, and signals. * Call only when holding lock. */ private void insert(E x) { @@ -95,7 +96,7 @@ public class ArrayBlockingQueue extends AbstractQueue } /** - * Extract element at current take position, advance, and signal. + * Extracts element at current take position, advances, and signals. * Call only when holding lock. */ private E extract() { @@ -139,6 +140,7 @@ public class ArrayBlockingQueue extends AbstractQueue /** * Creates an ArrayBlockingQueue with the given (fixed) * capacity and default access policy. + * * @param capacity the capacity of this queue * @throws IllegalArgumentException if capacity is less than 1 */ @@ -149,10 +151,11 @@ public class ArrayBlockingQueue extends AbstractQueue /** * Creates an ArrayBlockingQueue with the given (fixed) * capacity and the specified access policy. + * * @param capacity the capacity of this queue * @param fair if true then queue accesses for threads blocked - * on insertion or removal, are processed in FIFO order; if false - * the access order is unspecified. + * on insertion or removal, are processed in FIFO order; + * if false the access order is unspecified. * @throws IllegalArgumentException if capacity is less than 1 */ public ArrayBlockingQueue(int capacity, boolean fair) { @@ -169,15 +172,16 @@ public class ArrayBlockingQueue extends AbstractQueue * capacity, the specified access policy and initially containing the * elements of the given collection, * added in traversal order of the collection's iterator. + * * @param capacity the capacity of this queue * @param fair if true then queue accesses for threads blocked - * on insertion or removal, are processed in FIFO order; if false - * the access order is unspecified. + * on insertion or removal, are processed in FIFO order; + * if false the access order is unspecified. * @param c the collection of elements to initially contain * @throws IllegalArgumentException if capacity is less than - * c.size(), or less than 1. - * @throws NullPointerException if c or any element within it - * is null + * c.size(), or less than 1. + * @throws NullPointerException if the specified collection or any + * of its elements are null */ public ArrayBlockingQueue(int capacity, boolean fair, Collection c) { @@ -190,23 +194,38 @@ public class ArrayBlockingQueue extends AbstractQueue } /** - * Inserts the specified element at the tail of this queue if possible, - * returning immediately if this queue is full. + * Inserts the specified element at the tail of this queue if it is + * possible to do so immediately without exceeding the queue's capacity, + * returning true upon success and throwing an + * IllegalStateException if this queue is full. + * + * @param e the element to add + * @return true (as specified by {@link Collection#add}) + * @throws IllegalStateException if this queue is full + * @throws NullPointerException if the specified element is null + */ + public boolean add(E e) { + return super.add(e); + } + + /** + * Inserts the specified element at the tail of this queue if it is + * possible to do so immediately without exceeding the queue's capacity, + * returning true upon success and false if this queue + * is full. This method is generally preferable to method {@link #add}, + * which can fail to insert an element only by throwing an exception. * - * @param o the element to add. - * @return true if it was possible to add the element to - * this queue, else false - * @throws NullPointerException if the specified element is null + * @throws NullPointerException if the specified element is null */ - public boolean offer(E o) { - if (o == null) throw new NullPointerException(); + public boolean offer(E e) { + if (e == null) throw new NullPointerException(); final ReentrantLock lock = this.lock; lock.lock(); try { if (count == items.length) return false; else { - insert(o); + insert(e); return true; } } finally { @@ -215,29 +234,50 @@ public class ArrayBlockingQueue extends AbstractQueue } /** - * Inserts the specified element at the tail of this queue, waiting if - * necessary up to the specified wait time for space to become available. - * @param o the element to add - * @param timeout how long to wait before giving up, in units of - * unit - * @param unit a TimeUnit determining how to interpret the - * timeout parameter - * @return true if successful, or false if - * the specified waiting time elapses before space is available. - * @throws InterruptedException if interrupted while waiting. - * @throws NullPointerException if the specified element is null. + * Inserts the specified element at the tail of this queue, waiting + * for space to become available if the queue is full. + * + * @throws InterruptedException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + */ + public void put(E e) throws InterruptedException { + if (e == null) throw new NullPointerException(); + final E[] items = this.items; + final ReentrantLock lock = this.lock; + lock.lockInterruptibly(); + try { + try { + while (count == items.length) + notFull.await(); + } catch (InterruptedException ie) { + notFull.signal(); // propagate to non-interrupted thread + throw ie; + } + insert(e); + } finally { + lock.unlock(); + } + } + + /** + * Inserts the specified element at the tail of this queue, waiting + * up to the specified wait time for space to become available if + * the queue is full. + * + * @throws InterruptedException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} */ - public boolean offer(E o, long timeout, TimeUnit unit) + public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException { - if (o == null) throw new NullPointerException(); + if (e == null) throw new NullPointerException(); + long nanos = unit.toNanos(timeout); final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { - long nanos = unit.toNanos(timeout); for (;;) { if (count != items.length) { - insert(o); + insert(e); return true; } if (nanos <= 0) @@ -254,7 +294,6 @@ public class ArrayBlockingQueue extends AbstractQueue } } - public E poll() { final ReentrantLock lock = this.lock; lock.lock(); @@ -268,11 +307,29 @@ public class ArrayBlockingQueue extends AbstractQueue } } + public E take() throws InterruptedException { + final ReentrantLock lock = this.lock; + lock.lockInterruptibly(); + try { + try { + while (count == 0) + notEmpty.await(); + } catch (InterruptedException ie) { + notEmpty.signal(); // propagate to non-interrupted thread + throw ie; + } + E x = extract(); + return x; + } finally { + lock.unlock(); + } + } + public E poll(long timeout, TimeUnit unit) throws InterruptedException { + long nanos = unit.toNanos(timeout); final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { - long nanos = unit.toNanos(timeout); for (;;) { if (count != 0) { E x = extract(); @@ -293,30 +350,6 @@ public class ArrayBlockingQueue extends AbstractQueue } } - - public boolean remove(Object o) { - if (o == null) return false; - final E[] items = this.items; - final ReentrantLock lock = this.lock; - lock.lock(); - try { - int i = takeIndex; - int k = 0; - for (;;) { - if (k++ >= count) - return false; - if (o.equals(items[i])) { - removeAt(i); - return true; - } - i = inc(i); - } - - } finally { - lock.unlock(); - } - } - public E peek() { final ReentrantLock lock = this.lock; lock.lock(); @@ -327,56 +360,12 @@ public class ArrayBlockingQueue extends AbstractQueue } } - public E take() throws InterruptedException { - final ReentrantLock lock = this.lock; - lock.lockInterruptibly(); - try { - try { - while (count == 0) - notEmpty.await(); - } catch (InterruptedException ie) { - notEmpty.signal(); // propagate to non-interrupted thread - throw ie; - } - E x = extract(); - return x; - } finally { - lock.unlock(); - } - } - - /** - * Adds the specified element to the tail of this queue, waiting if - * necessary for space to become available. - * @param o the element to add - * @throws InterruptedException if interrupted while waiting. - * @throws NullPointerException if the specified element is null. - */ - public void put(E o) throws InterruptedException { - if (o == null) throw new NullPointerException(); - final E[] items = this.items; - final ReentrantLock lock = this.lock; - lock.lockInterruptibly(); - try { - try { - while (count == items.length) - notFull.await(); - } catch (InterruptedException ie) { - notFull.signal(); // propagate to non-interrupted thread - throw ie; - } - insert(o); - } finally { - lock.unlock(); - } - } - // this doc comment is overridden to remove the reference to collections // greater in size than Integer.MAX_VALUE /** * Returns the number of elements in this queue. * - * @return the number of elements in this queue. + * @return the number of elements in this queue */ public int size() { final ReentrantLock lock = this.lock; @@ -391,17 +380,15 @@ public class ArrayBlockingQueue extends AbstractQueue // this doc comment is a modified copy of the inherited doc comment, // without the reference to unlimited queues. /** - * Returns the number of elements that this queue can ideally (in - * the absence of memory or resource constraints) accept without + * Returns the number of additional elements that this queue can ideally + * (in the absence of memory or resource constraints) accept without * blocking. This is always equal to the initial capacity of this queue * less the current size of this queue. - *

      Note that you cannot always tell if - * an attempt to add an element will succeed by - * inspecting remainingCapacity because it may be the - * case that a waiting consumer is ready to take an - * element out of an otherwise full queue. - * - * @return the remaining capacity + * + *

      Note that you cannot always tell if an attempt to insert + * an element will succeed by inspecting remainingCapacity + * because it may be the case that another thread is about to + * insert or remove an element. */ public int remainingCapacity() { final ReentrantLock lock = this.lock; @@ -413,7 +400,48 @@ public class ArrayBlockingQueue extends AbstractQueue } } + /** + * Removes a single instance of the specified element from this queue, + * if it is present. More formally, removes an element e such + * that o.equals(e), if this queue contains one or more such + * elements. + * Returns true if this queue contained the specified element + * (or equivalently, if this queue changed as a result of the call). + * + * @param o element to be removed from this queue, if present + * @return true if this queue changed as a result of the call + */ + public boolean remove(Object o) { + if (o == null) return false; + final E[] items = this.items; + final ReentrantLock lock = this.lock; + lock.lock(); + try { + int i = takeIndex; + int k = 0; + for (;;) { + if (k++ >= count) + return false; + if (o.equals(items[i])) { + removeAt(i); + return true; + } + i = inc(i); + } + + } finally { + lock.unlock(); + } + } + /** + * Returns true if this queue contains the specified element. + * More formally, returns true if and only if this queue contains + * at least one element e such that o.equals(e). + * + * @param o object to be checked for containment in this queue + * @return true if this queue contains the specified element + */ public boolean contains(Object o) { if (o == null) return false; final E[] items = this.items; @@ -433,6 +461,19 @@ public class ArrayBlockingQueue extends AbstractQueue } } + /** + * Returns an array containing all of the elements in this queue, in + * proper sequence. + * + *

      The returned array will be "safe" in that no references to it are + * maintained by this queue. (In other words, this method must allocate + * a new array). The caller is thus free to modify the returned array. + * + *

      This method acts as bridge between array-based and collection-based + * APIs. + * + * @return an array containing all of the elements in this queue + */ public Object[] toArray() { final E[] items = this.items; final ReentrantLock lock = this.lock; @@ -451,6 +492,42 @@ public class ArrayBlockingQueue extends AbstractQueue } } + /** + * Returns an array containing all of the elements in this queue, in + * proper sequence; the runtime type of the returned array is that of + * the specified array. If the queue fits in the specified array, it + * is returned therein. Otherwise, a new array is allocated with the + * runtime type of the specified array and the size of this queue. + * + *

      If this queue fits in the specified array with room to spare + * (i.e., the array has more elements than this queue), the element in + * the array immediately following the end of the queue is set to + * null. + * + *

      Like the {@link #toArray()} method, this method acts as bridge between + * array-based and collection-based APIs. Further, this method allows + * precise control over the runtime type of the output array, and may, + * under certain circumstances, be used to save allocation costs. + * + *

      Suppose x is a queue known to contain only strings. + * The following code can be used to dump the queue into a newly + * allocated array of String: + * + *

      +     *     String[] y = x.toArray(new String[0]);
      + * + * Note that toArray(new Object[0]) is identical in function to + * toArray(). + * + * @param a the array into which the elements of the queue are to + * be stored, if it is big enough; otherwise, a new array of the + * same runtime type is allocated for this purpose + * @return an array containing all of the elements in this queue + * @throws ArrayStoreException if the runtime type of the specified array + * is not a supertype of the runtime type of every element in + * this queue + * @throws NullPointerException if the specified array is null + */ public T[] toArray(T[] a) { final E[] items = this.items; final ReentrantLock lock = this.lock; @@ -486,7 +563,10 @@ public class ArrayBlockingQueue extends AbstractQueue } } - + /** + * Atomically removes all of the elements from this queue. + * The queue will be empty after this call returns. + */ public void clear() { final E[] items = this.items; final ReentrantLock lock = this.lock; @@ -507,6 +587,12 @@ public class ArrayBlockingQueue extends AbstractQueue } } + /** + * @throws UnsupportedOperationException {@inheritDoc} + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + */ public int drainTo(Collection c) { if (c == null) throw new NullPointerException(); @@ -537,7 +623,12 @@ public class ArrayBlockingQueue extends AbstractQueue } } - + /** + * @throws UnsupportedOperationException {@inheritDoc} + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + */ public int drainTo(Collection c, int maxElements) { if (c == null) throw new NullPointerException(); @@ -574,12 +665,12 @@ public class ArrayBlockingQueue extends AbstractQueue /** * Returns an iterator over the elements in this queue in proper sequence. * The returned Iterator is a "weakly consistent" iterator that - * will never throw {@link java.util.ConcurrentModificationException}, + * will never throw {@link ConcurrentModificationException}, * and guarantees to traverse elements as they existed upon * construction of the iterator, and may (but is not guaranteed to) * reflect any modifications subsequent to construction. * - * @return an iterator over the elements in this queue in proper sequence. + * @return an iterator over the elements in this queue in proper sequence */ public Iterator iterator() { final ReentrantLock lock = this.lock; @@ -606,7 +697,7 @@ public class ArrayBlockingQueue extends AbstractQueue * that an element exists in hasNext(), we must return it in * the following next() call even if it was in the process of * being removed when hasNext() was called. - **/ + */ private E nextItem; /** @@ -635,7 +726,7 @@ public class ArrayBlockingQueue extends AbstractQueue } /** - * Check whether nextIndex is valid; if so setting nextItem. + * Checks whether nextIndex is valid; if so setting nextItem. * Stops iterator when either hits putIndex or sees null item. */ private void checkNext() { diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/BlockingQueue.java b/libcore/concurrent/src/main/java/java/util/concurrent/BlockingQueue.java index ddb7d77ad..d01c097f5 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/BlockingQueue.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/BlockingQueue.java @@ -15,9 +15,50 @@ import java.util.Queue; /** * A {@link java.util.Queue} that additionally supports operations - * that wait for the queue to become non-empty when retrieving an element, - * and wait for space to become available in the queue when storing an - * element. + * that wait for the queue to become non-empty when retrieving an + * element, and wait for space to become available in the queue when + * storing an element. + * + *

      BlockingQueue methods come in four forms, with different ways + * of handling operations that cannot be satisfied immediately, but may be + * satisfied at some point in the future: + * one throws an exception, the second returns a special value (either + * null or false, depending on the operation), the third + * blocks the current thread indefinitely until the operation can succeed, + * and the fourth blocks for only a given maximum time limit before giving + * up. These methods are summarized in the following table: + * + *

      + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
      Throws exceptionSpecial valueBlocksTimes out
      Insert{@link #add add(e)}{@link #offer offer(e)}{@link #put put(e)}{@link #offer offer(e, time, unit)}
      Remove{@link #remove remove()}{@link #poll poll()}{@link #take take()}{@link #poll(long, TimeUnit) poll(time, unit)}
      Examine{@link #element element()}{@link #peek peek()}not applicablenot applicable
      * *

      A BlockingQueue does not accept null elements. * Implementations throw NullPointerException on attempts @@ -31,16 +72,22 @@ import java.util.Queue; * A BlockingQueue without any intrinsic capacity constraints always * reports a remaining capacity of Integer.MAX_VALUE. * - *

      While BlockingQueue is designed to be used primarily - * for producer-consumer queues, it additionally supports the {@link - * java.util.Collection} interface. So, for example, it is possible - * to remove an arbitrary element from a queue using + *

      BlockingQueue implementations are designed to be used + * primarily for producer-consumer queues, but additionally support + * the {@link java.util.Collection} interface. So, for example, it is + * possible to remove an arbitrary element from a queue using * remove(x). However, such operations are in general * not performed very efficiently, and are intended for only - * occasional use, such as when a queued message is cancelled. Also, - * the bulk Collection operations, most notably addAll, are - * not necessarily performed atomically, so it is possible - * for addAll(c) to fail (throwing an exception) after adding + * occasional use, such as when a queued message is cancelled. + * + *

      BlockingQueue implementations are thread-safe. All + * queuing methods achieve their effects atomically using internal + * locks or other forms of concurrency control. However, the + * bulk Collection operations addAll, + * containsAll, retainAll and removeAll are + * not necessarily performed atomically unless specified + * otherwise in an implementation. So it is possible, for example, for + * addAll(c) to fail (throwing an exception) after adding * only some of the elements in c. * *

      A BlockingQueue does not intrinsically support @@ -61,7 +108,7 @@ import java.util.Queue; * Producer(BlockingQueue q) { queue = q; } * public void run() { * try { - * while(true) { queue.put(produce()); } + * while (true) { queue.put(produce()); } * } catch (InterruptedException ex) { ... handle ...} * } * Object produce() { ... } @@ -72,7 +119,7 @@ import java.util.Queue; * Consumer(BlockingQueue q) { queue = q; } * public void run() { * try { - * while(true) { consume(queue.take()); } + * while (true) { consume(queue.take()); } * } catch (InterruptedException ex) { ... handle ...} * } * void consume(Object x) { ... } @@ -91,137 +138,207 @@ import java.util.Queue; * } * * + *

      Memory consistency effects: As with other concurrent + * collections, actions in a thread prior to placing an object into a + * {@code BlockingQueue} + * happen-before + * actions subsequent to the access or removal of that element from + * the {@code BlockingQueue} in another thread. + * * @since 1.5 * @author Doug Lea * @param the type of elements held in this collection */ public interface BlockingQueue extends Queue { + /** + * Inserts the specified element into this queue if it is possible to do + * so immediately without violating capacity restrictions, returning + * true upon success and throwing an + * IllegalStateException if no space is currently available. + * When using a capacity-restricted queue, it is generally preferable to + * use {@link #offer offer}. + * + * @param e the element to add + * @return true (as specified by {@link Collection#add}) + * @throws IllegalStateException if the element cannot be added at this + * time due to capacity restrictions + * @throws ClassCastException if the class of the specified element + * prevents it from being added to this queue + * @throws NullPointerException if the specified element is null + * @throws IllegalArgumentException if some property of the specified + * element prevents it from being added to this queue + */ + boolean add(E e); /** - * Inserts the specified element into this queue, if possible. When - * using queues that may impose insertion restrictions (for - * example capacity bounds), method offer is generally - * preferable to method {@link Collection#add}, which can fail to - * insert an element only by throwing an exception. + * Inserts the specified element into this queue if it is possible to do + * so immediately without violating capacity restrictions, returning + * true upon success and false if no space is currently + * available. When using a capacity-restricted queue, this method is + * generally preferable to {@link #add}, which can fail to insert an + * element only by throwing an exception. * - * @param o the element to add. - * @return true if it was possible to add the element to - * this queue, else false - * @throws NullPointerException if the specified element is null + * @param e the element to add + * @return true if the element was added to this queue, else + * false + * @throws ClassCastException if the class of the specified element + * prevents it from being added to this queue + * @throws NullPointerException if the specified element is null + * @throws IllegalArgumentException if some property of the specified + * element prevents it from being added to this queue */ - boolean offer(E o); - + boolean offer(E e); + /** * Inserts the specified element into this queue, waiting if necessary - * up to the specified wait time for space to become available. - * @param o the element to add - * @param timeout how long to wait before giving up, in units of - * unit - * @param unit a TimeUnit determining how to interpret the - * timeout parameter - * @return true if successful, or false if - * the specified waiting time elapses before space is available. - * @throws InterruptedException if interrupted while waiting. - * @throws NullPointerException if the specified element is null. + * for space to become available. + * + * @param e the element to add + * @throws InterruptedException if interrupted while waiting + * @throws ClassCastException if the class of the specified element + * prevents it from being added to this queue + * @throws NullPointerException if the specified element is null + * @throws IllegalArgumentException if some property of the specified + * element prevents it from being added to this queue */ - boolean offer(E o, long timeout, TimeUnit unit) - throws InterruptedException; + void put(E e) throws InterruptedException; /** - * Retrieves and removes the head of this queue, waiting - * if necessary up to the specified wait time if no elements are - * present on this queue. + * Inserts the specified element into this queue, waiting up to the + * specified wait time if necessary for space to become available. + * + * @param e the element to add * @param timeout how long to wait before giving up, in units of - * unit + * unit * @param unit a TimeUnit determining how to interpret the - * timeout parameter - * @return the head of this queue, or null if the - * specified waiting time elapses before an element is present. - * @throws InterruptedException if interrupted while waiting. + * timeout parameter + * @return true if successful, or false if + * the specified waiting time elapses before space is available + * @throws InterruptedException if interrupted while waiting + * @throws ClassCastException if the class of the specified element + * prevents it from being added to this queue + * @throws NullPointerException if the specified element is null + * @throws IllegalArgumentException if some property of the specified + * element prevents it from being added to this queue */ - E poll(long timeout, TimeUnit unit) + boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException; /** - * Retrieves and removes the head of this queue, waiting - * if no elements are present on this queue. + * Retrieves and removes the head of this queue, waiting if necessary + * until an element becomes available. + * * @return the head of this queue - * @throws InterruptedException if interrupted while waiting. + * @throws InterruptedException if interrupted while waiting */ E take() throws InterruptedException; /** - * Adds the specified element to this queue, waiting if necessary for - * space to become available. - * @param o the element to add - * @throws InterruptedException if interrupted while waiting. - * @throws NullPointerException if the specified element is null. + * Retrieves and removes the head of this queue, waiting up to the + * specified wait time if necessary for an element to become available. + * + * @param timeout how long to wait before giving up, in units of + * unit + * @param unit a TimeUnit determining how to interpret the + * timeout parameter + * @return the head of this queue, or null if the + * specified waiting time elapses before an element is available + * @throws InterruptedException if interrupted while waiting */ - void put(E o) throws InterruptedException; + E poll(long timeout, TimeUnit unit) + throws InterruptedException; /** - * Returns the number of elements that this queue can ideally (in - * the absence of memory or resource constraints) accept without - * blocking, or Integer.MAX_VALUE if there is no - * intrinsic limit. - *

      Note that you cannot always tell if - * an attempt to add an element will succeed by - * inspecting remainingCapacity because it may be the - * case that a waiting consumer is ready to take an - * element out of an otherwise full queue. + * Returns the number of additional elements that this queue can ideally + * (in the absence of memory or resource constraints) accept without + * blocking, or Integer.MAX_VALUE if there is no intrinsic + * limit. + * + *

      Note that you cannot always tell if an attempt to insert + * an element will succeed by inspecting remainingCapacity + * because it may be the case that another thread is about to + * insert or remove an element. + * * @return the remaining capacity */ int remainingCapacity(); /** - * Adds the specified element to this queue if it is possible to - * do so immediately, returning true upon success, else - * throwing an IllegalStateException. - * @param o the element - * @return true (as per the general contract of - * Collection.add). + * Removes a single instance of the specified element from this queue, + * if it is present. More formally, removes an element e such + * that o.equals(e), if this queue contains one or more such + * elements. + * Returns true if this queue contained the specified element + * (or equivalently, if this queue changed as a result of the call). + * + * @param o element to be removed from this queue, if present + * @return true if this queue changed as a result of the call + * @throws ClassCastException if the class of the specified element + * is incompatible with this queue (optional) + * @throws NullPointerException if the specified element is null (optional) + */ + boolean remove(Object o); + + /** + * Returns true if this queue contains the specified element. + * More formally, returns true if and only if this queue contains + * at least one element e such that o.equals(e). * - * @throws NullPointerException if the specified element is null - * @throws IllegalStateException if element cannot be added + * @param o object to be checked for containment in this queue + * @return true if this queue contains the specified element + * @throws ClassCastException if the class of the specified element + * is incompatible with this queue (optional) + * @throws NullPointerException if the specified element is null (optional) */ - boolean add(E o); + public boolean contains(Object o); /** * Removes all available elements from this queue and adds them - * into the given collection. This operation may be more + * to the given collection. This operation may be more * efficient than repeatedly polling this queue. A failure - * encountered while attempting to add elements to + * encountered while attempting to add elements to * collection c may result in elements being in neither, * either or both collections when the associated exception is - * thrown. Attempts to drain a queue to itself result in + * thrown. Attempts to drain a queue to itself result in * IllegalArgumentException. Further, the behavior of * this operation is undefined if the specified collection is * modified while the operation is in progress. * * @param c the collection to transfer elements into - * @return the number of elements transferred. - * @throws NullPointerException if c is null - * @throws IllegalArgumentException if c is this queue - * + * @return the number of elements transferred + * @throws UnsupportedOperationException if addition of elements + * is not supported by the specified collection + * @throws ClassCastException if the class of an element of this queue + * prevents it from being added to the specified collection + * @throws NullPointerException if the specified collection is null + * @throws IllegalArgumentException if the specified collection is this + * queue, or some property of an element of this queue prevents + * it from being added to the specified collection */ int drainTo(Collection c); - + /** * Removes at most the given number of available elements from - * this queue and adds them into the given collection. A failure - * encountered while attempting to add elements to + * this queue and adds them to the given collection. A failure + * encountered while attempting to add elements to * collection c may result in elements being in neither, * either or both collections when the associated exception is - * thrown. Attempts to drain a queue to itself result in + * thrown. Attempts to drain a queue to itself result in * IllegalArgumentException. Further, the behavior of * this operation is undefined if the specified collection is * modified while the operation is in progress. * * @param c the collection to transfer elements into * @param maxElements the maximum number of elements to transfer - * @return the number of elements transferred. - * @throws NullPointerException if c is null - * @throws IllegalArgumentException if c is this queue + * @return the number of elements transferred + * @throws UnsupportedOperationException if addition of elements + * is not supported by the specified collection + * @throws ClassCastException if the class of an element of this queue + * prevents it from being added to the specified collection + * @throws NullPointerException if the specified collection is null + * @throws IllegalArgumentException if the specified collection is this + * queue, or some property of an element of this queue prevents + * it from being added to the specified collection */ int drainTo(Collection c, int maxElements); } diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/CompletionService.java b/libcore/concurrent/src/main/java/java/util/concurrent/CompletionService.java index e349e5f5e..df9f719c4 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/CompletionService.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/CompletionService.java @@ -16,52 +16,56 @@ package java.util.concurrent; * submitted in one part of a program or system, and then acted upon * in a different part of the program when the reads complete, * possibly in a different order than they were requested. - - *

      * - * Typically, a CompletionService relies on a separate {@link - * Executor} to actually execute the tasks, in which case the + *

      Typically, a CompletionService relies on a separate + * {@link Executor} to actually execute the tasks, in which case the * CompletionService only manages an internal completion * queue. The {@link ExecutorCompletionService} class provides an * implementation of this approach. * + *

      Memory consistency effects: Actions in a thread prior to + * submitting a task to a {@code CompletionService} + * happen-before + * actions taken by that task, which in turn happen-before + * actions following a successful return from the corresponding {@code take()}. + * */ public interface CompletionService { /** * Submits a value-returning task for execution and returns a Future - * representing the pending results of the task. Upon completion, + * representing the pending results of the task. Upon completion, * this task may be taken or polled. * * @param task the task to submit * @return a Future representing pending completion of the task - * @throws RejectedExecutionException if task cannot be scheduled - * for execution - * @throws NullPointerException if task null + * @throws RejectedExecutionException if the task cannot be + * scheduled for execution + * @throws NullPointerException if the task is null */ Future submit(Callable task); - /** - * Submits a Runnable task for execution and returns a Future - * representing that task. Upon completion, - * this task may be taken or polled. + * Submits a Runnable task for execution and returns a Future + * representing that task. Upon completion, this task may be + * taken or polled. * * @param task the task to submit * @param result the result to return upon successful completion * @return a Future representing pending completion of the task, - * and whose get() method will return the given result value - * upon completion - * @throws RejectedExecutionException if task cannot be scheduled - * for execution - * @throws NullPointerException if task null + * and whose get() method will return the given + * result value upon completion + * @throws RejectedExecutionException if the task cannot be + * scheduled for execution + * @throws NullPointerException if the task is null */ Future submit(Runnable task, V result); /** * Retrieves and removes the Future representing the next * completed task, waiting if none are yet present. + * * @return the Future representing the next completed task - * @throws InterruptedException if interrupted while waiting. + * @throws InterruptedException if interrupted while waiting */ Future take() throws InterruptedException; @@ -71,7 +75,7 @@ public interface CompletionService { * completed task or null if none are present. * * @return the Future representing the next completed task, or - * null if none are present. + * null if none are present */ Future poll(); @@ -79,14 +83,15 @@ public interface CompletionService { * Retrieves and removes the Future representing the next * completed task, waiting if necessary up to the specified wait * time if none are yet present. + * * @param timeout how long to wait before giving up, in units of - * unit + * unit * @param unit a TimeUnit determining how to interpret the - * timeout parameter + * timeout parameter * @return the Future representing the next completed task or - * null if the specified waiting time elapses before one - * is present. - * @throws InterruptedException if interrupted while waiting. + * null if the specified waiting time elapses + * before one is present + * @throws InterruptedException if interrupted while waiting */ Future poll(long timeout, TimeUnit unit) throws InterruptedException; } diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/ConcurrentHashMap.java b/libcore/concurrent/src/main/java/java/util/concurrent/ConcurrentHashMap.java index f72c6fee7..cb5fb3f0d 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/ConcurrentHashMap.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/ConcurrentHashMap.java @@ -14,7 +14,6 @@ import java.io.ObjectOutputStream; // BEGIN android-note // removed link to collections framework docs -// removed cloneable interface from ConcurrentMap interface // END android-note /** @@ -38,13 +37,12 @@ import java.io.ObjectOutputStream; * removal of only some entries. Similarly, Iterators and * Enumerations return elements reflecting the state of the hash table * at some point at or since the creation of the iterator/enumeration. - * They do not throw - * {@link ConcurrentModificationException}. However, iterators are - * designed to be used by only one thread at a time. + * They do not throw {@link ConcurrentModificationException}. + * However, iterators are designed to be used by only one thread at a time. * *

      The allowed concurrency among update operations is guided by * the optional concurrencyLevel constructor argument - * (default 16), which is used as a hint for internal sizing. The + * (default 16), which is used as a hint for internal sizing. The * table is internally partitioned to try to permit the indicated * number of concurrent updates without contention. Because placement * in hash tables is essentially random, the actual concurrency will @@ -54,20 +52,23 @@ import java.io.ObjectOutputStream; * and a significantly lower value can lead to thread contention. But * overestimates and underestimates within an order of magnitude do * not usually have much noticeable impact. A value of one is - * appropriate when it is known that only one thread will modify - * and all others will only read. + * appropriate when it is known that only one thread will modify and + * all others will only read. Also, resizing this or any other kind of + * hash table is a relatively slow operation, so, when possible, it is + * a good idea to provide estimates of expected table sizes in + * constructors. * - *

      This class implements all of the optional methods - * of the {@link Map} and {@link Iterator} interfaces. + *

      This class and its views and iterators implement all of the + * optional methods of the {@link Map} and {@link Iterator} + * interfaces. * - *

      Like {@link java.util.Hashtable} but unlike {@link - * java.util.HashMap}, this class does NOT allow null to be - * used as a key or value. + *

      Like {@link Hashtable} but unlike {@link HashMap}, this class + * does not allow null to be used as a key or value. * * @since 1.5 * @author Doug Lea * @param the type of keys maintained by this map - * @param the type of mapped values + * @param the type of mapped values */ public class ConcurrentHashMap extends AbstractMap implements ConcurrentMap, Serializable { @@ -81,29 +82,30 @@ public class ConcurrentHashMap extends AbstractMap /* ---------------- Constants -------------- */ /** - * The default initial number of table slots for this table. - * Used when not otherwise specified in constructor. + * The default initial capacity for this table, + * used when not otherwise specified in a constructor. */ - static int DEFAULT_INITIAL_CAPACITY = 16; + static final int DEFAULT_INITIAL_CAPACITY = 16; /** - * The maximum capacity, used if a higher value is implicitly - * specified by either of the constructors with arguments. MUST - * be a power of two <= 1<<30 to ensure that entries are indexible - * using ints. + * The default load factor for this table, used when not + * otherwise specified in a constructor. */ - static final int MAXIMUM_CAPACITY = 1 << 30; + static final float DEFAULT_LOAD_FACTOR = 0.75f; /** - * The default load factor for this table. Used when not - * otherwise specified in constructor. + * The default concurrency level for this table, used when not + * otherwise specified in a constructor. */ - static final float DEFAULT_LOAD_FACTOR = 0.75f; + static final int DEFAULT_CONCURRENCY_LEVEL = 16; /** - * The default number of concurrency control segments. - **/ - static final int DEFAULT_SEGMENTS = 16; + * The maximum capacity, used if a higher value is implicitly + * specified by either of the constructors with arguments. MUST + * be a power of two <= 1<<30 to ensure that entries are indexable + * using ints. + */ + static final int MAXIMUM_CAPACITY = 1 << 30; /** * The maximum number of segments to allow; used to bound @@ -111,23 +113,31 @@ public class ConcurrentHashMap extends AbstractMap */ static final int MAX_SEGMENTS = 1 << 16; // slightly conservative + /** + * Number of unsynchronized retries in size and containsValue + * methods before resorting to locking. This is used to avoid + * unbounded retries if tables undergo continuous modification + * which would make it impossible to obtain an accurate result. + */ + static final int RETRIES_BEFORE_LOCK = 2; + /* ---------------- Fields -------------- */ /** * Mask value for indexing into segments. The upper bits of a * key's hash code are used to choose the segment. - **/ + */ final int segmentMask; /** * Shift value for indexing within segments. - **/ + */ final int segmentShift; /** * The segments, each of which is a specialized hash table */ - final Segment[] segments; + final Segment[] segments; transient Set keySet; transient Set> entrySet; @@ -136,18 +146,21 @@ public class ConcurrentHashMap extends AbstractMap /* ---------------- Small Utilities -------------- */ /** - * Returns a hash code for non-null Object x. - * Uses the same hash code spreader as most other java.util hash tables. - * @param x the object serving as a key - * @return the hash code + * Applies a supplemental hash function to a given hashCode, which + * defends against poor quality hash functions. This is critical + * because ConcurrentHashMap uses power-of-two length hash tables, + * that otherwise encounter collisions for hashCodes that do not + * differ in lower or upper bits. */ - static int hash(Object x) { - int h = x.hashCode(); - h += ~(h << 9); - h ^= (h >>> 14); - h += (h << 4); - h ^= (h >>> 10); - return h; + private static int hash(int h) { + // Spread bits to regularize both segment and index locations, + // using variant of single-word Wang/Jenkins hash. + h += (h << 15) ^ 0xffffcd7d; + h ^= (h >>> 10); + h += (h << 3); + h ^= (h >>> 6); + h += (h << 2) + (h << 14); + return h ^ (h >>> 16); } /** @@ -156,16 +169,47 @@ public class ConcurrentHashMap extends AbstractMap * @return the segment */ final Segment segmentFor(int hash) { - return (Segment) segments[(hash >>> segmentShift) & segmentMask]; + return segments[(hash >>> segmentShift) & segmentMask]; } /* ---------------- Inner Classes -------------- */ + /** + * ConcurrentHashMap list entry. Note that this is never exported + * out as a user-visible Map.Entry. + * + * Because the value field is volatile, not final, it is legal wrt + * the Java Memory Model for an unsynchronized reader to see null + * instead of initial value when read via a data race. Although a + * reordering leading to this is not likely to ever actually + * occur, the Segment.readValueUnderLock method is used as a + * backup in case a null (pre-initialized) value is ever seen in + * an unsynchronized access method. + */ + static final class HashEntry { + final K key; + final int hash; + volatile V value; + final HashEntry next; + + HashEntry(K key, int hash, HashEntry next, V value) { + this.key = key; + this.hash = hash; + this.next = next; + this.value = value; + } + + @SuppressWarnings("unchecked") + static final HashEntry[] newArray(int i) { + return new HashEntry[i]; + } + } + /** * Segments are specialized versions of hash tables. This * subclasses from ReentrantLock opportunistically, just to * simplify some locking and avoid separate construction. - **/ + */ static final class Segment extends ReentrantLock implements Serializable { /* * Segments maintain a table of entry lists that are ALWAYS @@ -179,58 +223,59 @@ public class ConcurrentHashMap extends AbstractMap * is less than two for the default load factor threshold.) * * Read operations can thus proceed without locking, but rely - * on a memory barrier to ensure that completed write - * operations performed by other threads are - * noticed. Conveniently, the "count" field, tracking the - * number of elements, can also serve as the volatile variable - * providing proper read/write barriers. This is convenient - * because this field needs to be read in many read operations - * anyway. - * - * Implementors note. The basic rules for all this are: + * on selected uses of volatiles to ensure that completed + * write operations performed by other threads are + * noticed. For most purposes, the "count" field, tracking the + * number of elements, serves as that volatile variable + * ensuring visibility. This is convenient because this field + * needs to be read in many read operations anyway: * - * - All unsynchronized read operations must first read the + * - All (unsynchronized) read operations must first read the * "count" field, and should not look at table entries if * it is 0. * - * - All synchronized write operations should write to - * the "count" field after updating. The operations must not - * take any action that could even momentarily cause - * a concurrent read operation to see inconsistent - * data. This is made easier by the nature of the read - * operations in Map. For example, no operation + * - All (synchronized) write operations should write to + * the "count" field after structurally changing any bin. + * The operations must not take any action that could even + * momentarily cause a concurrent read operation to see + * inconsistent data. This is made easier by the nature of + * the read operations in Map. For example, no operation * can reveal that the table has grown but the threshold * has not yet been updated, so there are no atomicity * requirements for this with respect to reads. * - * As a guide, all critical volatile reads and writes are marked - * in code comments. + * As a guide, all critical volatile reads and writes to the + * count field are marked in code comments. */ private static final long serialVersionUID = 2249069246763182397L; /** * The number of elements in this segment's region. - **/ + */ transient volatile int count; /** - * Number of updates; used for checking lack of modifications - * in bulk-read methods. + * Number of updates that alter the size of the table. This is + * used during bulk-read methods to make sure they see a + * consistent snapshot: If modCounts change during a traversal + * of segments computing size or checking containsValue, then + * we might have an inconsistent view of state so (usually) + * must retry. */ transient int modCount; /** * The table is rehashed when its size exceeds this threshold. - * (The value of this field is always (int)(capacity * - * loadFactor).) + * (The value of this field is always (int)(capacity * + * loadFactor).) */ transient int threshold; /** - * The per-segment table + * The per-segment table. */ - transient HashEntry[] table; + transient volatile HashEntry[] table; /** * The load factor for the hash table. Even though this value @@ -242,29 +287,59 @@ public class ConcurrentHashMap extends AbstractMap Segment(int initialCapacity, float lf) { loadFactor = lf; - setTable(new HashEntry[initialCapacity]); + setTable(HashEntry.newArray(initialCapacity)); + } + + @SuppressWarnings("unchecked") + static final Segment[] newArray(int i) { + return new Segment[i]; } /** - * Set table to new HashEntry array. + * Sets table to new HashEntry array. * Call only while holding lock or in constructor. - **/ - void setTable(HashEntry[] newTable) { - table = newTable; + */ + void setTable(HashEntry[] newTable) { threshold = (int)(newTable.length * loadFactor); - count = count; // write-volatile + table = newTable; + } + + /** + * Returns properly casted first entry of bin for given hash. + */ + HashEntry getFirst(int hash) { + HashEntry[] tab = table; + return tab[hash & (tab.length - 1)]; + } + + /** + * Reads value field of an entry under lock. Called if value + * field ever appears to be null. This is possible only if a + * compiler happens to reorder a HashEntry initialization with + * its table assignment, which is legal under memory model + * but is not known to ever occur. + */ + V readValueUnderLock(HashEntry e) { + lock(); + try { + return e.value; + } finally { + unlock(); + } } /* Specialized implementations of map methods */ V get(Object key, int hash) { if (count != 0) { // read-volatile - HashEntry[] tab = table; - int index = hash & (tab.length - 1); - HashEntry e = (HashEntry) tab[index]; + HashEntry e = getFirst(hash); while (e != null) { - if (e.hash == hash && key.equals(e.key)) - return e.value; + if (e.hash == hash && key.equals(e.key)) { + V v = e.value; + if (v != null) + return v; + return readValueUnderLock(e); // recheck + } e = e.next; } } @@ -273,9 +348,7 @@ public class ConcurrentHashMap extends AbstractMap boolean containsKey(Object key, int hash) { if (count != 0) { // read-volatile - HashEntry[] tab = table; - int index = hash & (tab.length - 1); - HashEntry e = (HashEntry) tab[index]; + HashEntry e = getFirst(hash); while (e != null) { if (e.hash == hash && key.equals(e.key)) return true; @@ -287,12 +360,17 @@ public class ConcurrentHashMap extends AbstractMap boolean containsValue(Object value) { if (count != 0) { // read-volatile - HashEntry[] tab = table; + HashEntry[] tab = table; int len = tab.length; - for (int i = 0 ; i < len; i++) - for (HashEntry e = (HashEntry)tab[i] ; e != null ; e = e.next) - if (value.equals(e.value)) + for (int i = 0 ; i < len; i++) { + for (HashEntry e = tab[i]; e != null; e = e.next) { + V v = e.value; + if (v == null) // recheck + v = readValueUnderLock(e); + if (value.equals(v)) return true; + } + } } return false; } @@ -300,27 +378,16 @@ public class ConcurrentHashMap extends AbstractMap boolean replace(K key, int hash, V oldValue, V newValue) { lock(); try { - int c = count; - HashEntry[] tab = table; - int index = hash & (tab.length - 1); - HashEntry first = (HashEntry) tab[index]; - HashEntry e = first; - for (;;) { - if (e == null) - return false; - if (e.hash == hash && key.equals(e.key)) - break; + HashEntry e = getFirst(hash); + while (e != null && (e.hash != hash || !key.equals(e.key))) e = e.next; - } - V v = e.value; - if (v == null || !oldValue.equals(v)) - return false; - - e.value = newValue; - count = c; // write-volatile - return true; - + boolean replaced = false; + if (e != null && oldValue.equals(e.value)) { + replaced = true; + e.value = newValue; + } + return replaced; } finally { unlock(); } @@ -329,24 +396,16 @@ public class ConcurrentHashMap extends AbstractMap V replace(K key, int hash, V newValue) { lock(); try { - int c = count; - HashEntry[] tab = table; - int index = hash & (tab.length - 1); - HashEntry first = (HashEntry) tab[index]; - HashEntry e = first; - for (;;) { - if (e == null) - return null; - if (e.hash == hash && key.equals(e.key)) - break; + HashEntry e = getFirst(hash); + while (e != null && (e.hash != hash || !key.equals(e.key))) e = e.next; - } - V v = e.value; - e.value = newValue; - count = c; // write-volatile - return v; - + V oldValue = null; + if (e != null) { + oldValue = e.value; + e.value = newValue; + } + return oldValue; } finally { unlock(); } @@ -357,37 +416,38 @@ public class ConcurrentHashMap extends AbstractMap lock(); try { int c = count; - HashEntry[] tab = table; + if (c++ > threshold) // ensure capacity + rehash(); + HashEntry[] tab = table; int index = hash & (tab.length - 1); - HashEntry first = (HashEntry) tab[index]; + HashEntry first = tab[index]; + HashEntry e = first; + while (e != null && (e.hash != hash || !key.equals(e.key))) + e = e.next; - for (HashEntry e = first; e != null; e = (HashEntry) e.next) { - if (e.hash == hash && key.equals(e.key)) { - V oldValue = e.value; - if (!onlyIfAbsent) - e.value = value; - ++modCount; - count = c; // write-volatile - return oldValue; - } + V oldValue; + if (e != null) { + oldValue = e.value; + if (!onlyIfAbsent) + e.value = value; } - - tab[index] = new HashEntry(hash, key, value, first); - ++modCount; - ++c; - count = c; // write-volatile - if (c > threshold) - setTable(rehash(tab)); - return null; + else { + oldValue = null; + ++modCount; + tab[index] = new HashEntry(key, hash, first, value); + count = c; // write-volatile + } + return oldValue; } finally { unlock(); } } - HashEntry[] rehash(HashEntry[] oldTable) { + void rehash() { + HashEntry[] oldTable = table; int oldCapacity = oldTable.length; if (oldCapacity >= MAXIMUM_CAPACITY) - return oldTable; + return; /* * Reclassify nodes in each list to new Map. Because we are @@ -403,12 +463,13 @@ public class ConcurrentHashMap extends AbstractMap * right now. */ - HashEntry[] newTable = new HashEntry[oldCapacity << 1]; + HashEntry[] newTable = HashEntry.newArray(oldCapacity<<1); + threshold = (int)(newTable.length * loadFactor); int sizeMask = newTable.length - 1; for (int i = 0; i < oldCapacity ; i++) { // We need to guarantee that any existing reads of old Map can // proceed. So we cannot yet null out each bin. - HashEntry e = (HashEntry)oldTable[i]; + HashEntry e = oldTable[i]; if (e != null) { HashEntry next = e.next; @@ -436,15 +497,14 @@ public class ConcurrentHashMap extends AbstractMap // Clone all remaining nodes for (HashEntry p = e; p != lastRun; p = p.next) { int k = p.hash & sizeMask; - newTable[k] = new HashEntry(p.hash, - p.key, - p.value, - (HashEntry) newTable[k]); + HashEntry n = newTable[k]; + newTable[k] = new HashEntry(p.key, p.hash, + n, p.value); } } } } - return newTable; + table = newTable; } /** @@ -453,33 +513,31 @@ public class ConcurrentHashMap extends AbstractMap V remove(Object key, int hash, Object value) { lock(); try { - int c = count; - HashEntry[] tab = table; + int c = count - 1; + HashEntry[] tab = table; int index = hash & (tab.length - 1); - HashEntry first = (HashEntry)tab[index]; - + HashEntry first = tab[index]; HashEntry e = first; - for (;;) { - if (e == null) - return null; - if (e.hash == hash && key.equals(e.key)) - break; + while (e != null && (e.hash != hash || !key.equals(e.key))) e = e.next; - } - V oldValue = e.value; - if (value != null && !value.equals(oldValue)) - return null; - - // All entries following removed node can stay in list, but - // all preceding ones need to be cloned. - HashEntry newFirst = e.next; - for (HashEntry p = first; p != e; p = p.next) - newFirst = new HashEntry(p.hash, p.key, - p.value, newFirst); - tab[index] = newFirst; - ++modCount; - count = c-1; // write-volatile + V oldValue = null; + if (e != null) { + V v = e.value; + if (value == null || value.equals(v)) { + oldValue = v; + // All entries following removed node can stay + // in list, but all preceding ones need to be + // cloned. + ++modCount; + HashEntry newFirst = e.next; + for (HashEntry p = first; p != e; p = p.next) + newFirst = new HashEntry(p.key, p.hash, + newFirst, p.value); + tab[index] = newFirst; + count = c; // write-volatile + } + } return oldValue; } finally { unlock(); @@ -487,50 +545,37 @@ public class ConcurrentHashMap extends AbstractMap } void clear() { - lock(); - try { - HashEntry[] tab = table; - for (int i = 0; i < tab.length ; i++) - tab[i] = null; - ++modCount; - count = 0; // write-volatile - } finally { - unlock(); + if (count != 0) { + lock(); + try { + HashEntry[] tab = table; + for (int i = 0; i < tab.length ; i++) + tab[i] = null; + ++modCount; + count = 0; // write-volatile + } finally { + unlock(); + } } } } - /** - * ConcurrentHashMap list entry. Note that this is never exported - * out as a user-visible Map.Entry - */ - static final class HashEntry { - final K key; - V value; - final int hash; - final HashEntry next; - - HashEntry(int hash, K key, V value, HashEntry next) { - this.value = value; - this.hash = hash; - this.key = key; - this.next = next; - } - } /* ---------------- Public operations -------------- */ /** * Creates a new, empty map with the specified initial - * capacity and the specified load factor. + * capacity, load factor and concurrency level. * * @param initialCapacity the initial capacity. The implementation * performs internal sizing to accommodate this many elements. * @param loadFactor the load factor threshold, used to control resizing. + * Resizing may be performed when the average number of elements per + * bin exceeds this threshold. * @param concurrencyLevel the estimated number of concurrently * updating threads. The implementation performs internal sizing - * to try to accommodate this many threads. + * to try to accommodate this many threads. * @throws IllegalArgumentException if the initial capacity is * negative or the load factor or concurrencyLevel are * nonpositive. @@ -552,7 +597,7 @@ public class ConcurrentHashMap extends AbstractMap } segmentShift = 32 - sshift; segmentMask = ssize - 1; - this.segments = new Segment[ssize]; + this.segments = Segment.newArray(ssize); if (initialCapacity > MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY; @@ -568,44 +613,50 @@ public class ConcurrentHashMap extends AbstractMap } /** - * Creates a new, empty map with the specified initial - * capacity, and with default load factor and concurrencyLevel. + * Creates a new, empty map with the specified initial capacity, + * and with default load factor (0.75) and concurrencyLevel (16). * - * @param initialCapacity The implementation performs internal - * sizing to accommodate this many elements. + * @param initialCapacity the initial capacity. The implementation + * performs internal sizing to accommodate this many elements. * @throws IllegalArgumentException if the initial capacity of * elements is negative. */ public ConcurrentHashMap(int initialCapacity) { - this(initialCapacity, DEFAULT_LOAD_FACTOR, DEFAULT_SEGMENTS); + this(initialCapacity, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL); } /** - * Creates a new, empty map with a default initial capacity, - * load factor, and concurrencyLevel. + * Creates a new, empty map with a default initial capacity (16), + * load factor (0.75) and concurrencyLevel (16). */ public ConcurrentHashMap() { - this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_SEGMENTS); + this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL); } /** - * Creates a new map with the same mappings as the given map. The - * map is created with a capacity of twice the number of mappings in - * the given map or 11 (whichever is greater), and a default load factor. - * @param t the map + * Creates a new map with the same mappings as the given map. + * The map is created with a capacity of 1.5 times the number + * of mappings in the given map or 16 (whichever is greater), + * and a default load factor (0.75) and concurrencyLevel (16). + * + * @param m the map */ - public ConcurrentHashMap(Map t) { - this(Math.max((int) (t.size() / DEFAULT_LOAD_FACTOR) + 1, - 11), - DEFAULT_LOAD_FACTOR, DEFAULT_SEGMENTS); - putAll(t); + public ConcurrentHashMap(Map m) { + this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1, + DEFAULT_INITIAL_CAPACITY), + DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL); + putAll(m); } - // inherit Map javadoc + /** + * Returns true if this map contains no key-value mappings. + * + * @return true if this map contains no key-value mappings + */ public boolean isEmpty() { - final Segment[] segments = this.segments; + final Segment[] segments = this.segments; /* - * We need to keep track of per-segment modCounts to avoid ABA + * We keep track of per-segment modCounts to avoid ABA * problems in which an element in one segment was added and * in another removed during traversal, in which case the * table was never actually empty at any point. Note the @@ -618,7 +669,7 @@ public class ConcurrentHashMap extends AbstractMap for (int i = 0; i < segments.length; ++i) { if (segments[i].count != 0) return false; - else + else mcsum += mc[i] = segments[i].modCount; } // If mcsum happens to be zero, then we know we got a snapshot @@ -627,25 +678,35 @@ public class ConcurrentHashMap extends AbstractMap if (mcsum != 0) { for (int i = 0; i < segments.length; ++i) { if (segments[i].count != 0 || - mc[i] != segments[i].modCount) + mc[i] != segments[i].modCount) return false; } } return true; } - // inherit Map javadoc + /** + * Returns the number of key-value mappings in this map. If the + * map contains more than Integer.MAX_VALUE elements, returns + * Integer.MAX_VALUE. + * + * @return the number of key-value mappings in this map + */ public int size() { - final Segment[] segments = this.segments; + final Segment[] segments = this.segments; + long sum = 0; + long check = 0; int[] mc = new int[segments.length]; - for (;;) { - long sum = 0; + // Try a few times to get accurate count. On failure due to + // continuous async changes in table, resort to locking. + for (int k = 0; k < RETRIES_BEFORE_LOCK; ++k) { + check = 0; + sum = 0; int mcsum = 0; for (int i = 0; i < segments.length; ++i) { sum += segments[i].count; mcsum += mc[i] = segments[i].modCount; } - int check = 0; if (mcsum != 0) { for (int i = 0; i < segments.length; ++i) { check += segments[i].count; @@ -655,43 +716,51 @@ public class ConcurrentHashMap extends AbstractMap } } } - if (check == sum) { - if (sum > Integer.MAX_VALUE) - return Integer.MAX_VALUE; - else - return (int)sum; - } + if (check == sum) + break; + } + if (check != sum) { // Resort to locking all segments + sum = 0; + for (int i = 0; i < segments.length; ++i) + segments[i].lock(); + for (int i = 0; i < segments.length; ++i) + sum += segments[i].count; + for (int i = 0; i < segments.length; ++i) + segments[i].unlock(); } + if (sum > Integer.MAX_VALUE) + return Integer.MAX_VALUE; + else + return (int)sum; } - /** - * Returns the value to which the specified key is mapped in this table. + * Returns the value to which the specified key is mapped, + * or {@code null} if this map contains no mapping for the key. + * + *

      More formally, if this map contains a mapping from a key + * {@code k} to a value {@code v} such that {@code key.equals(k)}, + * then this method returns {@code v}; otherwise it returns + * {@code null}. (There can be at most one such mapping.) * - * @param key a key in the table. - * @return the value to which the key is mapped in this table; - * null if the key is not mapped to any value in - * this table. - * @throws NullPointerException if the key is - * null. + * @throws NullPointerException if the specified key is null */ public V get(Object key) { - int hash = hash(key); // throws NullPointerException if key null + int hash = hash(key.hashCode()); return segmentFor(hash).get(key, hash); } /** * Tests if the specified object is a key in this table. * - * @param key possible key. - * @return true if and only if the specified object - * is a key in this table, as determined by the - * equals method; false otherwise. - * @throws NullPointerException if the key is - * null. + * @param key possible key + * @return true if and only if the specified object + * is a key in this table, as determined by the + * equals method; false otherwise. + * @throws NullPointerException if the specified key is null */ public boolean containsKey(Object key) { - int hash = hash(key); // throws NullPointerException if key null + int hash = hash(key.hashCode()); return segmentFor(hash).containsKey(key, hash); } @@ -701,18 +770,22 @@ public class ConcurrentHashMap extends AbstractMap * traversal of the hash table, and so is much slower than * method containsKey. * - * @param value value whose presence in this map is to be tested. + * @param value value whose presence in this map is to be tested * @return true if this map maps one or more keys to the - * specified value. - * @throws NullPointerException if the value is null. + * specified value + * @throws NullPointerException if the specified value is null */ public boolean containsValue(Object value) { if (value == null) throw new NullPointerException(); - final Segment[] segments = this.segments; + // See explanation of modCount use above + + final Segment[] segments = this.segments; int[] mc = new int[segments.length]; - for (;;) { + + // Try a few times without locking + for (int k = 0; k < RETRIES_BEFORE_LOCK; ++k) { int sum = 0; int mcsum = 0; for (int i = 0; i < segments.length; ++i) { @@ -734,287 +807,217 @@ public class ConcurrentHashMap extends AbstractMap if (cleanSweep) return false; } + // Resort to locking all segments + for (int i = 0; i < segments.length; ++i) + segments[i].lock(); + boolean found = false; + try { + for (int i = 0; i < segments.length; ++i) { + if (segments[i].containsValue(value)) { + found = true; + break; + } + } + } finally { + for (int i = 0; i < segments.length; ++i) + segments[i].unlock(); + } + return found; } /** * Legacy method testing if some key maps into the specified value * in this table. This method is identical in functionality to - * {@link #containsValue}, and exists solely to ensure + * {@link #containsValue}, and exists solely to ensure * full compatibility with class {@link java.util.Hashtable}, * which supported this method prior to introduction of the * Java Collections framework. - * @param value a value to search for. - * @return true if and only if some key maps to the - * value argument in this table as - * determined by the equals method; - * false otherwise. - * @throws NullPointerException if the value is null. + * @param value a value to search for + * @return true if and only if some key maps to the + * value argument in this table as + * determined by the equals method; + * false otherwise + * @throws NullPointerException if the specified value is null */ public boolean contains(Object value) { return containsValue(value); } /** - * Maps the specified key to the specified - * value in this table. Neither the key nor the - * value can be null. + * Maps the specified key to the specified value in this table. + * Neither the key nor the value can be null. * *

      The value can be retrieved by calling the get method * with a key that is equal to the original key. * - * @param key the table key. - * @param value the value. - * @return the previous value of the specified key in this table, - * or null if it did not have one. - * @throws NullPointerException if the key or value is - * null. + * @param key key with which the specified value is to be associated + * @param value value to be associated with the specified key + * @return the previous value associated with key, or + * null if there was no mapping for key + * @throws NullPointerException if the specified key or value is null */ public V put(K key, V value) { if (value == null) throw new NullPointerException(); - int hash = hash(key); + int hash = hash(key.hashCode()); return segmentFor(hash).put(key, hash, value, false); } /** - * If the specified key is not already associated - * with a value, associate it with the given value. - * This is equivalent to - *

      -     *   if (!map.containsKey(key)) 
      -     *      return map.put(key, value);
      -     *   else
      -     *      return map.get(key);
      -     * 
      - * Except that the action is performed atomically. - * @param key key with which the specified value is to be associated. - * @param value value to be associated with the specified key. - * @return previous value associated with specified key, or null - * if there was no mapping for key. A null return can - * also indicate that the map previously associated null - * with the specified key, if the implementation supports - * null values. - * - * @throws UnsupportedOperationException if the put operation is - * not supported by this map. - * @throws ClassCastException if the class of the specified key or value - * prevents it from being stored in this map. - * @throws NullPointerException if the specified key or value is - * null. + * {@inheritDoc} * - **/ + * @return the previous value associated with the specified key, + * or null if there was no mapping for the key + * @throws NullPointerException if the specified key or value is null + */ public V putIfAbsent(K key, V value) { if (value == null) throw new NullPointerException(); - int hash = hash(key); + int hash = hash(key.hashCode()); return segmentFor(hash).put(key, hash, value, true); } - /** * Copies all of the mappings from the specified map to this one. - * * These mappings replace any mappings that this map had for any of the - * keys currently in the specified Map. + * keys currently in the specified map. * - * @param t Mappings to be stored in this map. + * @param m mappings to be stored in this map */ - public void putAll(Map t) { - for (Iterator> it = (Iterator>) t.entrySet().iterator(); it.hasNext(); ) { - Entry e = it.next(); + public void putAll(Map m) { + for (Map.Entry e : m.entrySet()) put(e.getKey(), e.getValue()); - } } /** - * Removes the key (and its corresponding value) from this - * table. This method does nothing if the key is not in the table. + * Removes the key (and its corresponding value) from this map. + * This method does nothing if the key is not in the map. * - * @param key the key that needs to be removed. - * @return the value to which the key had been mapped in this table, - * or null if the key did not have a mapping. - * @throws NullPointerException if the key is - * null. + * @param key the key that needs to be removed + * @return the previous value associated with key, or + * null if there was no mapping for key + * @throws NullPointerException if the specified key is null */ public V remove(Object key) { - int hash = hash(key); + int hash = hash(key.hashCode()); return segmentFor(hash).remove(key, hash, null); } /** - * Remove entry for key only if currently mapped to given value. - * Acts as - *
       
      -     *  if (map.get(key).equals(value)) {
      -     *     map.remove(key);
      -     *     return true;
      -     * } else return false;
      -     * 
      - * except that the action is performed atomically. - * @param key key with which the specified value is associated. - * @param value value associated with the specified key. - * @return true if the value was removed - * @throws NullPointerException if the specified key is - * null. + * {@inheritDoc} + * + * @throws NullPointerException if the specified key is null */ public boolean remove(Object key, Object value) { - int hash = hash(key); + int hash = hash(key.hashCode()); + if (value == null) + return false; return segmentFor(hash).remove(key, hash, value) != null; } - /** - * Replace entry for key only if currently mapped to given value. - * Acts as - *
       
      -     *  if (map.get(key).equals(oldValue)) {
      -     *     map.put(key, newValue);
      -     *     return true;
      -     * } else return false;
      -     * 
      - * except that the action is performed atomically. - * @param key key with which the specified value is associated. - * @param oldValue value expected to be associated with the specified key. - * @param newValue value to be associated with the specified key. - * @return true if the value was replaced - * @throws NullPointerException if the specified key or values are - * null. + * {@inheritDoc} + * + * @throws NullPointerException if any of the arguments are null */ public boolean replace(K key, V oldValue, V newValue) { if (oldValue == null || newValue == null) throw new NullPointerException(); - int hash = hash(key); + int hash = hash(key.hashCode()); return segmentFor(hash).replace(key, hash, oldValue, newValue); } /** - * Replace entry for key only if currently mapped to some value. - * Acts as - *
       
      -     *  if ((map.containsKey(key)) {
      -     *     return map.put(key, value);
      -     * } else return null;
      -     * 
      - * except that the action is performed atomically. - * @param key key with which the specified value is associated. - * @param value value to be associated with the specified key. - * @return previous value associated with specified key, or null - * if there was no mapping for key. - * @throws NullPointerException if the specified key or value is - * null. + * {@inheritDoc} + * + * @return the previous value associated with the specified key, + * or null if there was no mapping for the key + * @throws NullPointerException if the specified key or value is null */ public V replace(K key, V value) { if (value == null) throw new NullPointerException(); - int hash = hash(key); + int hash = hash(key.hashCode()); return segmentFor(hash).replace(key, hash, value); } - /** - * Removes all mappings from this map. + * Removes all of the mappings from this map. */ public void clear() { for (int i = 0; i < segments.length; ++i) segments[i].clear(); } - - // BEGIN android-removed - // /** - // * Returns a shallow copy of this - // * ConcurrentHashMap instance: the keys and - // * values themselves are not cloned. - // * - // * @return a shallow copy of this map. - // */ - // public Object clone() { - // // We cannot call super.clone, since it would share final - // // segments array, and there's no way to reassign finals. - // - // float lf = segments[0].loadFactor; - // int segs = segments.length; - // int cap = (int)(size() / lf); - // if (cap < segs) cap = segs; - // ConcurrentHashMap t = new ConcurrentHashMap(cap, lf, segs); - // t.putAll(this); - // return t; - // } - // END android-changed - /** - * Returns a set view of the keys contained in this map. The set is - * backed by the map, so changes to the map are reflected in the set, and - * vice-versa. The set supports element removal, which removes the - * corresponding mapping from this map, via the Iterator.remove, - * Set.remove, removeAll, retainAll, and - * clear operations. It does not support the add or + * Returns a {@link Set} view of the keys contained in this map. + * The set is backed by the map, so changes to the map are + * reflected in the set, and vice-versa. The set supports element + * removal, which removes the corresponding mapping from this map, + * via the Iterator.remove, Set.remove, + * removeAll, retainAll, and clear + * operations. It does not support the add or * addAll operations. - * The returned iterator is a "weakly consistent" iterator that - * will never throw {@link java.util.ConcurrentModificationException}, + * + *

      The view's iterator is a "weakly consistent" iterator + * that will never throw {@link ConcurrentModificationException}, * and guarantees to traverse elements as they existed upon * construction of the iterator, and may (but is not guaranteed to) * reflect any modifications subsequent to construction. - * - * @return a set view of the keys contained in this map. */ public Set keySet() { Set ks = keySet; return (ks != null) ? ks : (keySet = new KeySet()); } - /** - * Returns a collection view of the values contained in this map. The - * collection is backed by the map, so changes to the map are reflected in - * the collection, and vice-versa. The collection supports element - * removal, which removes the corresponding mapping from this map, via the - * Iterator.remove, Collection.remove, - * removeAll, retainAll, and clear operations. - * It does not support the add or addAll operations. - * The returned iterator is a "weakly consistent" iterator that - * will never throw {@link java.util.ConcurrentModificationException}, + * Returns a {@link Collection} view of the values contained in this map. + * The collection is backed by the map, so changes to the map are + * reflected in the collection, and vice-versa. The collection + * supports element removal, which removes the corresponding + * mapping from this map, via the Iterator.remove, + * Collection.remove, removeAll, + * retainAll, and clear operations. It does not + * support the add or addAll operations. + * + *

      The view's iterator is a "weakly consistent" iterator + * that will never throw {@link ConcurrentModificationException}, * and guarantees to traverse elements as they existed upon * construction of the iterator, and may (but is not guaranteed to) * reflect any modifications subsequent to construction. - * - * @return a collection view of the values contained in this map. */ public Collection values() { Collection vs = values; return (vs != null) ? vs : (values = new Values()); } - /** - * Returns a collection view of the mappings contained in this map. Each - * element in the returned collection is a Map.Entry. The - * collection is backed by the map, so changes to the map are reflected in - * the collection, and vice-versa. The collection supports element - * removal, which removes the corresponding mapping from the map, via the - * Iterator.remove, Collection.remove, - * removeAll, retainAll, and clear operations. - * It does not support the add or addAll operations. - * The returned iterator is a "weakly consistent" iterator that - * will never throw {@link java.util.ConcurrentModificationException}, + * Returns a {@link Set} view of the mappings contained in this map. + * The set is backed by the map, so changes to the map are + * reflected in the set, and vice-versa. The set supports element + * removal, which removes the corresponding mapping from the map, + * via the Iterator.remove, Set.remove, + * removeAll, retainAll, and clear + * operations. It does not support the add or + * addAll operations. + * + *

      The view's iterator is a "weakly consistent" iterator + * that will never throw {@link ConcurrentModificationException}, * and guarantees to traverse elements as they existed upon * construction of the iterator, and may (but is not guaranteed to) * reflect any modifications subsequent to construction. - * - * @return a collection view of the mappings contained in this map. */ public Set> entrySet() { Set> es = entrySet; - return (es != null) ? es : (entrySet = (Set>) (Set) new EntrySet()); + return (es != null) ? es : (entrySet = new EntrySet()); } - /** * Returns an enumeration of the keys in this table. * - * @return an enumeration of the keys in this table. - * @see #keySet + * @return an enumeration of the keys in this table + * @see #keySet() */ public Enumeration keys() { return new KeyIterator(); @@ -1023,8 +1026,8 @@ public class ConcurrentHashMap extends AbstractMap /** * Returns an enumeration of the values in this table. * - * @return an enumeration of the values in this table. - * @see #values + * @return an enumeration of the values in this table + * @see #values() */ public Enumeration elements() { return new ValueIterator(); @@ -1035,7 +1038,7 @@ public class ConcurrentHashMap extends AbstractMap abstract class HashIterator { int nextSegmentIndex; int nextTableIndex; - HashEntry[] currentTable; + HashEntry[] currentTable; HashEntry nextEntry; HashEntry lastReturned; @@ -1052,16 +1055,16 @@ public class ConcurrentHashMap extends AbstractMap return; while (nextTableIndex >= 0) { - if ( (nextEntry = (HashEntry)currentTable[nextTableIndex--]) != null) + if ( (nextEntry = currentTable[nextTableIndex--]) != null) return; } while (nextSegmentIndex >= 0) { - Segment seg = (Segment)segments[nextSegmentIndex--]; + Segment seg = segments[nextSegmentIndex--]; if (seg.count != 0) { currentTable = seg.table; for (int j = currentTable.length - 1; j >= 0; --j) { - if ( (nextEntry = (HashEntry)currentTable[j]) != null) { + if ( (nextEntry = currentTable[j]) != null) { nextTableIndex = j - 1; return; } @@ -1088,81 +1091,58 @@ public class ConcurrentHashMap extends AbstractMap } } - final class KeyIterator extends HashIterator implements Iterator, Enumeration { - public K next() { return super.nextEntry().key; } + final class KeyIterator + extends HashIterator + implements Iterator, Enumeration + { + public K next() { return super.nextEntry().key; } public K nextElement() { return super.nextEntry().key; } } - final class ValueIterator extends HashIterator implements Iterator, Enumeration { - public V next() { return super.nextEntry().value; } + final class ValueIterator + extends HashIterator + implements Iterator, Enumeration + { + public V next() { return super.nextEntry().value; } public V nextElement() { return super.nextEntry().value; } } - - /** - * Entry iterator. Exported Entry objects must write-through - * changes in setValue, even if the nodes have been cloned. So we - * cannot return internal HashEntry objects. Instead, the iterator - * itself acts as a forwarding pseudo-entry. + * Custom Entry class used by EntryIterator.next(), that relays + * setValue changes to the underlying map. */ - final class EntryIterator extends HashIterator implements Map.Entry, Iterator> { - public Map.Entry next() { - nextEntry(); - return this; - } - - public K getKey() { - if (lastReturned == null) - throw new IllegalStateException("Entry was removed"); - return lastReturned.key; - } - - public V getValue() { - if (lastReturned == null) - throw new IllegalStateException("Entry was removed"); - return ConcurrentHashMap.this.get(lastReturned.key); + final class WriteThroughEntry + extends SimpleEntry + { + WriteThroughEntry(K k, V v) { + super(k,v); } + /** + * Set our entry's value and write through to the map. The + * value to return is somewhat arbitrary here. Since a + * WriteThroughEntry does not necessarily track asynchronous + * changes, the most recent "previous" value could be + * different from what we return (or could even have been + * removed in which case the put will re-establish). We do not + * and cannot guarantee more. + */ public V setValue(V value) { - if (lastReturned == null) - throw new IllegalStateException("Entry was removed"); - return ConcurrentHashMap.this.put(lastReturned.key, value); - } - - public boolean equals(Object o) { - // If not acting as entry, just use default. - if (lastReturned == null) - return super.equals(o); - if (!(o instanceof Map.Entry)) - return false; - Map.Entry e = (Map.Entry)o; - return eq(getKey(), e.getKey()) && eq(getValue(), e.getValue()); - } - - public int hashCode() { - // If not acting as entry, just use default. - if (lastReturned == null) - return super.hashCode(); - - Object k = getKey(); - Object v = getValue(); - return ((k == null) ? 0 : k.hashCode()) ^ - ((v == null) ? 0 : v.hashCode()); - } - - public String toString() { - // If not acting as entry, just use default. - if (lastReturned == null) - return super.toString(); - else - return getKey() + "=" + getValue(); + if (value == null) throw new NullPointerException(); + V v = super.setValue(value); + ConcurrentHashMap.this.put(getKey(), value); + return v; } + } - boolean eq(Object o1, Object o2) { - return (o1 == null ? o2 == null : o1.equals(o2)); + final class EntryIterator + extends HashIterator + implements Iterator> + { + public Map.Entry next() { + HashEntry e = super.nextEntry(); + return new WriteThroughEntry(e.key, e.value); } - } final class KeySet extends AbstractSet { @@ -1172,6 +1152,9 @@ public class ConcurrentHashMap extends AbstractMap public int size() { return ConcurrentHashMap.this.size(); } + public boolean isEmpty() { + return ConcurrentHashMap.this.isEmpty(); + } public boolean contains(Object o) { return ConcurrentHashMap.this.containsKey(o); } @@ -1190,6 +1173,9 @@ public class ConcurrentHashMap extends AbstractMap public int size() { return ConcurrentHashMap.this.size(); } + public boolean isEmpty() { + return ConcurrentHashMap.this.isEmpty(); + } public boolean contains(Object o) { return ConcurrentHashMap.this.containsValue(o); } @@ -1205,44 +1191,32 @@ public class ConcurrentHashMap extends AbstractMap public boolean contains(Object o) { if (!(o instanceof Map.Entry)) return false; - Map.Entry e = (Map.Entry)o; + Map.Entry e = (Map.Entry)o; V v = ConcurrentHashMap.this.get(e.getKey()); return v != null && v.equals(e.getValue()); } public boolean remove(Object o) { if (!(o instanceof Map.Entry)) return false; - Map.Entry e = (Map.Entry)o; + Map.Entry e = (Map.Entry)o; return ConcurrentHashMap.this.remove(e.getKey(), e.getValue()); } public int size() { return ConcurrentHashMap.this.size(); } + public boolean isEmpty() { + return ConcurrentHashMap.this.isEmpty(); + } public void clear() { ConcurrentHashMap.this.clear(); } - public Object[] toArray() { - // Since we don't ordinarily have distinct Entry objects, we - // must pack elements using exportable SimpleEntry - Collection> c = new ArrayList>(size()); - for (Iterator> i = iterator(); i.hasNext(); ) - c.add(new SimpleEntry(i.next())); - return c.toArray(); - } - public T[] toArray(T[] a) { - Collection> c = new ArrayList>(size()); - for (Iterator> i = iterator(); i.hasNext(); ) - c.add(new SimpleEntry(i.next())); - return c.toArray(a); - } - } /** * This duplicates java.util.AbstractMap.SimpleEntry until this class * is made accessible. */ - static final class SimpleEntry implements Entry { + static class SimpleEntry implements Entry { K key; V value; @@ -1279,7 +1253,7 @@ public class ConcurrentHashMap extends AbstractMap public int hashCode() { return ((key == null) ? 0 : key.hashCode()) ^ - ((value == null) ? 0 : value.hashCode()); + ((value == null) ? 0 : value.hashCode()); } public String toString() { @@ -1291,12 +1265,11 @@ public class ConcurrentHashMap extends AbstractMap } } - /* ---------------- Serialization Support -------------- */ + /* ---------------- Serialization Support -------------- */ /** - * Save the state of the ConcurrentHashMap - * instance to a stream (i.e., - * serialize it). + * Save the state of the ConcurrentHashMap instance to a + * stream (i.e., serialize it). * @param s the stream * @serialData * the key (Object) and value (Object) @@ -1307,12 +1280,12 @@ public class ConcurrentHashMap extends AbstractMap s.defaultWriteObject(); for (int k = 0; k < segments.length; ++k) { - Segment seg = (Segment)segments[k]; + Segment seg = segments[k]; seg.lock(); try { - HashEntry[] tab = seg.table; + HashEntry[] tab = seg.table; for (int i = 0; i < tab.length; ++i) { - for (HashEntry e = (HashEntry)tab[i]; e != null; e = e.next) { + for (HashEntry e = tab[i]; e != null; e = e.next) { s.writeObject(e.key); s.writeObject(e.value); } @@ -1326,9 +1299,8 @@ public class ConcurrentHashMap extends AbstractMap } /** - * Reconstitute the ConcurrentHashMap - * instance from a stream (i.e., - * deserialize it). + * Reconstitute the ConcurrentHashMap instance from a + * stream (i.e., deserialize it). * @param s the stream */ private void readObject(java.io.ObjectInputStream s) @@ -1350,4 +1322,3 @@ public class ConcurrentHashMap extends AbstractMap } } } - diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/ConcurrentLinkedQueue.java b/libcore/concurrent/src/main/java/java/util/concurrent/ConcurrentLinkedQueue.java index 794142da3..2253823e9 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/ConcurrentLinkedQueue.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/ConcurrentLinkedQueue.java @@ -14,7 +14,7 @@ import java.util.concurrent.atomic.*; // END android-note /** - * An unbounded thread-safe {@linkplain Queue queue} based on linked nodes. + * An unbounded thread-safe {@linkplain Queue queue} based on linked nodes. * This queue orders elements FIFO (first-in-first-out). * The head of the queue is that element that has been on the * queue the longest time. @@ -22,23 +22,31 @@ import java.util.concurrent.atomic.*; * queue the shortest time. New elements * are inserted at the tail of the queue, and the queue retrieval * operations obtain elements at the head of the queue. - * A ConcurrentLinkedQueue is an appropriate choice when + * A {@code ConcurrentLinkedQueue} is an appropriate choice when * many threads will share access to a common collection. - * This queue does not permit null elements. + * This queue does not permit {@code null} elements. * - *

      This implementation employs an efficient "wait-free" + *

      This implementation employs an efficient "wait-free" * algorithm based on one described in Simple, * Fast, and Practical Non-Blocking and Blocking Concurrent Queue * Algorithms by Maged M. Michael and Michael L. Scott. * - *

      Beware that, unlike in most collections, the size method + *

      Beware that, unlike in most collections, the {@code size} method * is NOT a constant-time operation. Because of the * asynchronous nature of these queues, determining the current number * of elements requires a traversal of the elements. * - *

      This class implements all of the optional methods - * of the {@link Collection} and {@link Iterator} interfaces. + *

      This class and its iterator implement all of the + * optional methods of the {@link Collection} and {@link + * Iterator} interfaces. + * + *

      Memory consistency effects: As with other concurrent + * collections, actions in a thread prior to placing an object into a + * {@code ConcurrentLinkedQueue} + * happen-before + * actions subsequent to the access or removal of that element from + * the {@code ConcurrentLinkedQueue} in another thread. * * @since 1.5 * @author Doug Lea @@ -50,68 +58,126 @@ public class ConcurrentLinkedQueue extends AbstractQueue private static final long serialVersionUID = 196745693267521676L; /* - * This is a straight adaptation of Michael & Scott algorithm. - * For explanation, read the paper. The only (minor) algorithmic - * difference is that this version supports lazy deletion of - * internal nodes (method remove(Object)) -- remove CAS'es item - * fields to null. The normal queue operations unlink but then - * pass over nodes with null item fields. Similarly, iteration - * methods ignore those with nulls. + * This is a modification of the Michael & Scott algorithm, + * adapted for a garbage-collected environment, with support for + * interior node deletion (to support remove(Object)). For + * explanation, read the paper. + * + * Note that like most non-blocking algorithms in this package, + * this implementation relies on the fact that in garbage + * collected systems, there is no possibility of ABA problems due + * to recycled nodes, so there is no need to use "counted + * pointers" or related techniques seen in versions used in + * non-GC'ed settings. + * + * The fundamental invariants are: + * - There is exactly one (last) Node with a null next reference, + * which is CASed when enqueueing. This last Node can be + * reached in O(1) time from tail, but tail is merely an + * optimization - it can always be reached in O(N) time from + * head as well. + * - The elements contained in the queue are the non-null items in + * Nodes that are reachable from head. CASing the item + * reference of a Node to null atomically removes it from the + * queue. Reachability of all elements from head must remain + * true even in the case of concurrent modifications that cause + * head to advance. A dequeued Node may remain in use + * indefinitely due to creation of an Iterator or simply a + * poll() that has lost its time slice. + * + * The above might appear to imply that all Nodes are GC-reachable + * from a predecessor dequeued Node. That would cause two problems: + * - allow a rogue Iterator to cause unbounded memory retention + * - cause cross-generational linking of old Nodes to new Nodes if + * a Node was tenured while live, which generational GCs have a + * hard time dealing with, causing repeated major collections. + * However, only non-deleted Nodes need to be reachable from + * dequeued Nodes, and reachability does not necessarily have to + * be of the kind understood by the GC. We use the trick of + * linking a Node that has just been dequeued to itself. Such a + * self-link implicitly means to advance to head. + * + * Both head and tail are permitted to lag. In fact, failing to + * update them every time one could is a significant optimization + * (fewer CASes). This is controlled by local "hops" variables + * that only trigger helping-CASes after experiencing multiple + * lags. + * + * Since head and tail are updated concurrently and independently, + * it is possible for tail to lag behind head (why not)? + * + * CASing a Node's item reference to null atomically removes the + * element from the queue. Iterators skip over Nodes with null + * items. Prior implementations of this class had a race between + * poll() and remove(Object) where the same element would appear + * to be successfully removed by two concurrent operations. The + * method remove(Object) also lazily unlinks deleted Nodes, but + * this is merely an optimization. + * + * When constructing a Node (before enqueuing it) we avoid paying + * for a volatile write to item by using lazySet instead of a + * normal write. This allows the cost of enqueue to be + * "one-and-a-half" CASes. + * + * Both head and tail may or may not point to a Node with a + * non-null item. If the queue is empty, all items must of course + * be null. Upon creation, both head and tail refer to a dummy + * Node with null item. Both head and tail are only updated using + * CAS, so they never regress, although again this is merely an + * optimization. */ - private static class Node { private volatile E item; private volatile Node next; - - private static final - AtomicReferenceFieldUpdater + + private static final + AtomicReferenceFieldUpdater nextUpdater = AtomicReferenceFieldUpdater.newUpdater (Node.class, Node.class, "next"); - private static final - AtomicReferenceFieldUpdater + private static final + AtomicReferenceFieldUpdater itemUpdater = AtomicReferenceFieldUpdater.newUpdater (Node.class, Object.class, "item"); - - Node(E x) { item = x; } - - Node(E x, Node n) { item = x; next = n; } - + + + Node(E item) { setItem(item); } + E getItem() { return item; } - + boolean casItem(E cmp, E val) { return itemUpdater.compareAndSet(this, cmp, val); } - + void setItem(E val) { itemUpdater.set(this, val); } - + Node getNext() { return next; } - + boolean casNext(Node cmp, Node val) { return nextUpdater.compareAndSet(this, cmp, val); } - + void setNext(Node val) { nextUpdater.set(this, val); - } - } - private static final - AtomicReferenceFieldUpdater - tailUpdater = + } + + private static final + AtomicReferenceFieldUpdater + tailUpdater = AtomicReferenceFieldUpdater.newUpdater (ConcurrentLinkedQueue.class, Node.class, "tail"); - private static final - AtomicReferenceFieldUpdater - headUpdater = + private static final + AtomicReferenceFieldUpdater + headUpdater = AtomicReferenceFieldUpdater.newUpdater (ConcurrentLinkedQueue.class, Node.class, "head"); @@ -124,118 +190,141 @@ public class ConcurrentLinkedQueue extends AbstractQueue } + /** - * Pointer to header node, initialized to a dummy node. The first - * actual node is at head.getNext(). + * Pointer to first node, initialized to a dummy node. */ - private transient volatile Node head = new Node(null, null); + private transient volatile Node head = new Node(null); - /** Pointer to last node on list **/ + /** Pointer to last node on list */ private transient volatile Node tail = head; /** - * Creates a ConcurrentLinkedQueue that is initially empty. + * Creates a {@code ConcurrentLinkedQueue} that is initially empty. */ public ConcurrentLinkedQueue() {} /** - * Creates a ConcurrentLinkedQueue + * Creates a {@code ConcurrentLinkedQueue} * initially containing the elements of the given collection, * added in traversal order of the collection's iterator. * @param c the collection of elements to initially contain - * @throws NullPointerException if c or any element within it - * is null + * @throws NullPointerException if the specified collection or any + * of its elements are null */ public ConcurrentLinkedQueue(Collection c) { for (Iterator it = c.iterator(); it.hasNext();) add(it.next()); } - // Have to override just to update the javadoc + // Have to override just to update the javadoc /** - * Adds the specified element to the tail of this queue. - * @param o the element to add. - * @return true (as per the general contract of - * Collection.add). + * Inserts the specified element at the tail of this queue. * - * @throws NullPointerException if the specified element is null + * @return {@code true} (as specified by {@link Collection#add}) + * @throws NullPointerException if the specified element is null */ - public boolean add(E o) { - return offer(o); + public boolean add(E e) { + return offer(e); } /** - * Inserts the specified element to the tail of this queue. + * We don't bother to update head or tail pointers if less than + * HOPS links from "true" location. We assume that volatile + * writes are significantly more expensive than volatile reads. + */ + private static final int HOPS = 1; + + /** + * Try to CAS head to p. If successful, repoint old head to itself + * as sentinel for succ(), below. + */ + final void updateHead(Node h, Node p) { + if (h != p && casHead(h, p)) + h.setNext(h); + } + + /** + * Returns the successor of p, or the head node if p.next has been + * linked to self, which will only be true if traversing with a + * stale pointer that is now off the list. + */ + final Node succ(Node p) { + Node next = p.getNext(); + return (p == next) ? head : next; + } + + /** + * Inserts the specified element at the tail of this queue. * - * @param o the element to add. - * @return true (as per the general contract of - * Queue.offer). - * @throws NullPointerException if the specified element is null + * @return {@code true} (as specified by {@link Queue#offer}) + * @throws NullPointerException if the specified element is null */ - public boolean offer(E o) { - if (o == null) throw new NullPointerException(); - Node n = new Node(o, null); - for(;;) { + public boolean offer(E e) { + if (e == null) throw new NullPointerException(); + Node n = new Node(e); + retry: + for (;;) { Node t = tail; - Node s = t.getNext(); - if (t == tail) { - if (s == null) { - if (t.casNext(s, n)) { - casTail(t, n); - return true; - } + Node p = t; + for (int hops = 0; ; hops++) { + Node next = succ(p); + if (next != null) { + if (hops > HOPS && t != tail) + continue retry; + p = next; + } else if (p.casNext(null, n)) { + if (hops >= HOPS) + casTail(t, n); // Failure is OK. + return true; } else { - casTail(t, s); + p = succ(p); } } } } public E poll() { - for (;;) { - Node h = head; - Node t = tail; - Node first = h.getNext(); - if (h == head) { - if (h == t) { - if (first == null) - return null; - else - casTail(t, first); - } else if (casHead(h, first)) { - E item = first.getItem(); - if (item != null) { - first.setItem(null); - return item; - } - // else skip over deleted item, continue loop, + Node h = head; + Node p = h; + for (int hops = 0; ; hops++) { + E item = p.getItem(); + + if (item != null && p.casItem(item, null)) { + if (hops >= HOPS) { + Node q = p.getNext(); + updateHead(h, (q != null) ? q : p); } + return item; } + Node next = succ(p); + if (next == null) { + updateHead(h, p); + break; + } + p = next; } + return null; } - public E peek() { // same as poll except don't remove item + public E peek() { + Node h = head; + Node p = h; + E item; for (;;) { - Node h = head; - Node t = tail; - Node first = h.getNext(); - if (h == head) { - if (h == t) { - if (first == null) - return null; - else - casTail(t, first); - } else { - E item = first.getItem(); - if (item != null) - return item; - else // remove deleted node and continue - casHead(h, first); - } + item = p.getItem(); + if (item != null) + break; + Node next = succ(p); + if (next == null) { + break; } + p = next; } + updateHead(h, p); + return item; } /** @@ -245,46 +334,50 @@ public class ConcurrentLinkedQueue extends AbstractQueue * introducing race.) */ Node first() { + Node h = head; + Node p = h; + Node result; for (;;) { - Node h = head; - Node t = tail; - Node first = h.getNext(); - if (h == head) { - if (h == t) { - if (first == null) - return null; - else - casTail(t, first); - } else { - if (first.getItem() != null) - return first; - else // remove deleted node and continue - casHead(h, first); - } + E item = p.getItem(); + if (item != null) { + result = p; + break; } + Node next = succ(p); + if (next == null) { + result = null; + break; + } + p = next; } + updateHead(h, p); + return result; } - + /** + * Returns {@code true} if this queue contains no elements. + * + * @return {@code true} if this queue contains no elements + */ public boolean isEmpty() { return first() == null; } /** * Returns the number of elements in this queue. If this queue - * contains more than Integer.MAX_VALUE elements, returns - * Integer.MAX_VALUE. + * contains more than {@code Integer.MAX_VALUE} elements, returns + * {@code Integer.MAX_VALUE}. * *

      Beware that, unlike in most collections, this method is * NOT a constant-time operation. Because of the * asynchronous nature of these queues, determining the current * number of elements requires an O(n) traversal. * - * @return the number of elements in this queue. + * @return the number of elements in this queue */ public int size() { int count = 0; - for (Node p = first(); p != null; p = p.getNext()) { + for (Node p = first(); p != null; p = succ(p)) { if (p.getItem() != null) { // Collections.size() spec says to max out if (++count == Integer.MAX_VALUE) @@ -294,9 +387,17 @@ public class ConcurrentLinkedQueue extends AbstractQueue return count; } + /** + * Returns {@code true} if this queue contains the specified element. + * More formally, returns {@code true} if and only if this queue contains + * at least one element {@code e} such that {@code o.equals(e)}. + * + * @param o object to be checked for containment in this queue + * @return {@code true} if this queue contains the specified element + */ public boolean contains(Object o) { if (o == null) return false; - for (Node p = first(); p != null; p = p.getNext()) { + for (Node p = first(); p != null; p = succ(p)) { E item = p.getItem(); if (item != null && o.equals(item)) @@ -305,22 +406,50 @@ public class ConcurrentLinkedQueue extends AbstractQueue return false; } + /** + * Removes a single instance of the specified element from this queue, + * if it is present. More formally, removes an element {@code e} such + * that {@code o.equals(e)}, if this queue contains one or more such + * elements. + * Returns {@code true} if this queue contained the specified element + * (or equivalently, if this queue changed as a result of the call). + * + * @param o element to be removed from this queue, if present + * @return {@code true} if this queue changed as a result of the call + */ public boolean remove(Object o) { if (o == null) return false; - for (Node p = first(); p != null; p = p.getNext()) { + Node pred = null; + for (Node p = first(); p != null; p = succ(p)) { E item = p.getItem(); - if (item != null && - o.equals(item) && - p.casItem(item, null)) + if (item != null && o.equals(item) && p.casItem(item, null)) { + Node next = succ(p); + if (pred != null && next != null) + pred.casNext(p, next); return true; + } + pred = p; } return false; } + /** + * Returns an array containing all of the elements in this queue, in + * proper sequence. + * + *

      The returned array will be "safe" in that no references to it are + * maintained by this queue. (In other words, this method must allocate + * a new array). The caller is thus free to modify the returned array. + * + *

      This method acts as bridge between array-based and collection-based + * APIs. + * + * @return an array containing all of the elements in this queue + */ public Object[] toArray() { // Use ArrayList to deal with resizing. ArrayList al = new ArrayList(); - for (Node p = first(); p != null; p = p.getNext()) { + for (Node p = first(); p != null; p = succ(p)) { E item = p.getItem(); if (item != null) al.add(item); @@ -328,11 +457,48 @@ public class ConcurrentLinkedQueue extends AbstractQueue return al.toArray(); } + /** + * Returns an array containing all of the elements in this queue, in + * proper sequence; the runtime type of the returned array is that of + * the specified array. If the queue fits in the specified array, it + * is returned therein. Otherwise, a new array is allocated with the + * runtime type of the specified array and the size of this queue. + * + *

      If this queue fits in the specified array with room to spare + * (i.e., the array has more elements than this queue), the element in + * the array immediately following the end of the queue is set to + * {@code null}. + * + *

      Like the {@link #toArray()} method, this method acts as bridge between + * array-based and collection-based APIs. Further, this method allows + * precise control over the runtime type of the output array, and may, + * under certain circumstances, be used to save allocation costs. + * + *

      Suppose {@code x} is a queue known to contain only strings. + * The following code can be used to dump the queue into a newly + * allocated array of {@code String}: + * + *

      +     *     String[] y = x.toArray(new String[0]);
      + * + * Note that {@code toArray(new Object[0])} is identical in function to + * {@code toArray()}. + * + * @param a the array into which the elements of the queue are to + * be stored, if it is big enough; otherwise, a new array of the + * same runtime type is allocated for this purpose + * @return an array containing all of the elements in this queue + * @throws ArrayStoreException if the runtime type of the specified array + * is not a supertype of the runtime type of every element in + * this queue + * @throws NullPointerException if the specified array is null + */ + @SuppressWarnings("unchecked") public T[] toArray(T[] a) { // try to use sent-in array int k = 0; Node p; - for (p = first(); p != null && k < a.length; p = p.getNext()) { + for (p = first(); p != null && k < a.length; p = succ(p)) { E item = p.getItem(); if (item != null) a[k++] = (T)item; @@ -345,23 +511,23 @@ public class ConcurrentLinkedQueue extends AbstractQueue // If won't fit, use ArrayList version ArrayList al = new ArrayList(); - for (Node q = first(); q != null; q = q.getNext()) { + for (Node q = first(); q != null; q = succ(q)) { E item = q.getItem(); if (item != null) al.add(item); } - return (T[])al.toArray(a); + return al.toArray(a); } /** * Returns an iterator over the elements in this queue in proper sequence. * The returned iterator is a "weakly consistent" iterator that - * will never throw {@link java.util.ConcurrentModificationException}, + * will never throw {@link ConcurrentModificationException}, * and guarantees to traverse elements as they existed upon * construction of the iterator, and may (but is not guaranteed to) * reflect any modifications subsequent to construction. * - * @return an iterator over the elements in this queue in proper sequence. + * @return an iterator over the elements in this queue in proper sequence */ public Iterator iterator() { return new Itr(); @@ -378,7 +544,7 @@ public class ConcurrentLinkedQueue extends AbstractQueue * that an element exists in hasNext(), we must return it in * the following next() call even if it was in the process of * being removed when hasNext() was called. - **/ + */ private E nextItem; /** @@ -398,7 +564,15 @@ public class ConcurrentLinkedQueue extends AbstractQueue lastRet = nextNode; E x = nextItem; - Node p = (nextNode == null)? first() : nextNode.getNext(); + Node pred, p; + if (nextNode == null) { + p = first(); + pred = null; + } else { + pred = nextNode; + p = succ(nextNode); + } + for (;;) { if (p == null) { nextNode = null; @@ -410,8 +584,13 @@ public class ConcurrentLinkedQueue extends AbstractQueue nextNode = p; nextItem = item; return x; - } else // skip over nulls - p = p.getNext(); + } else { + // skip over nulls + Node next = succ(p); + if (pred != null && next != null) + pred.casNext(p, next); + p = next; + } } } @@ -436,7 +615,7 @@ public class ConcurrentLinkedQueue extends AbstractQueue /** * Save the state to a stream (that is, serialize it). * - * @serialData All of the elements (each an E) in + * @serialData All of the elements (each an {@code E}) in * the proper order, followed by a null * @param s the stream */ @@ -447,7 +626,7 @@ public class ConcurrentLinkedQueue extends AbstractQueue s.defaultWriteObject(); // Write out all elements in the proper order. - for (Node p = first(); p != null; p = p.getNext()) { + for (Node p = first(); p != null; p = succ(p)) { Object item = p.getItem(); if (item != null) s.writeObject(item); @@ -466,10 +645,11 @@ public class ConcurrentLinkedQueue extends AbstractQueue throws java.io.IOException, ClassNotFoundException { // Read in capacity, and any hidden stuff s.defaultReadObject(); - head = new Node(null, null); + head = new Node(null); tail = head; // Read in all elements and place in queue for (;;) { + @SuppressWarnings("unchecked") E item = (E)s.readObject(); if (item == null) break; diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/ConcurrentMap.java b/libcore/concurrent/src/main/java/java/util/concurrent/ConcurrentMap.java index 32dc000a5..2daebc549 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/ConcurrentMap.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/ConcurrentMap.java @@ -15,10 +15,17 @@ import java.util.Map; * A {@link java.util.Map} providing additional atomic * putIfAbsent, remove, and replace methods. * + *

      Memory consistency effects: As with other concurrent + * collections, actions in a thread prior to placing an object into a + * {@code ConcurrentMap} as a key or value + * happen-before + * actions subsequent to the access or removal of that object from + * the {@code ConcurrentMap} in another thread. + * * @since 1.5 * @author Doug Lea * @param the type of keys maintained by this map - * @param the type of mapped values + * @param the type of mapped values */ public interface ConcurrentMap extends Map { /** @@ -26,93 +33,102 @@ public interface ConcurrentMap extends Map { * with a value, associate it with the given value. * This is equivalent to *

      -     *   if (!map.containsKey(key)) 
      -     *      return map.put(key, value);
      +     *   if (!map.containsKey(key))
      +     *       return map.put(key, value);
            *   else
      -     *      return map.get(key);
      -     * 
      - * Except that the action is performed atomically. - * @param key key with which the specified value is to be associated. - * @param value value to be associated with the specified key. - * @return previous value associated with specified key, or null - * if there was no mapping for key. A null return can - * also indicate that the map previously associated null - * with the specified key, if the implementation supports - * null values. + * return map.get(key); + * except that the action is performed atomically. * - * @throws UnsupportedOperationException if the put operation is - * not supported by this map. + * @param key key with which the specified value is to be associated + * @param value value to be associated with the specified key + * @return the previous value associated with the specified key, or + * null if there was no mapping for the key. + * (A null return can also indicate that the map + * previously associated null with the key, + * if the implementation supports null values.) + * @throws UnsupportedOperationException if the put operation + * is not supported by this map * @throws ClassCastException if the class of the specified key or value - * prevents it from being stored in this map. - * @throws IllegalArgumentException if some aspect of this key or value - * prevents it from being stored in this map. - * @throws NullPointerException if this map does not permit null - * keys or values, and the specified key or value is - * null. + * prevents it from being stored in this map + * @throws NullPointerException if the specified key or value is null, + * and this map does not permit null keys or values + * @throws IllegalArgumentException if some property of the specified key + * or value prevents it from being stored in this map * */ V putIfAbsent(K key, V value); /** - * Remove entry for key only if currently mapped to given value. - * Acts as - *
       
      -     *  if ((map.containsKey(key) && map.get(key).equals(value)) {
      -     *     map.remove(key);
      -     *     return true;
      -     * } else return false;
      -     * 
      + * Removes the entry for a key only if currently mapped to a given value. + * This is equivalent to + *
      +     *   if (map.containsKey(key) && map.get(key).equals(value)) {
      +     *       map.remove(key);
      +     *       return true;
      +     *   } else return false;
      * except that the action is performed atomically. - * @param key key with which the specified value is associated. - * @param value value associated with the specified key. - * @return true if the value was removed, false otherwise - * @throws NullPointerException if this map does not permit null - * keys or values, and the specified key or value is - * null. + * + * @param key key with which the specified value is associated + * @param value value expected to be associated with the specified key + * @return true if the value was removed + * @throws UnsupportedOperationException if the remove operation + * is not supported by this map + * @throws ClassCastException if the key or value is of an inappropriate + * type for this map (optional) + * @throws NullPointerException if the specified key or value is null, + * and this map does not permit null keys or values (optional) */ boolean remove(Object key, Object value); - /** - * Replace entry for key only if currently mapped to given value. - * Acts as - *
       
      -     *  if ((map.containsKey(key) && map.get(key).equals(oldValue)) {
      -     *     map.put(key, newValue);
      -     *     return true;
      -     * } else return false;
      -     * 
      + * Replaces the entry for a key only if currently mapped to a given value. + * This is equivalent to + *
      +     *   if (map.containsKey(key) && map.get(key).equals(oldValue)) {
      +     *       map.put(key, newValue);
      +     *       return true;
      +     *   } else return false;
      * except that the action is performed atomically. - * @param key key with which the specified value is associated. - * @param oldValue value expected to be associated with the specified key. - * @param newValue value to be associated with the specified key. - * @return true if the value was replaced - * @throws NullPointerException if this map does not permit null - * keys or values, and the specified key or value is - * null. + * + * @param key key with which the specified value is associated + * @param oldValue value expected to be associated with the specified key + * @param newValue value to be associated with the specified key + * @return true if the value was replaced + * @throws UnsupportedOperationException if the put operation + * is not supported by this map + * @throws ClassCastException if the class of a specified key or value + * prevents it from being stored in this map + * @throws NullPointerException if a specified key or value is null, + * and this map does not permit null keys or values + * @throws IllegalArgumentException if some property of a specified key + * or value prevents it from being stored in this map */ boolean replace(K key, V oldValue, V newValue); /** - * Replace entry for key only if currently mapped to some value. - * Acts as - *
       
      -     *  if ((map.containsKey(key)) {
      -     *     return map.put(key, value);
      -     * } else return null;
      -     * 
      + * Replaces the entry for a key only if currently mapped to some value. + * This is equivalent to + *
      +     *   if (map.containsKey(key)) {
      +     *       return map.put(key, value);
      +     *   } else return null;
      * except that the action is performed atomically. - * @param key key with which the specified value is associated. - * @param value value to be associated with the specified key. - * @return previous value associated with specified key, or null - * if there was no mapping for key. A null return can - * also indicate that the map previously associated null - * with the specified key, if the implementation supports - * null values. - * @throws NullPointerException if this map does not permit null - * keys or values, and the specified key or value is - * null. + * + * @param key key with which the specified value is associated + * @param value value to be associated with the specified key + * @return the previous value associated with the specified key, or + * null if there was no mapping for the key. + * (A null return can also indicate that the map + * previously associated null with the key, + * if the implementation supports null values.) + * @throws UnsupportedOperationException if the put operation + * is not supported by this map + * @throws ClassCastException if the class of the specified key or value + * prevents it from being stored in this map + * @throws NullPointerException if the specified key or value is null, + * and this map does not permit null keys or values + * @throws IllegalArgumentException if some property of the specified key + * or value prevents it from being stored in this map */ V replace(K key, V value); - } diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/CopyOnWriteArrayList.java b/libcore/concurrent/src/main/java/java/util/concurrent/CopyOnWriteArrayList.java index 72745951d..f0c8ac6de 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/CopyOnWriteArrayList.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/CopyOnWriteArrayList.java @@ -1,1190 +1,1317 @@ /* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with this - * work for additional information regarding copyright ownership. The ASF - * licenses this file to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group. Adapted and released, under explicit permission, + * from JDK ArrayList.java which carries the following copyright: + * + * Copyright 1997 by Sun Microsystems, Inc., + * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A. + * All rights reserved. + * + * This software is the confidential and proprietary information + * of Sun Microsystems, Inc. ("Confidential Information"). You + * shall not disclose such Confidential Information and shall use + * it only in accordance with the terms of the license agreement + * you entered into with Sun. */ package java.util.concurrent; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; +import java.util.*; +import java.util.concurrent.locks.*; import java.lang.reflect.Array; -import java.util.Collection; -import java.util.ConcurrentModificationException; -import java.util.Iterator; -import java.util.List; -import java.util.ListIterator; -import java.util.NoSuchElementException; -import java.util.RandomAccess; -import java.util.concurrent.locks.ReentrantLock; - -// BEGIN android-added + +import sun.misc.Unsafe; + +// BEGIN android-note +// removed link to collections framework docs +// END android-note + /** - * Implements a {@link java.util.ArrayList} variant that is thread-safe. All - * write operation result in a new copy of the underlying data being created. - * Iterators reflect the state of the CopyOnWriteArrayList at the time they were - * created. They are not updated to reflect subsequent changes to the list. In - * addition, these iterators cannot be used for modifying the underlying - * CopyOnWriteArrayList. - * - * @param the element type + * A thread-safe variant of {@link java.util.ArrayList} in which all mutative + * operations (add, set, and so on) are implemented by + * making a fresh copy of the underlying array. + * + *

      This is ordinarily too costly, but may be more efficient + * than alternatives when traversal operations vastly outnumber + * mutations, and is useful when you cannot or don't want to + * synchronize traversals, yet need to preclude interference among + * concurrent threads. The "snapshot" style iterator method uses a + * reference to the state of the array at the point that the iterator + * was created. This array never changes during the lifetime of the + * iterator, so interference is impossible and the iterator is + * guaranteed not to throw ConcurrentModificationException. + * The iterator will not reflect additions, removals, or changes to + * the list since the iterator was created. Element-changing + * operations on iterators themselves (remove, set, and + * add) are not supported. These methods throw + * UnsupportedOperationException. + * + *

      All elements are permitted, including null. + * + *

      Memory consistency effects: As with other concurrent + * collections, actions in a thread prior to placing an object into a + * {@code CopyOnWriteArrayList} + * happen-before + * actions subsequent to the access or removal of that element from + * the {@code CopyOnWriteArrayList} in another thread. + * + * @since 1.5 + * @author Doug Lea + * @param the type of elements held in this collection */ -// END android-added -public class CopyOnWriteArrayList implements List, RandomAccess, Cloneable, Serializable { - +public class CopyOnWriteArrayList + implements List, RandomAccess, Cloneable, java.io.Serializable { private static final long serialVersionUID = 8673264195747942595L; - private transient volatile E[] arr; + /** The lock protecting all mutators */ + transient final ReentrantLock lock = new ReentrantLock(); - /** - * Lock for the queue write methods - */ - private final transient ReentrantLock lock = new ReentrantLock(); + /** The array, accessed only via getArray/setArray. */ + private volatile transient Object[] array; - // BEGIN android-added /** - * Creates a new, empty instance of CopyOnWriteArrayList. + * Gets the array. Non-private so as to also be accessible + * from CopyOnWriteArraySet class. */ - // END android-added - public CopyOnWriteArrayList() { + final Object[] getArray() { + return array; } - // BEGIN android-added /** - * Creates a new instance of CopyOnWriteArrayList and fills it with the - * contents of a given Collection. - * - * @param c the collection the elements of which are to be copied into - * the new instance. + * Sets the array. */ - // END android-added - public CopyOnWriteArrayList(Collection c) { - this((E[]) c.toArray()); + final void setArray(Object[] a) { + array = a; } - // BEGIN android-added /** - * Creates a new instance of CopyOnWriteArrayList and fills it with the - * contents of a given array. - * - * @param array the array the elements of which are to be copied into the - * new instance. + * Creates an empty list. */ - // END android-added - public CopyOnWriteArrayList(E[] array) { - int size = array.length; - E[] data = newElementArray(size); - for (int i = 0; i < size; i++) { - data[i] = array[i]; - } - arr = data; - } - - public boolean add(E e) { - lock.lock(); - try { - E[] data; - E[] old = getData(); - int size = old.length; - data = newElementArray(size + 1); - System.arraycopy(old, 0, data, 0, size); - data[size] = e; - setData(data); - return true; - } finally { - lock.unlock(); - } + public CopyOnWriteArrayList() { + setArray(new Object[0]); } - public void add(int index, E e) { - lock.lock(); - try { - E[] data; - E[] old = getData(); - int size = old.length; - checkIndexInclusive(index, size); - data = newElementArray(size+1); - System.arraycopy(old, 0, data, 0, index); - data[index] = e; - if (size > index) { - System.arraycopy(old, index, data, index + 1, size - index); - } - setData(data); - } finally { - lock.unlock(); - } + /** + * Creates a list containing the elements of the specified + * collection, in the order they are returned by the collection's + * iterator. + * + * @param c the collection of initially held elements + * @throws NullPointerException if the specified collection is null + */ + public CopyOnWriteArrayList(Collection c) { + Object[] elements = c.toArray(); + // c.toArray might (incorrectly) not return Object[] (see 6260652) + if (elements.getClass() != Object[].class) + elements = Java6Arrays.copyOf(elements, elements.length, Object[].class); + setArray(elements); } - public boolean addAll(Collection c) { - Iterator it = c.iterator(); - int ssize = c.size(); - lock.lock(); - try { - int size = size(); - E[] data; - E[] old = getData(); - int nSize = size + ssize; - data = newElementArray(nSize); - System.arraycopy(old, 0, data, 0, size); - while (it.hasNext()) { - data[size++] = (E) it.next(); - } - setData(data); - } finally { - lock.unlock(); - } - return true; + /** + * Creates a list holding a copy of the given array. + * + * @param toCopyIn the array (a copy of this array is used as the + * internal array) + * @throws NullPointerException if the specified array is null + */ + public CopyOnWriteArrayList(E[] toCopyIn) { + setArray(Java6Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class)); } - public boolean addAll(int index, Collection c) { - Iterator it = c.iterator(); - int ssize = c.size(); - lock.lock(); - try { - int size = size(); - checkIndexInclusive(index, size); - E[] data; - E[] old = getData(); - int nSize = size + ssize; - data = newElementArray(nSize); - System.arraycopy(old, 0, data, 0, index); - int i = index; - while (it.hasNext()) { - data[i++] = (E) it.next(); - } - if (size > index) { - System.arraycopy(old, index, data, index + ssize, size - index); - } - setData(data); - } finally { - lock.unlock(); - } - return true; + /** + * Returns the number of elements in this list. + * + * @return the number of elements in this list + */ + public int size() { + return getArray().length; } - // BEGIN android-added /** - * Adds to this CopyOnWriteArrayList all those elements from a given - * collection that are not yet part of the list. - * - * @param c the collection from which the potential new elements are - * taken. - * - * @return the number of elements actually added to this list. + * Returns true if this list contains no elements. + * + * @return true if this list contains no elements */ - // END android-added - public int addAllAbsent(Collection c) { - if (c.size() == 0) { - return 0; - } - lock.lock(); - try { - E[] old = getData(); - int size = old.length; - E[] toAdd = newElementArray(c.size()); - int i = 0; - for (Iterator it = c.iterator(); it.hasNext();) { - E o = (E) it.next(); - if (indexOf(o) < 0) { - toAdd[i++] = o; - } - } - E[] data = newElementArray(size + i); - System.arraycopy(old, 0, data, 0, size); - System.arraycopy(toAdd, 0, data, size, i); - setData(data); - return i; - } finally { - lock.unlock(); - } + public boolean isEmpty() { + return size() == 0; } - // BEGIN android-added /** - * Adds to this CopyOnWriteArrayList another element, given that this - * element is not yet part of the list. - * - * @param e the potential new element. - * - * @return true if the element was added, or false otherwise. + * Test for equality, coping with nulls. */ - // END android-added - public boolean addIfAbsent(E e) { - lock.lock(); - try { - E[] data; - E[] old = getData(); - int size = old.length; - if (size != 0) { - if (indexOf(e) >= 0) { - return false; - } - } - data = newElementArray(size + 1); - System.arraycopy(old, 0, data, 0, size); - data[size] = e; - setData(data); - return true; - } finally { - lock.unlock(); - } + private static boolean eq(Object o1, Object o2) { + return (o1 == null ? o2 == null : o1.equals(o2)); } - public void clear() { - lock.lock(); - try { - setData(newElementArray(0)); - } finally { - lock.unlock(); + /** + * static version of indexOf, to allow repeated calls without + * needing to re-acquire array each time. + * @param o element to search for + * @param elements the array + * @param index first index to search + * @param fence one past last index to search + * @return index of element, or -1 if absent + */ + private static int indexOf(Object o, Object[] elements, + int index, int fence) { + if (o == null) { + for (int i = index; i < fence; i++) + if (elements[i] == null) + return i; + } else { + for (int i = index; i < fence; i++) + if (o.equals(elements[i])) + return i; } + return -1; } - @Override - public Object clone() { - try { - CopyOnWriteArrayList thisClone = (CopyOnWriteArrayList) super.clone(); - thisClone.setData(this.getData()); - return thisClone; - } catch (CloneNotSupportedException e) { - throw new RuntimeException("CloneNotSupportedException is not expected here"); + /** + * static version of lastIndexOf. + * @param o element to search for + * @param elements the array + * @param index first index to search + * @return index of element, or -1 if absent + */ + private static int lastIndexOf(Object o, Object[] elements, int index) { + if (o == null) { + for (int i = index; i >= 0; i--) + if (elements[i] == null) + return i; + } else { + for (int i = index; i >= 0; i--) + if (o.equals(elements[i])) + return i; } + return -1; } + /** + * Returns true if this list contains the specified element. + * More formally, returns true if and only if this list contains + * at least one element e such that + * (o==null ? e==null : o.equals(e)). + * + * @param o element whose presence in this list is to be tested + * @return true if this list contains the specified element + */ public boolean contains(Object o) { - return indexOf(o) >= 0; - } - - public boolean containsAll(Collection c) { - E[] data = getData(); - return containsAll(c, data, 0, data.length); - } - - public boolean equals(Object o) { - if (o == this) { - return true; - } - if (!(o instanceof List)) { - return false; - } - List l = (List) o; - Iterator it = l.listIterator(); - Iterator ourIt = listIterator(); - while (it.hasNext()) { - if (!ourIt.hasNext()) { - return false; - } - Object thisListElem = it.next(); - Object anotherListElem = ourIt.next(); - if (!(thisListElem == null ? anotherListElem == null : thisListElem - .equals(anotherListElem))) { - return false; - } - } - if (ourIt.hasNext()) { - return false; - } - return true; + Object[] elements = getArray(); + return indexOf(o, elements, 0, elements.length) >= 0; } - public E get(int index) { - E[] data = getData(); - return data[index]; - } - - public int hashCode() { - int hashCode = 1; - Iterator it = listIterator(); - while (it.hasNext()) { - Object obj = it.next(); - hashCode = 31 * hashCode + (obj == null ? 0 : obj.hashCode()); - } - return hashCode; + /** + * {@inheritDoc} + */ + public int indexOf(Object o) { + Object[] elements = getArray(); + return indexOf(o, elements, 0, elements.length); } - // BEGIN android-added /** - * Returns the index of a given element, starting the search from a given - * position in the list. - * - * @param e the element to search. - * @param index the index at which to start the search. - * - * @return the index of the element or null, if the element has not been - * found at or beyond the given start index. + * Returns the index of the first occurrence of the specified element in + * this list, searching forwards from index, or returns -1 if + * the element is not found. + * More formally, returns the lowest index i such that + * (i >= index && (e==null ? get(i)==null : e.equals(get(i)))), + * or -1 if there is no such index. + * + * @param e element to search for + * @param index index to start searching from + * @return the index of the first occurrence of the element in + * this list at position index or later in the list; + * -1 if the element is not found. + * @throws IndexOutOfBoundsException if the specified index is negative */ - // END android-added public int indexOf(E e, int index) { - E[] data = getData(); - return indexOf(e, data, index, data.length - index); + Object[] elements = getArray(); + return indexOf(e, elements, index, elements.length); } - public int indexOf(Object o) { - E[] data = getData(); - return indexOf(o, data, 0, data.length); + /** + * {@inheritDoc} + */ + public int lastIndexOf(Object o) { + Object[] elements = getArray(); + return lastIndexOf(o, elements, elements.length - 1); } - public boolean isEmpty() { - return size() == 0; + /** + * Returns the index of the last occurrence of the specified element in + * this list, searching backwards from index, or returns -1 if + * the element is not found. + * More formally, returns the highest index i such that + * (i <= index && (e==null ? get(i)==null : e.equals(get(i)))), + * or -1 if there is no such index. + * + * @param e element to search for + * @param index index to start searching backwards from + * @return the index of the last occurrence of the element at position + * less than or equal to index in this list; + * -1 if the element is not found. + * @throws IndexOutOfBoundsException if the specified index is greater + * than or equal to the current size of this list + */ + public int lastIndexOf(E e, int index) { + Object[] elements = getArray(); + return lastIndexOf(e, elements, index); } - public Iterator iterator() { - return new ListIteratorImpl(getData(), 0); + /** + * Returns a shallow copy of this list. (The elements themselves + * are not copied.) + * + * @return a clone of this list + */ + public Object clone() { + try { + CopyOnWriteArrayList c = (CopyOnWriteArrayList)(super.clone()); + c.resetLock(); + return c; + } catch (CloneNotSupportedException e) { + // this shouldn't happen, since we are Cloneable + throw new InternalError(); + } } - // BEGIN android-added /** - * Returns the last index of a given element, starting the search from - * a given position in the list and going backwards. - * - * @param e the element to search. - * @param index the index at which to start the search. - * - * @return the index of the element or null, if the element has not been - * found at or before the given start index. + * Returns an array containing all of the elements in this list + * in proper sequence (from first to last element). + * + *

      The returned array will be "safe" in that no references to it are + * maintained by this list. (In other words, this method must allocate + * a new array). The caller is thus free to modify the returned array. + * + *

      This method acts as bridge between array-based and collection-based + * APIs. + * + * @return an array containing all the elements in this list */ - // END android-added - public int lastIndexOf(E e, int index) { - E[] data = getData(); - return lastIndexOf(e, data, 0, index); + public Object[] toArray() { + Object[] elements = getArray(); + return Java6Arrays.copyOf(elements, elements.length); } - public int lastIndexOf(Object o) { - E[] data = getData(); - return lastIndexOf(o, data, 0, data.length); + /** + * Returns an array containing all of the elements in this list in + * proper sequence (from first to last element); the runtime type of + * the returned array is that of the specified array. If the list fits + * in the specified array, it is returned therein. Otherwise, a new + * array is allocated with the runtime type of the specified array and + * the size of this list. + * + *

      If this list fits in the specified array with room to spare + * (i.e., the array has more elements than this list), the element in + * the array immediately following the end of the list is set to + * null. (This is useful in determining the length of this + * list only if the caller knows that this list does not contain + * any null elements.) + * + *

      Like the {@link #toArray()} method, this method acts as bridge between + * array-based and collection-based APIs. Further, this method allows + * precise control over the runtime type of the output array, and may, + * under certain circumstances, be used to save allocation costs. + * + *

      Suppose x is a list known to contain only strings. + * The following code can be used to dump the list into a newly + * allocated array of String: + * + *

      +     *     String[] y = x.toArray(new String[0]);
      + * + * Note that toArray(new Object[0]) is identical in function to + * toArray(). + * + * @param a the array into which the elements of the list are to + * be stored, if it is big enough; otherwise, a new array of the + * same runtime type is allocated for this purpose. + * @return an array containing all the elements in this list + * @throws ArrayStoreException if the runtime type of the specified array + * is not a supertype of the runtime type of every element in + * this list + * @throws NullPointerException if the specified array is null + */ + @SuppressWarnings("unchecked") + public T[] toArray(T a[]) { + Object[] elements = getArray(); + int len = elements.length; + if (a.length < len) + return (T[]) Java6Arrays.copyOf(elements, len, a.getClass()); + else { + System.arraycopy(elements, 0, a, 0, len); + if (a.length > len) + a[len] = null; + return a; + } } - public ListIterator listIterator() { - return new ListIteratorImpl(getData(), 0); - } + // Positional Access Operations - public ListIterator listIterator(int index) { - E[] data = getData(); - checkIndexInclusive(index, data.length); - return new ListIteratorImpl(data, index); + @SuppressWarnings("unchecked") + private E get(Object[] a, int index) { + return (E) a[index]; } - public E remove(int index) { - return removeRange(index, 1); + /** + * {@inheritDoc} + * + * @throws IndexOutOfBoundsException {@inheritDoc} + */ + public E get(int index) { + return get(getArray(), index); } - public boolean remove(Object o) { + /** + * Replaces the element at the specified position in this list with the + * specified element. + * + * @throws IndexOutOfBoundsException {@inheritDoc} + */ + public E set(int index, E element) { + final ReentrantLock lock = this.lock; lock.lock(); try { - int index = indexOf(o); - if (index == -1) { - return false; + Object[] elements = getArray(); + E oldValue = get(elements, index); + + if (oldValue != element) { + int len = elements.length; + Object[] newElements = Java6Arrays.copyOf(elements, len); + newElements[index] = element; + setArray(newElements); + } else { + // Not quite a no-op; ensures volatile write semantics + setArray(elements); } - remove(index); - return true; + return oldValue; } finally { lock.unlock(); } } - public boolean removeAll(Collection c) { + /** + * Appends the specified element to the end of this list. + * + * @param e element to be appended to this list + * @return true (as specified by {@link Collection#add}) + */ + public boolean add(E e) { + final ReentrantLock lock = this.lock; lock.lock(); try { - return removeAll(c, 0, getData().length) != 0; + Object[] elements = getArray(); + int len = elements.length; + Object[] newElements = Java6Arrays.copyOf(elements, len + 1); + newElements[len] = e; + setArray(newElements); + return true; } finally { lock.unlock(); } } - public boolean retainAll(Collection c) { - if (c == null) { - throw new NullPointerException(); - } + /** + * Inserts the specified element at the specified position in this + * list. Shifts the element currently at that position (if any) and + * any subsequent elements to the right (adds one to their indices). + * + * @throws IndexOutOfBoundsException {@inheritDoc} + */ + public void add(int index, E element) { + final ReentrantLock lock = this.lock; lock.lock(); try { - return retainAll(c, 0, getData().length) != 0; + Object[] elements = getArray(); + int len = elements.length; + if (index > len || index < 0) + throw new IndexOutOfBoundsException("Index: "+index+ + ", Size: "+len); + Object[] newElements; + int numMoved = len - index; + if (numMoved == 0) + newElements = Java6Arrays.copyOf(elements, len + 1); + else { + newElements = new Object[len + 1]; + System.arraycopy(elements, 0, newElements, 0, index); + System.arraycopy(elements, index, newElements, index + 1, + numMoved); + } + newElements[index] = element; + setArray(newElements); } finally { lock.unlock(); } } - public E set(int index, E e) { + /** + * Removes the element at the specified position in this list. + * Shifts any subsequent elements to the left (subtracts one from their + * indices). Returns the element that was removed from the list. + * + * @throws IndexOutOfBoundsException {@inheritDoc} + */ + public E remove(int index) { + final ReentrantLock lock = this.lock; lock.lock(); try { - int size = size(); - checkIndexExlusive(index, size); - E[] data; - data = newElementArray(size); - E[] oldArr = getData(); - System.arraycopy(oldArr, 0, data, 0, size); - E old = data[index]; - data[index] = e; - setData(data); - return old; + Object[] elements = getArray(); + int len = elements.length; + E oldValue = get(elements, index); + int numMoved = len - index - 1; + if (numMoved == 0) + setArray(Java6Arrays.copyOf(elements, len - 1)); + else { + Object[] newElements = new Object[len - 1]; + System.arraycopy(elements, 0, newElements, 0, index); + System.arraycopy(elements, index + 1, newElements, index, + numMoved); + setArray(newElements); + } + return oldValue; } finally { lock.unlock(); } } - public int size() { - return getData().length; - } - - public List subList(int fromIndex, int toIndex) { - return new SubList(this, fromIndex, toIndex); - } - - public Object[] toArray() { - E[] data = getData(); - return toArray(data, 0, data.length); - } - - public T[] toArray(T[] a) { - E[] data = getData(); - return (T[]) toArray(a, data, 0, data.length); - } - - @Override - public String toString() { - StringBuffer sb = new StringBuffer("["); + /** + * Removes the first occurrence of the specified element from this list, + * if it is present. If this list does not contain the element, it is + * unchanged. More formally, removes the element with the lowest index + * i such that + * (o==null ? get(i)==null : o.equals(get(i))) + * (if such an element exists). Returns true if this list + * contained the specified element (or equivalently, if this list + * changed as a result of the call). + * + * @param o element to be removed from this list, if present + * @return true if this list contained the specified element + */ + public boolean remove(Object o) { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + Object[] elements = getArray(); + int len = elements.length; + if (len != 0) { + // Copy while searching for element to remove + // This wins in the normal case of element being present + int newlen = len - 1; + Object[] newElements = new Object[newlen]; + + for (int i = 0; i < newlen; ++i) { + if (eq(o, elements[i])) { + // found one; copy remaining and exit + for (int k = i + 1; k < len; ++k) + newElements[k-1] = elements[k]; + setArray(newElements); + return true; + } else + newElements[i] = elements[i]; + } - Iterator it = listIterator(); - while (it.hasNext()) { - sb.append(String.valueOf(it.next())); - sb.append(", "); - } - if (sb.length() > 1) { - sb.setLength(sb.length() - 2); + // special handling for last cell + if (eq(o, elements[newlen])) { + setArray(newElements); + return true; + } + } + return false; + } finally { + lock.unlock(); } - sb.append("]"); - return sb.toString(); } - // private and package private methods - - @SuppressWarnings("unchecked") - private final E[] newElementArray(int size) { - return (E[])new Object[size]; + /** + * Removes from this list all of the elements whose index is between + * fromIndex, inclusive, and toIndex, exclusive. + * Shifts any succeeding elements to the left (reduces their index). + * This call shortens the list by (toIndex - fromIndex) elements. + * (If toIndex==fromIndex, this operation has no effect.) + * + * @param fromIndex index of first element to be removed + * @param toIndex index after last element to be removed + * @throws IndexOutOfBoundsException if fromIndex or toIndex out of range + * (@code{fromIndex < 0 || toIndex > size() || toIndex < fromIndex}) + */ + private void removeRange(int fromIndex, int toIndex) { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + Object[] elements = getArray(); + int len = elements.length; + + if (fromIndex < 0 || toIndex > len || toIndex < fromIndex) + throw new IndexOutOfBoundsException(); + int newlen = len - (toIndex - fromIndex); + int numMoved = len - toIndex; + if (numMoved == 0) + setArray(Java6Arrays.copyOf(elements, newlen)); + else { + Object[] newElements = new Object[newlen]; + System.arraycopy(elements, 0, newElements, 0, fromIndex); + System.arraycopy(elements, toIndex, newElements, + fromIndex, numMoved); + setArray(newElements); + } + } finally { + lock.unlock(); + } } /** - * sets the internal data array + * Append the element if not present. * - * @param data array to set + * @param e element to be added to this list, if absent + * @return true if the element was added */ - private final void setData(E[] data) { - arr = data; + public boolean addIfAbsent(E e) { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + // Copy while checking if already present. + // This wins in the most common case where it is not present + Object[] elements = getArray(); + int len = elements.length; + Object[] newElements = new Object[len + 1]; + for (int i = 0; i < len; ++i) { + if (eq(e, elements[i])) + return false; // exit, throwing away copy + else + newElements[i] = elements[i]; + } + newElements[len] = e; + setArray(newElements); + return true; + } finally { + lock.unlock(); + } } /** - * gets the internal data array + * Returns true if this list contains all of the elements of the + * specified collection. * - * @return the data array + * @param c collection to be checked for containment in this list + * @return true if this list contains all of the elements of the + * specified collection + * @throws NullPointerException if the specified collection is null + * @see #contains(Object) */ - final E[] getData() { - if (arr == null) { - return newElementArray(0); + public boolean containsAll(Collection c) { + Object[] elements = getArray(); + int len = elements.length; + for (Object e : c) { + if (indexOf(e, elements, 0, len) < 0) + return false; } - return arr; + return true; } /** - * Removes from the specified range of this list - * all the elements that are contained in the specified collection - *

      - * !should be called under lock + * Removes from this list all of its elements that are contained in + * the specified collection. This is a particularly expensive operation + * in this class because of the need for an internal temporary array. * - * @return Returns the number of removed elements + * @param c collection containing elements to be removed from this list + * @return true if this list changed as a result of the call + * @throws ClassCastException if the class of an element of this list + * is incompatible with the specified collection (optional) + * @throws NullPointerException if this list contains a null element and the + * specified collection does not permit null elements (optional), + * or if the specified collection is null + * @see #remove(Object) */ - final int removeAll(Collection c, int start, int size) { - int ssize = c.size(); - if (ssize == 0) { - return 0; - } - Object[] old = getData(); - int arrsize = old.length; - if (arrsize == 0) { - return 0; - } - Object[] data = new Object[size]; - int j = 0; - for (int i = start; i < (start + size); i++) { - if (!c.contains(old[i])) { - data[j++] = old[i]; + public boolean removeAll(Collection c) { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + Object[] elements = getArray(); + int len = elements.length; + if (len != 0) { + // temp array holds those elements we know we want to keep + int newlen = 0; + Object[] temp = new Object[len]; + for (int i = 0; i < len; ++i) { + Object element = elements[i]; + if (!c.contains(element)) + temp[newlen++] = element; + } + if (newlen != len) { + setArray(Java6Arrays.copyOf(temp, newlen)); + return true; + } } + return false; + } finally { + lock.unlock(); } - if (j != size) { - E[] result = newElementArray(arrsize - (size - j)); - System.arraycopy(old, 0, result, 0, start); - System.arraycopy(data, 0, result, start, j); - System.arraycopy(old, start + size, result, start + j, arrsize - - (start + size)); - setData(result); - return (size - j); - } - return 0; } /** - * Retains only the elements in the specified range of this list - * that are contained in the specified collection + * Retains only the elements in this list that are contained in the + * specified collection. In other words, removes from this list all of + * its elements that are not contained in the specified collection. * - * @return Returns the number of removed elements + * @param c collection containing elements to be retained in this list + * @return true if this list changed as a result of the call + * @throws ClassCastException if the class of an element of this list + * is incompatible with the specified collection (optional) + * @throws NullPointerException if this list contains a null element and the + * specified collection does not permit null elements (optional), + * or if the specified collection is null + * @see #remove(Object) */ - // should be called under lock - int retainAll(Collection c, int start, int size) { - Object[] old = getData(); - if (size == 0) { - return 0; - } - if (c.size() == 0) { - E[] data; - if (size == old.length) { - data = newElementArray(0); - } else { - data = newElementArray(old.length - size); - System.arraycopy(old, 0, data, 0, start); - System.arraycopy(old, start + size, data, start, old.length - - start - size); - } - setData(data); - return size; - } - Object[] temp = new Object[size]; - int pos = 0; - for (int i = start; i < (start + size); i++) { - if (c.contains(old[i])) { - temp[pos++] = old[i]; + public boolean retainAll(Collection c) { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + Object[] elements = getArray(); + int len = elements.length; + if (len != 0) { + // temp array holds those elements we know we want to keep + int newlen = 0; + Object[] temp = new Object[len]; + for (int i = 0; i < len; ++i) { + Object element = elements[i]; + if (c.contains(element)) + temp[newlen++] = element; + } + if (newlen != len) { + setArray(Java6Arrays.copyOf(temp, newlen)); + return true; + } } + return false; + } finally { + lock.unlock(); } - if (pos == size) { - return 0; - } - E[] data = newElementArray(pos + old.length - size); - System.arraycopy(old, 0, data, 0, start); - System.arraycopy(temp, 0, data, start, pos); - System.arraycopy(old, start + size, data, start + pos, old.length - - start - size); - setData(data); - return (size - pos); } /** - * Removes specified range from this list + * Appends all of the elements in the specified collection that + * are not already contained in this list, to the end of + * this list, in the order that they are returned by the + * specified collection's iterator. + * + * @param c collection containing elements to be added to this list + * @return the number of elements added + * @throws NullPointerException if the specified collection is null + * @see #addIfAbsent(Object) */ - E removeRange(int start, int size) { + public int addAllAbsent(Collection c) { + Object[] cs = c.toArray(); + if (cs.length == 0) + return 0; + Object[] uniq = new Object[cs.length]; + final ReentrantLock lock = this.lock; lock.lock(); try { - int sizeArr = size(); - checkIndexExlusive(start, sizeArr); - checkIndexInclusive(start + size, sizeArr); - E[] data; - data = newElementArray(sizeArr - size); - E[] oldArr = getData(); - System.arraycopy(oldArr, 0, data, 0, start); - E old = oldArr[start]; - if (sizeArr > (start + size)) { - System.arraycopy(oldArr, start + size, data, start, sizeArr - - (start + size)); + Object[] elements = getArray(); + int len = elements.length; + int added = 0; + for (int i = 0; i < cs.length; ++i) { // scan for duplicates + Object e = cs[i]; + if (indexOf(e, elements, 0, len) < 0 && + indexOf(e, uniq, 0, added) < 0) + uniq[added++] = e; } - setData(data); - return old; + if (added > 0) { + Object[] newElements = Java6Arrays.copyOf(elements, len + added); + System.arraycopy(uniq, 0, newElements, len, added); + setArray(newElements); + } + return added; } finally { lock.unlock(); } } - // some util static functions to use by iterators and methods /** - * Returns an array containing all of the elements - * in the specified range of the array in proper sequence + * Removes all of the elements from this list. + * The list will be empty after this call returns. */ - static Object[] toArray(Object[] data, int start, int size) { - Object[] result = new Object[size]; - System.arraycopy(data, start, result, 0, size); - return result; + public void clear() { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + setArray(new Object[0]); + } finally { + lock.unlock(); + } } /** - * Returns an array containing all of the elements - * in the specified range of the array in proper sequence, - * stores the result in the array, specified by first parameter - * (as for public instance method toArray(Object[] to) + * Appends all of the elements in the specified collection to the end + * of this list, in the order that they are returned by the specified + * collection's iterator. + * + * @param c collection containing elements to be added to this list + * @return true if this list changed as a result of the call + * @throws NullPointerException if the specified collection is null + * @see #add(Object) */ - static Object[] toArray(Object[] to, Object[] data, int start, int size) { - int l = data.length; - if (to.length < l) { - to = (Object[]) Array.newInstance(to.getClass().getComponentType(), - l); - } else { - if (to.length > l) { - to[l] = null; - } + public boolean addAll(Collection c) { + Object[] cs = c.toArray(); + if (cs.length == 0) + return false; + final ReentrantLock lock = this.lock; + lock.lock(); + try { + Object[] elements = getArray(); + int len = elements.length; + Object[] newElements = Java6Arrays.copyOf(elements, len + cs.length); + System.arraycopy(cs, 0, newElements, len, cs.length); + setArray(newElements); + return true; + } finally { + lock.unlock(); } - System.arraycopy(data, start, to, 0, size); - return to; } /** - * Checks if the specified range of the - * array contains all of the elements in the collection + * Inserts all of the elements in the specified collection into this + * list, starting at the specified position. Shifts the element + * currently at that position (if any) and any subsequent elements to + * the right (increases their indices). The new elements will appear + * in this list in the order that they are returned by the + * specified collection's iterator. * - * @param c collection with elements - * @param data array where to search the elements - * @param start start index - * @param size size of the range + * @param index index at which to insert the first element + * from the specified collection + * @param c collection containing elements to be added to this list + * @return true if this list changed as a result of the call + * @throws IndexOutOfBoundsException {@inheritDoc} + * @throws NullPointerException if the specified collection is null + * @see #add(int,Object) */ - static final boolean containsAll(Collection c, Object[] data, int start, - int size) { - if (size == 0) { - return false; - } - Iterator it = c.iterator(); - while (it.hasNext()) { - Object next = it.next(); - if (indexOf(next, data, start, size) < 0) { + public boolean addAll(int index, Collection c) { + Object[] cs = c.toArray(); + final ReentrantLock lock = this.lock; + lock.lock(); + try { + Object[] elements = getArray(); + int len = elements.length; + if (index > len || index < 0) + throw new IndexOutOfBoundsException("Index: "+index+ + ", Size: "+len); + if (cs.length == 0) return false; + int numMoved = len - index; + Object[] newElements; + if (numMoved == 0) + newElements = Java6Arrays.copyOf(elements, len + cs.length); + else { + newElements = new Object[len + cs.length]; + System.arraycopy(elements, 0, newElements, 0, index); + System.arraycopy(elements, index, + newElements, index + cs.length, + numMoved); } + System.arraycopy(cs, 0, newElements, index, cs.length); + setArray(newElements); + return true; + } finally { + lock.unlock(); } - return true; } /** - * Returns the index in the specified range of the data array - * of the last occurrence of the specified element + * Save the state of the list to a stream (i.e., serialize it). * - * @param o element to search - * @param data array where to search - * @param start start index - * @param size size of the range - * @return + * @serialData The length of the array backing the list is emitted + * (int), followed by all of its elements (each an Object) + * in the proper order. + * @param s the stream */ - static final int lastIndexOf(Object o, Object[] data, int start, int size) { - if (size == 0) { - return -1; - } - if (o != null) { - for (int i = start + size - 1; i > start - 1; i--) { - if (o.equals(data[i])) { - return i; - } - } - } else { - for (int i = start + size - 1; i > start - 1; i--) { - if (data[i] == null) { - return i; - } - } - } - return -1; + private void writeObject(java.io.ObjectOutputStream s) + throws java.io.IOException{ + + // Write out element count, and any hidden stuff + s.defaultWriteObject(); + + Object[] elements = getArray(); + int len = elements.length; + // Write out array length + s.writeInt(len); + + // Write out all elements in the proper order. + for (int i = 0; i < len; i++) + s.writeObject(elements[i]); } /** - * Returns the index in the specified range of the data array - * of the first occurrence of the specified element + * Reconstitute the list from a stream (i.e., deserialize it). + * @param s the stream + */ + private void readObject(java.io.ObjectInputStream s) + throws java.io.IOException, ClassNotFoundException { + + // Read in size, and any hidden stuff + s.defaultReadObject(); + + // bind to new lock + resetLock(); + + // Read in array length and allocate array + int len = s.readInt(); + Object[] elements = new Object[len]; + + // Read in all elements in the proper order. + for (int i = 0; i < len; i++) + elements[i] = s.readObject(); + setArray(elements); + } + + /** + * Returns a string representation of this list. The string + * representation consists of the string representations of the list's + * elements in the order they are returned by its iterator, enclosed in + * square brackets ("[]"). Adjacent elements are separated by + * the characters ", " (comma and space). Elements are + * converted to strings as by {@link String#valueOf(Object)}. * - * @param o element to search - * @param data array where to search - * @param start start index - * @param size end index - * @return + * @return a string representation of this list */ - static final int indexOf(Object o, Object[] data, int start, int size) { - if (size == 0) { - return -1; - } - if (o == null) { - for (int i = start; i < start + size; i++) { - if (data[i] == null) { - return i; - } - } - } else { - for (int i = start; i < start + size; i++) { - if (o.equals(data[i])) { - return i; - } - } - } - return -1; + public String toString() { + return Arrays.toString(getArray()); } /** - * Throws IndexOutOfBoundsException if index - * is out of the list bounds. + * Compares the specified object with this list for equality. + * Returns {@code true} if the specified object is the same object + * as this object, or if it is also a {@link List} and the sequence + * of elements returned by an {@linkplain List#iterator() iterator} + * over the specified list is the same as the sequence returned by + * an iterator over this list. The two sequences are considered to + * be the same if they have the same length and corresponding + * elements at the same position in the sequence are equal. + * Two elements {@code e1} and {@code e2} are considered + * equal if {@code (e1==null ? e2==null : e1.equals(e2))}. * - * @param index element index to check. + * @param o the object to be compared for equality with this list + * @return {@code true} if the specified object is equal to this list */ - static final void checkIndexInclusive(int index, int size) { - if (index < 0 || index > size) { - throw new IndexOutOfBoundsException("Index is " + index + ", size is " + size); - } + public boolean equals(Object o) { + if (o == this) + return true; + if (!(o instanceof List)) + return false; + + List list = (List)(o); + Iterator it = list.iterator(); + Object[] elements = getArray(); + int len = elements.length; + for (int i = 0; i < len; ++i) + if (!it.hasNext() || !eq(elements[i], it.next())) + return false; + if (it.hasNext()) + return false; + return true; } /** - * Throws IndexOutOfBoundsException if index - * is out of the list bounds. Excluding the last element. + * Returns the hash code value for this list. + * + *

      This implementation uses the definition in {@link List#hashCode}. * - * @param index element index to check. + * @return the hash code value for this list */ - static final void checkIndexExlusive(int index, int size) { - if (index < 0 || index >= size) { - throw new IndexOutOfBoundsException("Index is " + index + ", size is " + size); + public int hashCode() { + int hashCode = 1; + Object[] elements = getArray(); + int len = elements.length; + for (int i = 0; i < len; ++i) { + Object obj = elements[i]; + hashCode = 31*hashCode + (obj==null ? 0 : obj.hashCode()); } + return hashCode; } /** - * list iterator implementation, - * when created gets snapshot of the list, - * so never throws ConcurrentModificationException + * Returns an iterator over the elements in this list in proper sequence. + * + *

      The returned iterator provides a snapshot of the state of the list + * when the iterator was constructed. No synchronization is needed while + * traversing the iterator. The iterator does NOT support the + * remove method. + * + * @return an iterator over the elements in this list in proper sequence */ - private static class ListIteratorImpl implements ListIterator { - private final Object[] arr; + public Iterator iterator() { + return new COWIterator(getArray(), 0); + } - private int current; + /** + * {@inheritDoc} + * + *

      The returned iterator provides a snapshot of the state of the list + * when the iterator was constructed. No synchronization is needed while + * traversing the iterator. The iterator does NOT support the + * remove, set or add methods. + */ + public ListIterator listIterator() { + return new COWIterator(getArray(), 0); + } - private final int size; + /** + * {@inheritDoc} + * + *

      The returned iterator provides a snapshot of the state of the list + * when the iterator was constructed. No synchronization is needed while + * traversing the iterator. The iterator does NOT support the + * remove, set or add methods. + * + * @throws IndexOutOfBoundsException {@inheritDoc} + */ + public ListIterator listIterator(final int index) { + Object[] elements = getArray(); + int len = elements.length; + if (index<0 || index>len) + throw new IndexOutOfBoundsException("Index: "+index); - final int size() { - return size; - } + return new COWIterator(elements, index); + } - public ListIteratorImpl(Object[] data, int current) { - this.current = current; - arr = data; - size = data.length; - } + private static class COWIterator implements ListIterator { + /** Snapshot of the array **/ + private final Object[] snapshot; + /** Index of element to be returned by subsequent call to next. */ + private int cursor; - public void add(Object o) { - throw new UnsupportedOperationException("Unsupported operation add"); + private COWIterator(Object[] elements, int initialCursor) { + cursor = initialCursor; + snapshot = elements; } public boolean hasNext() { - if (current < size) { - return true; - } - return false; + return cursor < snapshot.length; } public boolean hasPrevious() { - return current > 0; + return cursor > 0; } - public Object next() { - if (hasNext()) { - return arr[current++]; - } - throw new NoSuchElementException("pos is " + current + ", size is " + size); + @SuppressWarnings("unchecked") + public E next() { + if (! hasNext()) + throw new NoSuchElementException(); + return (E) snapshot[cursor++]; } - public int nextIndex() { - return current; + @SuppressWarnings("unchecked") + public E previous() { + if (! hasPrevious()) + throw new NoSuchElementException(); + return (E) snapshot[--cursor]; } - public Object previous() { - if (hasPrevious()) { - return arr[--current]; - } - throw new NoSuchElementException("pos is " + (current-1) + ", size is " + size); + public int nextIndex() { + return cursor; } public int previousIndex() { - return current - 1; + return cursor-1; } + /** + * Not supported. Always throws UnsupportedOperationException. + * @throws UnsupportedOperationException always; remove + * is not supported by this iterator. + */ public void remove() { - throw new UnsupportedOperationException("Unsupported operation remove"); + throw new UnsupportedOperationException(); } - public void set(Object o) { - throw new UnsupportedOperationException("Unsupported operation set"); + /** + * Not supported. Always throws UnsupportedOperationException. + * @throws UnsupportedOperationException always; set + * is not supported by this iterator. + */ + public void set(E e) { + throw new UnsupportedOperationException(); } + /** + * Not supported. Always throws UnsupportedOperationException. + * @throws UnsupportedOperationException always; add + * is not supported by this iterator. + */ + public void add(E e) { + throw new UnsupportedOperationException(); + } } /** - * Keeps a state of sublist implementation, - * size and array declared as final, - * so we'll never get the unconsistent state + * Returns a view of the portion of this list between + * fromIndex, inclusive, and toIndex, exclusive. + * The returned list is backed by this list, so changes in the + * returned list are reflected in this list. + * + *

      The semantics of the list returned by this method become + * undefined if the backing list (i.e., this list) is modified in + * any way other than via the returned list. + * + * @param fromIndex low endpoint (inclusive) of the subList + * @param toIndex high endpoint (exclusive) of the subList + * @return a view of the specified range within this list + * @throws IndexOutOfBoundsException {@inheritDoc} */ - static final class SubListReadData { - final int size; - - final Object[] data; - - SubListReadData(int size, Object[] data) { - this.size = size; - this.data = data; + public List subList(int fromIndex, int toIndex) { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + Object[] elements = getArray(); + int len = elements.length; + if (fromIndex < 0 || toIndex > len || fromIndex > toIndex) + throw new IndexOutOfBoundsException(); + return new COWSubList(this, fromIndex, toIndex); + } finally { + lock.unlock(); } } /** - * Represents a list returned by sublist(). + * Sublist for CopyOnWriteArrayList. + * This class extends AbstractList merely for convenience, to + * avoid having to define addAll, etc. This doesn't hurt, but + * is wasteful. This class does not need or use modCount + * mechanics in AbstractList, but does need to check for + * concurrent modification using similar mechanics. On each + * operation, the array that we expect the backing list to use + * is checked and updated. Since we do this for all of the + * base operations invoked by those defined in AbstractList, + * all is well. While inefficient, this is not worth + * improving. The kinds of list operations inherited from + * AbstractList are already so slow on COW sublists that + * adding a bit more space/time doesn't seem even noticeable. */ - static class SubList implements List { - private final CopyOnWriteArrayList list; - - private volatile SubListReadData read; - - private final int start; - - /** - * Sublist constructor. - * - * @param list backing list. - * @param fromIdx startingIndex, inclusive - * @param toIdx endIndex, exclusive - */ - public SubList(CopyOnWriteArrayList list, int fromIdx, int toIdx) { - this.list = list; - Object[] data = list.getData(); - int size = toIdx - fromIdx; - checkIndexExlusive(fromIdx, data.length); - checkIndexInclusive(toIdx, data.length); - read = new SubListReadData(size, list.getData()); - start = fromIdx; - } - - /** - * throws ConcurrentModificationException when the list - * is structurally modified in the other way other than via this subList - *

      - * Should be called under lock! - */ - private void checkModifications() { - if (read.data != list.getData()) { + private static class COWSubList + extends AbstractList + implements RandomAccess + { + private final CopyOnWriteArrayList l; + private final int offset; + private int size; + private Object[] expectedArray; + + // only call this holding l's lock + COWSubList(CopyOnWriteArrayList list, + int fromIndex, int toIndex) { + l = list; + expectedArray = l.getArray(); + offset = fromIndex; + size = toIndex - fromIndex; + } + + // only call this holding l's lock + private void checkForComodification() { + if (l.getArray() != expectedArray) throw new ConcurrentModificationException(); - } } - /** - * @see java.util.List#listIterator(int) - */ - public ListIterator listIterator(int startIdx) { - return new SubListIterator(startIdx, read); + // only call this holding l's lock + private void rangeCheck(int index) { + if (index<0 || index>=size) + throw new IndexOutOfBoundsException("Index: "+index+ + ",Size: "+size); } - /** - * @see java.util.List#set(int, java.lang.Object) - */ - public Object set(int index, Object obj) { - list.lock.lock(); + public E set(int index, E element) { + final ReentrantLock lock = l.lock; + lock.lock(); try { - checkIndexExlusive(index, read.size); - checkModifications(); - Object result = list.set(index + start, obj); - read = new SubListReadData(read.size, list.getData()); - return result; + rangeCheck(index); + checkForComodification(); + E x = l.set(index+offset, element); + expectedArray = l.getArray(); + return x; } finally { - list.lock.unlock(); + lock.unlock(); } } - /** - * @see java.util.List#get(int) - */ - public Object get(int index) { - SubListReadData data = read; - if (data.data != list.getData()) { - list.lock.lock(); - try { - data = read; - if (data.data != list.getData()) { - throw new ConcurrentModificationException(); - } - } finally { - list.lock.unlock(); - } - } - checkIndexExlusive(index, data.size); - return data.data[index + start]; - } - - /** - * @see java.util.Collection#size() - */ - public int size() { - return read.size; - } - - /** - * @see java.util.List#remove(int) - */ - public Object remove(int index) { - list.lock.lock(); + public E get(int index) { + final ReentrantLock lock = l.lock; + lock.lock(); try { - checkIndexExlusive(index, read.size); - checkModifications(); - Object obj = list.remove(index + start); - read = new SubListReadData(read.size - 1, list.getData()); - return obj; + rangeCheck(index); + checkForComodification(); + return l.get(index+offset); } finally { - list.lock.unlock(); + lock.unlock(); } } - /** - * @see java.util.List#add(int, java.lang.Object) - */ - public void add(int index, Object object) { - list.lock.lock(); + public int size() { + final ReentrantLock lock = l.lock; + lock.lock(); try { - checkIndexInclusive(index, read.size); - checkModifications(); - list.add(index + start, object); - read = new SubListReadData(read.size + 1, list.getData()); + checkForComodification(); + return size; } finally { - list.lock.unlock(); + lock.unlock(); } } - public boolean add(Object o) { - list.lock.lock(); + public void add(int index, E element) { + final ReentrantLock lock = l.lock; + lock.lock(); try { - checkModifications(); - list.add(start + read.size, o); - read = new SubListReadData(read.size + 1, list.getData()); - return true; + checkForComodification(); + if (index<0 || index>size) + throw new IndexOutOfBoundsException(); + l.add(index+offset, element); + expectedArray = l.getArray(); + size++; } finally { - list.lock.unlock(); + lock.unlock(); } } - public boolean addAll(Collection c) { - list.lock.lock(); + public void clear() { + final ReentrantLock lock = l.lock; + lock.lock(); try { - checkModifications(); - int d = list.size(); - list.addAll(start + read.size, c); - read = new SubListReadData(read.size + (list.size() - d), list - .getData()); - return true; + checkForComodification(); + l.removeRange(offset, offset+size); + expectedArray = l.getArray(); + size = 0; } finally { - list.lock.unlock(); + lock.unlock(); } } - public void clear() { - list.lock.lock(); + public E remove(int index) { + final ReentrantLock lock = l.lock; + lock.lock(); try { - checkModifications(); - list.removeRange(start, read.size); - read = new SubListReadData(0, list.getData()); + rangeCheck(index); + checkForComodification(); + E result = l.remove(index+offset); + expectedArray = l.getArray(); + size--; + return result; } finally { - list.lock.unlock(); + lock.unlock(); } } - public boolean contains(Object o) { - return indexOf(o) != -1; - } - - public boolean containsAll(Collection c) { - SubListReadData b = read; - return CopyOnWriteArrayList.containsAll(c, b.data, start, b.size); - } - - public int indexOf(Object o) { - SubListReadData b = read; - int ind = CopyOnWriteArrayList.indexOf(o, b.data, start, b.size) - - start; - return ind < 0 ? -1 : ind; - } - - public boolean isEmpty() { - return read.size == 0; - } - - public Iterator iterator() { - return new SubListIterator(0, read); - } - - public int lastIndexOf(Object o) { - SubListReadData b = read; - int ind = CopyOnWriteArrayList - .lastIndexOf(o, b.data, start, b.size) - - start; - return ind < 0 ? -1 : ind; - } - - public ListIterator listIterator() { - return new SubListIterator(0, read); + public boolean remove(Object o) { + int index = indexOf(o); + if (index == -1) + return false; + remove(index); + return true; } - public boolean remove(Object o) { - list.lock.lock(); + public Iterator iterator() { + final ReentrantLock lock = l.lock; + lock.lock(); try { - checkModifications(); - int i = indexOf(o); - if (i == -1) { - return false; - } - boolean result = list.remove(i + start) != null; - if (result) { - read = new SubListReadData(read.size - 1, list.getData()); - } - return result; + checkForComodification(); + return new COWSubListIterator(l, 0, offset, size); } finally { - list.lock.unlock(); + lock.unlock(); } } - public boolean removeAll(Collection c) { - list.lock.lock(); + public ListIterator listIterator(final int index) { + final ReentrantLock lock = l.lock; + lock.lock(); try { - checkModifications(); - int removed = list.removeAll(c, start, read.size); - if (removed > 0) { - read = new SubListReadData(read.size - removed, list - .getData()); - return true; - } + checkForComodification(); + if (index<0 || index>size) + throw new IndexOutOfBoundsException("Index: "+index+ + ", Size: "+size); + return new COWSubListIterator(l, index, offset, size); } finally { - list.lock.unlock(); + lock.unlock(); } - return false; } - public boolean retainAll(Collection c) { - list.lock.lock(); + public List subList(int fromIndex, int toIndex) { + final ReentrantLock lock = l.lock; + lock.lock(); try { - checkModifications(); - int removed = list.retainAll(c, start, read.size); - if (removed > 0) { - read = new SubListReadData(read.size - removed, list - .getData()); - return true; - } - return false; + checkForComodification(); + if (fromIndex<0 || toIndex>size) + throw new IndexOutOfBoundsException(); + return new COWSubList(l, fromIndex + offset, + toIndex + offset); } finally { - list.lock.unlock(); + lock.unlock(); } } - public List subList(int fromIndex, int toIndex) { - return new SubList(list, start + fromIndex, start + toIndex); - } + } + - public Object[] toArray() { - SubListReadData r = read; - return CopyOnWriteArrayList.toArray(r.data, start, r.size); + private static class COWSubListIterator implements ListIterator { + private final ListIterator i; + private final int index; + private final int offset; + private final int size; + + COWSubListIterator(List l, int index, int offset, + int size) { + this.index = index; + this.offset = offset; + this.size = size; + i = l.listIterator(index+offset); } - public Object[] toArray(Object[] a) { - SubListReadData r = read; - return CopyOnWriteArrayList.toArray(a, r.data, start, r.size); + public boolean hasNext() { + return nextIndex() < size; } - /** - * @see java.util.List#addAll(int, java.util.Collection) - */ - public boolean addAll(int index, Collection collection) { - list.lock.lock(); - try { - checkIndexInclusive(index, read.size); - checkModifications(); - int d = list.size(); - boolean rt = list.addAll(index + start, collection); - read = new SubListReadData(read.size + list.size() - d, list - .getData()); - return rt; - } finally { - list.lock.unlock(); - } + public E next() { + if (hasNext()) + return i.next(); + else + throw new NoSuchElementException(); } - /** - * Implementation of ListIterator for the - * SubList - * gets a snapshot of the sublist, - * never throws ConcurrentModificationException - */ - private class SubListIterator extends ListIteratorImpl { - private final SubListReadData dataR; - - /** - * Constructs an iterator starting with the given index - * - * @param index index of the first element to iterate. - */ - private SubListIterator(int index, SubListReadData d) { - super(d.data, index + start); - this.dataR = d; - } + public boolean hasPrevious() { + return previousIndex() >= 0; + } - /** - * @see java.util.ListIterator#nextIndex() - */ - public int nextIndex() { - return super.nextIndex() - start; - } + public E previous() { + if (hasPrevious()) + return i.previous(); + else + throw new NoSuchElementException(); + } - /** - * @see java.util.ListIterator#previousIndex() - */ - public int previousIndex() { - return super.previousIndex() - start; - } + public int nextIndex() { + return i.nextIndex() - offset; + } - /** - * @see java.util.Iterator#hasNext() - */ - public boolean hasNext() { - return nextIndex() < dataR.size; - } + public int previousIndex() { + return i.previousIndex() - offset; + } - /** - * @see java.util.ListIterator#hasPrevious() - */ - public boolean hasPrevious() { - return previousIndex() > -1; - } + public void remove() { + throw new UnsupportedOperationException(); } - } + public void set(E e) { + throw new UnsupportedOperationException(); + } - //serialization support - /** - * Writes the object state to the ObjectOutputStream. - * - * @param oos ObjectOutputStream to write object to. - * @throws IOException if an I/O error occur. - */ - private void writeObject(ObjectOutputStream oos) throws IOException { - E[] back = getData(); - int size = back.length; - oos.defaultWriteObject(); - oos.writeInt(size); - for (int i = 0; i < size; i++) { - oos.writeObject(back[i]); + public void add(E e) { + throw new UnsupportedOperationException(); } } - /** - * Reads the object state from the ObjectOutputStream. - * - * @param ois ObjectInputStream to read object from. - * @throws IOException if an I/O error occur. - */ - private void readObject(ObjectInputStream ois) throws IOException, - ClassNotFoundException { - ois.defaultReadObject(); - int length = ois.readInt(); - if (length == 0) { - setData(newElementArray(0)); - } else { - E[] back = newElementArray(length); - for (int i = 0; i < back.length; i++) { - back[i] = (E) ois.readObject(); - } - setData(back); - } + // Support for resetting lock while deserializing + private static final Unsafe unsafe = Unsafe.getUnsafe(); + private static final long lockOffset; + static { + try { + lockOffset = unsafe.objectFieldOffset + (CopyOnWriteArrayList.class.getDeclaredField("lock")); + } catch (Exception ex) { throw new Error(ex); } + } + private void resetLock() { + unsafe.putObjectVolatile(this, lockOffset, new ReentrantLock()); } } diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/CopyOnWriteArraySet.java b/libcore/concurrent/src/main/java/java/util/concurrent/CopyOnWriteArraySet.java index 532952bb8..65a05de45 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/CopyOnWriteArraySet.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/CopyOnWriteArraySet.java @@ -1,7 +1,7 @@ /* * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain. Use, modify, and - * redistribute this code in any way without acknowledgement. + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/licenses/publicdomain */ package java.util.concurrent; @@ -9,39 +9,37 @@ import java.util.*; // BEGIN android-note // removed link to collections framework docs -// Removed clonable interface to be closer to the RI. // END android-note /** - * A {@link java.util.Set} that uses {@link - * java.util.concurrent.CopyOnWriteArrayList} for all of its - * operations. Thus, it shares the same basic properties: + * A {@link java.util.Set} that uses an internal {@link CopyOnWriteArrayList} + * for all of its operations. Thus, it shares the same basic properties: *

        *
      • It is best suited for applications in which set sizes generally * stay small, read-only operations * vastly outnumber mutative operations, and you need * to prevent interference among threads during traversal. - *
      • Mutative operations(add, set, remove, etc) are expensive - * since they usually entail copying the entire underlying array. - *
      • Iterators do not support the mutative remove operation - *
      • Traversal via iterators is very fast and cannot ever encounter + *
      • It is thread-safe. + *
      • Mutative operations (add, set, remove, etc.) + * are expensive since they usually entail copying the entire underlying + * array. + *
      • Iterators do not support the mutative remove operation. + *
      • Traversal via iterators is fast and cannot encounter * interference from other threads. Iterators rely on * unchanging snapshots of the array at the time the iterators were - * constructed. + * constructed. *
      - *

      - * Sample Usage. Probably the main application - * of copy-on-write sets are classes that maintain - * sets of Handler objects - * that must be multicasted to upon an update command. This - * is a classic case where you do not want to be holding a - * lock while sending a message, and where traversals normally - * vastly overwhelm additions. + * + *

      Sample Usage. The following code sketch uses a + * copy-on-write set to maintain a set of Handler objects that + * perform some action upon state updates. + * *

        * class Handler { void handle(); ... }
        *
        * class X {
      - *    private final CopyOnWriteArraySet<Handler> handlers = new CopyOnWriteArraySet<Handler>();
      + *    private final CopyOnWriteArraySet<Handler> handlers
      + *       = new CopyOnWriteArraySet<Handler>();
        *    public void addHandler(Handler h) { handlers.add(h); }
        *
        *    private long internalState;
      @@ -49,14 +47,13 @@ import java.util.*;
        *
        *    public void update() {
        *       changeState();
      - *       Iterator it = handlers.iterator();
      - *       while (it.hasNext())
      - *          it.next().handle();
      + *       for (Handler handler : handlers)
      + *          handler.handle();
        *    }
        * }
        * 
      - * @see CopyOnWriteArrayList * + * @see CopyOnWriteArrayList * @since 1.5 * @author Doug Lea * @param the type of elements held in this collection @@ -76,27 +73,292 @@ public class CopyOnWriteArraySet extends AbstractSet /** * Creates a set containing all of the elements of the specified - * Collection. - * @param c the collection + * collection. + * + * @param c the collection of elements to initially contain + * @throws NullPointerException if the specified collection is null */ public CopyOnWriteArraySet(Collection c) { al = new CopyOnWriteArrayList(); al.addAllAbsent(c); } + /** + * Returns the number of elements in this set. + * + * @return the number of elements in this set + */ + public int size() { + return al.size(); + } + + /** + * Returns true if this set contains no elements. + * + * @return true if this set contains no elements + */ + public boolean isEmpty() { + return al.isEmpty(); + } + + /** + * Returns true if this set contains the specified element. + * More formally, returns true if and only if this set + * contains an element e such that + * (o==null ? e==null : o.equals(e)). + * + * @param o element whose presence in this set is to be tested + * @return true if this set contains the specified element + */ + public boolean contains(Object o) { + return al.contains(o); + } + + /** + * Returns an array containing all of the elements in this set. + * If this set makes any guarantees as to what order its elements + * are returned by its iterator, this method must return the + * elements in the same order. + * + *

      The returned array will be "safe" in that no references to it + * are maintained by this set. (In other words, this method must + * allocate a new array even if this set is backed by an array). + * The caller is thus free to modify the returned array. + * + *

      This method acts as bridge between array-based and collection-based + * APIs. + * + * @return an array containing all the elements in this set + */ + public Object[] toArray() { + return al.toArray(); + } + + /** + * Returns an array containing all of the elements in this set; the + * runtime type of the returned array is that of the specified array. + * If the set fits in the specified array, it is returned therein. + * Otherwise, a new array is allocated with the runtime type of the + * specified array and the size of this set. + * + *

      If this set fits in the specified array with room to spare + * (i.e., the array has more elements than this set), the element in + * the array immediately following the end of the set is set to + * null. (This is useful in determining the length of this + * set only if the caller knows that this set does not contain + * any null elements.) + * + *

      If this set makes any guarantees as to what order its elements + * are returned by its iterator, this method must return the elements + * in the same order. + * + *

      Like the {@link #toArray()} method, this method acts as bridge between + * array-based and collection-based APIs. Further, this method allows + * precise control over the runtime type of the output array, and may, + * under certain circumstances, be used to save allocation costs. + * + *

      Suppose x is a set known to contain only strings. + * The following code can be used to dump the set into a newly allocated + * array of String: + * + *

      +     *     String[] y = x.toArray(new String[0]);
      + * + * Note that toArray(new Object[0]) is identical in function to + * toArray(). + * + * @param a the array into which the elements of this set are to be + * stored, if it is big enough; otherwise, a new array of the same + * runtime type is allocated for this purpose. + * @return an array containing all the elements in this set + * @throws ArrayStoreException if the runtime type of the specified array + * is not a supertype of the runtime type of every element in this + * set + * @throws NullPointerException if the specified array is null + */ + public T[] toArray(T[] a) { + return al.toArray(a); + } + + /** + * Removes all of the elements from this set. + * The set will be empty after this call returns. + */ + public void clear() { + al.clear(); + } - public int size() { return al.size(); } - public boolean isEmpty() { return al.isEmpty(); } - public boolean contains(Object o) { return al.contains(o); } - public Object[] toArray() { return al.toArray(); } - public T[] toArray(T[] a) { return al.toArray(a); } - public void clear() { al.clear(); } - public Iterator iterator() { return al.iterator(); } - public boolean remove(Object o) { return al.remove(o); } - public boolean add(E o) { return al.addIfAbsent(o); } - public boolean containsAll(Collection c) { return al.containsAll(c); } - public boolean addAll(Collection c) { return al.addAllAbsent(c) > 0; } - public boolean removeAll(Collection c) { return al.removeAll(c); } - public boolean retainAll(Collection c) { return al.retainAll(c); } + /** + * Removes the specified element from this set if it is present. + * More formally, removes an element e such that + * (o==null ? e==null : o.equals(e)), + * if this set contains such an element. Returns true if + * this set contained the element (or equivalently, if this set + * changed as a result of the call). (This set will not contain the + * element once the call returns.) + * + * @param o object to be removed from this set, if present + * @return true if this set contained the specified element + */ + public boolean remove(Object o) { + return al.remove(o); + } + /** + * Adds the specified element to this set if it is not already present. + * More formally, adds the specified element e to this set if + * the set contains no element e2 such that + * (e==null ? e2==null : e.equals(e2)). + * If this set already contains the element, the call leaves the set + * unchanged and returns false. + * + * @param e element to be added to this set + * @return true if this set did not already contain the specified + * element + */ + public boolean add(E e) { + return al.addIfAbsent(e); + } + + /** + * Returns true if this set contains all of the elements of the + * specified collection. If the specified collection is also a set, this + * method returns true if it is a subset of this set. + * + * @param c collection to be checked for containment in this set + * @return true if this set contains all of the elements of the + * specified collection + * @throws NullPointerException if the specified collection is null + * @see #contains(Object) + */ + public boolean containsAll(Collection c) { + return al.containsAll(c); + } + + /** + * Adds all of the elements in the specified collection to this set if + * they're not already present. If the specified collection is also a + * set, the addAll operation effectively modifies this set so + * that its value is the union of the two sets. The behavior of + * this operation is undefined if the specified collection is modified + * while the operation is in progress. + * + * @param c collection containing elements to be added to this set + * @return true if this set changed as a result of the call + * @throws NullPointerException if the specified collection is null + * @see #add(Object) + */ + public boolean addAll(Collection c) { + return al.addAllAbsent(c) > 0; + } + + /** + * Removes from this set all of its elements that are contained in the + * specified collection. If the specified collection is also a set, + * this operation effectively modifies this set so that its value is the + * asymmetric set difference of the two sets. + * + * @param c collection containing elements to be removed from this set + * @return true if this set changed as a result of the call + * @throws ClassCastException if the class of an element of this set + * is incompatible with the specified collection (optional) + * @throws NullPointerException if this set contains a null element and the + * specified collection does not permit null elements (optional), + * or if the specified collection is null + * @see #remove(Object) + */ + public boolean removeAll(Collection c) { + return al.removeAll(c); + } + + /** + * Retains only the elements in this set that are contained in the + * specified collection. In other words, removes from this set all of + * its elements that are not contained in the specified collection. If + * the specified collection is also a set, this operation effectively + * modifies this set so that its value is the intersection of the + * two sets. + * + * @param c collection containing elements to be retained in this set + * @return true if this set changed as a result of the call + * @throws ClassCastException if the class of an element of this set + * is incompatible with the specified collection (optional) + * @throws NullPointerException if this set contains a null element and the + * specified collection does not permit null elements (optional), + * or if the specified collection is null + * @see #remove(Object) + */ + public boolean retainAll(Collection c) { + return al.retainAll(c); + } + + /** + * Returns an iterator over the elements contained in this set + * in the order in which these elements were added. + * + *

      The returned iterator provides a snapshot of the state of the set + * when the iterator was constructed. No synchronization is needed while + * traversing the iterator. The iterator does NOT support the + * remove method. + * + * @return an iterator over the elements in this set + */ + public Iterator iterator() { + return al.iterator(); + } + + /** + * Compares the specified object with this set for equality. + * Returns {@code true} if the specified object is the same object + * as this object, or if it is also a {@link Set} and the elements + * returned by an {@linkplain List#iterator() iterator} over the + * specified set are the same as the elements returned by an + * iterator over this set. More formally, the two iterators are + * considered to return the same elements if they return the same + * number of elements and for every element {@code e1} returned by + * the iterator over the specified set, there is an element + * {@code e2} returned by the iterator over this set such that + * {@code (e1==null ? e2==null : e1.equals(e2))}. + * + * @param o object to be compared for equality with this set + * @return {@code true} if the specified object is equal to this set + */ + public boolean equals(Object o) { + if (o == this) + return true; + if (!(o instanceof Set)) + return false; + Set set = (Set)(o); + Iterator it = set.iterator(); + + // Uses O(n^2) algorithm that is only appropriate + // for small sets, which CopyOnWriteArraySets should be. + + // Use a single snapshot of underlying array + Object[] elements = al.getArray(); + int len = elements.length; + // Mark matched elements to avoid re-checking + boolean[] matched = new boolean[len]; + int k = 0; + outer: while (it.hasNext()) { + if (++k > len) + return false; + Object x = it.next(); + for (int i = 0; i < len; ++i) { + if (!matched[i] && eq(x, elements[i])) { + matched[i] = true; + continue outer; + } + } + return false; + } + return k == len; + } + + /** + * Test for equality, coping with nulls. + */ + private static boolean eq(Object o1, Object o2) { + return (o1 == null ? o2 == null : o1.equals(o2)); + } } diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/CountDownLatch.java b/libcore/concurrent/src/main/java/java/util/concurrent/CountDownLatch.java index 721744ccd..2b945dd51 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/CountDownLatch.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/CountDownLatch.java @@ -12,25 +12,25 @@ import java.util.concurrent.atomic.*; * A synchronization aid that allows one or more threads to wait until * a set of operations being performed in other threads completes. * - *

      A CountDownLatch is initialized with a given - * count. The {@link #await await} methods block until the current - * {@link #getCount count} reaches zero due to invocations of the - * {@link #countDown} method, after which all waiting threads are - * released and any subsequent invocations of {@link #await await} return - * immediately. This is a one-shot phenomenon -- the count cannot be - * reset. If you need a version that resets the count, consider using - * a {@link CyclicBarrier}. + *

      A {@code CountDownLatch} is initialized with a given count. + * The {@link #await await} methods block until the current count reaches + * zero due to invocations of the {@link #countDown} method, after which + * all waiting threads are released and any subsequent invocations of + * {@link #await await} return immediately. This is a one-shot phenomenon + * -- the count cannot be reset. If you need a version that resets the + * count, consider using a {@link CyclicBarrier}. * - *

      A CountDownLatch is a versatile synchronization tool + *

      A {@code CountDownLatch} is a versatile synchronization tool * and can be used for a number of purposes. A - * CountDownLatch initialized with a count of one serves as a + * {@code CountDownLatch} initialized with a count of one serves as a * simple on/off latch, or gate: all threads invoking {@link #await await} * wait at the gate until it is opened by a thread invoking {@link - * #countDown}. A CountDownLatch initialized to N + * #countDown}. A {@code CountDownLatch} initialized to N * can be used to make one thread wait until N threads have * completed some action, or some action has been completed N times. - *

      A useful property of a CountDownLatch is that it - * doesn't require that threads calling countDown wait for + * + *

      A useful property of a {@code CountDownLatch} is that it + * doesn't require that threads calling {@code countDown} wait for * the count to reach zero before proceeding, it simply prevents any * thread from proceeding past an {@link #await await} until all * threads could pass. @@ -119,6 +119,13 @@ import java.util.concurrent.atomic.*; * * * + *

      Memory consistency effects: Until the count reaches + * zero, actions in a thread prior to calling + * {@code countDown()} + * happen-before + * actions following a successful return from a corresponding + * {@code await()} in another thread. + * * @since 1.5 * @author Doug Lea */ @@ -128,118 +135,120 @@ public class CountDownLatch { * Uses AQS state to represent count. */ private static final class Sync extends AbstractQueuedSynchronizer { + private static final long serialVersionUID = 4982264981922014374L; + Sync(int count) { - setState(count); + setState(count); } - + int getCount() { return getState(); } - public int tryAcquireShared(int acquires) { + protected int tryAcquireShared(int acquires) { return getState() == 0? 1 : -1; } - - public boolean tryReleaseShared(int releases) { + + protected boolean tryReleaseShared(int releases) { // Decrement count; signal when transition to zero for (;;) { int c = getState(); if (c == 0) return false; int nextc = c-1; - if (compareAndSetState(c, nextc)) + if (compareAndSetState(c, nextc)) return nextc == 0; } } } private final Sync sync; + /** - * Constructs a CountDownLatch initialized with the given - * count. - * - * @param count the number of times {@link #countDown} must be invoked - * before threads can pass through {@link #await}. + * Constructs a {@code CountDownLatch} initialized with the given count. * - * @throws IllegalArgumentException if count is less than zero. + * @param count the number of times {@link #countDown} must be invoked + * before threads can pass through {@link #await} + * @throws IllegalArgumentException if {@code count} is negative */ - public CountDownLatch(int count) { + public CountDownLatch(int count) { if (count < 0) throw new IllegalArgumentException("count < 0"); this.sync = new Sync(count); } /** - * Causes the current thread to wait until the latch has counted down to - * zero, unless the thread is {@link Thread#interrupt interrupted}. + * Causes the current thread to wait until the latch has counted down to + * zero, unless the thread is {@linkplain Thread#interrupt interrupted}. * - *

      If the current {@link #getCount count} is zero then this method - * returns immediately. - *

      If the current {@link #getCount count} is greater than zero then - * the current thread becomes disabled for thread scheduling - * purposes and lies dormant until one of two things happen: + *

      If the current count is zero then this method returns immediately. + * + *

      If the current count is greater than zero then the current + * thread becomes disabled for thread scheduling purposes and lies + * dormant until one of two things happen: *

        *
      • The count reaches zero due to invocations of the * {@link #countDown} method; or - *
      • Some other thread {@link Thread#interrupt interrupts} the current - * thread. + *
      • Some other thread {@linkplain Thread#interrupt interrupts} + * the current thread. *
      + * *

      If the current thread: *

        - *
      • has its interrupted status set on entry to this method; or - *
      • is {@link Thread#interrupt interrupted} while waiting, + *
      • has its interrupted status set on entry to this method; or + *
      • is {@linkplain Thread#interrupt interrupted} while waiting, *
      - * then {@link InterruptedException} is thrown and the current thread's - * interrupted status is cleared. + * then {@link InterruptedException} is thrown and the current thread's + * interrupted status is cleared. * * @throws InterruptedException if the current thread is interrupted - * while waiting. + * while waiting */ public void await() throws InterruptedException { sync.acquireSharedInterruptibly(1); } /** - * Causes the current thread to wait until the latch has counted down to - * zero, unless the thread is {@link Thread#interrupt interrupted}, + * Causes the current thread to wait until the latch has counted down to + * zero, unless the thread is {@linkplain Thread#interrupt interrupted}, * or the specified waiting time elapses. * - *

      If the current {@link #getCount count} is zero then this method - * returns immediately with the value true. + *

      If the current count is zero then this method returns immediately + * with the value {@code true}. * - *

      If the current {@link #getCount count} is greater than zero then - * the current thread becomes disabled for thread scheduling - * purposes and lies dormant until one of three things happen: + *

      If the current count is greater than zero then the current + * thread becomes disabled for thread scheduling purposes and lies + * dormant until one of three things happen: *

        *
      • The count reaches zero due to invocations of the * {@link #countDown} method; or - *
      • Some other thread {@link Thread#interrupt interrupts} the current - * thread; or + *
      • Some other thread {@linkplain Thread#interrupt interrupts} + * the current thread; or *
      • The specified waiting time elapses. *
      + * *

      If the count reaches zero then the method returns with the - * value true. + * value {@code true}. + * *

      If the current thread: *

        - *
      • has its interrupted status set on entry to this method; or - *
      • is {@link Thread#interrupt interrupted} while waiting, + *
      • has its interrupted status set on entry to this method; or + *
      • is {@linkplain Thread#interrupt interrupted} while waiting, *
      - * then {@link InterruptedException} is thrown and the current thread's - * interrupted status is cleared. + * then {@link InterruptedException} is thrown and the current thread's + * interrupted status is cleared. * - *

      If the specified waiting time elapses then the value false - * is returned. - * If the time is - * less than or equal to zero, the method will not wait at all. + *

      If the specified waiting time elapses then the value {@code false} + * is returned. If the time is less than or equal to zero, the method + * will not wait at all. * * @param timeout the maximum time to wait - * @param unit the time unit of the timeout argument. - * @return true if the count reached zero and false - * if the waiting time elapsed before the count reached zero. - * + * @param unit the time unit of the {@code timeout} argument + * @return {@code true} if the count reached zero and {@code false} + * if the waiting time elapsed before the count reached zero * @throws InterruptedException if the current thread is interrupted - * while waiting. + * while waiting */ - public boolean await(long timeout, TimeUnit unit) + public boolean await(long timeout, TimeUnit unit) throws InterruptedException { return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout)); } @@ -247,11 +256,12 @@ public class CountDownLatch { /** * Decrements the count of the latch, releasing all waiting threads if * the count reaches zero. - *

      If the current {@link #getCount count} is greater than zero then - * it is decremented. If the new count is zero then all waiting threads - * are re-enabled for thread scheduling purposes. - *

      If the current {@link #getCount count} equals zero then nothing - * happens. + * + *

      If the current count is greater than zero then it is decremented. + * If the new count is zero then all waiting threads are re-enabled for + * thread scheduling purposes. + * + *

      If the current count equals zero then nothing happens. */ public void countDown() { sync.releaseShared(1); @@ -259,8 +269,10 @@ public class CountDownLatch { /** * Returns the current count. + * *

      This method is typically used for debugging and testing purposes. - * @return the current count. + * + * @return the current count */ public long getCount() { return sync.getCount(); @@ -268,13 +280,12 @@ public class CountDownLatch { /** * Returns a string identifying this latch, as well as its state. - * The state, in brackets, includes the String - * "Count =" followed by the current count. - * @return a string identifying this latch, as well as its - * state + * The state, in brackets, includes the String {@code "Count ="} + * followed by the current count. + * + * @return a string identifying this latch, as well as its state */ public String toString() { return super.toString() + "[Count = " + sync.getCount() + "]"; } - } diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/CyclicBarrier.java b/libcore/concurrent/src/main/java/java/util/concurrent/CyclicBarrier.java index d70c4c7f6..d5738c5ae 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/CyclicBarrier.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/CyclicBarrier.java @@ -17,10 +17,10 @@ import java.util.concurrent.locks.*; * *

      A CyclicBarrier supports an optional {@link Runnable} command * that is run once per barrier point, after the last thread in the party - * arrives, but before any threads are released. + * arrives, but before any threads are released. * This barrier action is useful * for updating shared-state before any of the parties continue. - * + * *

      Sample usage: Here is an example of * using a barrier in a parallel decomposition design: *

      @@ -28,7 +28,7 @@ import java.util.concurrent.locks.*;
        *   final int N;
        *   final float[][] data;
        *   final CyclicBarrier barrier;
      - *   
      + *
        *   class Worker implements Runnable {
        *     int myRow;
        *     Worker(int row) { myRow = row; }
      @@ -37,11 +37,11 @@ import java.util.concurrent.locks.*;
        *         processRow(myRow);
        *
        *         try {
      - *           barrier.await(); 
      - *         } catch (InterruptedException ex) { 
      - *           return; 
      - *         } catch (BrokenBarrierException ex) { 
      - *           return; 
      + *           barrier.await();
      + *         } catch (InterruptedException ex) {
      + *           return;
      + *         } catch (BrokenBarrierException ex) {
      + *           return;
        *         }
        *       }
        *     }
      @@ -50,22 +50,22 @@ import java.util.concurrent.locks.*;
        *   public Solver(float[][] matrix) {
        *     data = matrix;
        *     N = matrix.length;
      - *     barrier = new CyclicBarrier(N, 
      + *     barrier = new CyclicBarrier(N,
        *                                 new Runnable() {
      - *                                   public void run() { 
      - *                                     mergeRows(...); 
      + *                                   public void run() {
      + *                                     mergeRows(...);
        *                                   }
        *                                 });
      - *     for (int i = 0; i < N; ++i) 
      + *     for (int i = 0; i < N; ++i)
        *       new Thread(new Worker(i)).start();
        *
        *     waitUntilDone();
        *   }
        * }
        * 
      - * Here, each worker thread processes a row of the matrix then waits at the + * Here, each worker thread processes a row of the matrix then waits at the * barrier until all rows have been processed. When all rows are processed - * the supplied {@link Runnable} barrier action is executed and merges the + * the supplied {@link Runnable} barrier action is executed and merges the * rows. If the merger * determines that a solution has been found then done() will return * true and each worker will terminate. @@ -74,19 +74,26 @@ import java.util.concurrent.locks.*; * it is executed, then any of the threads in the party could execute that * action when it is released. To facilitate this, each invocation of * {@link #await} returns the arrival index of that thread at the barrier. - * You can then choose which thread should execute the barrier action, for + * You can then choose which thread should execute the barrier action, for * example: *
        if (barrier.await() == 0) {
        *     // log the completion of this iteration
        *   }
      * - *

      The CyclicBarrier uses a fast-fail all-or-none breakage - * model for failed synchronization attempts: If a thread leaves a - * barrier point prematurely because of interruption, failure, or - * timeout, all other threads, even those that have not yet resumed - * from a previous {@link #await}. will also leave abnormally via - * {@link BrokenBarrierException} (or InterruptedException if - * they too were interrupted at about the same time). + *

      The CyclicBarrier uses an all-or-none breakage model + * for failed synchronization attempts: If a thread leaves a barrier + * point prematurely because of interruption, failure, or timeout, all + * other threads waiting at that barrier point will also leave + * abnormally via {@link BrokenBarrierException} (or + * {@link InterruptedException} if they too were interrupted at about + * the same time). + * + *

      Memory consistency effects: Actions in a thread prior to calling + * {@code await()} + * happen-before + * actions that are part of the barrier action, which in turn + * happen-before actions following a successful return from the + * corresponding {@code await()} in other threads. * * @since 1.5 * @see CountDownLatch @@ -94,6 +101,21 @@ import java.util.concurrent.locks.*; * @author Doug Lea */ public class CyclicBarrier { + /** + * Each use of the barrier is represented as a generation instance. + * The generation changes whenever the barrier is tripped, or + * is reset. There can be many generations associated with threads + * using the barrier - due to the non-deterministic way the lock + * may be allocated to waiting threads - but only one of these + * can be active at a time (the one to which count applies) + * and all the rest are either broken or tripped. + * There need not be an active generation if there has been a break + * but no subsequent reset. + */ + private static class Generation { + boolean broken = false; + } + /** The lock for guarding barrier entry */ private final ReentrantLock lock = new ReentrantLock(); /** Condition to wait on until tripped */ @@ -102,53 +124,50 @@ public class CyclicBarrier { private final int parties; /* The command to run when tripped */ private final Runnable barrierCommand; - - /** - * The generation number. Incremented upon barrier trip. - * Retracted upon reset. - */ - private long generation; - - /** - * Breakage indicator. - */ - private boolean broken; + /** The current generation */ + private Generation generation = new Generation(); /** * Number of parties still waiting. Counts down from parties to 0 - * on each cycle. + * on each generation. It is reset to parties on each new + * generation or when broken. */ - private int count; + private int count; /** - * Updates state on barrier trip and wake up everyone. - */ + * Updates state on barrier trip and wakes up everyone. + * Called only while holding lock. + */ private void nextGeneration() { - count = parties; - ++generation; + // signal completion of last generation trip.signalAll(); + // set up next generation + count = parties; + generation = new Generation(); } /** - * Sets barrier as broken and wake up everyone + * Sets current barrier generation as broken and wakes up everyone. + * Called only while holding lock. */ private void breakBarrier() { - broken = true; + generation.broken = true; + count = parties; trip.signalAll(); } /** * Main barrier code, covering the various policies. */ - private int dowait(boolean timed, long nanos) - throws InterruptedException, BrokenBarrierException, TimeoutException { + private int dowait(boolean timed, long nanos) + throws InterruptedException, BrokenBarrierException, + TimeoutException { final ReentrantLock lock = this.lock; lock.lock(); try { - int index = --count; - long g = generation; + final Generation g = generation; - if (broken) + if (g.broken) throw new BrokenBarrierException(); if (Thread.interrupted()) { @@ -156,37 +175,45 @@ public class CyclicBarrier { throw new InterruptedException(); } - if (index == 0) { // tripped - nextGeneration(); - boolean ranAction = false; - try { - Runnable command = barrierCommand; - if (command != null) - command.run(); - ranAction = true; - return 0; - } finally { - if (!ranAction) - breakBarrier(); - } - } + int index = --count; + if (index == 0) { // tripped + boolean ranAction = false; + try { + final Runnable command = barrierCommand; + if (command != null) + command.run(); + ranAction = true; + nextGeneration(); + return 0; + } finally { + if (!ranAction) + breakBarrier(); + } + } + // loop until tripped, broken, interrupted, or timed out for (;;) { try { - if (!timed) + if (!timed) trip.await(); else if (nanos > 0L) nanos = trip.awaitNanos(nanos); } catch (InterruptedException ie) { - breakBarrier(); - throw ie; + if (g == generation && ! g.broken) { + breakBarrier(); + throw ie; + } else { + // We're about to finish waiting even if we had not + // been interrupted, so this interrupt is deemed to + // "belong" to subsequent execution. + Thread.currentThread().interrupt(); + } } - - if (broken || - g > generation) // true if a reset occurred while waiting + + if (g.broken) throw new BrokenBarrierException(); - if (g < generation) + if (g != generation) return index; if (timed && nanos <= 0L) { @@ -194,7 +221,6 @@ public class CyclicBarrier { throw new TimeoutException(); } } - } finally { lock.unlock(); } @@ -207,15 +233,14 @@ public class CyclicBarrier { * performed by the last thread entering the barrier. * * @param parties the number of threads that must invoke {@link #await} - * before the barrier is tripped. + * before the barrier is tripped * @param barrierAction the command to execute when the barrier is - * tripped, or null if there is no action. - * - * @throws IllegalArgumentException if parties is less than 1. + * tripped, or {@code null} if there is no action + * @throws IllegalArgumentException if {@code parties} is less than 1 */ public CyclicBarrier(int parties, Runnable barrierAction) { if (parties <= 0) throw new IllegalArgumentException(); - this.parties = parties; + this.parties = parties; this.count = parties; this.barrierCommand = barrierAction; } @@ -223,12 +248,11 @@ public class CyclicBarrier { /** * Creates a new CyclicBarrier that will trip when the * given number of parties (threads) are waiting upon it, and - * does not perform a predefined action upon each barrier. + * does not perform a predefined action when the barrier is tripped. * * @param parties the number of threads that must invoke {@link #await} - * before the barrier is tripped. - * - * @throws IllegalArgumentException if parties is less than 1. + * before the barrier is tripped + * @throws IllegalArgumentException if {@code parties} is less than 1 */ public CyclicBarrier(int parties) { this(parties, null); @@ -236,64 +260,66 @@ public class CyclicBarrier { /** * Returns the number of parties required to trip this barrier. - * @return the number of parties required to trip this barrier. - **/ + * + * @return the number of parties required to trip this barrier + */ public int getParties() { return parties; } /** - * Waits until all {@link #getParties parties} have invoked await - * on this barrier. + * Waits until all {@linkplain #getParties parties} have invoked + * await on this barrier. * *

      If the current thread is not the last to arrive then it is * disabled for thread scheduling purposes and lies dormant until - * one of following things happens: + * one of the following things happens: *

        *
      • The last thread arrives; or - *
      • Some other thread {@link Thread#interrupt interrupts} the current - * thread; or - *
      • Some other thread {@link Thread#interrupt interrupts} one of the - * other waiting threads; or + *
      • Some other thread {@linkplain Thread#interrupt interrupts} + * the current thread; or + *
      • Some other thread {@linkplain Thread#interrupt interrupts} + * one of the other waiting threads; or *
      • Some other thread times out while waiting for barrier; or *
      • Some other thread invokes {@link #reset} on this barrier. *
      + * *

      If the current thread: *

        *
      • has its interrupted status set on entry to this method; or - *
      • is {@link Thread#interrupt interrupted} while waiting + *
      • is {@linkplain Thread#interrupt interrupted} while waiting *
      * then {@link InterruptedException} is thrown and the current thread's * interrupted status is cleared. * - *

      If the barrier is {@link #reset} while any thread is waiting, or if - * the barrier {@link #isBroken is broken} when await is invoked, - * or while any thread is waiting, - * then {@link BrokenBarrierException} is thrown. + *

      If the barrier is {@link #reset} while any thread is waiting, + * or if the barrier {@linkplain #isBroken is broken} when + * await is invoked, or while any thread is waiting, then + * {@link BrokenBarrierException} is thrown. * - *

      If any thread is {@link Thread#interrupt interrupted} while waiting, - * then all other waiting threads will throw + *

      If any thread is {@linkplain Thread#interrupt interrupted} while waiting, + * then all other waiting threads will throw * {@link BrokenBarrierException} and the barrier is placed in the broken * state. * *

      If the current thread is the last thread to arrive, and a * non-null barrier action was supplied in the constructor, then the - * current thread runs the action before allowing the other threads to + * current thread runs the action before allowing the other threads to * continue. * If an exception occurs during the barrier action then that exception * will be propagated in the current thread and the barrier is placed in * the broken state. * * @return the arrival index of the current thread, where index - * {@link #getParties()} - 1 indicates the first to arrive and - * zero indicates the last to arrive. - * - * @throws InterruptedException if the current thread was interrupted - * while waiting + * {@link #getParties()} - 1 indicates the first + * to arrive and zero indicates the last to arrive + * @throws InterruptedException if the current thread was interrupted + * while waiting * @throws BrokenBarrierException if another thread was - * interrupted while the current thread was waiting, or the barrier was - * reset, or the barrier was broken when await was called, - * or the barrier action (if present) failed due an exception. + * interrupted or timed out while the current thread was + * waiting, or the barrier was reset, or the barrier was + * broken when {@code await} was called, or the barrier + * action (if present) failed due an exception. */ public int await() throws InterruptedException, BrokenBarrierException { try { @@ -304,8 +330,8 @@ public class CyclicBarrier { } /** - * Waits until all {@link #getParties parties} have invoked await - * on this barrier. + * Waits until all {@linkplain #getParties parties} have invoked + * await on this barrier, or the specified waiting time elapses. * *

      If the current thread is not the last to arrive then it is * disabled for thread scheduling purposes and lies dormant until @@ -313,34 +339,39 @@ public class CyclicBarrier { *

        *
      • The last thread arrives; or *
      • The specified timeout elapses; or - *
      • Some other thread {@link Thread#interrupt interrupts} the current - * thread; or - *
      • Some other thread {@link Thread#interrupt interrupts} one of the - * other waiting threads; or + *
      • Some other thread {@linkplain Thread#interrupt interrupts} + * the current thread; or + *
      • Some other thread {@linkplain Thread#interrupt interrupts} + * one of the other waiting threads; or *
      • Some other thread times out while waiting for barrier; or *
      • Some other thread invokes {@link #reset} on this barrier. *
      + * *

      If the current thread: *

        *
      • has its interrupted status set on entry to this method; or - *
      • is {@link Thread#interrupt interrupted} while waiting + *
      • is {@linkplain Thread#interrupt interrupted} while waiting *
      * then {@link InterruptedException} is thrown and the current thread's * interrupted status is cleared. * - *

      If the barrier is {@link #reset} while any thread is waiting, or if - * the barrier {@link #isBroken is broken} when await is invoked, - * or while any thread is waiting, - * then {@link BrokenBarrierException} is thrown. + *

      If the specified waiting time elapses then {@link TimeoutException} + * is thrown. If the time is less than or equal to zero, the + * method will not wait at all. * - *

      If any thread is {@link Thread#interrupt interrupted} while waiting, - * then all other waiting threads will throw - * {@link BrokenBarrierException} and the barrier is placed in the broken + *

      If the barrier is {@link #reset} while any thread is waiting, + * or if the barrier {@linkplain #isBroken is broken} when + * await is invoked, or while any thread is waiting, then + * {@link BrokenBarrierException} is thrown. + * + *

      If any thread is {@linkplain Thread#interrupt interrupted} while + * waiting, then all other waiting threads will throw {@link + * BrokenBarrierException} and the barrier is placed in the broken * state. * *

      If the current thread is the last thread to arrive, and a * non-null barrier action was supplied in the constructor, then the - * current thread runs the action before allowing the other threads to + * current thread runs the action before allowing the other threads to * continue. * If an exception occurs during the barrier action then that exception * will be propagated in the current thread and the barrier is placed in @@ -349,36 +380,37 @@ public class CyclicBarrier { * @param timeout the time to wait for the barrier * @param unit the time unit of the timeout parameter * @return the arrival index of the current thread, where index - * {@link #getParties()} - 1 indicates the first to arrive and - * zero indicates the last to arrive. - * - * @throws InterruptedException if the current thread was interrupted - * while waiting - * @throws TimeoutException if the specified timeout elapses. + * {@link #getParties()} - 1 indicates the first + * to arrive and zero indicates the last to arrive + * @throws InterruptedException if the current thread was interrupted + * while waiting + * @throws TimeoutException if the specified timeout elapses * @throws BrokenBarrierException if another thread was - * interrupted while the current thread was waiting, or the barrier was - * reset, or the barrier was broken when await was called, - * or the barrier action (if present) failed due an exception. + * interrupted or timed out while the current thread was + * waiting, or the barrier was reset, or the barrier was broken + * when {@code await} was called, or the barrier action (if + * present) failed due an exception */ - public int await(long timeout, TimeUnit unit) - throws InterruptedException, - BrokenBarrierException, - TimeoutException { + public int await(long timeout, TimeUnit unit) + throws InterruptedException, + BrokenBarrierException, + TimeoutException { return dowait(true, unit.toNanos(timeout)); } /** * Queries if this barrier is in a broken state. - * @return true if one or more parties broke out of this - * barrier due to interruption or timeout since construction or - * the last reset, or a barrier action failed due to an exception; - * and false otherwise. + * + * @return {@code true} if one or more parties broke out of this + * barrier due to interruption or timeout since + * construction or the last reset, or a barrier action + * failed due to an exception; {@code false} otherwise. */ public boolean isBroken() { final ReentrantLock lock = this.lock; lock.lock(); try { - return broken; + return generation.broken; } finally { lock.unlock(); } @@ -397,15 +429,8 @@ public class CyclicBarrier { final ReentrantLock lock = this.lock; lock.lock(); try { - /* - * Retract generation number enough to cover threads - * currently waiting on current and still resuming from - * previous generation, plus similarly accommodating spans - * after the reset. - */ - generation -= 4; - broken = false; - trip.signalAll(); + breakBarrier(); // break the current generation + nextGeneration(); // start a new generation } finally { lock.unlock(); } @@ -416,7 +441,7 @@ public class CyclicBarrier { * This method is primarily useful for debugging and assertions. * * @return the number of parties currently blocked in {@link #await} - **/ + */ public int getNumberWaiting() { final ReentrantLock lock = this.lock; lock.lock(); @@ -426,5 +451,4 @@ public class CyclicBarrier { lock.unlock(); } } - } diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/DelayQueue.java b/libcore/concurrent/src/main/java/java/util/concurrent/DelayQueue.java index 156dc7e24..52ac4a087 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/DelayQueue.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/DelayQueue.java @@ -14,14 +14,22 @@ import java.util.*; // END android-note /** - * An unbounded {@linkplain BlockingQueue blocking queue} of Delayed - * elements, in which an element can only be taken when its delay has expired. - * The head of the queue is that Delayed element whose delay - * expired furthest in the past - if no delay has expired there is no head and - * poll will return null. - * This queue does not permit null elements. - *

      This class implements all of the optional methods - * of the {@link Collection} and {@link Iterator} interfaces. + * An unbounded {@linkplain BlockingQueue blocking queue} of + * Delayed elements, in which an element can only be taken + * when its delay has expired. The head of the queue is that + * Delayed element whose delay expired furthest in the + * past. If no delay has expired there is no head and poll + * will return null. Expiration occurs when an element's + * getDelay(TimeUnit.NANOSECONDS) method returns a value less + * than or equal to zero. Even though unexpired elements cannot be + * removed using take or poll, they are otherwise + * treated as normal elements. For example, the size method + * returns the count of both expired and unexpired elements. + * This queue does not permit null elements. + * + *

      This class and its iterator implement all of the + * optional methods of the {@link Collection} and {@link + * Iterator} interfaces. * * @since 1.5 * @author Doug Lea @@ -32,9 +40,33 @@ public class DelayQueue extends AbstractQueue implements BlockingQueue { private transient final ReentrantLock lock = new ReentrantLock(); - private transient final Condition available = lock.newCondition(); private final PriorityQueue q = new PriorityQueue(); + /** + * Thread designated to wait for the element at the head of + * the queue. This variant of the Leader-Follower pattern + * (http://www.cs.wustl.edu/~schmidt/POSA/POSA2/) serves to + * minimize unnecessary timed waiting. When a thread becomes + * the leader, it waits only for the next delay to elapse, but + * other threads await indefinitely. The leader thread must + * signal some other thread before returning from take() or + * poll(...), unless some other thread becomes leader in the + * interim. Whenever the head of the queue is replaced with + * an element with an earlier expiration time, the leader + * field is invalidated by being reset to null, and some + * waiting thread, but not necessarily the current leader, is + * signalled. So waiting threads must be prepared to acquire + * and lose leadership while waiting. + */ + private Thread leader = null; + + /** + * Condition signalled when a newer element becomes available + * at the head of the queue or a new thread may need to + * become leader. + */ + private final Condition available = lock.newCondition(); + /** * Creates a new DelayQueue that is initially empty. */ @@ -44,10 +76,9 @@ public class DelayQueue extends AbstractQueue * Creates a DelayQueue initially containing the elements of the * given collection of {@link Delayed} instances. * - * @param c the collection - * @throws NullPointerException if c or any element within it - * is null - * + * @param c the collection of elements to initially contain + * @throws NullPointerException if the specified collection or any + * of its elements are null */ public DelayQueue(Collection c) { this.addAll(c); @@ -56,91 +87,136 @@ public class DelayQueue extends AbstractQueue /** * Inserts the specified element into this delay queue. * - * @param o the element to add + * @param e the element to add + * @return true (as specified by {@link Collection#add}) + * @throws NullPointerException if the specified element is null + */ + public boolean add(E e) { + return offer(e); + } + + /** + * Inserts the specified element into this delay queue. + * + * @param e the element to add * @return true - * @throws NullPointerException if the specified element is null. + * @throws NullPointerException if the specified element is null */ - public boolean offer(E o) { + public boolean offer(E e) { final ReentrantLock lock = this.lock; lock.lock(); try { - E first = q.peek(); - q.offer(o); - if (first == null || o.compareTo(first) < 0) - available.signalAll(); + q.offer(e); + if (q.peek() == e) { + leader = null; + available.signal(); + } return true; } finally { lock.unlock(); } } - /** - * Adds the specified element to this delay queue. As the queue is + * Inserts the specified element into this delay queue. As the queue is * unbounded this method will never block. - * @param o the element to add - * @throws NullPointerException if the specified element is null. + * + * @param e the element to add + * @throws NullPointerException {@inheritDoc} */ - public void put(E o) { - offer(o); + public void put(E e) { + offer(e); } /** * Inserts the specified element into this delay queue. As the queue is * unbounded this method will never block. - * @param o the element to add + * + * @param e the element to add * @param timeout This parameter is ignored as the method never blocks * @param unit This parameter is ignored as the method never blocks * @return true - * @throws NullPointerException if the specified element is null. + * @throws NullPointerException {@inheritDoc} */ - public boolean offer(E o, long timeout, TimeUnit unit) { - return offer(o); + public boolean offer(E e, long timeout, TimeUnit unit) { + return offer(e); } /** - * Adds the specified element to this queue. - * @param o the element to add - * @return true (as per the general contract of - * Collection.add). + * Retrieves and removes the head of this queue, or returns null + * if this queue has no elements with an expired delay. * - * @throws NullPointerException if the specified element is null. + * @return the head of this queue, or null if this + * queue has no elements with an expired delay */ - public boolean add(E o) { - return offer(o); + public E poll() { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + E first = q.peek(); + if (first == null || first.getDelay(TimeUnit.NANOSECONDS) > 0) + return null; + else + return q.poll(); + } finally { + lock.unlock(); + } } + /** + * Retrieves and removes the head of this queue, waiting if necessary + * until an element with an expired delay is available on this queue. + * + * @return the head of this queue + * @throws InterruptedException {@inheritDoc} + */ public E take() throws InterruptedException { final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { for (;;) { E first = q.peek(); - if (first == null) { + if (first == null) available.await(); - } else { - long delay = first.getDelay(TimeUnit.NANOSECONDS); - if (delay > 0) { - long tl = available.awaitNanos(delay); - } else { - E x = q.poll(); - assert x != null; - if (q.size() != 0) - available.signalAll(); // wake up other takers - return x; - + else { + long delay = first.getDelay(TimeUnit.NANOSECONDS); + if (delay <= 0) + return q.poll(); + else if (leader != null) + available.await(); + else { + Thread thisThread = Thread.currentThread(); + leader = thisThread; + try { + available.awaitNanos(delay); + } finally { + if (leader == thisThread) + leader = null; + } } } } } finally { + if (leader == null && q.peek() != null) + available.signal(); lock.unlock(); } } - public E poll(long time, TimeUnit unit) throws InterruptedException { + /** + * Retrieves and removes the head of this queue, waiting if necessary + * until an element with an expired delay is available on this queue, + * or the specified wait time expires. + * + * @return the head of this queue, or null if the + * specified waiting time elapses before an element with + * an expired delay becomes available + * @throws InterruptedException {@inheritDoc} + */ + public E poll(long timeout, TimeUnit unit) throws InterruptedException { + long nanos = unit.toNanos(timeout); final ReentrantLock lock = this.lock; lock.lockInterruptibly(); - long nanos = unit.toNanos(time); try { for (;;) { E first = q.peek(); @@ -150,46 +226,43 @@ public class DelayQueue extends AbstractQueue else nanos = available.awaitNanos(nanos); } else { - long delay = first.getDelay(TimeUnit.NANOSECONDS); - if (delay > 0) { - if (delay > nanos) - delay = nanos; - long timeLeft = available.awaitNanos(delay); - nanos -= delay - timeLeft; - } else { - E x = q.poll(); - assert x != null; - if (q.size() != 0) - available.signalAll(); - return x; + long delay = first.getDelay(TimeUnit.NANOSECONDS); + if (delay <= 0) + return q.poll(); + if (nanos <= 0) + return null; + if (nanos < delay || leader != null) + nanos = available.awaitNanos(nanos); + else { + Thread thisThread = Thread.currentThread(); + leader = thisThread; + try { + long timeLeft = available.awaitNanos(delay); + nanos -= delay - timeLeft; + } finally { + if (leader == thisThread) + leader = null; + } } } } } finally { + if (leader == null && q.peek() != null) + available.signal(); lock.unlock(); } } - - public E poll() { - final ReentrantLock lock = this.lock; - lock.lock(); - try { - E first = q.peek(); - if (first == null || first.getDelay(TimeUnit.NANOSECONDS) > 0) - return null; - else { - E x = q.poll(); - assert x != null; - if (q.size() != 0) - available.signalAll(); - return x; - } - } finally { - lock.unlock(); - } - } - + /** + * Retrieves, but does not remove, the head of this queue, or + * returns null if this queue is empty. Unlike + * poll, if no expired elements are available in the queue, + * this method returns the element that will expire next, + * if one exists. + * + * @return the head of this queue, or null if this + * queue is empty. + */ public E peek() { final ReentrantLock lock = this.lock; lock.lock(); @@ -210,6 +283,12 @@ public class DelayQueue extends AbstractQueue } } + /** + * @throws UnsupportedOperationException {@inheritDoc} + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + */ public int drainTo(Collection c) { if (c == null) throw new NullPointerException(); @@ -226,14 +305,18 @@ public class DelayQueue extends AbstractQueue c.add(q.poll()); ++n; } - if (n > 0) - available.signalAll(); return n; } finally { lock.unlock(); } } + /** + * @throws UnsupportedOperationException {@inheritDoc} + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + */ public int drainTo(Collection c, int maxElements) { if (c == null) throw new NullPointerException(); @@ -252,8 +335,6 @@ public class DelayQueue extends AbstractQueue c.add(q.poll()); ++n; } - if (n > 0) - available.signalAll(); return n; } finally { lock.unlock(); @@ -263,6 +344,8 @@ public class DelayQueue extends AbstractQueue /** * Atomically removes all of the elements from this delay queue. * The queue will be empty after this call returns. + * Elements with an unexpired delay are not waited for; they are + * simply discarded from the queue. */ public void clear() { final ReentrantLock lock = this.lock; @@ -277,12 +360,26 @@ public class DelayQueue extends AbstractQueue /** * Always returns Integer.MAX_VALUE because * a DelayQueue is not capacity constrained. + * * @return Integer.MAX_VALUE */ public int remainingCapacity() { return Integer.MAX_VALUE; } + /** + * Returns an array containing all of the elements in this queue. + * The returned array elements are in no particular order. + * + *

      The returned array will be "safe" in that no references to it are + * maintained by this queue. (In other words, this method must allocate + * a new array). The caller is thus free to modify the returned array. + * + *

      This method acts as bridge between array-based and collection-based + * APIs. + * + * @return an array containing all of the elements in this queue + */ public Object[] toArray() { final ReentrantLock lock = this.lock; lock.lock(); @@ -293,16 +390,56 @@ public class DelayQueue extends AbstractQueue } } - public T[] toArray(T[] array) { + /** + * Returns an array containing all of the elements in this queue; the + * runtime type of the returned array is that of the specified array. + * The returned array elements are in no particular order. + * If the queue fits in the specified array, it is returned therein. + * Otherwise, a new array is allocated with the runtime type of the + * specified array and the size of this queue. + * + *

      If this queue fits in the specified array with room to spare + * (i.e., the array has more elements than this queue), the element in + * the array immediately following the end of the queue is set to + * null. + * + *

      Like the {@link #toArray()} method, this method acts as bridge between + * array-based and collection-based APIs. Further, this method allows + * precise control over the runtime type of the output array, and may, + * under certain circumstances, be used to save allocation costs. + * + *

      The following code can be used to dump a delay queue into a newly + * allocated array of Delayed: + * + *

      +     *     Delayed[] a = q.toArray(new Delayed[0]);
      + * + * Note that toArray(new Object[0]) is identical in function to + * toArray(). + * + * @param a the array into which the elements of the queue are to + * be stored, if it is big enough; otherwise, a new array of the + * same runtime type is allocated for this purpose + * @return an array containing all of the elements in this queue + * @throws ArrayStoreException if the runtime type of the specified array + * is not a supertype of the runtime type of every element in + * this queue + * @throws NullPointerException if the specified array is null + */ + public T[] toArray(T[] a) { final ReentrantLock lock = this.lock; lock.lock(); try { - return q.toArray(array); + return q.toArray(a); } finally { lock.unlock(); } } + /** + * Removes a single instance of the specified element from this + * queue, if it is present, whether or not it has expired. + */ public boolean remove(Object o) { final ReentrantLock lock = this.lock; lock.lock(); @@ -314,49 +451,61 @@ public class DelayQueue extends AbstractQueue } /** - * Returns an iterator over the elements in this queue. The iterator - * does not return the elements in any particular order. The - * returned iterator is a thread-safe "fast-fail" iterator that will - * throw {@link java.util.ConcurrentModificationException} - * upon detected interference. + * Returns an iterator over all the elements (both expired and + * unexpired) in this queue. The iterator does not return the + * elements in any particular order. The returned + * Iterator is a "weakly consistent" iterator that will + * never throw {@link ConcurrentModificationException}, and + * guarantees to traverse elements as they existed upon + * construction of the iterator, and may (but is not guaranteed + * to) reflect any modifications subsequent to construction. * - * @return an iterator over the elements in this queue. + * @return an iterator over the elements in this queue */ public Iterator iterator() { - final ReentrantLock lock = this.lock; - lock.lock(); - try { - return new Itr(q.iterator()); - } finally { - lock.unlock(); - } + return new Itr(toArray()); } - private class Itr implements Iterator { - private final Iterator iter; - Itr(Iterator i) { - iter = i; + /** + * Snapshot iterator that works off copy of underlying q array. + */ + private class Itr implements Iterator { + final Object[] array; // Array of all elements + int cursor; // index of next element to return; + int lastRet; // index of last element, or -1 if no such + + Itr(Object[] array) { + lastRet = -1; + this.array = array; } public boolean hasNext() { - return iter.hasNext(); + return cursor < array.length; } + @SuppressWarnings("unchecked") public E next() { - final ReentrantLock lock = DelayQueue.this.lock; - lock.lock(); - try { - return iter.next(); - } finally { - lock.unlock(); - } + if (cursor >= array.length) + throw new NoSuchElementException(); + lastRet = cursor; + return (E)array[cursor++]; } public void remove() { - final ReentrantLock lock = DelayQueue.this.lock; + if (lastRet < 0) + throw new IllegalStateException(); + Object x = array[lastRet]; + lastRet = -1; + // Traverse underlying queue to find == element, + // not just a .equals element. lock.lock(); try { - iter.remove(); + for (Iterator it = q.iterator(); it.hasNext(); ) { + if (it.next() == x) { + it.remove(); + return; + } + } } finally { lock.unlock(); } diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/Delayed.java b/libcore/concurrent/src/main/java/java/util/concurrent/Delayed.java index bd4829209..af413003f 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/Delayed.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/Delayed.java @@ -4,6 +4,11 @@ * http://creativecommons.org/licenses/publicdomain */ +/* + * Modified in Apache Harmony to comply with Java 5 signature + * specification. + */ + package java.util.concurrent; import java.util.*; @@ -26,11 +31,12 @@ import java.util.*; public interface Delayed extends Comparable { /** - * Returns the delay associated with this object, in the given time unit. + * Returns the remaining delay associated with this object, in the + * given time unit. * * @param unit the time unit - * @return the delay; zero or negative values indicate that the - * delay has already elapsed + * @return the remaining delay; zero or negative values indicate + * that the delay has already elapsed */ long getDelay(TimeUnit unit); } diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/Exchanger.java b/libcore/concurrent/src/main/java/java/util/concurrent/Exchanger.java index da8c815ab..f67659c8c 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/Exchanger.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/Exchanger.java @@ -1,27 +1,31 @@ /* - * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain, as explained at + * Written by Doug Lea, Bill Scherer, and Michael Scott with + * assistance from members of JCP JSR-166 Expert Group and released to + * the public domain, as explained at * http://creativecommons.org/licenses/publicdomain */ package java.util.concurrent; -import java.util.concurrent.locks.*; +import java.util.concurrent.atomic.*; +import java.util.concurrent.locks.LockSupport; /** - * A synchronization point at which two threads can exchange objects. - * Each thread presents some object on entry to the {@link #exchange - * exchange} method, and receives the object presented by the other - * thread on return. + * A synchronization point at which threads can pair and swap elements + * within pairs. Each thread presents some object on entry to the + * {@link #exchange exchange} method, matches with a partner thread, + * and receives its partner's object on return. An Exchanger may be + * viewed as a bidirectional form of a {@link SynchronousQueue}. + * Exchangers may be useful in applications such as genetic algorithms + * and pipeline designs. * *

      Sample Usage: - * Here are the highlights of a class that uses an Exchanger to - * swap buffers between threads so that the thread filling the - * buffer gets a freshly - * emptied one when it needs it, handing off the filled one to - * the thread emptying the buffer. - *

      + * Here are the highlights of a class that uses an {@code Exchanger}
      + * to swap buffers between threads so that the thread filling the
      + * buffer gets a freshly emptied one when it needs it, handing off the
      + * filled one to the thread emptying the buffer.
      + * 
      {@code
        * class FillAndEmpty {
      - *   Exchanger<DataBuffer> exchanger = new Exchanger();
      + *   Exchanger exchanger = new Exchanger();
        *   DataBuffer initialEmptyBuffer = ... a made-up type
        *   DataBuffer initialFullBuffer = ...
        *
      @@ -31,7 +35,7 @@ import java.util.concurrent.locks.*;
        *       try {
        *         while (currentBuffer != null) {
        *           addToBuffer(currentBuffer);
      - *           if (currentBuffer.full())
      + *           if (currentBuffer.isFull())
        *             currentBuffer = exchanger.exchange(currentBuffer);
        *         }
        *       } catch (InterruptedException ex) { ... handle ... }
      @@ -44,7 +48,7 @@ import java.util.concurrent.locks.*;
        *       try {
        *         while (currentBuffer != null) {
        *           takeFromBuffer(currentBuffer);
      - *           if (currentBuffer.empty())
      + *           if (currentBuffer.isEmpty())
        *             currentBuffer = exchanger.exchange(currentBuffer);
        *         }
        *       } catch (InterruptedException ex) { ... handle ...}
      @@ -56,192 +60,597 @@ import java.util.concurrent.locks.*;
        *     new Thread(new EmptyingLoop()).start();
        *   }
        * }
      - * 
      + * }
      + * + *

      Memory consistency effects: For each pair of threads that + * successfully exchange objects via an {@code Exchanger}, actions + * prior to the {@code exchange()} in each thread + * happen-before + * those subsequent to a return from the corresponding {@code exchange()} + * in the other thread. * * @since 1.5 - * @author Doug Lea + * @author Doug Lea and Bill Scherer and Michael Scott * @param The type of objects that may be exchanged */ public class Exchanger { - private final ReentrantLock lock = new ReentrantLock(); - private final Condition taken = lock.newCondition(); + /* + * Algorithm Description: + * + * The basic idea is to maintain a "slot", which is a reference to + * a Node containing both an Item to offer and a "hole" waiting to + * get filled in. If an incoming "occupying" thread sees that the + * slot is null, it CAS'es (compareAndSets) a Node there and waits + * for another to invoke exchange. That second "fulfilling" thread + * sees that the slot is non-null, and so CASes it back to null, + * also exchanging items by CASing the hole, plus waking up the + * occupying thread if it is blocked. In each case CAS'es may + * fail because a slot at first appears non-null but is null upon + * CAS, or vice-versa. So threads may need to retry these + * actions. + * + * This simple approach works great when there are only a few + * threads using an Exchanger, but performance rapidly + * deteriorates due to CAS contention on the single slot when + * there are lots of threads using an exchanger. So instead we use + * an "arena"; basically a kind of hash table with a dynamically + * varying number of slots, any one of which can be used by + * threads performing an exchange. Incoming threads pick slots + * based on a hash of their Thread ids. If an incoming thread + * fails to CAS in its chosen slot, it picks an alternative slot + * instead. And similarly from there. If a thread successfully + * CASes into a slot but no other thread arrives, it tries + * another, heading toward the zero slot, which always exists even + * if the table shrinks. The particular mechanics controlling this + * are as follows: + * + * Waiting: Slot zero is special in that it is the only slot that + * exists when there is no contention. A thread occupying slot + * zero will block if no thread fulfills it after a short spin. + * In other cases, occupying threads eventually give up and try + * another slot. Waiting threads spin for a while (a period that + * should be a little less than a typical context-switch time) + * before either blocking (if slot zero) or giving up (if other + * slots) and restarting. There is no reason for threads to block + * unless there are unlikely to be any other threads present. + * Occupants are mainly avoiding memory contention so sit there + * quietly polling for a shorter period than it would take to + * block and then unblock them. Non-slot-zero waits that elapse + * because of lack of other threads waste around one extra + * context-switch time per try, which is still on average much + * faster than alternative approaches. + * + * Sizing: Usually, using only a few slots suffices to reduce + * contention. Especially with small numbers of threads, using + * too many slots can lead to just as poor performance as using + * too few of them, and there's not much room for error. The + * variable "max" maintains the number of slots actually in + * use. It is increased when a thread sees too many CAS + * failures. (This is analogous to resizing a regular hash table + * based on a target load factor, except here, growth steps are + * just one-by-one rather than proportional.) Growth requires + * contention failures in each of three tried slots. Requiring + * multiple failures for expansion copes with the fact that some + * failed CASes are not due to contention but instead to simple + * races between two threads or thread pre-emptions occurring + * between reading and CASing. Also, very transient peak + * contention can be much higher than the average sustainable + * levels. The max limit is decreased on average 50% of the times + * that a non-slot-zero wait elapses without being fulfilled. + * Threads experiencing elapsed waits move closer to zero, so + * eventually find existing (or future) threads even if the table + * has been shrunk due to inactivity. The chosen mechanics and + * thresholds for growing and shrinking are intrinsically + * entangled with indexing and hashing inside the exchange code, + * and can't be nicely abstracted out. + * + * Hashing: Each thread picks its initial slot to use in accord + * with a simple hashcode. The sequence is the same on each + * encounter by any given thread, but effectively random across + * threads. Using arenas encounters the classic cost vs quality + * tradeoffs of all hash tables. Here, we use a one-step FNV-1a + * hash code based on the current thread's Thread.getId(), along + * with a cheap approximation to a mod operation to select an + * index. The downside of optimizing index selection in this way + * is that the code is hardwired to use a maximum table size of + * 32. But this value more than suffices for known platforms and + * applications. + * + * Probing: On sensed contention of a selected slot, we probe + * sequentially through the table, analogously to linear probing + * after collision in a hash table. (We move circularly, in + * reverse order, to mesh best with table growth and shrinkage + * rules.) Except that to minimize the effects of false-alarms + * and cache thrashing, we try the first selected slot twice + * before moving. + * + * Padding: Even with contention management, slots are heavily + * contended, so use cache-padding to avoid poor memory + * performance. Because of this, slots are lazily constructed + * only when used, to avoid wasting this space unnecessarily. + * While isolation of locations is not much of an issue at first + * in an application, as time goes on and garbage-collectors + * perform compaction, slots are very likely to be moved adjacent + * to each other, which can cause much thrashing of cache lines on + * MPs unless padding is employed. + * + * This is an improvement of the algorithm described in the paper + * "A Scalable Elimination-based Exchange Channel" by William + * Scherer, Doug Lea, and Michael Scott in Proceedings of SCOOL05 + * workshop. Available at: http://hdl.handle.net/1802/2104 + */ + + /** The number of CPUs, for sizing and spin control */ + private static final int NCPU = Runtime.getRuntime().availableProcessors(); + + /** + * The capacity of the arena. Set to a value that provides more + * than enough space to handle contention. On small machines + * most slots won't be used, but it is still not wasted because + * the extra space provides some machine-level address padding + * to minimize interference with heavily CAS'ed Slot locations. + * And on very large machines, performance eventually becomes + * bounded by memory bandwidth, not numbers of threads/CPUs. + * This constant cannot be changed without also modifying + * indexing and hashing algorithms. + */ + private static final int CAPACITY = 32; - /** Holder for the item being exchanged */ - private V item; - /** - * Arrival count transitions from 0 to 1 to 2 then back to 0 - * during an exchange. + * The value of "max" that will hold all threads without + * contention. When this value is less than CAPACITY, some + * otherwise wasted expansion can be avoided. */ - private int arrivalCount; + private static final int FULL = + Math.max(0, Math.min(CAPACITY, NCPU / 2) - 1); + + /** + * The number of times to spin (doing nothing except polling a + * memory location) before blocking or giving up while waiting to + * be fulfilled. Should be zero on uniprocessors. On + * multiprocessors, this value should be large enough so that two + * threads exchanging items as fast as possible block only when + * one of them is stalled (due to GC or preemption), but not much + * longer, to avoid wasting CPU resources. Seen differently, this + * value is a little over half the number of cycles of an average + * context switch time on most systems. The value here is + * approximately the average of those across a range of tested + * systems. + */ + private static final int SPINS = (NCPU == 1) ? 0 : 2000; + + /** + * The number of times to spin before blocking in timed waits. + * Timed waits spin more slowly because checking the time takes + * time. The best value relies mainly on the relative rate of + * System.nanoTime vs memory accesses. The value is empirically + * derived to work well across a variety of systems. + */ + private static final int TIMED_SPINS = SPINS / 20; + + /** + * Sentinel item representing cancellation of a wait due to + * interruption, timeout, or elapsed spin-waits. This value is + * placed in holes on cancellation, and used as a return value + * from waiting methods to indicate failure to set or get hole. + */ + private static final Object CANCEL = new Object(); + + /** + * Value representing null arguments/returns from public + * methods. This disambiguates from internal requirement that + * holes start out as null to mean they are not yet set. + */ + private static final Object NULL_ITEM = new Object(); + + /** + * Nodes hold partially exchanged data. This class + * opportunistically subclasses AtomicReference to represent the + * hole. So get() returns hole, and compareAndSet CAS'es value + * into hole. This class cannot be parameterized as "V" because + * of the use of non-V CANCEL sentinels. + */ + private static final class Node extends AtomicReference { + /** The element offered by the Thread creating this node. */ + public final Object item; + + /** The Thread waiting to be signalled; null until waiting. */ + public volatile Thread waiter; + + /** + * Creates node with given item and empty hole. + * @param item the item + */ + public Node(Object item) { + this.item = item; + } + } + + /** + * A Slot is an AtomicReference with heuristic padding to lessen + * cache effects of this heavily CAS'ed location. While the + * padding adds noticeable space, all slots are created only on + * demand, and there will be more than one of them only when it + * would improve throughput more than enough to outweigh using + * extra space. + */ + private static final class Slot extends AtomicReference { + // Improve likelihood of isolation on <= 64 byte cache lines + long q0, q1, q2, q3, q4, q5, q6, q7, q8, q9, qa, qb, qc, qd, qe; + } + + /** + * Slot array. Elements are lazily initialized when needed. + * Declared volatile to enable double-checked lazy construction. + */ + private volatile Slot[] arena = new Slot[CAPACITY]; + + /** + * The maximum slot index being used. The value sometimes + * increases when a thread experiences too many CAS contentions, + * and sometimes decreases when a spin-wait elapses. Changes + * are performed only via compareAndSet, to avoid stale values + * when a thread happens to stall right before setting. + */ + private final AtomicInteger max = new AtomicInteger(); /** * Main exchange function, handling the different policy variants. + * Uses Object, not "V" as argument and return value to simplify + * handling of sentinel values. Callers from public methods decode + * and cast accordingly. + * + * @param item the (non-null) item to exchange + * @param timed true if the wait is timed + * @param nanos if timed, the maximum wait time + * @return the other thread's item, or CANCEL if interrupted or timed out */ - private V doExchange(V x, boolean timed, long nanos) throws InterruptedException, TimeoutException { - lock.lock(); - try { - V other; - - // If arrival count already at two, we must wait for - // a previous pair to finish and reset the count; - while (arrivalCount == 2) { - if (!timed) - taken.await(); - else if (nanos > 0) - nanos = taken.awaitNanos(nanos); - else - throw new TimeoutException(); + private Object doExchange(Object item, boolean timed, long nanos) { + Node me = new Node(item); // Create in case occupying + int index = hashIndex(); // Index of current slot + int fails = 0; // Number of CAS failures + + for (;;) { + Object y; // Contents of current slot + Slot slot = arena[index]; + if (slot == null) // Lazily initialize slots + createSlot(index); // Continue loop to reread + else if ((y = slot.get()) != null && // Try to fulfill + slot.compareAndSet(y, null)) { + Node you = (Node)y; // Transfer item + if (you.compareAndSet(null, item)) { + LockSupport.unpark(you.waiter); + return you.item; + } // Else cancelled; continue + } + else if (y == null && // Try to occupy + slot.compareAndSet(null, me)) { + if (index == 0) // Blocking wait for slot 0 + return timed? awaitNanos(me, slot, nanos): await(me, slot); + Object v = spinWait(me, slot); // Spin wait for non-0 + if (v != CANCEL) + return v; + me = new Node(item); // Throw away cancelled node + int m = max.get(); + if (m > (index >>>= 1)) // Decrease index + max.compareAndSet(m, m - 1); // Maybe shrink table + } + else if (++fails > 1) { // Allow 2 fails on 1st slot + int m = max.get(); + if (fails > 3 && m < FULL && max.compareAndSet(m, m + 1)) + index = m + 1; // Grow on 3rd failed slot + else if (--index < 0) + index = m; // Circularly traverse } + } + } - int count = ++arrivalCount; + /** + * Returns a hash index for the current thread. Uses a one-step + * FNV-1a hash code (http://www.isthe.com/chongo/tech/comp/fnv/) + * based on the current thread's Thread.getId(). These hash codes + * have more uniform distribution properties with respect to small + * moduli (here 1-31) than do other simple hashing functions. + * + *

      To return an index between 0 and max, we use a cheap + * approximation to a mod operation, that also corrects for bias + * due to non-power-of-2 remaindering (see {@link + * java.util.Random#nextInt}). Bits of the hashcode are masked + * with "nbits", the ceiling power of two of table size (looked up + * in a table packed into three ints). If too large, this is + * retried after rotating the hash by nbits bits, while forcing new + * top bit to 0, which guarantees eventual termination (although + * with a non-random-bias). This requires an average of less than + * 2 tries for all table sizes, and has a maximum 2% difference + * from perfectly uniform slot probabilities when applied to all + * possible hash codes for sizes less than 32. + * + * @return a per-thread-random index, 0 <= index < max + */ + private final int hashIndex() { + long id = Thread.currentThread().getId(); + int hash = (((int)(id ^ (id >>> 32))) ^ 0x811c9dc5) * 0x01000193; - // If item is already waiting, replace it and signal other thread - if (count == 2) { - other = item; - item = x; - taken.signal(); - return other; - } + int m = max.get(); + int nbits = (((0xfffffc00 >> m) & 4) | // Compute ceil(log2(m+1)) + ((0x000001f8 >>> m) & 2) | // The constants hold + ((0xffff00f2 >>> m) & 1)); // a lookup table + int index; + while ((index = hash & ((1 << nbits) - 1)) > m) // May retry on + hash = (hash >>> nbits) | (hash << (33 - nbits)); // non-power-2 m + return index; + } - // Otherwise, set item and wait for another thread to - // replace it and signal us. - - item = x; - InterruptedException interrupted = null; - try { - while (arrivalCount != 2) { - if (!timed) - taken.await(); - else if (nanos > 0) - nanos = taken.awaitNanos(nanos); - else - break; // timed out - } - } catch (InterruptedException ie) { - interrupted = ie; - } + /** + * Creates a new slot at given index. Called only when the slot + * appears to be null. Relies on double-check using builtin + * locks, since they rarely contend. This in turn relies on the + * arena array being declared volatile. + * + * @param index the index to add slot at + */ + private void createSlot(int index) { + // Create slot outside of lock to narrow sync region + Slot newSlot = new Slot(); + Slot[] a = arena; + synchronized (a) { + if (a[index] == null) + a[index] = newSlot; + } + } + + /** + * Tries to cancel a wait for the given node waiting in the given + * slot, if so, helping clear the node from its slot to avoid + * garbage retention. + * + * @param node the waiting node + * @param slot the slot it is waiting in + * @return true if successfully cancelled + */ + private static boolean tryCancel(Node node, Slot slot) { + if (!node.compareAndSet(null, CANCEL)) + return false; + if (slot.get() == node) // pre-check to minimize contention + slot.compareAndSet(node, null); + return true; + } + + // Three forms of waiting. Each just different enough not to merge + // code with others. + + /** + * Spin-waits for hole for a non-0 slot. Fails if spin elapses + * before hole filled. Does not check interrupt, relying on check + * in public exchange method to abort if interrupted on entry. + * + * @param node the waiting node + * @return on success, the hole; on failure, CANCEL + */ + private static Object spinWait(Node node, Slot slot) { + int spins = SPINS; + for (;;) { + Object v = node.get(); + if (v != null) + return v; + else if (spins > 0) + --spins; + else + tryCancel(node, slot); + } + } + + /** + * Waits for (by spinning and/or blocking) and gets the hole + * filled in by another thread. Fails if interrupted before + * hole filled. + * + * When a node/thread is about to block, it sets its waiter field + * and then rechecks state at least one more time before actually + * parking, thus covering race vs fulfiller noticing that waiter + * is non-null so should be woken. + * + * Thread interruption status is checked only surrounding calls to + * park. The caller is assumed to have checked interrupt status + * on entry. + * + * @param node the waiting node + * @return on success, the hole; on failure, CANCEL + */ + private static Object await(Node node, Slot slot) { + Thread w = Thread.currentThread(); + int spins = SPINS; + for (;;) { + Object v = node.get(); + if (v != null) + return v; + else if (spins > 0) // Spin-wait phase + --spins; + else if (node.waiter == null) // Set up to block next + node.waiter = w; + else if (w.isInterrupted()) // Abort on interrupt + tryCancel(node, slot); + else // Block + LockSupport.park(); + } + } - // Get and reset item and count after the wait. - // (We need to do this even if wait was aborted.) - other = item; - item = null; - count = arrivalCount; - arrivalCount = 0; - taken.signal(); - - // If the other thread replaced item, then we must - // continue even if cancelled. - if (count == 2) { - if (interrupted != null) - Thread.currentThread().interrupt(); - return other; + /** + * Waits for (at index 0) and gets the hole filled in by another + * thread. Fails if timed out or interrupted before hole filled. + * Same basic logic as untimed version, but a bit messier. + * + * @param node the waiting node + * @param nanos the wait time + * @return on success, the hole; on failure, CANCEL + */ + private Object awaitNanos(Node node, Slot slot, long nanos) { + int spins = TIMED_SPINS; + long lastTime = 0; + Thread w = null; + for (;;) { + Object v = node.get(); + if (v != null) + return v; + long now = System.nanoTime(); + if (w == null) + w = Thread.currentThread(); + else + nanos -= now - lastTime; + lastTime = now; + if (nanos > 0) { + if (spins > 0) + --spins; + else if (node.waiter == null) + node.waiter = w; + else if (w.isInterrupted()) + tryCancel(node, slot); + else + LockSupport.parkNanos(nanos); } + else if (tryCancel(node, slot) && !w.isInterrupted()) + return scanOnTimeout(node); + } + } - // If no one is waiting for us, we can back out - if (interrupted != null) - throw interrupted; - else // must be timeout - throw new TimeoutException(); - } finally { - lock.unlock(); + /** + * Sweeps through arena checking for any waiting threads. Called + * only upon return from timeout while waiting in slot 0. When a + * thread gives up on a timed wait, it is possible that a + * previously-entered thread is still waiting in some other + * slot. So we scan to check for any. This is almost always + * overkill, but decreases the likelihood of timeouts when there + * are other threads present to far less than that in lock-based + * exchangers in which earlier-arriving threads may still be + * waiting on entry locks. + * + * @param node the waiting node + * @return another thread's item, or CANCEL + */ + private Object scanOnTimeout(Node node) { + Object y; + for (int j = arena.length - 1; j >= 0; --j) { + Slot slot = arena[j]; + if (slot != null) { + while ((y = slot.get()) != null) { + if (slot.compareAndSet(y, null)) { + Node you = (Node)y; + if (you.compareAndSet(null, node.item)) { + LockSupport.unpark(you.waiter); + return you.item; + } + } + } + } } + return CANCEL; } /** - * Create a new Exchanger. - **/ + * Creates a new Exchanger. + */ public Exchanger() { } /** * Waits for another thread to arrive at this exchange point (unless - * it is {@link Thread#interrupt interrupted}), + * the current thread is {@linkplain Thread#interrupt interrupted}), * and then transfers the given object to it, receiving its object * in return. + * *

      If another thread is already waiting at the exchange point then * it is resumed for thread scheduling purposes and receives the object - * passed in by the current thread. The current thread returns immediately, + * passed in by the current thread. The current thread returns immediately, * receiving the object passed to the exchange by that other thread. - *

      If no other thread is already waiting at the exchange then the + * + *

      If no other thread is already waiting at the exchange then the * current thread is disabled for thread scheduling purposes and lies * dormant until one of two things happens: *

        *
      • Some other thread enters the exchange; or - *
      • Some other thread {@link Thread#interrupt interrupts} the current + *
      • Some other thread {@linkplain Thread#interrupt interrupts} the current * thread. *
      *

      If the current thread: *

        - *
      • has its interrupted status set on entry to this method; or - *
      • is {@link Thread#interrupt interrupted} while waiting - * for the exchange, + *
      • has its interrupted status set on entry to this method; or + *
      • is {@linkplain Thread#interrupt interrupted} while waiting + * for the exchange, *
      - * then {@link InterruptedException} is thrown and the current thread's - * interrupted status is cleared. + * then {@link InterruptedException} is thrown and the current thread's + * interrupted status is cleared. * * @param x the object to exchange - * @return the object provided by the other thread. - * @throws InterruptedException if current thread was interrupted - * while waiting - **/ + * @return the object provided by the other thread + * @throws InterruptedException if the current thread was + * interrupted while waiting + */ public V exchange(V x) throws InterruptedException { - try { - return doExchange(x, false, 0); - } catch (TimeoutException cannotHappen) { - throw new Error(cannotHappen); + if (!Thread.interrupted()) { + Object v = doExchange(x == null? NULL_ITEM : x, false, 0); + if (v == NULL_ITEM) + return null; + if (v != CANCEL) + return (V)v; + Thread.interrupted(); // Clear interrupt status on IE throw } + throw new InterruptedException(); } /** * Waits for another thread to arrive at this exchange point (unless - * it is {@link Thread#interrupt interrupted}, or the specified waiting - * time elapses), - * and then transfers the given object to it, receiving its object - * in return. + * the current thread is {@linkplain Thread#interrupt interrupted} or + * the specified waiting time elapses), and then transfers the given + * object to it, receiving its object in return. * *

      If another thread is already waiting at the exchange point then * it is resumed for thread scheduling purposes and receives the object - * passed in by the current thread. The current thread returns immediately, + * passed in by the current thread. The current thread returns immediately, * receiving the object passed to the exchange by that other thread. * - *

      If no other thread is already waiting at the exchange then the + *

      If no other thread is already waiting at the exchange then the * current thread is disabled for thread scheduling purposes and lies * dormant until one of three things happens: *

        *
      • Some other thread enters the exchange; or - *
      • Some other thread {@link Thread#interrupt interrupts} the current - * thread; or + *
      • Some other thread {@linkplain Thread#interrupt interrupts} + * the current thread; or *
      • The specified waiting time elapses. *
      *

      If the current thread: *

        - *
      • has its interrupted status set on entry to this method; or - *
      • is {@link Thread#interrupt interrupted} while waiting - * for the exchange, + *
      • has its interrupted status set on entry to this method; or + *
      • is {@linkplain Thread#interrupt interrupted} while waiting + * for the exchange, *
      - * then {@link InterruptedException} is thrown and the current thread's - * interrupted status is cleared. + * then {@link InterruptedException} is thrown and the current thread's + * interrupted status is cleared. * - *

      If the specified waiting time elapses then {@link TimeoutException} - * is thrown. - * If the time is - * less than or equal to zero, the method will not wait at all. + *

      If the specified waiting time elapses then {@link + * TimeoutException} is thrown. If the time is less than or equal + * to zero, the method will not wait at all. * * @param x the object to exchange * @param timeout the maximum time to wait - * @param unit the time unit of the timeout argument. - * @return the object provided by the other thread. - * @throws InterruptedException if current thread was interrupted - * while waiting - * @throws TimeoutException if the specified waiting time elapses before - * another thread enters the exchange. - **/ - public V exchange(V x, long timeout, TimeUnit unit) + * @param unit the time unit of the timeout argument + * @return the object provided by the other thread + * @throws InterruptedException if the current thread was + * interrupted while waiting + * @throws TimeoutException if the specified waiting time elapses + * before another thread enters the exchange + */ + public V exchange(V x, long timeout, TimeUnit unit) throws InterruptedException, TimeoutException { - return doExchange(x, true, unit.toNanos(timeout)); + if (!Thread.interrupted()) { + Object v = doExchange(x == null? NULL_ITEM : x, + true, unit.toNanos(timeout)); + if (v == NULL_ITEM) + return null; + if (v != CANCEL) + return (V)v; + if (!Thread.interrupted()) + throw new TimeoutException(); + } + throw new InterruptedException(); } - } - - diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/ExecutionException.java b/libcore/concurrent/src/main/java/java/util/concurrent/ExecutionException.java index 8f0e44810..bc561e58e 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/ExecutionException.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/ExecutionException.java @@ -19,14 +19,14 @@ public class ExecutionException extends Exception { private static final long serialVersionUID = 7830266012832686185L; /** - * Constructs a ExecutionException with no detail message. + * Constructs an ExecutionException with no detail message. * The cause is not initialized, and may subsequently be * initialized by a call to {@link #initCause(Throwable) initCause}. */ protected ExecutionException() { } /** - * Constructs a ExecutionException with the specified detail + * Constructs an ExecutionException with the specified detail * message. The cause is not initialized, and may subsequently be * initialized by a call to {@link #initCause(Throwable) initCause}. * @@ -37,7 +37,7 @@ public class ExecutionException extends Exception { } /** - * Constructs a ExecutionException with the specified detail + * Constructs an ExecutionException with the specified detail * message and cause. * * @param message the detail message @@ -49,7 +49,7 @@ public class ExecutionException extends Exception { } /** - * Constructs a ExecutionException with the specified cause. + * Constructs an ExecutionException with the specified cause. * The detail message is set to: *

            *  (cause == null ? null : cause.toString())
      diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/Executor.java b/libcore/concurrent/src/main/java/java/util/concurrent/Executor.java index 60fe1936a..a61e92152 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/Executor.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/Executor.java @@ -21,7 +21,7 @@ package java.util.concurrent; * executor.execute(new RunnableTask2()); * ... * - * + * * However, the Executor interface does not strictly * require that execution be asynchronous. In the simplest case, an * executor can run the submitted task immediately in the caller's @@ -52,7 +52,7 @@ package java.util.concurrent; * *
        * class SerialExecutor implements Executor {
      - *     final Queue<Runnable> tasks = new LinkedBlockingQueue<Runnable>();
      + *     final Queue<Runnable> tasks = new ArrayDeque<Runnable>();
        *     final Executor executor;
        *     Runnable active;
        *
      @@ -88,6 +88,11 @@ package java.util.concurrent;
        * extensible thread pool implementation. The {@link Executors} class
        * provides convenient factory methods for these Executors.
        *
      + * 

      Memory consistency effects: Actions in a thread prior to + * submitting a {@code Runnable} object to an {@code Executor} + * happen-before + * its execution begins, perhaps in another thread. + * * @since 1.5 * @author Doug Lea */ diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/ExecutorCompletionService.java b/libcore/concurrent/src/main/java/java/util/concurrent/ExecutorCompletionService.java index 4f9c9d95b..02606f960 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/ExecutorCompletionService.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/ExecutorCompletionService.java @@ -6,11 +6,10 @@ package java.util.concurrent; - /** * A {@link CompletionService} that uses a supplied {@link Executor} * to execute tasks. This class arranges that submitted tasks are, - * upon completion, placed on a queue accessible using take. + * upon completion, placed on a queue accessible using {@code take}. * The class is lightweight enough to be suitable for transient use * when processing groups of tasks. * @@ -19,84 +18,102 @@ package java.util.concurrent; * Usage Examples. * * Suppose you have a set of solvers for a certain problem, each - * returning a value of some type Result, and would like to + * returning a value of some type {@code Result}, and would like to * run them concurrently, processing the results of each of them that - * return a non-null value, in some method use(Result r). You + * return a non-null value, in some method {@code use(Result r)}. You * could write this as: * - *

      - *    void solve(Executor e, Collection<Callable<Result>> solvers)
      - *      throws InterruptedException, ExecutionException {
      - *        CompletionService<Result> ecs = new ExecutorCompletionService<Result>(e);
      - *        for (Callable<Result> s : solvers)
      - *            ecs.submit(s);
      - *        int n = solvers.size();
      - *        for (int i = 0; i < n; ++i) {
      - *            Result r = ecs.take().get();
      - *            if (r != null) 
      - *                use(r);
      - *        }
      - *    }
      - * 
      + *
       {@code
      + * void solve(Executor e,
      + *            Collection> solvers)
      + *     throws InterruptedException, ExecutionException {
      + *     CompletionService ecs
      + *         = new ExecutorCompletionService(e);
      + *     for (Callable s : solvers)
      + *         ecs.submit(s);
      + *     int n = solvers.size();
      + *     for (int i = 0; i < n; ++i) {
      + *         Result r = ecs.take().get();
      + *         if (r != null)
      + *             use(r);
      + *     }
      + * }}
      * * Suppose instead that you would like to use the first non-null result * of the set of tasks, ignoring any that encounter exceptions, * and cancelling all other tasks when the first one is ready: * - *
      - *    void solve(Executor e, Collection<Callable<Result>> solvers) 
      - *      throws InterruptedException {
      - *        CompletionService<Result> ecs = new ExecutorCompletionService<Result>(e);
      - *        int n = solvers.size();
      - *        List<Future<Result>> futures = new ArrayList<Future<Result>>(n);
      - *        Result result = null;
      - *        try {
      - *            for (Callable<Result> s : solvers)
      - *                futures.add(ecs.submit(s));
      - *            for (int i = 0; i < n; ++i) {
      - *                try {
      - *                    Result r = ecs.take().get();
      - *                    if (r != null) {
      - *                        result = r;
      - *                        break;
      - *                    }
      - *                } catch(ExecutionException ignore) {}
      - *            }
      - *        }
      - *        finally {
      - *            for (Future<Result> f : futures)
      - *                f.cancel(true);
      - *        }
      + * 
       {@code
      + * void solve(Executor e,
      + *            Collection> solvers)
      + *     throws InterruptedException {
      + *     CompletionService ecs
      + *         = new ExecutorCompletionService(e);
      + *     int n = solvers.size();
      + *     List> futures
      + *         = new ArrayList>(n);
      + *     Result result = null;
      + *     try {
      + *         for (Callable s : solvers)
      + *             futures.add(ecs.submit(s));
      + *         for (int i = 0; i < n; ++i) {
      + *             try {
      + *                 Result r = ecs.take().get();
      + *                 if (r != null) {
      + *                     result = r;
      + *                     break;
      + *                 }
      + *             } catch (ExecutionException ignore) {}
      + *         }
      + *     }
      + *     finally {
      + *         for (Future f : futures)
      + *             f.cancel(true);
      + *     }
        *
      - *        if (result != null)
      - *            use(result);
      - *    }
      - * 
      + * if (result != null) + * use(result); + * }}
      */ public class ExecutorCompletionService implements CompletionService { private final Executor executor; + private final AbstractExecutorService aes; private final BlockingQueue> completionQueue; /** * FutureTask extension to enqueue upon completion */ - private class QueueingFuture extends FutureTask { - QueueingFuture(Callable c) { super(c); } - QueueingFuture(Runnable t, V r) { super(t, r); } - protected void done() { completionQueue.add(this); } + private class QueueingFuture extends FutureTask { + QueueingFuture(FutureTask task) { + super(task, null); + this.task = task; + } + protected void done() { completionQueue.add(task); } + private final Future task; + } + + private FutureTask newTaskFor(Callable task) { + return new FutureTask(task); + } + + private FutureTask newTaskFor(Runnable task, V result) { + return new FutureTask(task, result); } /** * Creates an ExecutorCompletionService using the supplied * executor for base task execution and a * {@link LinkedBlockingQueue} as a completion queue. + * * @param executor the executor to use - * @throws NullPointerException if executor is null + * @throws NullPointerException if executor is {@code null} */ public ExecutorCompletionService(Executor executor) { - if (executor == null) + if (executor == null) throw new NullPointerException(); this.executor = executor; + this.aes = (executor instanceof AbstractExecutorService) ? + (AbstractExecutorService) executor : null; this.completionQueue = new LinkedBlockingQueue>(); } @@ -104,30 +121,36 @@ public class ExecutorCompletionService implements CompletionService { * Creates an ExecutorCompletionService using the supplied * executor for base task execution and the supplied queue as its * completion queue. + * * @param executor the executor to use * @param completionQueue the queue to use as the completion queue - * normally one dedicated for use by this service - * @throws NullPointerException if executor or completionQueue are null + * normally one dedicated for use by this service. This + * queue is treated as unbounded -- failed attempted + * {@code Queue.add} operations for completed taskes cause + * them not to be retrievable. + * @throws NullPointerException if executor or completionQueue are {@code null} */ public ExecutorCompletionService(Executor executor, BlockingQueue> completionQueue) { - if (executor == null || completionQueue == null) + if (executor == null || completionQueue == null) throw new NullPointerException(); this.executor = executor; + this.aes = (executor instanceof AbstractExecutorService) ? + (AbstractExecutorService) executor : null; this.completionQueue = completionQueue; } public Future submit(Callable task) { if (task == null) throw new NullPointerException(); - QueueingFuture f = new QueueingFuture(task); - executor.execute(f); + FutureTask f = newTaskFor(task); + executor.execute(new QueueingFuture(f)); return f; } public Future submit(Runnable task, V result) { if (task == null) throw new NullPointerException(); - QueueingFuture f = new QueueingFuture(task, result); - executor.execute(f); + FutureTask f = newTaskFor(task, result); + executor.execute(new QueueingFuture(f)); return f; } @@ -144,5 +167,3 @@ public class ExecutorCompletionService implements CompletionService { } } - - diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/ExecutorService.java b/libcore/concurrent/src/main/java/java/util/concurrent/ExecutorService.java index e178eb2e2..757f7cbad 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/ExecutorService.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/ExecutorService.java @@ -5,7 +5,6 @@ */ package java.util.concurrent; - import java.util.List; import java.util.Collection; import java.security.PrivilegedAction; @@ -14,14 +13,18 @@ import java.security.PrivilegedExceptionAction; /** * An {@link Executor} that provides methods to manage termination and * methods that can produce a {@link Future} for tracking progress of - * one or more asynchronous tasks. + * one or more asynchronous tasks. * - *

      - * An ExecutorService can be shut down, which will cause it - * to stop accepting new tasks. After being shut down, the executor - * will eventually terminate, at which point no tasks are actively - * executing, no tasks are awaiting execution, and no new tasks can be - * submitted. + *

      An ExecutorService can be shut down, which will cause + * it to reject new tasks. Two different methods are provided for + * shutting down an ExecutorService. The {@link #shutdown} + * method will allow previously submitted tasks to execute before + * terminating, while the {@link #shutdownNow} method prevents waiting + * tasks from starting and attempts to stop currently executing tasks. + * Upon termination, an executor has no tasks actively executing, no + * tasks awaiting execution, and no new tasks can be submitted. An + * unused ExecutorService should be shut down to allow + * reclamation of its resources. * *

      Method submit extends base method {@link * Executor#execute} by creating and returning a {@link Future} that @@ -35,41 +38,74 @@ import java.security.PrivilegedExceptionAction; *

      The {@link Executors} class provides factory methods for the * executor services provided in this package. * - *

      Usage Example

      + *

      Usage Examples

      * * Here is a sketch of a network service in which threads in a thread * pool service incoming requests. It uses the preconfigured {@link * Executors#newFixedThreadPool} factory method: * *
      - * class NetworkService {
      - *    private final ServerSocket serverSocket;
      - *    private final ExecutorService pool;
      + * class NetworkService implements Runnable {
      + *   private final ServerSocket serverSocket;
      + *   private final ExecutorService pool;
      + *
      + *   public NetworkService(int port, int poolSize)
      + *       throws IOException {
      + *     serverSocket = new ServerSocket(port);
      + *     pool = Executors.newFixedThreadPool(poolSize);
      + *   }
      + *
      + *   public void run() { // run the service
      + *     try {
      + *       for (;;) {
      + *         pool.execute(new Handler(serverSocket.accept()));
      + *       }
      + *     } catch (IOException ex) {
      + *       pool.shutdown();
      + *     }
      + *   }
      + * }
      + *
      + * class Handler implements Runnable {
      + *   private final Socket socket;
      + *   Handler(Socket socket) { this.socket = socket; }
      + *   public void run() {
      + *     // read and service request on socket
      + *   }
      + * }
      + * 
      * - * public NetworkService(int port, int poolSize) throws IOException { - * serverSocket = new ServerSocket(port); - * pool = Executors.newFixedThreadPool(poolSize); - * } - * - * public void serve() { - * try { - * for (;;) { - * pool.execute(new Handler(serverSocket.accept())); - * } - * } catch (IOException ex) { - * pool.shutdown(); - * } - * } - * } + * The following method shuts down an ExecutorService in two phases, + * first by calling shutdown to reject incoming tasks, and then + * calling shutdownNow, if necessary, to cancel any lingering tasks: * - * class Handler implements Runnable { - * private final Socket socket; - * Handler(Socket socket) { this.socket = socket; } - * public void run() { - * // read and service request - * } + *
      + * void shutdownAndAwaitTermination(ExecutorService pool) {
      + *   pool.shutdown(); // Disable new tasks from being submitted
      + *   try {
      + *     // Wait a while for existing tasks to terminate
      + *     if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
      + *       pool.shutdownNow(); // Cancel currently executing tasks
      + *       // Wait a while for tasks to respond to being cancelled
      + *       if (!pool.awaitTermination(60, TimeUnit.SECONDS))
      + *           System.err.println("Pool did not terminate");
      + *     }
      + *   } catch (InterruptedException ie) {
      + *     // (Re-)Cancel if current thread also interrupted
      + *     pool.shutdownNow();
      + *     // Preserve interrupt status
      + *     Thread.currentThread().interrupt();
      + *   }
        * }
        * 
      + * + *

      Memory consistency effects: Actions in a thread prior to the + * submission of a {@code Runnable} or {@code Callable} task to an + * {@code ExecutorService} + * happen-before + * any actions taken by that task, which in turn happen-before the + * result is retrieved via {@code Future.get()}. + * * @since 1.5 * @author Doug Lea */ @@ -77,33 +113,45 @@ public interface ExecutorService extends Executor { /** * Initiates an orderly shutdown in which previously submitted - * tasks are executed, but no new tasks will be - * accepted. Invocation has no additional effect if already shut - * down. + * tasks are executed, but no new tasks will be accepted. + * Invocation has no additional effect if already shut down. + * + *

      This method does not wait for previously submitted tasks to + * complete execution. Use {@link #awaitTermination awaitTermination} + * to do that. + * * @throws SecurityException if a security manager exists and - * shutting down this ExecutorService may manipulate threads that - * the caller is not permitted to modify because it does not hold - * {@link java.lang.RuntimePermission}("modifyThread"), - * or the security manager's checkAccess method denies access. + * shutting down this ExecutorService may manipulate + * threads that the caller is not permitted to modify + * because it does not hold {@link + * java.lang.RuntimePermission}("modifyThread"), + * or the security manager's checkAccess method + * denies access. */ void shutdown(); /** * Attempts to stop all actively executing tasks, halts the - * processing of waiting tasks, and returns a list of the tasks that were - * awaiting execution. - * + * processing of waiting tasks, and returns a list of the tasks + * that were awaiting execution. + * + *

      This method does not wait for actively executing tasks to + * terminate. Use {@link #awaitTermination awaitTermination} to + * do that. + * *

      There are no guarantees beyond best-effort attempts to stop * processing actively executing tasks. For example, typical - * implementations will cancel via {@link Thread#interrupt}, so if any - * tasks mask or fail to respond to interrupts, they may never terminate. + * implementations will cancel via {@link Thread#interrupt}, so any + * task that fails to respond to interrupts may never terminate. * * @return list of tasks that never commenced execution * @throws SecurityException if a security manager exists and - * shutting down this ExecutorService may manipulate threads that - * the caller is not permitted to modify because it does not hold - * {@link java.lang.RuntimePermission}("modifyThread"), - * or the security manager's checkAccess method denies access. + * shutting down this ExecutorService may manipulate + * threads that the caller is not permitted to modify + * because it does not hold {@link + * java.lang.RuntimePermission}("modifyThread"), + * or the security manager's checkAccess method + * denies access. */ List shutdownNow(); @@ -130,8 +178,8 @@ public interface ExecutorService extends Executor { * * @param timeout the maximum time to wait * @param unit the time unit of the timeout argument - * @return true if this executor terminated and false - * if the timeout elapsed before termination + * @return true if this executor terminated and + * false if the timeout elapsed before termination * @throws InterruptedException if interrupted while waiting */ boolean awaitTermination(long timeout, TimeUnit unit) @@ -139,8 +187,10 @@ public interface ExecutorService extends Executor { /** - * Submits a value-returning task for execution and returns a Future - * representing the pending results of the task. + * Submits a value-returning task for execution and returns a + * Future representing the pending results of the task. The + * Future's get method will return the task's result upon + * successful completion. * *

      * If you would like to immediately block waiting @@ -154,88 +204,92 @@ public interface ExecutorService extends Executor { * * @param task the task to submit * @return a Future representing pending completion of the task - * @throws RejectedExecutionException if task cannot be scheduled - * for execution - * @throws NullPointerException if task null + * @throws RejectedExecutionException if the task cannot be + * scheduled for execution + * @throws NullPointerException if the task is null */ Future submit(Callable task); /** - * Submits a Runnable task for execution and returns a Future - * representing that task that will upon completion return - * the given result + * Submits a Runnable task for execution and returns a Future + * representing that task. The Future's get method will + * return the given result upon successful completion. * * @param task the task to submit * @param result the result to return - * @return a Future representing pending completion of the task, - * and whose get() method will return the given result - * upon completion. - * @throws RejectedExecutionException if task cannot be scheduled - * for execution - * @throws NullPointerException if task null + * @return a Future representing pending completion of the task + * @throws RejectedExecutionException if the task cannot be + * scheduled for execution + * @throws NullPointerException if the task is null */ Future submit(Runnable task, T result); /** - * Submits a Runnable task for execution and returns a Future - * representing that task. + * Submits a Runnable task for execution and returns a Future + * representing that task. The Future's get method will + * return null upon successful completion. * * @param task the task to submit - * @return a Future representing pending completion of the task, - * and whose get() method will return null - * upon completion. - * @throws RejectedExecutionException if task cannot be scheduled - * for execution - * @throws NullPointerException if task null + * @return a Future representing pending completion of the task + * @throws RejectedExecutionException if the task cannot be + * scheduled for execution + * @throws NullPointerException if the task is null */ Future submit(Runnable task); /** - * Executes the given tasks, returning their results - * when all complete. + * Executes the given tasks, returning a list of Futures holding + * their status and results when all complete. + * {@link Future#isDone} is true for each + * element of the returned list. * Note that a completed task could have * terminated either normally or by throwing an exception. * The results of this method are undefined if the given * collection is modified while this operation is in progress. + * * @param tasks the collection of tasks * @return A list of Futures representing the tasks, in the same - * sequential order as produced by the iterator for the given task - * list, each of which has completed. + * sequential order as produced by the iterator for the + * given task list, each of which has completed. * @throws InterruptedException if interrupted while waiting, in - * which case unfinished tasks are cancelled. + * which case unfinished tasks are cancelled. * @throws NullPointerException if tasks or any of its elements are null - * @throws RejectedExecutionException if any task cannot be scheduled - * for execution + * @throws RejectedExecutionException if any task cannot be + * scheduled for execution */ List> invokeAll(Collection> tasks) throws InterruptedException; /** - * Executes the given tasks, returning their results + * Executes the given tasks, returning a list of Futures holding + * their status and results * when all complete or the timeout expires, whichever happens first. + * {@link Future#isDone} is true for each + * element of the returned list. * Upon return, tasks that have not completed are cancelled. * Note that a completed task could have * terminated either normally or by throwing an exception. * The results of this method are undefined if the given * collection is modified while this operation is in progress. + * * @param tasks the collection of tasks * @param timeout the maximum time to wait * @param unit the time unit of the timeout argument - * @return A list of Futures representing the tasks, in the same - * sequential order as produced by the iterator for the given - * task list. If the operation did not time out, each task will - * have completed. If it did time out, some of thiese tasks will - * not have completed. + * @return a list of Futures representing the tasks, in the same + * sequential order as produced by the iterator for the + * given task list. If the operation did not time out, + * each task will have completed. If it did time out, some + * of these tasks will not have completed. * @throws InterruptedException if interrupted while waiting, in - * which case unfinished tasks are cancelled. + * which case unfinished tasks are cancelled * @throws NullPointerException if tasks, any of its elements, or - * unit are null + * unit are null * @throws RejectedExecutionException if any task cannot be scheduled - * for execution + * for execution */ - List> invokeAll(Collection> tasks, - long timeout, TimeUnit unit) + List> invokeAll(Collection> tasks, + long timeout, TimeUnit unit) throws InterruptedException; /** @@ -245,15 +299,16 @@ public interface ExecutorService extends Executor { * tasks that have not completed are cancelled. * The results of this method are undefined if the given * collection is modified while this operation is in progress. + * * @param tasks the collection of tasks - * @return The result returned by one of the tasks. + * @return the result returned by one of the tasks * @throws InterruptedException if interrupted while waiting * @throws NullPointerException if tasks or any of its elements - * are null - * @throws IllegalArgumentException if tasks empty + * are null + * @throws IllegalArgumentException if tasks is empty * @throws ExecutionException if no task successfully completes * @throws RejectedExecutionException if tasks cannot be scheduled - * for execution + * for execution */ T invokeAny(Collection> tasks) throws InterruptedException, ExecutionException; @@ -266,21 +321,21 @@ public interface ExecutorService extends Executor { * completed are cancelled. * The results of this method are undefined if the given * collection is modified while this operation is in progress. + * * @param tasks the collection of tasks * @param timeout the maximum time to wait * @param unit the time unit of the timeout argument - * @return The result returned by one of the tasks. + * @return the result returned by one of the tasks. * @throws InterruptedException if interrupted while waiting * @throws NullPointerException if tasks, any of its elements, or - * unit are null + * unit are null * @throws TimeoutException if the given timeout elapses before - * any task successfully completes + * any task successfully completes * @throws ExecutionException if no task successfully completes * @throws RejectedExecutionException if tasks cannot be scheduled - * for execution + * for execution */ - T invokeAny(Collection> tasks, - long timeout, TimeUnit unit) + T invokeAny(Collection> tasks, + long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; - } diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/Executors.java b/libcore/concurrent/src/main/java/java/util/concurrent/Executors.java index 23f1b75f8..a57abe5ec 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/Executors.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/Executors.java @@ -11,6 +11,7 @@ import java.security.AccessControlContext; import java.security.AccessController; import java.security.PrivilegedAction; import java.security.PrivilegedExceptionAction; +import java.security.PrivilegedActionException; import java.security.AccessControlException; /** @@ -18,18 +19,18 @@ import java.security.AccessControlException; * ExecutorService}, {@link ScheduledExecutorService}, {@link * ThreadFactory}, and {@link Callable} classes defined in this * package. This class supports the following kinds of methods: - * + * *

        - *
      • Methods that create and return an {@link ExecutorService} - * set up with commonly useful configuration settings. - *
      • Methods that create and return a {@link ScheduledExecutorService} - * set up with commonly useful configuration settings. + *
      • Methods that create and return an {@link ExecutorService} + * set up with commonly useful configuration settings. + *
      • Methods that create and return a {@link ScheduledExecutorService} + * set up with commonly useful configuration settings. *
      • Methods that create and return a "wrapped" ExecutorService, that * disables reconfiguration by making implementation-specific methods * inaccessible. *
      • Methods that create and return a {@link ThreadFactory} * that sets newly created threads to a known state. - *
      • Methods that create and return a {@link Callable} + *
      • Methods that create and return a {@link Callable} * out of other closure-like forms, so they can be used * in execution methods requiring Callable. *
      @@ -40,14 +41,19 @@ import java.security.AccessControlException; public class Executors { /** - * Creates a thread pool that reuses a fixed set of threads - * operating off a shared unbounded queue. If any thread - * terminates due to a failure during execution prior to shutdown, - * a new one will take its place if needed to execute subsequent - * tasks. + * Creates a thread pool that reuses a fixed number of threads + * operating off a shared unbounded queue. At any point, at most + * nThreads threads will be active processing tasks. + * If additional tasks are submitted when all threads are active, + * they will wait in the queue until a thread is available. + * If any thread terminates due to a failure during execution + * prior to shutdown, a new one will take its place if needed to + * execute subsequent tasks. The threads in the pool will exist + * until it is explicitly {@link ExecutorService#shutdown shutdown}. * * @param nThreads the number of threads in the pool * @return the newly created thread pool + * @throws IllegalArgumentException if nThreads <= 0 */ public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, @@ -56,13 +62,23 @@ public class Executors { } /** - * Creates a thread pool that reuses a fixed set of threads + * Creates a thread pool that reuses a fixed number of threads * operating off a shared unbounded queue, using the provided - * ThreadFactory to create new threads when needed. + * ThreadFactory to create new threads when needed. At any point, + * at most nThreads threads will be active processing + * tasks. If additional tasks are submitted when all threads are + * active, they will wait in the queue until a thread is + * available. If any thread terminates due to a failure during + * execution prior to shutdown, a new one will take its place if + * needed to execute subsequent tasks. The threads in the pool will + * exist until it is explicitly {@link ExecutorService#shutdown + * shutdown}. * * @param nThreads the number of threads in the pool * @param threadFactory the factory to use when creating new threads * @return the newly created thread pool + * @throws NullPointerException if threadFactory is null + * @throws IllegalArgumentException if nThreads <= 0 */ public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) { return new ThreadPoolExecutor(nThreads, nThreads, @@ -85,7 +101,7 @@ public class Executors { * @return the newly created single-threaded Executor */ public static ExecutorService newSingleThreadExecutor() { - return new DelegatedExecutorService + return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue())); @@ -95,17 +111,18 @@ public class Executors { * Creates an Executor that uses a single worker thread operating * off an unbounded queue, and uses the provided ThreadFactory to * create a new thread when needed. Unlike the otherwise - * equivalent newFixedThreadPool(1, threadFactory) the returned executor - * is guaranteed not to be reconfigurable to use additional - * threads. - * + * equivalent newFixedThreadPool(1, threadFactory) the + * returned executor is guaranteed not to be reconfigurable to use + * additional threads. + * * @param threadFactory the factory to use when creating new * threads * * @return the newly created single-threaded Executor + * @throws NullPointerException if threadFactory is null */ public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) { - return new DelegatedExecutorService + return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(), @@ -141,6 +158,7 @@ public class Executors { * ThreadFactory to create new threads when needed. * @param threadFactory the factory to use when creating new threads * @return the newly created thread pool + * @throws NullPointerException if threadFactory is null */ public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, @@ -148,7 +166,7 @@ public class Executors { new SynchronousQueue(), threadFactory); } - + /** * Creates a single-threaded executor that can schedule commands * to run after a given delay, or to execute periodically. @@ -181,31 +199,35 @@ public class Executors { * @param threadFactory the factory to use when creating new * threads * @return a newly created scheduled executor + * @throws NullPointerException if threadFactory is null */ public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory) { return new DelegatedScheduledExecutorService (new ScheduledThreadPoolExecutor(1, threadFactory)); } - + /** - * Creates a thread pool that can schedule commands to run after a + * Creates a thread pool that can schedule commands to run after a * given delay, or to execute periodically. * @param corePoolSize the number of threads to keep in the pool, * even if they are idle. * @return a newly created scheduled thread pool + * @throws IllegalArgumentException if corePoolSize < 0 */ public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { return new ScheduledThreadPoolExecutor(corePoolSize); } /** - * Creates a thread pool that can schedule commands to run after a + * Creates a thread pool that can schedule commands to run after a * given delay, or to execute periodically. * @param corePoolSize the number of threads to keep in the pool, * even if they are idle. * @param threadFactory the factory to use when the executor - * creates a new thread. + * creates a new thread. * @return a newly created scheduled thread pool + * @throws IllegalArgumentException if corePoolSize < 0 + * @throws NullPointerException if threadFactory is null */ public static ScheduledExecutorService newScheduledThreadPool( int corePoolSize, ThreadFactory threadFactory) { @@ -244,7 +266,7 @@ public class Executors { throw new NullPointerException(); return new DelegatedScheduledExecutorService(executor); } - + /** * Returns a default thread factory used to create new threads. * This factory creates all new threads used by an Executor in the @@ -252,8 +274,9 @@ public class Executors { * java.lang.SecurityManager}, it uses the group of {@link * System#getSecurityManager}, else the group of the thread * invoking this defaultThreadFactory method. Each new - * thread is created as a non-daemon thread with priority - * Thread.NORM_PRIORITY. New threads have names + * thread is created as a non-daemon thread with priority set to + * the smaller of Thread.NORM_PRIORITY and the maximum + * priority permitted in the thread group. New threads have names * accessible via {@link Thread#getName} of * pool-N-thread-M, where N is the sequence * number of this factory, and M is the sequence number @@ -307,8 +330,8 @@ public class Executors { * Callable to an otherwise resultless action. * @param task the task to run * @param result the result to return - * @throws NullPointerException if task null * @return a callable object + * @throws NullPointerException if task null */ public static Callable callable(Runnable task, T result) { if (task == null) @@ -336,10 +359,11 @@ public class Executors { * @return a callable object * @throws NullPointerException if action null */ - public static Callable callable(PrivilegedAction action) { + public static Callable callable(final PrivilegedAction action) { if (action == null) throw new NullPointerException(); - return new PrivilegedActionAdapter(action); + return new Callable() { + public Object call() { return action.run(); }}; } /** @@ -350,10 +374,11 @@ public class Executors { * @return a callable object * @throws NullPointerException if action null */ - public static Callable callable(PrivilegedExceptionAction action) { + public static Callable callable(final PrivilegedExceptionAction action) { if (action == null) throw new NullPointerException(); - return new PrivilegedExceptionActionAdapter(action); + return new Callable() { + public Object call() throws Exception { return action.run(); }}; } /** @@ -373,9 +398,9 @@ public class Executors { public static Callable privilegedCallable(Callable callable) { if (callable == null) throw new NullPointerException(); - return new PrivilegedCallable(callable); + return new PrivilegedCallable(callable); } - + /** * Returns a {@link Callable} object that will, when * called, execute the given callable under the current @@ -397,7 +422,7 @@ public class Executors { public static Callable privilegedCallableUsingCurrentClassLoader(Callable callable) { if (callable == null) throw new NullPointerException(); - return new PrivilegedCallableUsingCurrentClassLoader(callable); + return new PrivilegedCallableUsingCurrentClassLoader(callable); } // Non-public classes supporting the public methods @@ -408,71 +433,39 @@ public class Executors { static final class RunnableAdapter implements Callable { final Runnable task; final T result; - RunnableAdapter(Runnable task, T result) { - this.task = task; + RunnableAdapter(Runnable task, T result) { + this.task = task; this.result = result; } - public T call() { - task.run(); - return result; - } - } - - /** - * A callable that runs given privileged action and returns its result - */ - static final class PrivilegedActionAdapter implements Callable { - PrivilegedActionAdapter(PrivilegedAction action) { - this.action = action; - } - public Object call () { - return action.run(); - } - private final PrivilegedAction action; - } - - /** - * A callable that runs given privileged exception action and returns its result - */ - static final class PrivilegedExceptionActionAdapter implements Callable { - PrivilegedExceptionActionAdapter(PrivilegedExceptionAction action) { - this.action = action; - } - public Object call () throws Exception { - return action.run(); + public T call() { + task.run(); + return result; } - private final PrivilegedExceptionAction action; } - /** * A callable that runs under established access control settings */ static final class PrivilegedCallable implements Callable { - private final AccessControlContext acc; private final Callable task; - private T result; - private Exception exception; + private final AccessControlContext acc; + PrivilegedCallable(Callable task) { this.task = task; this.acc = AccessController.getContext(); } public T call() throws Exception { - AccessController.doPrivileged(new PrivilegedAction() { - public Object run() { - try { - result = task.call(); - } catch(Exception ex) { - exception = ex; + try { + return AccessController.doPrivileged( + new PrivilegedExceptionAction() { + public T run() throws Exception { + return task.call(); } - return null; - } - }, acc); - if (exception != null) - throw exception; - else - return result; + }, acc); + } catch (PrivilegedActionException e) { + throw e.getException(); + } } } @@ -481,44 +474,50 @@ public class Executors { * current ClassLoader */ static final class PrivilegedCallableUsingCurrentClassLoader implements Callable { - private final ClassLoader ccl; - private final AccessControlContext acc; private final Callable task; - private T result; - private Exception exception; + private final AccessControlContext acc; + private final ClassLoader ccl; + PrivilegedCallableUsingCurrentClassLoader(Callable task) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + // Calls to getContextClassLoader from this class + // never trigger a security check, but we check + // whether our callers have this permission anyways. + sm.checkPermission(new RuntimePermission("getContextClassLoader")); + + // Whether setContextClassLoader turns out to be necessary + // or not, we fail fast if permission is not available. + sm.checkPermission(new RuntimePermission("setContextClassLoader")); + } this.task = task; - this.ccl = Thread.currentThread().getContextClassLoader(); this.acc = AccessController.getContext(); - acc.checkPermission(new RuntimePermission("getContextClassLoader")); - acc.checkPermission(new RuntimePermission("setContextClassLoader")); + this.ccl = Thread.currentThread().getContextClassLoader(); } public T call() throws Exception { - AccessController.doPrivileged(new PrivilegedAction() { - public Object run() { - ClassLoader savedcl = null; - Thread t = Thread.currentThread(); - try { - ClassLoader cl = t.getContextClassLoader(); - if (ccl != cl) { - t.setContextClassLoader(ccl); - savedcl = cl; + try { + return AccessController.doPrivileged( + new PrivilegedExceptionAction() { + public T run() throws Exception { + ClassLoader savedcl = null; + Thread t = Thread.currentThread(); + try { + ClassLoader cl = t.getContextClassLoader(); + if (ccl != cl) { + t.setContextClassLoader(ccl); + savedcl = cl; + } + return task.call(); + } finally { + if (savedcl != null) + t.setContextClassLoader(savedcl); } - result = task.call(); - } catch(Exception ex) { - exception = ex; - } finally { - if (savedcl != null) - t.setContextClassLoader(savedcl); } - return null; - } - }, acc); - if (exception != null) - throw exception; - else - return result; + }, acc); + } catch (PrivilegedActionException e) { + throw e.getException(); + } } } @@ -526,22 +525,22 @@ public class Executors { * The default thread factory */ static class DefaultThreadFactory implements ThreadFactory { - static final AtomicInteger poolNumber = new AtomicInteger(1); - final ThreadGroup group; - final AtomicInteger threadNumber = new AtomicInteger(1); - final String namePrefix; + private static final AtomicInteger poolNumber = new AtomicInteger(1); + private final ThreadGroup group; + private final AtomicInteger threadNumber = new AtomicInteger(1); + private final String namePrefix; DefaultThreadFactory() { SecurityManager s = System.getSecurityManager(); group = (s != null)? s.getThreadGroup() : Thread.currentThread().getThreadGroup(); - namePrefix = "pool-" + - poolNumber.getAndIncrement() + + namePrefix = "pool-" + + poolNumber.getAndIncrement() + "-thread-"; } public Thread newThread(Runnable r) { - Thread t = new Thread(group, r, + Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0); if (t.isDaemon()) @@ -553,38 +552,46 @@ public class Executors { } /** - * Thread factory capturing access control and class loader + * Thread factory capturing access control context and class loader */ static class PrivilegedThreadFactory extends DefaultThreadFactory { - private final ClassLoader ccl; private final AccessControlContext acc; + private final ClassLoader ccl; PrivilegedThreadFactory() { super(); - this.ccl = Thread.currentThread().getContextClassLoader(); + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + // Calls to getContextClassLoader from this class + // never trigger a security check, but we check + // whether our callers have this permission anyways. + sm.checkPermission(new RuntimePermission("getContextClassLoader")); + + // Fail fast + sm.checkPermission(new RuntimePermission("setContextClassLoader")); + } this.acc = AccessController.getContext(); - acc.checkPermission(new RuntimePermission("setContextClassLoader")); + this.ccl = Thread.currentThread().getContextClassLoader(); } - + public Thread newThread(final Runnable r) { return super.newThread(new Runnable() { public void run() { - AccessController.doPrivileged(new PrivilegedAction() { - public Object run() { + AccessController.doPrivileged(new PrivilegedAction() { + public Void run() { Thread.currentThread().setContextClassLoader(ccl); r.run(); - return null; + return null; } }, acc); } }); } - } - /** + /** * A wrapper class that exposes only the ExecutorService methods - * of an implementation. + * of an ExecutorService implementation. */ static class DelegatedExecutorService extends AbstractExecutorService { private final ExecutorService e; @@ -611,8 +618,8 @@ public class Executors { throws InterruptedException { return e.invokeAll(tasks); } - public List> invokeAll(Collection> tasks, - long timeout, TimeUnit unit) + public List> invokeAll(Collection> tasks, + long timeout, TimeUnit unit) throws InterruptedException { return e.invokeAll(tasks, timeout, unit); } @@ -620,19 +627,29 @@ public class Executors { throws InterruptedException, ExecutionException { return e.invokeAny(tasks); } - public T invokeAny(Collection> tasks, - long timeout, TimeUnit unit) + public T invokeAny(Collection> tasks, + long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return e.invokeAny(tasks, timeout, unit); } } - + + static class FinalizableDelegatedExecutorService + extends DelegatedExecutorService { + FinalizableDelegatedExecutorService(ExecutorService executor) { + super(executor); + } + protected void finalize() { + super.shutdown(); + } + } + /** - * A wrapper class that exposes only the ExecutorService and - * ScheduleExecutor methods of a ScheduledExecutorService implementation. + * A wrapper class that exposes only the ScheduledExecutorService + * methods of a ScheduledExecutorService implementation. */ static class DelegatedScheduledExecutorService - extends DelegatedExecutorService + extends DelegatedExecutorService implements ScheduledExecutorService { private final ScheduledExecutorService e; DelegatedScheduledExecutorService(ScheduledExecutorService executor) { @@ -653,7 +670,7 @@ public class Executors { } } - + /** Cannot instantiate. */ private Executors() {} } diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/Future.java b/libcore/concurrent/src/main/java/java/util/concurrent/Future.java index 38fa8c6e4..0459ee453 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/Future.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/Future.java @@ -29,10 +29,13 @@ package java.util.concurrent; * class App { * ExecutorService executor = ... * ArchiveSearcher searcher = ... - * void showSearch(final String target) throws InterruptedException { - * Future<String> future = executor.submit(new Callable<String>() { - * public String call() { return searcher.search(target); } - * }); + * void showSearch(final String target) + * throws InterruptedException { + * Future<String> future + * = executor.submit(new Callable<String>() { + * public String call() { + * return searcher.search(target); + * }}); * displayOtherThings(); // do other things while searching * try { * displayText(future.get()); // use future @@ -41,8 +44,8 @@ package java.util.concurrent; * } * * - * The {@link FutureTask} class is an implementation of Future that - * implements Runnable, and so may be executed by an Executor. + * The {@link FutureTask} class is an implementation of Future that + * implements Runnable, and so may be executed by an Executor. * For example, the above construction with submit could be replaced by: *
        *     FutureTask<String> future =
      @@ -52,6 +55,11 @@ package java.util.concurrent;
        *       }});
        *     executor.execute(future);
        * 
      + * + *

      Memory consistency effects: Actions taken by the asynchronous computation + * happen-before + * actions following the corresponding {@code Future.get()} in another thread. + * * @see FutureTask * @see Executor * @since 1.5 @@ -62,7 +70,7 @@ public interface Future { /** * Attempts to cancel execution of this task. This attempt will - * fail if the task has already completed, already been cancelled, + * fail if the task has already completed, has already been cancelled, * or could not be cancelled for some other reason. If successful, * and this task has not started when cancel is called, * this task should never run. If the task has already started, @@ -70,6 +78,10 @@ public interface Future { * whether the thread executing this task should be interrupted in * an attempt to stop the task. * + *

      After this method returns, subsequent calls to {@link #isDone} will + * always return true. Subsequent calls to {@link #isCancelled} + * will always return true if this method returned true. + * * @param mayInterruptIfRunning true if the thread executing this * task should be interrupted; otherwise, in-progress tasks are allowed * to complete @@ -83,18 +95,18 @@ public interface Future { * Returns true if this task was cancelled before it completed * normally. * - * @return true if task was cancelled before it completed + * @return true if this task was cancelled before it completed */ boolean isCancelled(); /** - * Returns true if this task completed. + * Returns true if this task completed. * * Completion may be due to normal termination, an exception, or * cancellation -- in all of these cases, this method will return * true. - * - * @return true if this task completed. + * + * @return true if this task completed */ boolean isDone(); @@ -128,6 +140,3 @@ public interface Future { V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; } - - - diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/FutureTask.java b/libcore/concurrent/src/main/java/java/util/concurrent/FutureTask.java index 87db1cd09..8aa9dd14f 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/FutureTask.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/FutureTask.java @@ -35,7 +35,7 @@ public class FutureTask implements Future, Runnable { private final Sync sync; /** - * Creates a FutureTask that will upon running, execute the + * Creates a FutureTask that will, upon running, execute the * given Callable. * * @param callable the callable task @@ -48,11 +48,11 @@ public class FutureTask implements Future, Runnable { } /** - * Creates a FutureTask that will upon running, execute the + * Creates a FutureTask that will, upon running, execute the * given Runnable, and arrange that get will return the * given result on successful completion. * - * @param runnable the runnable task + * @param runnable the runnable task * @param result the result to return on successful completion. If * you don't need a particular result, consider using * constructions of the form: @@ -66,7 +66,7 @@ public class FutureTask implements Future, Runnable { public boolean isCancelled() { return sync.innerIsCancelled(); } - + public boolean isDone() { return sync.innerIsDone(); } @@ -74,11 +74,17 @@ public class FutureTask implements Future, Runnable { public boolean cancel(boolean mayInterruptIfRunning) { return sync.innerCancel(mayInterruptIfRunning); } - + + /** + * @throws CancellationException {@inheritDoc} + */ public V get() throws InterruptedException, ExecutionException { return sync.innerGet(); } + /** + * @throws CancellationException {@inheritDoc} + */ public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return sync.innerGet(unit.toNanos(timeout)); @@ -98,8 +104,10 @@ public class FutureTask implements Future, Runnable { /** * Sets the result of this Future to the given value unless * this future has already been set or has been cancelled. + * This method is invoked internally by the run method + * upon successful completion of the computation. * @param v the value - */ + */ protected void set(V v) { sync.innerSet(v); } @@ -108,15 +116,22 @@ public class FutureTask implements Future, Runnable { * Causes this future to report an ExecutionException * with the given throwable as its cause, unless this Future has * already been set or has been cancelled. - * @param t the cause of failure. - */ + * This method is invoked internally by the run method + * upon failure of the computation. + * @param t the cause of failure + */ protected void setException(Throwable t) { sync.innerSetException(t); } - + + // The following (duplicated) doc comment can be removed once + // + // 6270645: Javadoc comments should be inherited from most derived + // superinterface or superclass + // is fixed. /** - * Sets this Future to the result of computation unless - * it has been cancelled. + * Sets this Future to the result of its computation + * unless it has been cancelled. */ public void run() { sync.innerRun(); @@ -143,6 +158,10 @@ public class FutureTask implements Future, Runnable { * Uses AQS sync state to represent run status */ private final class Sync extends AbstractQueuedSynchronizer { + private static final long serialVersionUID = -7828117401763700385L; + + /** State value representing that task is ready to run */ + private static final int READY = 0; /** State value representing that task is running */ private static final int RUNNING = 1; /** State value representing that task ran */ @@ -157,10 +176,10 @@ public class FutureTask implements Future, Runnable { /** The exception to throw from get() */ private Throwable exception; - /** + /** * The thread running task. When nulled after set/cancel, this * indicates that the results are accessible. Must be - * volatile, to serve as write barrier on completion. + * volatile, to ensure visibility upon completion. */ private volatile Thread runner; @@ -176,7 +195,7 @@ public class FutureTask implements Future, Runnable { * Implements AQS base acquire to succeed if ran or cancelled */ protected int tryAcquireShared(int ignore) { - return innerIsDone()? 1 : -1; + return innerIsDone() ? 1 : -1; } /** @@ -185,13 +204,13 @@ public class FutureTask implements Future, Runnable { */ protected boolean tryReleaseShared(int ignore) { runner = null; - return true; + return true; } boolean innerIsCancelled() { return getState() == CANCELLED; } - + boolean innerIsDone() { return ranOrCancelled(getState()) && runner == null; } @@ -207,7 +226,7 @@ public class FutureTask implements Future, Runnable { V innerGet(long nanosTimeout) throws InterruptedException, ExecutionException, TimeoutException { if (!tryAcquireSharedNanos(0, nanosTimeout)) - throw new TimeoutException(); + throw new TimeoutException(); if (getState() == CANCELLED) throw new CancellationException(); if (exception != null) @@ -216,28 +235,55 @@ public class FutureTask implements Future, Runnable { } void innerSet(V v) { - int s = getState(); - if (ranOrCancelled(s) || !compareAndSetState(s, RAN)) - return; - result = v; - releaseShared(0); - done(); + for (;;) { + int s = getState(); + if (s == RAN) + return; + if (s == CANCELLED) { + // aggressively release to set runner to null, + // in case we are racing with a cancel request + // that will try to interrupt runner + releaseShared(0); + return; + } + if (compareAndSetState(s, RAN)) { + result = v; + releaseShared(0); + done(); + return; + } + } } void innerSetException(Throwable t) { - int s = getState(); - if (ranOrCancelled(s) || !compareAndSetState(s, RAN)) - return; - exception = t; - result = null; - releaseShared(0); - done(); + for (;;) { + int s = getState(); + if (s == RAN) + return; + if (s == CANCELLED) { + // aggressively release to set runner to null, + // in case we are racing with a cancel request + // that will try to interrupt runner + releaseShared(0); + return; + } + if (compareAndSetState(s, RAN)) { + exception = t; + releaseShared(0); + done(); + return; + } + } } boolean innerCancel(boolean mayInterruptIfRunning) { - int s = getState(); - if (ranOrCancelled(s) || !compareAndSetState(s, CANCELLED)) - return false; + for (;;) { + int s = getState(); + if (ranOrCancelled(s)) + return false; + if (compareAndSetState(s, CANCELLED)) + break; + } if (mayInterruptIfRunning) { Thread r = runner; if (r != null) @@ -249,28 +295,37 @@ public class FutureTask implements Future, Runnable { } void innerRun() { - if (!compareAndSetState(0, RUNNING)) + if (!compareAndSetState(READY, RUNNING)) return; - try { - runner = Thread.currentThread(); - innerSet(callable.call()); - } catch(Throwable ex) { - innerSetException(ex); - } + + runner = Thread.currentThread(); + if (getState() == RUNNING) { // recheck after setting thread + V result; + try { + result = callable.call(); + } catch (Throwable ex) { + setException(ex); + return; + } + set(result); + } else { + releaseShared(0); // cancel + } } boolean innerRunAndReset() { - if (!compareAndSetState(0, RUNNING)) + if (!compareAndSetState(READY, RUNNING)) return false; try { runner = Thread.currentThread(); - callable.call(); // don't set result + if (getState() == RUNNING) + callable.call(); // don't set result runner = null; - return compareAndSetState(RUNNING, 0); - } catch(Throwable ex) { - innerSetException(ex); + return compareAndSetState(RUNNING, READY); + } catch (Throwable ex) { + setException(ex); return false; - } + } } } } diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/Java6Arrays.java b/libcore/concurrent/src/main/java/java/util/concurrent/Java6Arrays.java new file mode 100644 index 000000000..6b728bef1 --- /dev/null +++ b/libcore/concurrent/src/main/java/java/util/concurrent/Java6Arrays.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.util.concurrent; + +import java.lang.reflect.Array; + +/** + * Arrays.copyOf and Arrays.copyOfRange backported from Java 6. + */ +class Java6Arrays { + + static T[] copyOf(T[] original, int newLength) { + if (null == original) { + throw new NullPointerException(); + } + if (0 <= newLength) { + return copyOfRange(original, 0, newLength); + } + throw new NegativeArraySizeException(); + } + + static T[] copyOf(U[] original, int newLength, + Class newType) { + if (0 <= newLength) { + return copyOfRange(original, 0, newLength, newType); + } + throw new NegativeArraySizeException(); + } + + @SuppressWarnings("unchecked") + static T[] copyOfRange(T[] original, int start, int end) { + if (original.length >= start && 0 <= start) { + if (start <= end) { + int length = end - start; + int copyLength = Math.min(length, original.length - start); + T[] copy = (T[]) Array.newInstance(original.getClass().getComponentType(), length); + System.arraycopy(original, start, copy, 0, copyLength); + return copy; + } + throw new IllegalArgumentException(); + } + throw new ArrayIndexOutOfBoundsException(); + } + + @SuppressWarnings("unchecked") + static T[] copyOfRange(U[] original, int start, int end, + Class newType) { + if (start <= end) { + if (original.length >= start && 0 <= start) { + int length = end - start; + int copyLength = Math.min(length, original.length - start); + T[] copy = (T[]) Array.newInstance(newType.getComponentType(), + length); + System.arraycopy(original, start, copy, 0, copyLength); + return copy; + } + throw new ArrayIndexOutOfBoundsException(); + } + throw new IllegalArgumentException(); + } + +} diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/LinkedBlockingQueue.java b/libcore/concurrent/src/main/java/java/util/concurrent/LinkedBlockingQueue.java index 662f63e5d..e06f7bd52 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/LinkedBlockingQueue.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/LinkedBlockingQueue.java @@ -32,14 +32,15 @@ import java.util.*; * dynamically created upon each insertion unless this would bring the * queue above capacity. * - *

      This class implements all of the optional methods - * of the {@link Collection} and {@link Iterator} interfaces. + *

      This class and its iterator implement all of the + * optional methods of the {@link Collection} and {@link + * Iterator} interfaces. * * @since 1.5 * @author Doug Lea * @param the type of elements held in this collection * - **/ + */ public class LinkedBlockingQueue extends AbstractQueue implements BlockingQueue, java.io.Serializable { private static final long serialVersionUID = -6903933977591709194L; @@ -56,7 +57,7 @@ public class LinkedBlockingQueue extends AbstractQueue * items have been entered since the signal. And symmetrically for * takes signalling puts. Operations such as remove(Object) and * iterators acquire both locks. - */ + */ /** * Linked list node class @@ -93,7 +94,7 @@ public class LinkedBlockingQueue extends AbstractQueue private final Condition notFull = putLock.newCondition(); /** - * Signal a waiting take. Called only from put/offer (which do not + * Signals a waiting take. Called only from put/offer (which do not * otherwise ordinarily lock takeLock.) */ private void signalNotEmpty() { @@ -107,7 +108,7 @@ public class LinkedBlockingQueue extends AbstractQueue } /** - * Signal a waiting put. Called only from take/poll. + * Signals a waiting put. Called only from take/poll. */ private void signalNotFull() { final ReentrantLock putLock = this.putLock; @@ -120,7 +121,7 @@ public class LinkedBlockingQueue extends AbstractQueue } /** - * Create a node and link it at end of queue + * Creates a node and links it at end of queue. * @param x the item */ private void insert(E x) { @@ -128,11 +129,13 @@ public class LinkedBlockingQueue extends AbstractQueue } /** - * Remove a node from head of queue, + * Removes a node from head of queue, * @return the node */ private E extract() { - Node first = head.next; + Node h = head; + Node first = h.next; + h.next = null; // help GC head = first; E x = first.item; first.item = null; @@ -167,9 +170,9 @@ public class LinkedBlockingQueue extends AbstractQueue /** * Creates a LinkedBlockingQueue with the given (fixed) capacity. * - * @param capacity the capacity of this queue. + * @param capacity the capacity of this queue * @throws IllegalArgumentException if capacity is not greater - * than zero. + * than zero */ public LinkedBlockingQueue(int capacity) { if (capacity <= 0) throw new IllegalArgumentException(); @@ -182,14 +185,15 @@ public class LinkedBlockingQueue extends AbstractQueue * {@link Integer#MAX_VALUE}, initially containing the elements of the * given collection, * added in traversal order of the collection's iterator. + * * @param c the collection of elements to initially contain - * @throws NullPointerException if c or any element within it - * is null + * @throws NullPointerException if the specified collection or any + * of its elements are null */ public LinkedBlockingQueue(Collection c) { this(Integer.MAX_VALUE); - for (Iterator it = c.iterator(); it.hasNext();) - add(it.next()); + for (E e : c) + add(e); } @@ -198,7 +202,7 @@ public class LinkedBlockingQueue extends AbstractQueue /** * Returns the number of elements in this queue. * - * @return the number of elements in this queue. + * @return the number of elements in this queue */ public int size() { return count.get(); @@ -207,31 +211,29 @@ public class LinkedBlockingQueue extends AbstractQueue // this doc comment is a modified copy of the inherited doc comment, // without the reference to unlimited queues. /** - * Returns the number of elements that this queue can ideally (in - * the absence of memory or resource constraints) accept without + * Returns the number of additional elements that this queue can ideally + * (in the absence of memory or resource constraints) accept without * blocking. This is always equal to the initial capacity of this queue * less the current size of this queue. - *

      Note that you cannot always tell if - * an attempt to add an element will succeed by - * inspecting remainingCapacity because it may be the - * case that a waiting consumer is ready to take an - * element out of an otherwise full queue. - * - * @return the remaining capacity + * + *

      Note that you cannot always tell if an attempt to insert + * an element will succeed by inspecting remainingCapacity + * because it may be the case that another thread is about to + * insert or remove an element. */ public int remainingCapacity() { return capacity - count.get(); } /** - * Adds the specified element to the tail of this queue, waiting if + * Inserts the specified element at the tail of this queue, waiting if * necessary for space to become available. - * @param o the element to add - * @throws InterruptedException if interrupted while waiting. - * @throws NullPointerException if the specified element is null. + * + * @throws InterruptedException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} */ - public void put(E o) throws InterruptedException { - if (o == null) throw new NullPointerException(); + public void put(E e) throws InterruptedException { + if (e == null) throw new NullPointerException(); // Note: convention in all put/take/etc is to preset // local var holding count negative to indicate failure unless set. int c = -1; @@ -255,7 +257,7 @@ public class LinkedBlockingQueue extends AbstractQueue notFull.signal(); // propagate to a non-interrupted thread throw ie; } - insert(o); + insert(e); c = count.getAndIncrement(); if (c + 1 < capacity) notFull.signal(); @@ -269,20 +271,16 @@ public class LinkedBlockingQueue extends AbstractQueue /** * Inserts the specified element at the tail of this queue, waiting if * necessary up to the specified wait time for space to become available. - * @param o the element to add - * @param timeout how long to wait before giving up, in units of - * unit - * @param unit a TimeUnit determining how to interpret the - * timeout parameter + * * @return true if successful, or false if - * the specified waiting time elapses before space is available. - * @throws InterruptedException if interrupted while waiting. - * @throws NullPointerException if the specified element is null. + * the specified waiting time elapses before space is available. + * @throws InterruptedException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} */ - public boolean offer(E o, long timeout, TimeUnit unit) + public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException { - if (o == null) throw new NullPointerException(); + if (e == null) throw new NullPointerException(); long nanos = unit.toNanos(timeout); int c = -1; final ReentrantLock putLock = this.putLock; @@ -291,7 +289,7 @@ public class LinkedBlockingQueue extends AbstractQueue try { for (;;) { if (count.get() < capacity) { - insert(o); + insert(e); c = count.getAndIncrement(); if (c + 1 < capacity) notFull.signal(); @@ -315,16 +313,18 @@ public class LinkedBlockingQueue extends AbstractQueue } /** - * Inserts the specified element at the tail of this queue if possible, - * returning immediately if this queue is full. + * Inserts the specified element at the tail of this queue if it is + * possible to do so immediately without exceeding the queue's capacity, + * returning true upon success and false if this queue + * is full. + * When using a capacity-restricted queue, this method is generally + * preferable to method {@link BlockingQueue#add add}, which can fail to + * insert an element only by throwing an exception. * - * @param o the element to add. - * @return true if it was possible to add the element to - * this queue, else false - * @throws NullPointerException if the specified element is null + * @throws NullPointerException if the specified element is null */ - public boolean offer(E o) { - if (o == null) throw new NullPointerException(); + public boolean offer(E e) { + if (e == null) throw new NullPointerException(); final AtomicInteger count = this.count; if (count.get() == capacity) return false; @@ -333,7 +333,7 @@ public class LinkedBlockingQueue extends AbstractQueue putLock.lock(); try { if (count.get() < capacity) { - insert(o); + insert(e); c = count.getAndIncrement(); if (c + 1 < capacity) notFull.signal(); @@ -447,6 +447,17 @@ public class LinkedBlockingQueue extends AbstractQueue } } + /** + * Removes a single instance of the specified element from this queue, + * if it is present. More formally, removes an element e such + * that o.equals(e), if this queue contains one or more such + * elements. + * Returns true if this queue contained the specified element + * (or equivalently, if this queue changed as a result of the call). + * + * @param o element to be removed from this queue, if present + * @return true if this queue changed as a result of the call + */ public boolean remove(Object o) { if (o == null) return false; boolean removed = false; @@ -465,6 +476,8 @@ public class LinkedBlockingQueue extends AbstractQueue if (removed) { p.item = null; trail.next = p.next; + if (last == p) + last = trail; if (count.getAndDecrement() == capacity) notFull.signalAll(); } @@ -474,6 +487,19 @@ public class LinkedBlockingQueue extends AbstractQueue return removed; } + /** + * Returns an array containing all of the elements in this queue, in + * proper sequence. + * + *

      The returned array will be "safe" in that no references to it are + * maintained by this queue. (In other words, this method must allocate + * a new array). The caller is thus free to modify the returned array. + * + *

      This method acts as bridge between array-based and collection-based + * APIs. + * + * @return an array containing all of the elements in this queue + */ public Object[] toArray() { fullyLock(); try { @@ -488,6 +514,42 @@ public class LinkedBlockingQueue extends AbstractQueue } } + /** + * Returns an array containing all of the elements in this queue, in + * proper sequence; the runtime type of the returned array is that of + * the specified array. If the queue fits in the specified array, it + * is returned therein. Otherwise, a new array is allocated with the + * runtime type of the specified array and the size of this queue. + * + *

      If this queue fits in the specified array with room to spare + * (i.e., the array has more elements than this queue), the element in + * the array immediately following the end of the queue is set to + * null. + * + *

      Like the {@link #toArray()} method, this method acts as bridge between + * array-based and collection-based APIs. Further, this method allows + * precise control over the runtime type of the output array, and may, + * under certain circumstances, be used to save allocation costs. + * + *

      Suppose x is a queue known to contain only strings. + * The following code can be used to dump the queue into a newly + * allocated array of String: + * + *

      +     *     String[] y = x.toArray(new String[0]);
      + * + * Note that toArray(new Object[0]) is identical in function to + * toArray(). + * + * @param a the array into which the elements of the queue are to + * be stored, if it is big enough; otherwise, a new array of the + * same runtime type is allocated for this purpose + * @return an array containing all of the elements in this queue + * @throws ArrayStoreException if the runtime type of the specified array + * is not a supertype of the runtime type of every element in + * this queue + * @throws NullPointerException if the specified array is null + */ public T[] toArray(T[] a) { fullyLock(); try { @@ -499,6 +561,8 @@ public class LinkedBlockingQueue extends AbstractQueue int k = 0; for (Node p = head.next; p != null; p = p.next) a[k++] = (T)p.item; + if (a.length > k) + a[k] = null; return a; } finally { fullyUnlock(); @@ -514,10 +578,16 @@ public class LinkedBlockingQueue extends AbstractQueue } } + /** + * Atomically removes all of the elements from this queue. + * The queue will be empty after this call returns. + */ public void clear() { fullyLock(); try { head.next = null; + assert head.item == null; + last = head; if (count.getAndSet(0) == capacity) notFull.signalAll(); } finally { @@ -525,16 +595,24 @@ public class LinkedBlockingQueue extends AbstractQueue } } + /** + * @throws UnsupportedOperationException {@inheritDoc} + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + */ public int drainTo(Collection c) { if (c == null) throw new NullPointerException(); if (c == this) throw new IllegalArgumentException(); - Node first; + Node first; fullyLock(); try { first = head.next; head.next = null; + assert head.item == null; + last = head; if (count.getAndSet(0) == capacity) notFull.signalAll(); } finally { @@ -549,14 +627,18 @@ public class LinkedBlockingQueue extends AbstractQueue } return n; } - + + /** + * @throws UnsupportedOperationException {@inheritDoc} + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + */ public int drainTo(Collection c, int maxElements) { if (c == null) throw new NullPointerException(); if (c == this) throw new IllegalArgumentException(); - if (maxElements <= 0) - return 0; fullyLock(); try { int n = 0; @@ -569,6 +651,9 @@ public class LinkedBlockingQueue extends AbstractQueue } if (n != 0) { head.next = p; + assert head.item == null; + if (p == null) + last = head; if (count.getAndAdd(-n) == capacity) notFull.signalAll(); } @@ -581,12 +666,12 @@ public class LinkedBlockingQueue extends AbstractQueue /** * Returns an iterator over the elements in this queue in proper sequence. * The returned Iterator is a "weakly consistent" iterator that - * will never throw {@link java.util.ConcurrentModificationException}, + * will never throw {@link ConcurrentModificationException}, * and guarantees to traverse elements as they existed upon * construction of the iterator, and may (but is not guaranteed to) * reflect any modifications subsequent to construction. * - * @return an iterator over the elements in this queue in proper sequence. + * @return an iterator over the elements in this queue in proper sequence */ public Iterator iterator() { return new Itr(); @@ -660,6 +745,8 @@ public class LinkedBlockingQueue extends AbstractQueue if (p == node) { p.item = null; trail.next = p.next; + if (last == p) + last = trail; int c = count.getAndDecrement(); if (c == capacity) notFull.signalAll(); diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/PriorityBlockingQueue.java b/libcore/concurrent/src/main/java/java/util/concurrent/PriorityBlockingQueue.java index 83f10400f..7a33dc7f2 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/PriorityBlockingQueue.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/PriorityBlockingQueue.java @@ -19,16 +19,47 @@ import java.util.*; * blocking retrieval operations. While this queue is logically * unbounded, attempted additions may fail due to resource exhaustion * (causing OutOfMemoryError). This class does not permit - * null elements. A priority queue relying on natural - * ordering also does not permit insertion of non-comparable objects - * (doing so results in ClassCastException). + * null elements. A priority queue relying on {@linkplain + * Comparable natural ordering} also does not permit insertion of + * non-comparable objects (doing so results in + * ClassCastException). * - *

      This class implements all of the optional methods - * of the {@link Collection} and {@link Iterator} interfaces. - *

      The Iterator provided in method {@link #iterator()} is - * not guaranteed to traverse the elements of the - * PriorityBlockingQueue in any particular order. If you need ordered - * traversal, consider using Arrays.sort(pq.toArray()). + *

      This class and its iterator implement all of the + * optional methods of the {@link Collection} and {@link + * Iterator} interfaces. The Iterator provided in method {@link + * #iterator()} is not guaranteed to traverse the elements of + * the PriorityBlockingQueue in any particular order. If you need + * ordered traversal, consider using + * Arrays.sort(pq.toArray()). Also, method drainTo + * can be used to remove some or all elements in priority + * order and place them in another collection. + * + *

      Operations on this class make no guarantees about the ordering + * of elements with equal priority. If you need to enforce an + * ordering, you can define custom classes or comparators that use a + * secondary key to break ties in primary priority values. For + * example, here is a class that applies first-in-first-out + * tie-breaking to comparable elements. To use it, you would insert a + * new FIFOEntry(anEntry) instead of a plain entry object. + * + *

      + * class FIFOEntry<E extends Comparable<? super E>>
      + *     implements Comparable<FIFOEntry<E>> {
      + *   final static AtomicLong seq = new AtomicLong();
      + *   final long seqNum;
      + *   final E entry;
      + *   public FIFOEntry(E entry) {
      + *     seqNum = seq.getAndIncrement();
      + *     this.entry = entry;
      + *   }
      + *   public E getEntry() { return entry; }
      + *   public int compareTo(FIFOEntry<E> other) {
      + *     int res = entry.compareTo(other.entry);
      + *     if (res == 0 && other.entry != this.entry)
      + *       res = (seqNum < other.seqNum ? -1 : 1);
      + *     return res;
      + *   }
      + * }
      * * @since 1.5 * @author Doug Lea @@ -43,24 +74,22 @@ public class PriorityBlockingQueue extends AbstractQueue private final Condition notEmpty = lock.newCondition(); /** - * Creates a PriorityBlockingQueue with the default initial - * capacity - * (11) that orders its elements according to their natural - * ordering (using Comparable). + * Creates a PriorityBlockingQueue with the default + * initial capacity (11) that orders its elements according to + * their {@linkplain Comparable natural ordering}. */ public PriorityBlockingQueue() { q = new PriorityQueue(); } /** - * Creates a PriorityBlockingQueue with the specified initial - * capacity - * that orders its elements according to their natural ordering - * (using Comparable). + * Creates a PriorityBlockingQueue with the specified + * initial capacity that orders its elements according to their + * {@linkplain Comparable natural ordering}. * - * @param initialCapacity the initial capacity for this priority queue. + * @param initialCapacity the initial capacity for this priority queue * @throws IllegalArgumentException if initialCapacity is less - * than 1 + * than 1 */ public PriorityBlockingQueue(int initialCapacity) { q = new PriorityQueue(initialCapacity, null); @@ -68,15 +97,15 @@ public class PriorityBlockingQueue extends AbstractQueue /** * Creates a PriorityBlockingQueue with the specified initial - * capacity - * that orders its elements according to the specified comparator. + * capacity that orders its elements according to the specified + * comparator. * - * @param initialCapacity the initial capacity for this priority queue. - * @param comparator the comparator used to order this priority queue. - * If null then the order depends on the elements' natural - * ordering. + * @param initialCapacity the initial capacity for this priority queue + * @param comparator the comparator that will be used to order this + * priority queue. If {@code null}, the {@linkplain Comparable + * natural ordering} of the elements will be used. * @throws IllegalArgumentException if initialCapacity is less - * than 1 + * than 1 */ public PriorityBlockingQueue(int initialCapacity, Comparator comparator) { @@ -85,75 +114,54 @@ public class PriorityBlockingQueue extends AbstractQueue /** * Creates a PriorityBlockingQueue containing the elements - * in the specified collection. The priority queue has an initial - * capacity of 110% of the size of the specified collection. If - * the specified collection is a {@link SortedSet} or a {@link - * PriorityQueue}, this priority queue will be sorted according to - * the same comparator, or according to its elements' natural - * order if the collection is sorted according to its elements' - * natural order. Otherwise, this priority queue is ordered - * according to its elements' natural order. + * in the specified collection. If the specified collection is a + * {@link SortedSet} or a {@link PriorityQueue}, this + * priority queue will be ordered according to the same ordering. + * Otherwise, this priority queue will be ordered according to the + * {@linkplain Comparable natural ordering} of its elements. * - * @param c the collection whose elements are to be placed - * into this priority queue. + * @param c the collection whose elements are to be placed + * into this priority queue * @throws ClassCastException if elements of the specified collection * cannot be compared to one another according to the priority - * queue's ordering. - * @throws NullPointerException if c or any element within it - * is null + * queue's ordering + * @throws NullPointerException if the specified collection or any + * of its elements are null */ public PriorityBlockingQueue(Collection c) { q = new PriorityQueue(c); } - - // these first few override just to update doc comments - /** - * Adds the specified element to this queue. - * @param o the element to add - * @return true (as per the general contract of - * Collection.add). + * Inserts the specified element into this priority queue. * - * @throws NullPointerException if the specified element is null. + * @param e the element to add + * @return true (as specified by {@link Collection#add}) * @throws ClassCastException if the specified element cannot be compared - * with elements currently in the priority queue according - * to the priority queue's ordering. + * with elements currently in the priority queue according to the + * priority queue's ordering + * @throws NullPointerException if the specified element is null */ - public boolean add(E o) { - return super.add(o); + public boolean add(E e) { + return offer(e); } // BEGIN android-changed - /** - * Returns the comparator used to order this collection, or null - * if this collection is sorted according to its elements natural ordering - * (using Comparable). - * - * @return the comparator used to order this collection, or null - * if this collection is sorted according to its elements natural ordering. - */ - public Comparator comparator() { - return q.comparator(); - } - // END android-changed - /** * Inserts the specified element into this priority queue. * - * @param o the element to add - * @return true + * @param e the element to add + * @return true (as specified by {@link Queue#offer}) * @throws ClassCastException if the specified element cannot be compared - * with elements currently in the priority queue according - * to the priority queue's ordering. - * @throws NullPointerException if the specified element is null. + * with elements currently in the priority queue according to the + * priority queue's ordering + * @throws NullPointerException if the specified element is null */ - public boolean offer(E o) { - if (o == null) throw new NullPointerException(); + public boolean offer(E e) { final ReentrantLock lock = this.lock; lock.lock(); try { - boolean ok = q.offer(o); + boolean ok = q.offer(e); assert ok; notEmpty.signal(); return true; @@ -163,32 +171,44 @@ public class PriorityBlockingQueue extends AbstractQueue } /** - * Adds the specified element to this priority queue. As the queue is + * Inserts the specified element into this priority queue. As the queue is * unbounded this method will never block. - * @param o the element to add - * @throws ClassCastException if the element cannot be compared - * with elements currently in the priority queue according - * to the priority queue's ordering. - * @throws NullPointerException if the specified element is null. + * + * @param e the element to add + * @throws ClassCastException if the specified element cannot be compared + * with elements currently in the priority queue according to the + * priority queue's ordering + * @throws NullPointerException if the specified element is null */ - public void put(E o) { - offer(o); // never need to block + public void put(E e) { + offer(e); // never need to block } /** * Inserts the specified element into this priority queue. As the queue is * unbounded this method will never block. - * @param o the element to add + * + * @param e the element to add * @param timeout This parameter is ignored as the method never blocks * @param unit This parameter is ignored as the method never blocks * @return true - * @throws ClassCastException if the element cannot be compared - * with elements currently in the priority queue according - * to the priority queue's ordering. - * @throws NullPointerException if the specified element is null. + * @throws ClassCastException if the specified element cannot be compared + * with elements currently in the priority queue according to the + * priority queue's ordering + * @throws NullPointerException if the specified element is null */ - public boolean offer(E o, long timeout, TimeUnit unit) { - return offer(o); // never need to block + public boolean offer(E e, long timeout, TimeUnit unit) { + return offer(e); // never need to block + } + + public E poll() { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + return q.poll(); + } finally { + lock.unlock(); + } } public E take() throws InterruptedException { @@ -210,17 +230,6 @@ public class PriorityBlockingQueue extends AbstractQueue } } - - public E poll() { - final ReentrantLock lock = this.lock; - lock.lock(); - try { - return q.poll(); - } finally { - lock.unlock(); - } - } - public E poll(long timeout, TimeUnit unit) throws InterruptedException { long nanos = unit.toNanos(timeout); final ReentrantLock lock = this.lock; @@ -254,6 +263,19 @@ public class PriorityBlockingQueue extends AbstractQueue } } + /** + * Returns the comparator used to order the elements in this queue, + * or null if this queue uses the {@linkplain Comparable + * natural ordering} of its elements. + * + * @return the comparator used to order the elements in this queue, + * or null if this queue uses the natural + * ordering of its elements + */ + public Comparator comparator() { + return q.comparator(); + } + public int size() { final ReentrantLock lock = this.lock; lock.lock(); @@ -273,6 +295,17 @@ public class PriorityBlockingQueue extends AbstractQueue return Integer.MAX_VALUE; } + /** + * Removes a single instance of the specified element from this queue, + * if it is present. More formally, removes an element {@code e} such + * that {@code o.equals(e)}, if this queue contains one or more such + * elements. Returns {@code true} if and only if this queue contained + * the specified element (or equivalently, if this queue changed as a + * result of the call). + * + * @param o element to be removed from this queue, if present + * @return true if this queue changed as a result of the call + */ public boolean remove(Object o) { final ReentrantLock lock = this.lock; lock.lock(); @@ -283,6 +316,14 @@ public class PriorityBlockingQueue extends AbstractQueue } } + /** + * Returns {@code true} if this queue contains the specified element. + * More formally, returns {@code true} if and only if this queue contains + * at least one element {@code e} such that {@code o.equals(e)}. + * + * @param o object to be checked for containment in this queue + * @return true if this queue contains the specified element + */ public boolean contains(Object o) { final ReentrantLock lock = this.lock; lock.lock(); @@ -293,6 +334,19 @@ public class PriorityBlockingQueue extends AbstractQueue } } + /** + * Returns an array containing all of the elements in this queue. + * The returned array elements are in no particular order. + * + *

      The returned array will be "safe" in that no references to it are + * maintained by this queue. (In other words, this method must allocate + * a new array). The caller is thus free to modify the returned array. + * + *

      This method acts as bridge between array-based and collection-based + * APIs. + * + * @return an array containing all of the elements in this queue + */ public Object[] toArray() { final ReentrantLock lock = this.lock; lock.lock(); @@ -314,6 +368,12 @@ public class PriorityBlockingQueue extends AbstractQueue } } + /** + * @throws UnsupportedOperationException {@inheritDoc} + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + */ public int drainTo(Collection c) { if (c == null) throw new NullPointerException(); @@ -334,6 +394,12 @@ public class PriorityBlockingQueue extends AbstractQueue } } + /** + * @throws UnsupportedOperationException {@inheritDoc} + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + */ public int drainTo(Collection c, int maxElements) { if (c == null) throw new NullPointerException(); @@ -357,7 +423,7 @@ public class PriorityBlockingQueue extends AbstractQueue } /** - * Atomically removes all of the elements from this delay queue. + * Atomically removes all of the elements from this queue. * The queue will be empty after this call returns. */ public void clear() { @@ -370,6 +436,43 @@ public class PriorityBlockingQueue extends AbstractQueue } } + /** + * Returns an array containing all of the elements in this queue; the + * runtime type of the returned array is that of the specified array. + * The returned array elements are in no particular order. + * If the queue fits in the specified array, it is returned therein. + * Otherwise, a new array is allocated with the runtime type of the + * specified array and the size of this queue. + * + *

      If this queue fits in the specified array with room to spare + * (i.e., the array has more elements than this queue), the element in + * the array immediately following the end of the queue is set to + * null. + * + *

      Like the {@link #toArray()} method, this method acts as bridge between + * array-based and collection-based APIs. Further, this method allows + * precise control over the runtime type of the output array, and may, + * under certain circumstances, be used to save allocation costs. + * + *

      Suppose x is a queue known to contain only strings. + * The following code can be used to dump the queue into a newly + * allocated array of String: + * + *

      +     *     String[] y = x.toArray(new String[0]);
      + * + * Note that toArray(new Object[0]) is identical in function to + * toArray(). + * + * @param a the array into which the elements of the queue are to + * be stored, if it is big enough; otherwise, a new array of the + * same runtime type is allocated for this purpose + * @return an array containing all of the elements in this queue + * @throws ArrayStoreException if the runtime type of the specified array + * is not a supertype of the runtime type of every element in + * this queue + * @throws NullPointerException if the specified array is null + */ public T[] toArray(T[] a) { final ReentrantLock lock = this.lock; lock.lock(); @@ -383,54 +486,58 @@ public class PriorityBlockingQueue extends AbstractQueue /** * Returns an iterator over the elements in this queue. The * iterator does not return the elements in any particular order. - * The returned iterator is a thread-safe "fast-fail" iterator - * that will throw {@link - * java.util.ConcurrentModificationException} upon detected - * interference. + * The returned Iterator is a "weakly consistent" + * iterator that will never throw {@link + * ConcurrentModificationException}, and guarantees to traverse + * elements as they existed upon construction of the iterator, and + * may (but is not guaranteed to) reflect any modifications + * subsequent to construction. * - * @return an iterator over the elements in this queue. + * @return an iterator over the elements in this queue */ public Iterator iterator() { - final ReentrantLock lock = this.lock; - lock.lock(); - try { - return new Itr(q.iterator()); - } finally { - lock.unlock(); - } + return new Itr(toArray()); } - private class Itr implements Iterator { - private final Iterator iter; - Itr(Iterator i) { - iter = i; + /** + * Snapshot iterator that works off copy of underlying q array. + */ + private class Itr implements Iterator { + final Object[] array; // Array of all elements + int cursor; // index of next element to return; + int lastRet; // index of last element, or -1 if no such + + Itr(Object[] array) { + lastRet = -1; + this.array = array; } public boolean hasNext() { - /* - * No sync -- we rely on underlying hasNext to be - * stateless, in which case we can return true by mistake - * only when next() will subsequently throw - * ConcurrentModificationException. - */ - return iter.hasNext(); + return cursor < array.length; } public E next() { - ReentrantLock lock = PriorityBlockingQueue.this.lock; - lock.lock(); - try { - return iter.next(); - } finally { - lock.unlock(); - } + if (cursor >= array.length) + throw new NoSuchElementException(); + lastRet = cursor; + return (E)array[cursor++]; } public void remove() { - ReentrantLock lock = PriorityBlockingQueue.this.lock; + if (lastRet < 0) + throw new IllegalStateException(); + Object x = array[lastRet]; + lastRet = -1; + // Traverse underlying queue to find == element, + // not just a .equals element. lock.lock(); try { - iter.remove(); + for (Iterator it = q.iterator(); it.hasNext(); ) { + if (it.next() == x) { + it.remove(); + return; + } + } } finally { lock.unlock(); } @@ -438,7 +545,7 @@ public class PriorityBlockingQueue extends AbstractQueue } /** - * Save the state to a stream (that is, serialize it). This + * Saves the state to a stream (that is, serializes it). This * merely wraps default serialization within lock. The * serialization strategy for items is left to underlying * Queue. Note that locking is not needed on deserialization, so diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/RejectedExecutionException.java b/libcore/concurrent/src/main/java/java/util/concurrent/RejectedExecutionException.java index 199b9de86..30b043d66 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/RejectedExecutionException.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/RejectedExecutionException.java @@ -9,7 +9,7 @@ package java.util.concurrent; /** * Exception thrown by an {@link Executor} when a task cannot be * accepted for execution. - * + * * @since 1.5 * @author Doug Lea */ diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/RejectedExecutionHandler.java b/libcore/concurrent/src/main/java/java/util/concurrent/RejectedExecutionHandler.java index 4b4bbeab1..417a27cdd 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/RejectedExecutionHandler.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/RejectedExecutionHandler.java @@ -7,8 +7,7 @@ package java.util.concurrent; /** - * A handler for tasks that cannot be executed by a {@link - * ThreadPoolExecutor}. + * A handler for tasks that cannot be executed by a {@link ThreadPoolExecutor}. * * @since 1.5 * @author Doug Lea @@ -17,13 +16,14 @@ public interface RejectedExecutionHandler { /** * Method that may be invoked by a {@link ThreadPoolExecutor} when - * execute cannot accept a task. This may occur when no - * more threads or queue slots are available because their bounds - * would be exceeded, or upon shutdown of the Executor. + * {@link ThreadPoolExecutor#execute execute} cannot accept a + * task. This may occur when no more threads or queue slots are + * available because their bounds would be exceeded, or upon + * shutdown of the Executor. * - * In the absence other alternatives, the method may throw an - * unchecked {@link RejectedExecutionException}, which will be - * propagated to the caller of execute. + *

      In the absence of other alternatives, the method may throw + * an unchecked {@link RejectedExecutionException}, which will be + * propagated to the caller of {@code execute}. * * @param r the runnable task requested to be executed * @param executor the executor attempting to execute this task diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/ScheduledExecutorService.java b/libcore/concurrent/src/main/java/java/util/concurrent/ScheduledExecutorService.java index 9579fefe3..c170c4a52 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/ScheduledExecutorService.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/ScheduledExecutorService.java @@ -10,13 +10,13 @@ import java.util.*; /** * An {@link ExecutorService} that can schedule commands to run after a given - * delay, or to execute periodically. + * delay, or to execute periodically. * *

      The schedule methods create tasks with various delays * and return a task object that can be used to cancel or check * execution. The scheduleAtFixedRate and * scheduleWithFixedDelay methods create and execute tasks - * that run periodically until cancelled. + * that run periodically until cancelled. * *

      Commands submitted using the {@link Executor#execute} and * {@link ExecutorService} submit methods are scheduled with @@ -33,27 +33,27 @@ import java.util.*; * TimeUnit.MILLISECONDS). Beware however that expiration of a * relative delay need not coincide with the current Date at * which the task is enabled due to network time synchronization - * protocols, clock drift, or other factors. + * protocols, clock drift, or other factors. * * The {@link Executors} class provides convenient factory methods for * the ScheduledExecutorService implementations provided in this package. * *

      Usage Example

      - * + * * Here is a class with a method that sets up a ScheduledExecutorService * to beep every ten seconds for an hour: * *
      - * import static java.util.concurrent.TimeUnit;
      + * import static java.util.concurrent.TimeUnit.*;
        * class BeeperControl {
      - *    private final ScheduledExecutorService scheduler = 
      + *    private final ScheduledExecutorService scheduler =
        *       Executors.newScheduledThreadPool(1);
        *
        *    public void beepForAnHour() {
        *        final Runnable beeper = new Runnable() {
        *                public void run() { System.out.println("beep"); }
        *            };
      - *        final ScheduledFuture<?> beeperHandle = 
      + *        final ScheduledFuture<?> beeperHandle =
        *            scheduler.scheduleAtFixedRate(beeper, 10, 10, SECONDS);
        *        scheduler.schedule(new Runnable() {
        *                public void run() { beeperHandle.cancel(true); }
      @@ -70,76 +70,90 @@ public interface ScheduledExecutorService extends ExecutorService {
           /**
            * Creates and executes a one-shot action that becomes enabled
            * after the given delay.
      -     * @param command the task to execute.
      -     * @param delay the time from now to delay execution.
      -     * @param unit the time unit of the delay parameter.
      -     * @return a Future representing pending completion of the task,
      -     * and whose get() method will return null
      -     * upon completion.
      -     * @throws RejectedExecutionException if task cannot be scheduled
      -     * for execution.
      +     *
      +     * @param command the task to execute
      +     * @param delay the time from now to delay execution
      +     * @param unit the time unit of the delay parameter
      +     * @return a ScheduledFuture representing pending completion of
      +     *         the task and whose get() method will return
      +     *         null upon completion
      +     * @throws RejectedExecutionException if the task cannot be
      +     *         scheduled for execution
            * @throws NullPointerException if command is null
            */
      -    public ScheduledFuture schedule(Runnable command, long delay,  TimeUnit unit);
      +    public ScheduledFuture schedule(Runnable command,
      +                                       long delay, TimeUnit unit);
       
           /**
            * Creates and executes a ScheduledFuture that becomes enabled after the
            * given delay.
      -     * @param callable the function to execute.
      -     * @param delay the time from now to delay execution.
      -     * @param unit the time unit of the delay parameter.
      -     * @return a ScheduledFuture that can be used to extract result or cancel.
      -     * @throws RejectedExecutionException if task cannot be scheduled
      -     * for execution.
      +     *
      +     * @param callable the function to execute
      +     * @param delay the time from now to delay execution
      +     * @param unit the time unit of the delay parameter
      +     * @return a ScheduledFuture that can be used to extract result or cancel
      +     * @throws RejectedExecutionException if the task cannot be
      +     *         scheduled for execution
            * @throws NullPointerException if callable is null
            */
      -    public  ScheduledFuture schedule(Callable callable, long delay, TimeUnit unit);
      +    public  ScheduledFuture schedule(Callable callable,
      +                                           long delay, TimeUnit unit);
       
           /**
            * Creates and executes a periodic action that becomes enabled first
            * after the given initial delay, and subsequently with the given
            * period; that is executions will commence after
            * initialDelay then initialDelay+period, then
      -     * initialDelay + 2 * period, and so on.  
      +     * initialDelay + 2 * period, and so on.
            * If any execution of the task
            * encounters an exception, subsequent executions are suppressed.
            * Otherwise, the task will only terminate via cancellation or
      -     * termination of the executor.
      -     * @param command the task to execute.
      -     * @param initialDelay the time to delay first execution.
      -     * @param period the period between successive executions.
      +     * termination of the executor.  If any execution of this task
      +     * takes longer than its period, then subsequent executions
      +     * may start late, but will not concurrently execute.
      +     *
      +     * @param command the task to execute
      +     * @param initialDelay the time to delay first execution
      +     * @param period the period between successive executions
            * @param unit the time unit of the initialDelay and period parameters
      -     * @return a Future representing pending completion of the task,
      -     * and whose get() method will throw an exception upon
      -     * cancellation.
      -     * @throws RejectedExecutionException if task cannot be scheduled
      -     * for execution.
      +     * @return a ScheduledFuture representing pending completion of
      +     *         the task, and whose get() method will throw an
      +     *         exception upon cancellation
      +     * @throws RejectedExecutionException if the task cannot be
      +     *         scheduled for execution
            * @throws NullPointerException if command is null
      -     * @throws IllegalArgumentException if period less than or equal to zero.
      +     * @throws IllegalArgumentException if period less than or equal to zero
            */
      -    public ScheduledFuture scheduleAtFixedRate(Runnable command, long initialDelay,  long period, TimeUnit unit);
      -    
      +    public ScheduledFuture scheduleAtFixedRate(Runnable command,
      +                                                  long initialDelay,
      +                                                  long period,
      +                                                  TimeUnit unit);
      +
           /**
            * Creates and executes a periodic action that becomes enabled first
            * after the given initial delay, and subsequently with the
            * given delay between the termination of one execution and the
      -     * commencement of the next. If any execution of the task
      +     * commencement of the next.  If any execution of the task
            * encounters an exception, subsequent executions are suppressed.
            * Otherwise, the task will only terminate via cancellation or
            * termination of the executor.
      -     * @param command the task to execute.
      -     * @param initialDelay the time to delay first execution.
      +     *
      +     * @param command the task to execute
      +     * @param initialDelay the time to delay first execution
            * @param delay the delay between the termination of one
      -     * execution and the commencement of the next.
      +     * execution and the commencement of the next
            * @param unit the time unit of the initialDelay and delay parameters
      -     * @return a Future representing pending completion of the task,
      -     * and whose get() method will throw an exception upon
      -     * cancellation.
      -     * @throws RejectedExecutionException if task cannot be scheduled
      -     * for execution.
      +     * @return a ScheduledFuture representing pending completion of
      +     *         the task, and whose get() method will throw an
      +     *         exception upon cancellation
      +     * @throws RejectedExecutionException if the task cannot be
      +     *         scheduled for execution
            * @throws NullPointerException if command is null
      -     * @throws IllegalArgumentException if delay less than or equal to zero.
      +     * @throws IllegalArgumentException if delay less than or equal to zero
            */
      -    public ScheduledFuture scheduleWithFixedDelay(Runnable command, long initialDelay,  long delay, TimeUnit unit);
      +    public ScheduledFuture scheduleWithFixedDelay(Runnable command,
      +                                                     long initialDelay,
      +                                                     long delay,
      +                                                     TimeUnit unit);
       
       }
      diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java b/libcore/concurrent/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java
      index 09ec681d6..83ffb99e3 100644
      --- a/libcore/concurrent/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java
      +++ b/libcore/concurrent/src/main/java/java/util/concurrent/ScheduledThreadPoolExecutor.java
      @@ -4,8 +4,13 @@
        * http://creativecommons.org/licenses/publicdomain
        */
       
      +/*
      + * Modified in Apache Harmony.
      + */
      +
       package java.util.concurrent;
       import java.util.concurrent.atomic.*;
      +import java.util.concurrent.locks.*;
       import java.util.*;
       
       /**
      @@ -16,25 +21,60 @@ import java.util.*;
        * flexibility or capabilities of {@link ThreadPoolExecutor} (which
        * this class extends) are required.
        *
      - * 

      Delayed tasks execute no sooner than they are enabled, but + *

      Delayed tasks execute no sooner than they are enabled, but * without any real-time guarantees about when, after they are * enabled, they will commence. Tasks scheduled for exactly the same * execution time are enabled in first-in-first-out (FIFO) order of * submission. * + *

      Successive executions of a task scheduled via + * scheduleAtFixedRate or + * scheduleWithFixedDelay do not overlap. While different + * executions may be performed by different threads, the effects of + * prior executions happen-before + * those of subsequent ones. + * *

      While this class inherits from {@link ThreadPoolExecutor}, a few * of the inherited tuning methods are not useful for it. In - * particular, because it acts as a fixed-sized pool using - * corePoolSize threads and an unbounded queue, adjustments - * to maximumPoolSize have no useful effect. + * particular, because it acts as a fixed-sized pool using {@code + * corePoolSize} threads and an unbounded queue, adjustments to {@code + * maximumPoolSize} have no useful effect. * * @since 1.5 * @author Doug Lea */ -public class ScheduledThreadPoolExecutor - extends ThreadPoolExecutor +public class ScheduledThreadPoolExecutor + extends ThreadPoolExecutor implements ScheduledExecutorService { + /* + * This class specializes ThreadPoolExecutor implementation by + * + * 1. Using a custom task type, ScheduledFutureTask for + * tasks, even those that don't require scheduling (i.e., + * those submitted using ExecutorService execute, not + * ScheduledExecutorService methods) which are treated as + * delayed tasks with a delay of zero. + * + * 2. Using a custom queue (DelayedWorkQueue), a variant of + * unbounded DelayQueue. The lack of capacity constraint and + * the fact that corePoolSize and maximumPoolSize are + * effectively identical simplifies some execution mechanics + * (see delayedExecute) compared to ThreadPoolExecutor. + * + * 3. Supporting optional run-after-shutdown parameters, which + * leads to overrides of shutdown methods to remove and cancel + * tasks that should NOT be run after shutdown, as well as + * different recheck logic when task (re)submission overlaps + * with a shutdown. + * + * 4. Task decoration methods to allow interception and + * instrumentation, which are needed because subclasses cannot + * otherwise override submit methods to get this effect. These + * don't have any impact on pool control logic though. + */ + /** * False if should cancel/suppress periodic tasks on shutdown. */ @@ -45,29 +85,41 @@ public class ScheduledThreadPoolExecutor */ private volatile boolean executeExistingDelayedTasksAfterShutdown = true; + /** + * True if ScheduledFutureTask.cancel should remove from queue + */ + private volatile boolean removeOnCancel = false; + /** * Sequence number to break scheduling ties, and in turn to * guarantee FIFO order among tied entries. */ private static final AtomicLong sequencer = new AtomicLong(0); - /** Base of nanosecond timings, to avoid wrapping */ - private static final long NANO_ORIGIN = System.nanoTime(); /** - * Returns nanosecond time offset by origin + * Value of System.nanoTime upon static initialization. This is + * used as an offset by now() to avoid wraparound of time values + * that would make them appear negative. */ - final long now() { - return System.nanoTime() - NANO_ORIGIN; + static final long initialNanoTime = System.nanoTime(); + + /** + * Returns current nanosecond time. + */ + static long now() { + return System.nanoTime() - initialNanoTime; } - private class ScheduledFutureTask + private class ScheduledFutureTask extends FutureTask implements ScheduledFuture { - + /** Sequence number to break ties FIFO */ private final long sequenceNumber; + /** The time the task is enabled to execute in nanoTime units */ private long time; + /** * Period in nanoseconds for repeating tasks. A positive * value indicates fixed-rate execution. A negative value @@ -76,8 +128,16 @@ public class ScheduledThreadPoolExecutor */ private final long period; + /** The actual task to be re-enqueued by reExecutePeriodic */ + ScheduledFutureTask outerTask = this; + /** - * Creates a one-shot action with given nanoTime-based trigger time + * Index into delay queue, to support faster cancellation. + */ + int heapIndex; + + /** + * Creates a one-shot action with given nanoTime-based trigger time. */ ScheduledFutureTask(Runnable r, V result, long ns) { super(r, result); @@ -87,9 +147,9 @@ public class ScheduledThreadPoolExecutor } /** - * Creates a periodic action with given nano time and period + * Creates a periodic action with given nano time and period. */ - ScheduledFutureTask(Runnable r, V result, long ns, long period) { + ScheduledFutureTask(Runnable r, V result, long ns, long period) { super(r, result); this.time = ns; this.period = period; @@ -97,7 +157,7 @@ public class ScheduledThreadPoolExecutor } /** - * Creates a one-shot action with given nanoTime-based trigger + * Creates a one-shot action with given nanoTime-based trigger. */ ScheduledFutureTask(Callable callable, long ns) { super(callable); @@ -107,122 +167,162 @@ public class ScheduledThreadPoolExecutor } public long getDelay(TimeUnit unit) { - long d = unit.convert(time - now(), TimeUnit.NANOSECONDS); - return d; + long d = time - now(); + return d<=0? 0 : unit.convert(d, TimeUnit.NANOSECONDS); } public int compareTo(Delayed other) { if (other == this) // compare zero ONLY if same object return 0; - ScheduledFutureTask x = (ScheduledFutureTask)other; - long diff = time - x.time; - if (diff < 0) - return -1; - else if (diff > 0) - return 1; - else if (sequenceNumber < x.sequenceNumber) - return -1; - else - return 1; + if (other instanceof ScheduledFutureTask) { + ScheduledFutureTask x = (ScheduledFutureTask)other; + long diff = time - x.time; + if (diff < 0) + return -1; + else if (diff > 0) + return 1; + else if (sequenceNumber < x.sequenceNumber) + return -1; + else + return 1; + } + long d = (getDelay(TimeUnit.NANOSECONDS) - + other.getDelay(TimeUnit.NANOSECONDS)); + return (d == 0) ? 0 : ((d < 0) ? -1 : 1); } /** * Returns true if this is a periodic (not a one-shot) action. + * * @return true if periodic */ - boolean isPeriodic() { + public boolean isPeriodic() { return period != 0; } /** - * Run a periodic task + * Sets the next time to run for a periodic task. */ - private void runPeriodic() { - boolean ok = ScheduledFutureTask.super.runAndReset(); - boolean down = isShutdown(); - // Reschedule if not cancelled and not shutdown or policy allows - if (ok && (!down || - (getContinueExistingPeriodicTasksAfterShutdownPolicy() && - !isTerminating()))) { - long p = period; - if (p > 0) - time += p; - else - time = now() - p; - ScheduledThreadPoolExecutor.super.getQueue().add(this); - } - // This might have been the final executed delayed - // task. Wake up threads to check. - else if (down) - interruptIdleWorkers(); + private void setNextRunTime() { + long p = period; + if (p > 0) + time += p; + else + time = now() - p; + } + + public boolean cancel(boolean mayInterruptIfRunning) { + boolean cancelled = super.cancel(mayInterruptIfRunning); + if (cancelled && removeOnCancel && heapIndex >= 0) + remove(this); + return cancelled; } /** * Overrides FutureTask version so as to reset/requeue if periodic. - */ + */ public void run() { - if (isPeriodic()) - runPeriodic(); - else + boolean periodic = isPeriodic(); + if (!canRunInCurrentRunState(periodic)) + cancel(false); + else if (!periodic) ScheduledFutureTask.super.run(); + else if (ScheduledFutureTask.super.runAndReset()) { + setNextRunTime(); + reExecutePeriodic(outerTask); + } + } + } + + /** + * Returns true if can run a task given current run state + * and run-after-shutdown parameters. + * + * @param periodic true if this task periodic, false if delayed + */ + boolean canRunInCurrentRunState(boolean periodic) { + return isRunningOrShutdown(periodic ? + continueExistingPeriodicTasksAfterShutdown : + executeExistingDelayedTasksAfterShutdown); + } + + /** + * Main execution method for delayed or periodic tasks. If pool + * is shut down, rejects the task. Otherwise adds task to queue + * and starts a thread, if necessary, to run it. (We cannot + * prestart the thread to run the task because the task (probably) + * shouldn't be run yet,) If the pool is shut down while the task + * is being added, cancel and remove it if required by state and + * run-after-shutdown parameters. + * + * @param task the task + */ + private void delayedExecute(ScheduledFutureTask task) { + if (isShutdown()) + reject(task); + else { + super.getQueue().add(task); + if (isShutdown() && + !canRunInCurrentRunState(task.isPeriodic()) && + remove(task)) + task.cancel(false); + else + prestartCoreThread(); } } /** - * Specialized variant of ThreadPoolExecutor.execute for delayed tasks. - */ - private void delayedExecute(Runnable command) { - if (isShutdown()) { - reject(command); - return; - } - // Prestart a thread if necessary. We cannot prestart it - // running the task because the task (probably) shouldn't be - // run yet, so thread will just idle until delay elapses. - if (getPoolSize() < getCorePoolSize()) - prestartCoreThread(); - - super.getQueue().add(command); + * Requeues a periodic task unless current run state precludes it. + * Same idea as delayedExecute except drops task rather than rejecting. + * + * @param task the task + */ + void reExecutePeriodic(ScheduledFutureTask task) { + if (canRunInCurrentRunState(true)) { + super.getQueue().add(task); + if (!canRunInCurrentRunState(true) && remove(task)) + task.cancel(false); + else + prestartCoreThread(); + } } /** - * Cancel and clear the queue of all tasks that should not be run - * due to shutdown policy. - */ - private void cancelUnwantedTasks() { - boolean keepDelayed = getExecuteExistingDelayedTasksAfterShutdownPolicy(); - boolean keepPeriodic = getContinueExistingPeriodicTasksAfterShutdownPolicy(); - if (!keepDelayed && !keepPeriodic) - super.getQueue().clear(); - else if (keepDelayed || keepPeriodic) { - Object[] entries = super.getQueue().toArray(); - for (int i = 0; i < entries.length; ++i) { - Object e = entries[i]; + * Cancels and clears the queue of all tasks that should not be run + * due to shutdown policy. Invoked within super.shutdown. + */ + @Override void onShutdown() { + BlockingQueue q = super.getQueue(); + boolean keepDelayed = + getExecuteExistingDelayedTasksAfterShutdownPolicy(); + boolean keepPeriodic = + getContinueExistingPeriodicTasksAfterShutdownPolicy(); + if (!keepDelayed && !keepPeriodic) + q.clear(); + else { + // Traverse snapshot to avoid iterator exceptions + for (Object e : q.toArray()) { if (e instanceof ScheduledFutureTask) { - ScheduledFutureTask t = (ScheduledFutureTask)e; - if (t.isPeriodic()? !keepPeriodic : !keepDelayed) - t.cancel(false); + ScheduledFutureTask t = + (ScheduledFutureTask)e; + if ((t.isPeriodic() ? !keepPeriodic : !keepDelayed) || + t.isCancelled()) { // also remove if already cancelled + if (q.remove(t)) + t.cancel(false); + } } } - entries = null; - purge(); } - } - - public boolean remove(Runnable task) { - if (!(task instanceof ScheduledFutureTask)) - return false; - return getQueue().remove(task); + tryTerminate(); } /** - * Creates a new ScheduledThreadPoolExecutor with the given core - * pool size. - * - * @param corePoolSize the number of threads to keep in the pool, - * even if they are idle. - * @throws IllegalArgumentException if corePoolSize less than or - * equal to zero + * Creates a new {@code ScheduledThreadPoolExecutor} with the + * given core pool size. + * + * @param corePoolSize the number of threads to keep in the pool, even + * if they are idle + * @throws IllegalArgumentException if {@code corePoolSize < 0} */ public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS, @@ -230,14 +330,15 @@ public class ScheduledThreadPoolExecutor } /** - * Creates a new ScheduledThreadPoolExecutor with the given - * initial parameters. - * - * @param corePoolSize the number of threads to keep in the pool, - * even if they are idle. + * Creates a new {@code ScheduledThreadPoolExecutor} with the + * given initial parameters. + * + * @param corePoolSize the number of threads to keep in the pool, even + * if they are idle * @param threadFactory the factory to use when the executor - * creates a new thread. - * @throws NullPointerException if threadFactory is null + * creates a new thread + * @throws IllegalArgumentException if {@code corePoolSize < 0} + * @throws NullPointerException if {@code threadFactory} is null */ public ScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory) { @@ -248,12 +349,13 @@ public class ScheduledThreadPoolExecutor /** * Creates a new ScheduledThreadPoolExecutor with the given * initial parameters. - * - * @param corePoolSize the number of threads to keep in the pool, - * even if they are idle. + * + * @param corePoolSize the number of threads to keep in the pool, even + * if they are idle * @param handler the handler to use when execution is blocked - * because the thread bounds and queue capacities are reached. - * @throws NullPointerException if handler is null + * because the thread bounds and queue capacities are reached + * @throws IllegalArgumentException if {@code corePoolSize < 0} + * @throws NullPointerException if {@code handler} is null */ public ScheduledThreadPoolExecutor(int corePoolSize, RejectedExecutionHandler handler) { @@ -264,14 +366,16 @@ public class ScheduledThreadPoolExecutor /** * Creates a new ScheduledThreadPoolExecutor with the given * initial parameters. - * - * @param corePoolSize the number of threads to keep in the pool, - * even if they are idle. + * + * @param corePoolSize the number of threads to keep in the pool, even + * if they are idle * @param threadFactory the factory to use when the executor - * creates a new thread. + * creates a new thread * @param handler the handler to use when execution is blocked - * because the thread bounds and queue capacities are reached. - * @throws NullPointerException if threadFactory or handler is null + * because the thread bounds and queue capacities are reached + * @throws IllegalArgumentException if {@code corePoolSize < 0} + * @throws NullPointerException if {@code threadFactory} or + * {@code handler} is null */ public ScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory, @@ -280,128 +384,178 @@ public class ScheduledThreadPoolExecutor new DelayedWorkQueue(), threadFactory, handler); } - public ScheduledFuture schedule(Runnable command, - long delay, + /** + * Returns the trigger time of a delayed action + */ + private static long nextTriggerTime(long delay, TimeUnit unit) { + long triggerTime; + long now = now(); + if (delay <= 0) + return now; // avoid negative trigger times + else if ((triggerTime = now + unit.toNanos(delay)) < 0) + return Long.MAX_VALUE; // avoid numerical overflow + else + return triggerTime; + } + + /** + * @throws RejectedExecutionException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + */ + public ScheduledFuture schedule(Runnable command, + long delay, TimeUnit unit) { if (command == null || unit == null) throw new NullPointerException(); - long triggerTime = now() + unit.toNanos(delay); - ScheduledFutureTask t = - new ScheduledFutureTask(command, null, triggerTime); + long triggerTime = nextTriggerTime(delay, unit); + ScheduledFutureTask t + = new ScheduledFutureTask(command, null, triggerTime); delayedExecute(t); return t; } - public ScheduledFuture schedule(Callable callable, - long delay, + /** + * @throws RejectedExecutionException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + */ + public ScheduledFuture schedule(Callable callable, + long delay, TimeUnit unit) { if (callable == null || unit == null) throw new NullPointerException(); - if (delay < 0) delay = 0; - long triggerTime = now() + unit.toNanos(delay); - ScheduledFutureTask t = - new ScheduledFutureTask(callable, triggerTime); + long triggerTime = nextTriggerTime(delay, unit); + ScheduledFutureTask t + = new ScheduledFutureTask(callable, triggerTime); delayedExecute(t); return t; } - public ScheduledFuture scheduleAtFixedRate(Runnable command, - long initialDelay, - long period, + /** + * @throws RejectedExecutionException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + */ + public ScheduledFuture scheduleAtFixedRate(Runnable command, + long initialDelay, + long period, TimeUnit unit) { if (command == null || unit == null) throw new NullPointerException(); if (period <= 0) throw new IllegalArgumentException(); if (initialDelay < 0) initialDelay = 0; - long triggerTime = now() + unit.toNanos(initialDelay); - ScheduledFutureTask t = - new ScheduledFutureTask(command, - null, - triggerTime, - unit.toNanos(period)); - delayedExecute(t); - return t; + long triggerTime = nextTriggerTime(initialDelay, unit); + ScheduledFutureTask sft = + new ScheduledFutureTask(command, + null, + triggerTime, + unit.toNanos(period)); + sft.outerTask = sft; + delayedExecute(sft); + return sft; } - - public ScheduledFuture scheduleWithFixedDelay(Runnable command, - long initialDelay, - long delay, + + /** + * @throws RejectedExecutionException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + */ + public ScheduledFuture scheduleWithFixedDelay(Runnable command, + long initialDelay, + long delay, TimeUnit unit) { if (command == null || unit == null) throw new NullPointerException(); if (delay <= 0) throw new IllegalArgumentException(); - if (initialDelay < 0) initialDelay = 0; - long triggerTime = now() + unit.toNanos(initialDelay); - ScheduledFutureTask t = - new ScheduledFutureTask(command, - null, - triggerTime, - unit.toNanos(-delay)); - delayedExecute(t); - return t; + long triggerTime = nextTriggerTime(initialDelay, unit); + ScheduledFutureTask sft = + new ScheduledFutureTask(command, + null, + triggerTime, + unit.toNanos(-delay)); + sft.outerTask = sft; + delayedExecute(sft); + return sft; } - /** - * Execute command with zero required delay. This has effect - * equivalent to schedule(command, 0, anyUnit). Note - * that inspections of the queue and of the list returned by - * shutdownNow will access the zero-delayed - * {@link ScheduledFuture}, not the command itself. + * Executes {@code command} with zero required delay. + * This has effect equivalent to + * {@link #schedule(Runnable,long,TimeUnit) schedule(command, 0, anyUnit)}. + * Note that inspections of the queue and of the list returned by + * {@code shutdownNow} will access the zero-delayed + * {@link ScheduledFuture}, not the {@code command} itself. + * + *

      A consequence of the use of {@code ScheduledFuture} objects is + * that {@link ThreadPoolExecutor#afterExecute afterExecute} is always + * called with a null second {@code Throwable} argument, even if the + * {@code command} terminated abruptly. Instead, the {@code Throwable} + * thrown by such a task can be obtained via {@link Future#get}. * - * @param command the task to execute * @throws RejectedExecutionException at discretion of - * RejectedExecutionHandler, if task cannot be accepted - * for execution because the executor has been shut down. - * @throws NullPointerException if command is null + * {@code RejectedExecutionHandler}, if the task + * cannot be accepted for execution because the + * executor has been shut down + * @throws NullPointerException {@inheritDoc} */ public void execute(Runnable command) { - if (command == null) - throw new NullPointerException(); schedule(command, 0, TimeUnit.NANOSECONDS); } // Override AbstractExecutorService methods + /** + * @throws RejectedExecutionException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + */ public Future submit(Runnable task) { return schedule(task, 0, TimeUnit.NANOSECONDS); } + /** + * @throws RejectedExecutionException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + */ public Future submit(Runnable task, T result) { - return schedule(Executors.callable(task, result), + return schedule(Executors.callable(task, result), 0, TimeUnit.NANOSECONDS); } + /** + * @throws RejectedExecutionException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + */ public Future submit(Callable task) { return schedule(task, 0, TimeUnit.NANOSECONDS); } /** - * Set policy on whether to continue executing existing periodic - * tasks even when this executor has been shutdown. In - * this case, these tasks will only terminate upon - * shutdownNow, or after setting the policy to - * false when already shutdown. This value is by default - * false. - * @param value if true, continue after shutdown, else don't. - * @see #getExecuteExistingDelayedTasksAfterShutdownPolicy + * Sets the policy on whether to continue executing existing + * periodic tasks even when this executor has been {@code shutdown}. + * In this case, these tasks will only terminate upon + * {@code shutdownNow} or after setting the policy to + * {@code false} when already shutdown. + * This value is by default {@code false}. + * + * @param value if {@code true}, continue after shutdown, else don't. + * @see #getContinueExistingPeriodicTasksAfterShutdownPolicy */ public void setContinueExistingPeriodicTasksAfterShutdownPolicy(boolean value) { continueExistingPeriodicTasksAfterShutdown = value; if (!value && isShutdown()) - cancelUnwantedTasks(); + onShutdown(); } /** - * Get the policy on whether to continue executing existing - * periodic tasks even when this executor has been - * shutdown. In this case, these tasks will only - * terminate upon shutdownNow or after setting the policy - * to false when already shutdown. This value is by - * default false. - * @return true if will continue after shutdown. + * Gets the policy on whether to continue executing existing + * periodic tasks even when this executor has been {@code shutdown}. + * In this case, these tasks will only terminate upon + * {@code shutdownNow} or after setting the policy to + * {@code false} when already shutdown. + * This value is by default {@code false}. + * + * @return {@code true} if will continue after shutdown * @see #setContinueExistingPeriodicTasksAfterShutdownPolicy */ public boolean getContinueExistingPeriodicTasksAfterShutdownPolicy() { @@ -409,66 +563,79 @@ public class ScheduledThreadPoolExecutor } /** - * Set policy on whether to execute existing delayed - * tasks even when this executor has been shutdown. In - * this case, these tasks will only terminate upon - * shutdownNow, or after setting the policy to - * false when already shutdown. This value is by default - * true. - * @param value if true, execute after shutdown, else don't. + * Sets the policy on whether to execute existing delayed + * tasks even when this executor has been {@code shutdown}. + * In this case, these tasks will only terminate upon + * {@code shutdownNow}, or after setting the policy to + * {@code false} when already shutdown. + * This value is by default {@code true}. + * + * @param value if {@code true}, execute after shutdown, else don't. * @see #getExecuteExistingDelayedTasksAfterShutdownPolicy */ public void setExecuteExistingDelayedTasksAfterShutdownPolicy(boolean value) { executeExistingDelayedTasksAfterShutdown = value; if (!value && isShutdown()) - cancelUnwantedTasks(); + onShutdown(); } /** - * Get policy on whether to execute existing delayed - * tasks even when this executor has been shutdown. In - * this case, these tasks will only terminate upon - * shutdownNow, or after setting the policy to - * false when already shutdown. This value is by default - * true. - * @return true if will execute after shutdown. + * Gets the policy on whether to execute existing delayed + * tasks even when this executor has been {@code shutdown}. + * In this case, these tasks will only terminate upon + * {@code shutdownNow}, or after setting the policy to + * {@code false} when already shutdown. + * This value is by default {@code true}. + * + * @return {@code true} if will execute after shutdown * @see #setExecuteExistingDelayedTasksAfterShutdownPolicy */ public boolean getExecuteExistingDelayedTasksAfterShutdownPolicy() { return executeExistingDelayedTasksAfterShutdown; } - /** * Initiates an orderly shutdown in which previously submitted - * tasks are executed, but no new tasks will be accepted. If the - * ExecuteExistingDelayedTasksAfterShutdownPolicy has - * been set false, existing delayed tasks whose delays - * have not yet elapsed are cancelled. And unless the - * ContinueExistingPeriodicTasksAfterShutdownPolicy has - * been set true, future executions of existing periodic - * tasks will be cancelled. + * tasks are executed, but no new tasks will be accepted. + * Invocation has no additional effect if already shut down. + * + *

      This method does not wait for previously submitted tasks to + * complete execution. Use {@link #awaitTermination awaitTermination} + * to do that. + * + *

      If the {@code ExecuteExistingDelayedTasksAfterShutdownPolicy} + * has been set {@code false}, existing delayed tasks whose delays + * have not yet elapsed are cancelled. And unless the {@code + * ContinueExistingPeriodicTasksAfterShutdownPolicy} has been set + * {@code true}, future executions of existing periodic tasks will + * be cancelled. + * + * @throws SecurityException {@inheritDoc} */ public void shutdown() { - cancelUnwantedTasks(); super.shutdown(); } /** * Attempts to stop all actively executing tasks, halts the - * processing of waiting tasks, and returns a list of the tasks that were - * awaiting execution. - * + * processing of waiting tasks, and returns a list of the tasks + * that were awaiting execution. + * + *

      This method does not wait for actively executing tasks to + * terminate. Use {@link #awaitTermination awaitTermination} to + * do that. + * *

      There are no guarantees beyond best-effort attempts to stop * processing actively executing tasks. This implementation - * cancels tasks via {@link Thread#interrupt}, so if any tasks mask or - * fail to respond to interrupts, they may never terminate. + * cancels tasks via {@link Thread#interrupt}, so any task that + * fails to respond to interrupts may never terminate. * - * @return list of tasks that never commenced execution. Each - * element of this list is a {@link ScheduledFuture}, - * including those tasks submitted using execute, which - * are for scheduling purposes used as the basis of a zero-delay - * ScheduledFuture. + * @return list of tasks that never commenced execution. + * Each element of this list is a {@link ScheduledFuture}, + * including those tasks submitted using {@code execute}, + * which are for scheduling purposes used as the basis of a + * zero-delay {@code ScheduledFuture}. + * @throws SecurityException {@inheritDoc} */ public List shutdownNow() { return super.shutdownNow(); @@ -477,9 +644,9 @@ public class ScheduledThreadPoolExecutor /** * Returns the task queue used by this executor. Each element of * this queue is a {@link ScheduledFuture}, including those - * tasks submitted using execute which are for scheduling + * tasks submitted using {@code execute} which are for scheduling * purposes used as the basis of a zero-delay - * ScheduledFuture. Iteration over this queue is + * {@code ScheduledFuture}. Iteration over this queue is * not guaranteed to traverse tasks in the order in * which they will execute. * @@ -490,52 +657,478 @@ public class ScheduledThreadPoolExecutor } /** - * An annoying wrapper class to convince generics compiler to - * use a DelayQueue as a BlockingQueue - */ - private static class DelayedWorkQueue - extends AbstractCollection + * Specialized delay queue. To mesh with TPE declarations, this + * class must be declared as a BlockingQueue even though + * it can only hold RunnableScheduledFutures. + */ + static class DelayedWorkQueue extends AbstractQueue implements BlockingQueue { - - private final DelayQueue dq = new DelayQueue(); - public Runnable poll() { return dq.poll(); } - public Runnable peek() { return dq.peek(); } - public Runnable take() throws InterruptedException { return dq.take(); } - public Runnable poll(long timeout, TimeUnit unit) throws InterruptedException { - return dq.poll(timeout, unit); - } - - public boolean add(Runnable x) { return dq.add((ScheduledFutureTask)x); } - public boolean offer(Runnable x) { return dq.offer((ScheduledFutureTask)x); } - public void put(Runnable x) { - dq.put((ScheduledFutureTask)x); - } - public boolean offer(Runnable x, long timeout, TimeUnit unit) { - return dq.offer((ScheduledFutureTask)x, timeout, unit); - } - - public Runnable remove() { return dq.remove(); } - public Runnable element() { return dq.element(); } - public void clear() { dq.clear(); } - public int drainTo(Collection c) { return dq.drainTo(c); } - public int drainTo(Collection c, int maxElements) { - return dq.drainTo(c, maxElements); - } - - public int remainingCapacity() { return dq.remainingCapacity(); } - public boolean remove(Object x) { return dq.remove(x); } - public boolean contains(Object x) { return dq.contains(x); } - public int size() { return dq.size(); } - public boolean isEmpty() { return dq.isEmpty(); } - public Object[] toArray() { return dq.toArray(); } - public T[] toArray(T[] array) { return dq.toArray(array); } - public Iterator iterator() { - return new Iterator() { - private Iterator it = dq.iterator(); - public boolean hasNext() { return it.hasNext(); } - public Runnable next() { return it.next(); } - public void remove() { it.remove(); } - }; + + /* + * A DelayedWorkQueue is based on a heap-based data structure + * like those in DelayQueue and PriorityQueue, except that + * every ScheduledFutureTask also records its index into the + * heap array. This eliminates the need to find a task upon + * cancellation, greatly speeding up removal (down from O(n) + * to O(log n)), and reducing garbage retention that would + * otherwise occur by waiting for the element to rise to top + * before clearing. But because the queue may also hold + * RunnableScheduledFutures that are not ScheduledFutureTasks, + * we are not guaranteed to have such indices available, in + * which case we fall back to linear search. (We expect that + * most tasks will not be decorated, and that the faster cases + * will be much more common.) + * + * All heap operations must record index changes -- mainly + * within siftUp and siftDown. Upon removal, a task's + * heapIndex is set to -1. Note that ScheduledFutureTasks can + * appear at most once in the queue (this need not be true for + * other kinds of tasks or work queues), so are uniquely + * identified by heapIndex. + */ + + private static final int INITIAL_CAPACITY = 16; + private ScheduledFutureTask[] queue = + new ScheduledFutureTask[INITIAL_CAPACITY]; + private final ReentrantLock lock = new ReentrantLock(); + private int size = 0; + + /** + * Thread designated to wait for the task at the head of the + * queue. This variant of the Leader-Follower pattern + * (http://www.cs.wustl.edu/~schmidt/POSA/POSA2/) serves to + * minimize unnecessary timed waiting. When a thread becomes + * the leader, it waits only for the next delay to elapse, but + * other threads await indefinitely. The leader thread must + * signal some other thread before returning from take() or + * poll(...), unless some other thread becomes leader in the + * interim. Whenever the head of the queue is replaced with a + * task with an earlier expiration time, the leader field is + * invalidated by being reset to null, and some waiting + * thread, but not necessarily the current leader, is + * signalled. So waiting threads must be prepared to acquire + * and lose leadership while waiting. + */ + private Thread leader = null; + + /** + * Condition signalled when a newer task becomes available at the + * head of the queue or a new thread may need to become leader. + */ + private final Condition available = lock.newCondition(); + + /** + * Set f's heapIndex if it is a ScheduledFutureTask. + */ + private void setIndex(ScheduledFutureTask f, int idx) { + if (f instanceof ScheduledFutureTask) + ((ScheduledFutureTask)f).heapIndex = idx; + } + + /** + * Sift element added at bottom up to its heap-ordered spot. + * Call only when holding lock. + */ + private void siftUp(int k, ScheduledFutureTask key) { + while (k > 0) { + int parent = (k - 1) >>> 1; + ScheduledFutureTask e = queue[parent]; + if (key.compareTo(e) >= 0) + break; + queue[k] = e; + setIndex(e, k); + k = parent; + } + queue[k] = key; + setIndex(key, k); + } + + /** + * Sift element added at top down to its heap-ordered spot. + * Call only when holding lock. + */ + private void siftDown(int k, ScheduledFutureTask key) { + int half = size >>> 1; + while (k < half) { + int child = (k << 1) + 1; + ScheduledFutureTask c = queue[child]; + int right = child + 1; + if (right < size && c.compareTo(queue[right]) > 0) + c = queue[child = right]; + if (key.compareTo(c) <= 0) + break; + queue[k] = c; + setIndex(c, k); + k = child; + } + queue[k] = key; + setIndex(key, k); + } + + /** + * Resize the heap array. Call only when holding lock. + */ + private void grow() { + int oldCapacity = queue.length; + int newCapacity = oldCapacity + (oldCapacity >> 1); // grow 50% + if (newCapacity < 0) // overflow + newCapacity = Integer.MAX_VALUE; + queue = Java6Arrays.copyOf(queue, newCapacity); + } + + /** + * Find index of given object, or -1 if absent + */ + private int indexOf(Object x) { + if (x != null) { + if (x instanceof ScheduledFutureTask) { + int i = ((ScheduledFutureTask) x).heapIndex; + // Sanity check; x could conceivably be a + // ScheduledFutureTask from some other pool. + if (i >= 0 && i < size && queue[i] == x) + return i; + } else { + for (int i = 0; i < size; i++) + if (x.equals(queue[i])) + return i; + } + } + return -1; + } + + public boolean contains(Object x) { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + return indexOf(x) != -1; + } finally { + lock.unlock(); + } + } + + public boolean remove(Object x) { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + int i = indexOf(x); + if (i < 0) + return false; + + setIndex(queue[i], -1); + int s = --size; + ScheduledFutureTask replacement = queue[s]; + queue[s] = null; + if (s != i) { + siftDown(i, replacement); + if (queue[i] == replacement) + siftUp(i, replacement); + } + return true; + } finally { + lock.unlock(); + } + } + + public int size() { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + return size; + } finally { + lock.unlock(); + } + } + + public boolean isEmpty() { + return size() == 0; + } + + public int remainingCapacity() { + return Integer.MAX_VALUE; + } + + public ScheduledFutureTask peek() { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + return queue[0]; + } finally { + lock.unlock(); + } + } + + public boolean offer(Runnable x) { + if (x == null) + throw new NullPointerException(); + ScheduledFutureTask e = (ScheduledFutureTask)x; + final ReentrantLock lock = this.lock; + lock.lock(); + try { + int i = size; + if (i >= queue.length) + grow(); + size = i + 1; + if (i == 0) { + queue[0] = e; + setIndex(e, 0); + } else { + siftUp(i, e); + } + if (queue[0] == e) { + leader = null; + available.signal(); + } + } finally { + lock.unlock(); + } + return true; + } + + public void put(Runnable e) { + offer(e); + } + + public boolean add(Runnable e) { + return offer(e); + } + + public boolean offer(Runnable e, long timeout, TimeUnit unit) { + return offer(e); + } + + /** + * Performs common bookkeeping for poll and take: Replaces + * first element with last and sifts it down. Call only when + * holding lock. + * @param f the task to remove and return + */ + private ScheduledFutureTask finishPoll(ScheduledFutureTask f) { + int s = --size; + ScheduledFutureTask x = queue[s]; + queue[s] = null; + if (s != 0) + siftDown(0, x); + setIndex(f, -1); + return f; + } + + public ScheduledFutureTask poll() { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + ScheduledFutureTask first = queue[0]; + if (first == null || first.getDelay(TimeUnit.NANOSECONDS) > 0) + return null; + else + return finishPoll(first); + } finally { + lock.unlock(); + } + } + + public ScheduledFutureTask take() throws InterruptedException { + final ReentrantLock lock = this.lock; + lock.lockInterruptibly(); + try { + for (;;) { + ScheduledFutureTask first = queue[0]; + if (first == null) + available.await(); + else { + long delay = first.getDelay(TimeUnit.NANOSECONDS); + if (delay <= 0) + return finishPoll(first); + else if (leader != null) + available.await(); + else { + Thread thisThread = Thread.currentThread(); + leader = thisThread; + try { + available.awaitNanos(delay); + } finally { + if (leader == thisThread) + leader = null; + } + } + } + } + } finally { + if (leader == null && queue[0] != null) + available.signal(); + lock.unlock(); + } + } + + public ScheduledFutureTask poll(long timeout, TimeUnit unit) + throws InterruptedException { + long nanos = unit.toNanos(timeout); + final ReentrantLock lock = this.lock; + lock.lockInterruptibly(); + try { + for (;;) { + ScheduledFutureTask first = queue[0]; + if (first == null) { + if (nanos <= 0) + return null; + else + nanos = available.awaitNanos(nanos); + } else { + long delay = first.getDelay(TimeUnit.NANOSECONDS); + if (delay <= 0) + return finishPoll(first); + if (nanos <= 0) + return null; + if (nanos < delay || leader != null) + nanos = available.awaitNanos(nanos); + else { + Thread thisThread = Thread.currentThread(); + leader = thisThread; + try { + long timeLeft = available.awaitNanos(delay); + nanos -= delay - timeLeft; + } finally { + if (leader == thisThread) + leader = null; + } + } + } + } + } finally { + if (leader == null && queue[0] != null) + available.signal(); + lock.unlock(); + } + } + + public void clear() { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + for (int i = 0; i < size; i++) { + ScheduledFutureTask t = queue[i]; + if (t != null) { + queue[i] = null; + setIndex(t, -1); + } + } + size = 0; + } finally { + lock.unlock(); + } + } + + /** + * Return and remove first element only if it is expired. + * Used only by drainTo. Call only when holding lock. + */ + private ScheduledFutureTask pollExpired() { + ScheduledFutureTask first = queue[0]; + if (first == null || first.getDelay(TimeUnit.NANOSECONDS) > 0) + return null; + return finishPoll(first); + } + + public int drainTo(Collection c) { + if (c == null) + throw new NullPointerException(); + if (c == this) + throw new IllegalArgumentException(); + final ReentrantLock lock = this.lock; + lock.lock(); + try { + ScheduledFutureTask first; + int n = 0; + while ((first = pollExpired()) != null) { + c.add(first); + ++n; + } + return n; + } finally { + lock.unlock(); + } + } + + public int drainTo(Collection c, int maxElements) { + if (c == null) + throw new NullPointerException(); + if (c == this) + throw new IllegalArgumentException(); + if (maxElements <= 0) + return 0; + final ReentrantLock lock = this.lock; + lock.lock(); + try { + ScheduledFutureTask first; + int n = 0; + while (n < maxElements && (first = pollExpired()) != null) { + c.add(first); + ++n; + } + return n; + } finally { + lock.unlock(); + } + } + + public Object[] toArray() { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + return Java6Arrays.copyOf(queue, size, Object[].class); + } finally { + lock.unlock(); + } + } + + @SuppressWarnings("unchecked") + public T[] toArray(T[] a) { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + if (a.length < size) + return (T[]) Java6Arrays.copyOf(queue, size, a.getClass()); + System.arraycopy(queue, 0, a, 0, size); + if (a.length > size) + a[size] = null; + return a; + } finally { + lock.unlock(); + } + } + + public Iterator iterator() { + return new Itr(Java6Arrays.copyOf(queue, size)); + } + + /** + * Snapshot iterator that works off copy of underlying q array. + */ + private class Itr implements Iterator { + final ScheduledFutureTask[] array; + int cursor = 0; // index of next element to return + int lastRet = -1; // index of last element, or -1 if no such + + Itr(ScheduledFutureTask[] array) { + this.array = array; + } + + public boolean hasNext() { + return cursor < array.length; + } + + public Runnable next() { + if (cursor >= array.length) + throw new NoSuchElementException(); + lastRet = cursor; + return array[cursor++]; + } + + public void remove() { + if (lastRet < 0) + throw new IllegalStateException(); + DelayedWorkQueue.this.remove(array[lastRet]); + lastRet = -1; + } } } } diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/Semaphore.java b/libcore/concurrent/src/main/java/java/util/concurrent/Semaphore.java index 6eb80bcb5..105236439 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/Semaphore.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/Semaphore.java @@ -14,7 +14,7 @@ import java.util.concurrent.atomic.*; * permits. Each {@link #acquire} blocks if necessary until a permit is * available, and then takes it. Each {@link #release} adds a permit, * potentially releasing a blocking acquirer. - * However, no actual permit objects are used; the Semaphore just + * However, no actual permit objects are used; the {@code Semaphore} just * keeps a count of the number available and acts accordingly. * *

      Semaphores are often used to restrict the number of threads than can @@ -22,7 +22,7 @@ import java.util.concurrent.atomic.*; * a class that uses a semaphore to control access to a pool of items: *

        * class Pool {
      - *   private static final MAX_AVAILABLE = 100;
      + *   private static final int MAX_AVAILABLE = 100;
        *   private final Semaphore available = new Semaphore(MAX_AVAILABLE, true);
        *
        *   public Object getItem() throws InterruptedException {
      @@ -93,15 +93,19 @@ import java.util.concurrent.atomic.*;
        * guarantees about the order in which threads acquire permits. In
        * particular, barging is permitted, that is, a thread
        * invoking {@link #acquire} can be allocated a permit ahead of a
      - * thread that has been waiting.  When fairness is set true, the
      + * thread that has been waiting - logically the new thread places itself at
      + * the head of the queue of waiting threads. When fairness is set true, the
        * semaphore guarantees that threads invoking any of the {@link
      - * #acquire() acquire} methods are allocated permits in the order in
      + * #acquire() acquire} methods are selected to obtain permits in the order in
        * which their invocation of those methods was processed
        * (first-in-first-out; FIFO). Note that FIFO ordering necessarily
        * applies to specific internal points of execution within these
        * methods.  So, it is possible for one thread to invoke
      - * acquire before another, but reach the ordering point after
      + * {@code acquire} before another, but reach the ordering point after
        * the other, and similarly upon return from the method.
      + * Also note that the untimed {@link #tryAcquire() tryAcquire} methods do not
      + * honor the fairness setting, but will take any permits that are
      + * available.
        *
        * 

      Generally, semaphores used to control resource access should be * initialized as fair, to ensure that no thread is starved out from @@ -114,6 +118,12 @@ import java.util.concurrent.atomic.*; * permits at a time. Beware of the increased risk of indefinite * postponement when these methods are used without fairness set true. * + *

      Memory consistency effects: Actions in a thread prior to calling + * a "release" method such as {@code release()} + * happen-before + * actions following a successful "acquire" method such as {@code acquire()} + * in another thread. + * * @since 1.5 * @author Doug Lea * @@ -130,10 +140,12 @@ public class Semaphore implements java.io.Serializable { * versions. */ abstract static class Sync extends AbstractQueuedSynchronizer { + private static final long serialVersionUID = 1192457210091910933L; + Sync(int permits) { setState(permits); } - + final int getPermits() { return getState(); } @@ -147,11 +159,11 @@ public class Semaphore implements java.io.Serializable { return remaining; } } - + protected final boolean tryReleaseShared(int releases) { for (;;) { int p = getState(); - if (compareAndSetState(p, p + releases)) + if (compareAndSetState(p, p + releases)) return true; } } @@ -178,10 +190,12 @@ public class Semaphore implements java.io.Serializable { * NonFair version */ final static class NonfairSync extends Sync { + private static final long serialVersionUID = -2694183684443567898L; + NonfairSync(int permits) { super(permits); } - + protected int tryAcquireShared(int acquires) { return nonfairTryAcquireShared(acquires); } @@ -191,10 +205,12 @@ public class Semaphore implements java.io.Serializable { * Fair version */ final static class FairSync extends Sync { + private static final long serialVersionUID = 2014338818796000944L; + FairSync(int permits) { super(permits); } - + protected int tryAcquireShared(int acquires) { Thread current = Thread.currentThread(); for (;;) { @@ -211,57 +227,59 @@ public class Semaphore implements java.io.Serializable { } /** - * Creates a Semaphore with the given number of + * Creates a {@code Semaphore} with the given number of * permits and nonfair fairness setting. - * @param permits the initial number of permits available. This - * value may be negative, in which case releases must - * occur before any acquires will be granted. + * + * @param permits the initial number of permits available. + * This value may be negative, in which case releases + * must occur before any acquires will be granted. */ - public Semaphore(int permits) { + public Semaphore(int permits) { sync = new NonfairSync(permits); } /** - * Creates a Semaphore with the given number of + * Creates a {@code Semaphore} with the given number of * permits and the given fairness setting. - * @param permits the initial number of permits available. This - * value may be negative, in which case releases must - * occur before any acquires will be granted. - * @param fair true if this semaphore will guarantee first-in - * first-out granting of permits under contention, else false. + * + * @param permits the initial number of permits available. + * This value may be negative, in which case releases + * must occur before any acquires will be granted. + * @param fair {@code true} if this semaphore will guarantee + * first-in first-out granting of permits under contention, + * else {@code false} */ - public Semaphore(int permits, boolean fair) { + public Semaphore(int permits, boolean fair) { sync = (fair)? new FairSync(permits) : new NonfairSync(permits); } /** * Acquires a permit from this semaphore, blocking until one is - * available, or the thread is {@link Thread#interrupt interrupted}. + * available, or the thread is {@linkplain Thread#interrupt interrupted}. * *

      Acquires a permit, if one is available and returns immediately, * reducing the number of available permits by one. + * *

      If no permit is available then the current thread becomes * disabled for thread scheduling purposes and lies dormant until * one of two things happens: *

        *
      • Some other thread invokes the {@link #release} method for this * semaphore and the current thread is next to be assigned a permit; or - *
      • Some other thread {@link Thread#interrupt interrupts} the current - * thread. + *
      • Some other thread {@linkplain Thread#interrupt interrupts} + * the current thread. *
      * *

      If the current thread: *

        *
      • has its interrupted status set on entry to this method; or - *
      • is {@link Thread#interrupt interrupted} while waiting + *
      • is {@linkplain Thread#interrupt interrupted} while waiting * for a permit, *
      * then {@link InterruptedException} is thrown and the current thread's * interrupted status is cleared. * * @throws InterruptedException if the current thread is interrupted - * - * @see Thread#interrupt */ public void acquire() throws InterruptedException { sync.acquireSharedInterruptibly(1); @@ -273,103 +291,105 @@ public class Semaphore implements java.io.Serializable { * *

      Acquires a permit, if one is available and returns immediately, * reducing the number of available permits by one. + * *

      If no permit is available then the current thread becomes * disabled for thread scheduling purposes and lies dormant until * some other thread invokes the {@link #release} method for this * semaphore and the current thread is next to be assigned a permit. * - *

      If the current thread - * is {@link Thread#interrupt interrupted} while waiting - * for a permit then it will continue to wait, but the time at which - * the thread is assigned a permit may change compared to the time it - * would have received the permit had no interruption occurred. When the - * thread does return from this method its interrupt status will be set. - * + *

      If the current thread is {@linkplain Thread#interrupt interrupted} + * while waiting for a permit then it will continue to wait, but the + * time at which the thread is assigned a permit may change compared to + * the time it would have received the permit had no interruption + * occurred. When the thread does return from this method its interrupt + * status will be set. */ public void acquireUninterruptibly() { sync.acquireShared(1); } /** - * Acquires a permit from this semaphore, only if one is available at the + * Acquires a permit from this semaphore, only if one is available at the * time of invocation. + * *

      Acquires a permit, if one is available and returns immediately, - * with the value true, + * with the value {@code true}, * reducing the number of available permits by one. * *

      If no permit is available then this method will return - * immediately with the value false. + * immediately with the value {@code false}. * *

      Even when this semaphore has been set to use a - * fair ordering policy, a call to tryAcquire() will + * fair ordering policy, a call to {@code tryAcquire()} will * immediately acquire a permit if one is available, whether or not - * other threads are currently waiting. - * This "barging" behavior can be useful in certain + * other threads are currently waiting. + * This "barging" behavior can be useful in certain * circumstances, even though it breaks fairness. If you want to honor - * the fairness setting, then use + * the fairness setting, then use * {@link #tryAcquire(long, TimeUnit) tryAcquire(0, TimeUnit.SECONDS) } * which is almost equivalent (it also detects interruption). * - * @return true if a permit was acquired and false - * otherwise. + * @return {@code true} if a permit was acquired and {@code false} + * otherwise */ public boolean tryAcquire() { return sync.nonfairTryAcquireShared(1) >= 0; } /** - * Acquires a permit from this semaphore, if one becomes available - * within the given waiting time and the - * current thread has not been {@link Thread#interrupt interrupted}. + * Acquires a permit from this semaphore, if one becomes available + * within the given waiting time and the current thread has not + * been {@linkplain Thread#interrupt interrupted}. + * *

      Acquires a permit, if one is available and returns immediately, - * with the value true, + * with the value {@code true}, * reducing the number of available permits by one. - *

      If no permit is available then - * the current thread becomes disabled for thread scheduling - * purposes and lies dormant until one of three things happens: + * + *

      If no permit is available then the current thread becomes + * disabled for thread scheduling purposes and lies dormant until + * one of three things happens: *

        *
      • Some other thread invokes the {@link #release} method for this * semaphore and the current thread is next to be assigned a permit; or - *
      • Some other thread {@link Thread#interrupt interrupts} the current - * thread; or + *
      • Some other thread {@linkplain Thread#interrupt interrupts} + * the current thread; or *
      • The specified waiting time elapses. *
      - *

      If a permit is acquired then the value true is returned. + * + *

      If a permit is acquired then the value {@code true} is returned. + * *

      If the current thread: *

        *
      • has its interrupted status set on entry to this method; or - *
      • is {@link Thread#interrupt interrupted} while waiting to acquire - * a permit, + *
      • is {@linkplain Thread#interrupt interrupted} while waiting + * to acquire a permit, *
      * then {@link InterruptedException} is thrown and the current thread's * interrupted status is cleared. - *

      If the specified waiting time elapses then the value false - * is returned. - * If the time is less than or equal to zero, the method will not wait - * at all. * - * @param timeout the maximum time to wait for a permit - * @param unit the time unit of the timeout argument. - * @return true if a permit was acquired and false - * if the waiting time elapsed before a permit was acquired. + *

      If the specified waiting time elapses then the value {@code false} + * is returned. If the time is less than or equal to zero, the method + * will not wait at all. * + * @param timeout the maximum time to wait for a permit + * @param unit the time unit of the {@code timeout} argument + * @return {@code true} if a permit was acquired and {@code false} + * if the waiting time elapsed before a permit was acquired * @throws InterruptedException if the current thread is interrupted - * - * @see Thread#interrupt - * */ - public boolean tryAcquire(long timeout, TimeUnit unit) + public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException { return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout)); } /** * Releases a permit, returning it to the semaphore. - *

      Releases a permit, increasing the number of available permits - * by one. - * If any threads are blocking trying to acquire a permit, then one - * is selected and given the permit that was just released. - * That thread is re-enabled for thread scheduling purposes. + * + *

      Releases a permit, increasing the number of available permits by + * one. If any threads are trying to acquire a permit, then one is + * selected and given the permit that was just released. That thread + * is (re)enabled for thread scheduling purposes. + * *

      There is no requirement that a thread that releases a permit must * have acquired that permit by calling {@link #acquire}. * Correct usage of a semaphore is established by programming convention @@ -378,45 +398,42 @@ public class Semaphore implements java.io.Serializable { public void release() { sync.releaseShared(1); } - + /** - * Acquires the given number of permits from this semaphore, - * blocking until all are available, - * or the thread is {@link Thread#interrupt interrupted}. + * Acquires the given number of permits from this semaphore, + * blocking until all are available, + * or the thread is {@linkplain Thread#interrupt interrupted}. * *

      Acquires the given number of permits, if they are available, - * and returns immediately, - * reducing the number of available permits by the given amount. + * and returns immediately, reducing the number of available permits + * by the given amount. * *

      If insufficient permits are available then the current thread becomes * disabled for thread scheduling purposes and lies dormant until * one of two things happens: *

        - *
      • Some other thread invokes one of the {@link #release() release} + *
      • Some other thread invokes one of the {@link #release() release} * methods for this semaphore, the current thread is next to be assigned * permits and the number of available permits satisfies this request; or - *
      • Some other thread {@link Thread#interrupt interrupts} the current - * thread. + *
      • Some other thread {@linkplain Thread#interrupt interrupts} + * the current thread. *
      * *

      If the current thread: *

        *
      • has its interrupted status set on entry to this method; or - *
      • is {@link Thread#interrupt interrupted} while waiting + *
      • is {@linkplain Thread#interrupt interrupted} while waiting * for a permit, *
      * then {@link InterruptedException} is thrown and the current thread's - * interrupted status is cleared. - * Any permits that were to be assigned to this thread are instead - * assigned to the next waiting thread(s), as if - * they had been made available by a call to {@link #release()}. + * interrupted status is cleared. + * Any permits that were to be assigned to this thread are instead + * assigned to other threads trying to acquire permits, as if + * permits had been made available by a call to {@link #release()}. * * @param permits the number of permits to acquire - * * @throws InterruptedException if the current thread is interrupted - * @throws IllegalArgumentException if permits less than zero. - * - * @see Thread#interrupt + * @throws IllegalArgumentException if {@code permits} is negative */ public void acquire(int permits) throws InterruptedException { if (permits < 0) throw new IllegalArgumentException(); @@ -424,27 +441,26 @@ public class Semaphore implements java.io.Serializable { } /** - * Acquires the given number of permits from this semaphore, + * Acquires the given number of permits from this semaphore, * blocking until all are available. * *

      Acquires the given number of permits, if they are available, - * and returns immediately, - * reducing the number of available permits by the given amount. + * and returns immediately, reducing the number of available permits + * by the given amount. * *

      If insufficient permits are available then the current thread becomes * disabled for thread scheduling purposes and lies dormant until - * some other thread invokes one of the {@link #release() release} + * some other thread invokes one of the {@link #release() release} * methods for this semaphore, the current thread is next to be assigned * permits and the number of available permits satisfies this request. * - *

      If the current thread - * is {@link Thread#interrupt interrupted} while waiting - * for permits then it will continue to wait and its position in the - * queue is not affected. When the - * thread does return from this method its interrupt status will be set. + *

      If the current thread is {@linkplain Thread#interrupt interrupted} + * while waiting for permits then it will continue to wait and its + * position in the queue is not affected. When the thread does return + * from this method its interrupt status will be set. * * @param permits the number of permits to acquire - * @throws IllegalArgumentException if permits less than zero. + * @throws IllegalArgumentException if {@code permits} is negative * */ public void acquireUninterruptibly(int permits) { @@ -456,16 +472,16 @@ public class Semaphore implements java.io.Serializable { * Acquires the given number of permits from this semaphore, only * if all are available at the time of invocation. * - *

      Acquires the given number of permits, if they are available, and - * returns immediately, with the value true, + *

      Acquires the given number of permits, if they are available, and + * returns immediately, with the value {@code true}, * reducing the number of available permits by the given amount. * *

      If insufficient permits are available then this method will return - * immediately with the value false and the number of available + * immediately with the value {@code false} and the number of available * permits is unchanged. * *

      Even when this semaphore has been set to use a fair ordering - * policy, a call to tryAcquire will + * policy, a call to {@code tryAcquire} will * immediately acquire a permit if one is available, whether or * not other threads are currently waiting. This * "barging" behavior can be useful in certain @@ -475,10 +491,9 @@ public class Semaphore implements java.io.Serializable { * which is almost equivalent (it also detects interruption). * * @param permits the number of permits to acquire - * - * @return true if the permits were acquired and false - * otherwise. - * @throws IllegalArgumentException if permits less than zero. + * @return {@code true} if the permits were acquired and + * {@code false} otherwise + * @throws IllegalArgumentException if {@code permits} is negative */ public boolean tryAcquire(int permits) { if (permits < 0) throw new IllegalArgumentException(); @@ -486,55 +501,54 @@ public class Semaphore implements java.io.Serializable { } /** - * Acquires the given number of permits from this semaphore, if all - * become available within the given waiting time and the - * current thread has not been {@link Thread#interrupt interrupted}. - *

      Acquires the given number of permits, if they are available and - * returns immediately, with the value true, + * Acquires the given number of permits from this semaphore, if all + * become available within the given waiting time and the current + * thread has not been {@linkplain Thread#interrupt interrupted}. + * + *

      Acquires the given number of permits, if they are available and + * returns immediately, with the value {@code true}, * reducing the number of available permits by the given amount. + * *

      If insufficient permits are available then * the current thread becomes disabled for thread scheduling * purposes and lies dormant until one of three things happens: *

        - *
      • Some other thread invokes one of the {@link #release() release} + *
      • Some other thread invokes one of the {@link #release() release} * methods for this semaphore, the current thread is next to be assigned * permits and the number of available permits satisfies this request; or - *
      • Some other thread {@link Thread#interrupt interrupts} the current - * thread; or + *
      • Some other thread {@linkplain Thread#interrupt interrupts} + * the current thread; or *
      • The specified waiting time elapses. *
      - *

      If the permits are acquired then the value true is returned. + * + *

      If the permits are acquired then the value {@code true} is returned. + * *

      If the current thread: *

        *
      • has its interrupted status set on entry to this method; or - *
      • is {@link Thread#interrupt interrupted} while waiting to acquire - * the permits, + *
      • is {@linkplain Thread#interrupt interrupted} while waiting + * to acquire the permits, *
      * then {@link InterruptedException} is thrown and the current thread's * interrupted status is cleared. - * Any permits that were to be assigned to this thread, are instead - * assigned to the next waiting thread(s), as if - * they had been made available by a call to {@link #release()}. - * - *

      If the specified waiting time elapses then the value false - * is returned. - * If the time is - * less than or equal to zero, the method will not wait at all. - * Any permits that were to be assigned to this thread, are instead - * assigned to the next waiting thread(s), as if - * they had been made available by a call to {@link #release()}. + * Any permits that were to be assigned to this thread, are instead + * assigned to other threads trying to acquire permits, as if + * the permits had been made available by a call to {@link #release()}. + * + *

      If the specified waiting time elapses then the value {@code false} + * is returned. If the time is less than or equal to zero, the method + * will not wait at all. Any permits that were to be assigned to this + * thread, are instead assigned to other threads trying to acquire + * permits, as if the permits had been made available by a call to + * {@link #release()}. * * @param permits the number of permits to acquire * @param timeout the maximum time to wait for the permits - * @param unit the time unit of the timeout argument. - * @return true if all permits were acquired and false - * if the waiting time elapsed before all permits were acquired. - * + * @param unit the time unit of the {@code timeout} argument + * @return {@code true} if all permits were acquired and {@code false} + * if the waiting time elapsed before all permits were acquired * @throws InterruptedException if the current thread is interrupted - * @throws IllegalArgumentException if permits less than zero. - * - * @see Thread#interrupt - * + * @throws IllegalArgumentException if {@code permits} is negative */ public boolean tryAcquire(int permits, long timeout, TimeUnit unit) throws InterruptedException { @@ -544,19 +558,17 @@ public class Semaphore implements java.io.Serializable { /** * Releases the given number of permits, returning them to the semaphore. - *

      Releases the given number of permits, increasing the number of + * + *

      Releases the given number of permits, increasing the number of * available permits by that amount. - * If any threads are blocking trying to acquire permits, then the - * one that has been waiting the longest + * If any threads are trying to acquire permits, then one * is selected and given the permits that were just released. * If the number of available permits satisfies that thread's request - * then that thread is re-enabled for thread scheduling purposes; otherwise - * the thread continues to wait. If there are still permits available - * after the first thread's request has been satisfied, then those permits - * are assigned to the next waiting thread. If it is satisfied then it is - * re-enabled for thread scheduling purposes. This continues until there - * are insufficient permits to satisfy the next waiting thread, or there - * are no more waiting threads. + * then that thread is (re)enabled for thread scheduling purposes; + * otherwise the thread will wait until sufficient permits are available. + * If there are still permits available + * after this thread's request has been satisfied, then those permits + * are assigned in turn to other threads trying to acquire permits. * *

      There is no requirement that a thread that releases a permit must * have acquired that permit by calling {@link Semaphore#acquire acquire}. @@ -564,7 +576,7 @@ public class Semaphore implements java.io.Serializable { * in the application. * * @param permits the number of permits to release - * @throws IllegalArgumentException if permits less than zero. + * @throws IllegalArgumentException if {@code permits} is negative */ public void release(int permits) { if (permits < 0) throw new IllegalArgumentException(); @@ -573,16 +585,19 @@ public class Semaphore implements java.io.Serializable { /** * Returns the current number of permits available in this semaphore. + * *

      This method is typically used for debugging and testing purposes. - * @return the number of permits available in this semaphore. + * + * @return the number of permits available in this semaphore */ public int availablePermits() { return sync.getPermits(); } /** - * Acquire and return all permits that are immediately available. - * @return the number of permits + * Acquires and returns all permits that are immediately available. + * + * @return the number of permits acquired */ public int drainPermits() { return sync.drainPermits(); @@ -592,19 +607,21 @@ public class Semaphore implements java.io.Serializable { * Shrinks the number of available permits by the indicated * reduction. This method can be useful in subclasses that use * semaphores to track resources that become unavailable. This - * method differs from acquire in that it does not block + * method differs from {@code acquire} in that it does not block * waiting for permits to become available. + * * @param reduction the number of permits to remove - * @throws IllegalArgumentException if reduction is negative + * @throws IllegalArgumentException if {@code reduction} is negative */ protected void reducePermits(int reduction) { - if (reduction < 0) throw new IllegalArgumentException(); + if (reduction < 0) throw new IllegalArgumentException(); sync.reducePermits(reduction); } /** - * Returns true if this semaphore has fairness set true. - * @return true if this semaphore has fairness set true. + * Returns {@code true} if this semaphore has fairness set true. + * + * @return {@code true} if this semaphore has fairness set true */ public boolean isFair() { return sync instanceof FairSync; @@ -612,25 +629,25 @@ public class Semaphore implements java.io.Serializable { /** * Queries whether any threads are waiting to acquire. Note that - * because cancellations may occur at any time, a true + * because cancellations may occur at any time, a {@code true} * return does not guarantee that any other thread will ever * acquire. This method is designed primarily for use in * monitoring of the system state. * - * @return true if there may be other threads waiting to acquire - * the lock. + * @return {@code true} if there may be other threads waiting to + * acquire the lock */ - public final boolean hasQueuedThreads() { + public final boolean hasQueuedThreads() { return sync.hasQueuedThreads(); } /** - * Returns an estimate of the number of threads waiting to - * acquire. The value is only an estimate because the number of - * threads may change dynamically while this method traverses - * internal data structures. This method is designed for use in - * monitoring of the system state, not for synchronization - * control. + * Returns an estimate of the number of threads waiting to acquire. + * The value is only an estimate because the number of threads may + * change dynamically while this method traverses internal data + * structures. This method is designed for use in monitoring of the + * system state, not for synchronization control. + * * @return the estimated number of threads waiting for this lock */ public final int getQueueLength() { @@ -638,13 +655,13 @@ public class Semaphore implements java.io.Serializable { } /** - * Returns a collection containing threads that may be waiting to - * acquire. Because the actual set of threads may change - * dynamically while constructing this result, the returned - * collection is only a best-effort estimate. The elements of the - * returned collection are in no particular order. This method is - * designed to facilitate construction of subclasses that provide - * more extensive monitoring facilities. + * Returns a collection containing threads that may be waiting to acquire. + * Because the actual set of threads may change dynamically while + * constructing this result, the returned collection is only a best-effort + * estimate. The elements of the returned collection are in no particular + * order. This method is designed to facilitate construction of + * subclasses that provide more extensive monitoring facilities. + * * @return the collection of threads */ protected Collection getQueuedThreads() { @@ -653,13 +670,12 @@ public class Semaphore implements java.io.Serializable { /** * Returns a string identifying this semaphore, as well as its state. - * The state, in brackets, includes the String - * "Permits =" followed by the number of permits. - * @return a string identifying this semaphore, as well as its - * state + * The state, in brackets, includes the String {@code "Permits ="} + * followed by the number of permits. + * + * @return a string identifying this semaphore, as well as its state */ public String toString() { return super.toString() + "[Permits = " + sync.getPermits() + "]"; } - } diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/SynchronousQueue.java b/libcore/concurrent/src/main/java/java/util/concurrent/SynchronousQueue.java index b8d35364e..0d297ff47 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/SynchronousQueue.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/SynchronousQueue.java @@ -1,11 +1,13 @@ /* - * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain, as explained at + * Written by Doug Lea, Bill Scherer, and Michael Scott with + * assistance from members of JCP JSR-166 Expert Group and released to + * the public domain, as explained at * http://creativecommons.org/licenses/publicdomain */ package java.util.concurrent; import java.util.concurrent.locks.*; +import java.util.concurrent.atomic.*; import java.util.*; // BEGIN android-note @@ -13,20 +15,21 @@ import java.util.*; // END android-note /** - * A {@linkplain BlockingQueue blocking queue} in which each - * put must wait for a take, and vice versa. A - * synchronous queue does not have any internal capacity, not even a - * capacity of one. You cannot peek at a synchronous queue - * because an element is only present when you try to take it; you - * cannot add an element (using any method) unless another thread is - * trying to remove it; you cannot iterate as there is nothing to - * iterate. The head of the queue is the element that the - * first queued thread is trying to add to the queue; if there are no - * queued threads then no element is being added and the head is - * null. For purposes of other Collection methods - * (for example contains), a SynchronousQueue acts - * as an empty collection. This queue does not permit null - * elements. + * A {@linkplain BlockingQueue blocking queue} in which each insert + * operation must wait for a corresponding remove operation by another + * thread, and vice versa. A synchronous queue does not have any + * internal capacity, not even a capacity of one. You cannot + * peek at a synchronous queue because an element is only + * present when you try to remove it; you cannot insert an element + * (using any method) unless another thread is trying to remove it; + * you cannot iterate as there is nothing to iterate. The + * head of the queue is the element that the first queued + * inserting thread is trying to add to the queue; if there is no such + * queued thread then no element is available for removal and + * poll() will return null. For purposes of other + * Collection methods (for example contains), a + * SynchronousQueue acts as an empty collection. This queue + * does not permit null elements. * *

      Synchronous queues are similar to rendezvous channels used in * CSP and Ada. They are well suited for handoff designs, in which an @@ -37,449 +40,841 @@ import java.util.*; *

      This class supports an optional fairness policy for ordering * waiting producer and consumer threads. By default, this ordering * is not guaranteed. However, a queue constructed with fairness set - * to true grants threads access in FIFO order. Fairness - * generally decreases throughput but reduces variability and avoids - * starvation. + * to true grants threads access in FIFO order. * - *

      This class implements all of the optional methods - * of the {@link Collection} and {@link Iterator} interfaces. + *

      This class and its iterator implement all of the + * optional methods of the {@link Collection} and {@link + * Iterator} interfaces. * * @since 1.5 - * @author Doug Lea + * @author Doug Lea and Bill Scherer and Michael Scott * @param the type of elements held in this collection */ public class SynchronousQueue extends AbstractQueue - implements BlockingQueue, java.io.Serializable { + implements BlockingQueue, java.io.Serializable { private static final long serialVersionUID = -3223113410248163686L; /* - This implementation divides actions into two cases for puts: - - * An arriving producer that does not already have a waiting consumer - creates a node holding item, and then waits for a consumer to take it. - * An arriving producer that does already have a waiting consumer fills - the slot node created by the consumer, and notifies it to continue. - - And symmetrically, two for takes: - - * An arriving consumer that does not already have a waiting producer - creates an empty slot node, and then waits for a producer to fill it. - * An arriving consumer that does already have a waiting producer takes - item from the node created by the producer, and notifies it to continue. - - When a put or take waiting for the actions of its counterpart - aborts due to interruption or timeout, it marks the node - it created as "CANCELLED", which causes its counterpart to retry - the entire put or take sequence. - - This requires keeping two simple queues, waitingProducers and - waitingConsumers. Each of these can be FIFO (preserves fairness) - or LIFO (improves throughput). - */ - - /** Lock protecting both wait queues */ - private final ReentrantLock qlock; - /** Queue holding waiting puts */ - private final WaitQueue waitingProducers; - /** Queue holding waiting takes */ - private final WaitQueue waitingConsumers; + * This class implements extensions of the dual stack and dual + * queue algorithms described in "Nonblocking Concurrent Objects + * with Condition Synchronization", by W. N. Scherer III and + * M. L. Scott. 18th Annual Conf. on Distributed Computing, + * Oct. 2004 (see also + * http://www.cs.rochester.edu/u/scott/synchronization/pseudocode/duals.html). + * The (Lifo) stack is used for non-fair mode, and the (Fifo) + * queue for fair mode. The performance of the two is generally + * similar. Fifo usually supports higher throughput under + * contention but Lifo maintains higher thread locality in common + * applications. + * + * A dual queue (and similarly stack) is one that at any given + * time either holds "data" -- items provided by put operations, + * or "requests" -- slots representing take operations, or is + * empty. A call to "fulfill" (i.e., a call requesting an item + * from a queue holding data or vice versa) dequeues a + * complementary node. The most interesting feature of these + * queues is that any operation can figure out which mode the + * queue is in, and act accordingly without needing locks. + * + * Both the queue and stack extend abstract class Transferer + * defining the single method transfer that does a put or a + * take. These are unified into a single method because in dual + * data structures, the put and take operations are symmetrical, + * so nearly all code can be combined. The resulting transfer + * methods are on the long side, but are easier to follow than + * they would be if broken up into nearly-duplicated parts. + * + * The queue and stack data structures share many conceptual + * similarities but very few concrete details. For simplicity, + * they are kept distinct so that they can later evolve + * separately. + * + * The algorithms here differ from the versions in the above paper + * in extending them for use in synchronous queues, as well as + * dealing with cancellation. The main differences include: + * + * 1. The original algorithms used bit-marked pointers, but + * the ones here use mode bits in nodes, leading to a number + * of further adaptations. + * 2. SynchronousQueues must block threads waiting to become + * fulfilled. + * 3. Support for cancellation via timeout and interrupts, + * including cleaning out cancelled nodes/threads + * from lists to avoid garbage retention and memory depletion. + * + * Blocking is mainly accomplished using LockSupport park/unpark, + * except that nodes that appear to be the next ones to become + * fulfilled first spin a bit (on multiprocessors only). On very + * busy synchronous queues, spinning can dramatically improve + * throughput. And on less busy ones, the amount of spinning is + * small enough not to be noticeable. + * + * Cleaning is done in different ways in queues vs stacks. For + * queues, we can almost always remove a node immediately in O(1) + * time (modulo retries for consistency checks) when it is + * cancelled. But if it may be pinned as the current tail, it must + * wait until some subsequent cancellation. For stacks, we need a + * potentially O(n) traversal to be sure that we can remove the + * node, but this can run concurrently with other threads + * accessing the stack. + * + * While garbage collection takes care of most node reclamation + * issues that otherwise complicate nonblocking algorithms, care + * is taken to "forget" references to data, other nodes, and + * threads that might be held on to long-term by blocked + * threads. In cases where setting to null would otherwise + * conflict with main algorithms, this is done by changing a + * node's link to now point to the node itself. This doesn't arise + * much for Stack nodes (because blocked threads do not hang on to + * old head pointers), but references in Queue nodes must be + * aggressively forgotten to avoid reachability of everything any + * node has ever referred to since arrival. + */ /** - * Creates a SynchronousQueue with nonfair access policy. + * Shared internal API for dual stacks and queues. */ - public SynchronousQueue() { - this(false); + static abstract class Transferer { + /** + * Performs a put or take. + * + * @param e if non-null, the item to be handed to a consumer; + * if null, requests that transfer return an item + * offered by producer. + * @param timed if this operation should timeout + * @param nanos the timeout, in nanoseconds + * @return if non-null, the item provided or received; if null, + * the operation failed due to timeout or interrupt -- + * the caller can distinguish which of these occurred + * by checking Thread.interrupted. + */ + abstract Object transfer(Object e, boolean timed, long nanos); } + /** The number of CPUs, for spin control */ + static final int NCPUS = Runtime.getRuntime().availableProcessors(); + /** - * Creates a SynchronousQueue with specified fairness policy. - * @param fair if true, threads contend in FIFO order for access; - * otherwise the order is unspecified. + * The number of times to spin before blocking in timed waits. + * The value is empirically derived -- it works well across a + * variety of processors and OSes. Empirically, the best value + * seems not to vary with number of CPUs (beyond 2) so is just + * a constant. */ - public SynchronousQueue(boolean fair) { - if (fair) { - qlock = new ReentrantLock(true); - waitingProducers = new FifoWaitQueue(); - waitingConsumers = new FifoWaitQueue(); - } - else { - qlock = new ReentrantLock(); - waitingProducers = new LifoWaitQueue(); - waitingConsumers = new LifoWaitQueue(); - } - } + static final int maxTimedSpins = (NCPUS < 2)? 0 : 32; /** - * Queue to hold waiting puts/takes; specialized to Fifo/Lifo below. - * These queues have all transient fields, but are serializable - * in order to recover fairness settings when deserialized. + * The number of times to spin before blocking in untimed waits. + * This is greater than timed value because untimed waits spin + * faster since they don't need to check times on each spin. */ - static abstract class WaitQueue implements java.io.Serializable { - /** Create, add, and return node for x */ - abstract Node enq(Object x); - /** Remove and return node, or null if empty */ - abstract Node deq(); - } + static final int maxUntimedSpins = maxTimedSpins * 16; /** - * FIFO queue to hold waiting puts/takes. + * The number of nanoseconds for which it is faster to spin + * rather than to use timed park. A rough estimate suffices. */ - static final class FifoWaitQueue extends WaitQueue implements java.io.Serializable { - private static final long serialVersionUID = -3623113410248163686L; - private transient Node head; - private transient Node last; - - Node enq(Object x) { - Node p = new Node(x); - if (last == null) - last = head = p; - else - last = last.next = p; - return p; - } + static final long spinForTimeoutThreshold = 1000L; + + /** Dual stack */ + static final class TransferStack extends Transferer { + /* + * This extends Scherer-Scott dual stack algorithm, differing, + * among other ways, by using "covering" nodes rather than + * bit-marked pointers: Fulfilling operations push on marker + * nodes (with FULFILLING bit set in mode) to reserve a spot + * to match a waiting node. + */ - Node deq() { - Node p = head; - if (p != null) { - if ((head = p.next) == null) - last = null; - p.next = null; + /* Modes for SNodes, ORed together in node fields */ + /** Node represents an unfulfilled consumer */ + static final int REQUEST = 0; + /** Node represents an unfulfilled producer */ + static final int DATA = 1; + /** Node is fulfilling another unfulfilled DATA or REQUEST */ + static final int FULFILLING = 2; + + /** Return true if m has fulfilling bit set */ + static boolean isFulfilling(int m) { return (m & FULFILLING) != 0; } + + /** Node class for TransferStacks. */ + static final class SNode { + volatile SNode next; // next node in stack + volatile SNode match; // the node matched to this + volatile Thread waiter; // to control park/unpark + Object item; // data; or null for REQUESTs + int mode; + // Note: item and mode fields don't need to be volatile + // since they are always written before, and read after, + // other volatile/atomic operations. + + SNode(Object item) { + this.item = item; } - return p; - } - } - /** - * LIFO queue to hold waiting puts/takes. - */ - static final class LifoWaitQueue extends WaitQueue implements java.io.Serializable { - private static final long serialVersionUID = -3633113410248163686L; - private transient Node head; + static final AtomicReferenceFieldUpdater + nextUpdater = AtomicReferenceFieldUpdater.newUpdater + (SNode.class, SNode.class, "next"); - Node enq(Object x) { - return head = new Node(x, head); - } + boolean casNext(SNode cmp, SNode val) { + return (cmp == next && + nextUpdater.compareAndSet(this, cmp, val)); + } + + static final AtomicReferenceFieldUpdater + matchUpdater = AtomicReferenceFieldUpdater.newUpdater + (SNode.class, SNode.class, "match"); + + /** + * Tries to match node s to this node, if so, waking up thread. + * Fulfillers call tryMatch to identify their waiters. + * Waiters block until they have been matched. + * + * @param s the node to match + * @return true if successfully matched to s + */ + boolean tryMatch(SNode s) { + if (match == null && + matchUpdater.compareAndSet(this, null, s)) { + Thread w = waiter; + if (w != null) { // waiters need at most one unpark + waiter = null; + LockSupport.unpark(w); + } + return true; + } + return match == s; + } + + /** + * Tries to cancel a wait by matching node to itself. + */ + void tryCancel() { + matchUpdater.compareAndSet(this, null, this); + } - Node deq() { - Node p = head; - if (p != null) { - head = p.next; - p.next = null; + boolean isCancelled() { + return match == this; } - return p; } - } - /** - * Nodes each maintain an item and handle waits and signals for - * getting and setting it. The class extends - * AbstractQueuedSynchronizer to manage blocking, using AQS state - * 0 for waiting, 1 for ack, -1 for cancelled. - */ - static final class Node extends AbstractQueuedSynchronizer { - /** Synchronization state value representing that node acked */ - private static final int ACK = 1; - /** Synchronization state value representing that node cancelled */ - private static final int CANCEL = -1; + /** The head (top) of the stack */ + volatile SNode head; - /** The item being transferred */ - Object item; - /** Next node in wait queue */ - Node next; + static final AtomicReferenceFieldUpdater + headUpdater = AtomicReferenceFieldUpdater.newUpdater + (TransferStack.class, SNode.class, "head"); - /** Creates a node with initial item */ - Node(Object x) { item = x; } + boolean casHead(SNode h, SNode nh) { + return h == head && headUpdater.compareAndSet(this, h, nh); + } - /** Creates a node with initial item and next */ - Node(Object x, Node n) { item = x; next = n; } + /** + * Creates or resets fields of a node. Called only from transfer + * where the node to push on stack is lazily created and + * reused when possible to help reduce intervals between reads + * and CASes of head and to avoid surges of garbage when CASes + * to push nodes fail due to contention. + */ + static SNode snode(SNode s, Object e, SNode next, int mode) { + if (s == null) s = new SNode(e); + s.mode = mode; + s.next = next; + return s; + } /** - * Implements AQS base acquire to succeed if not in WAITING state + * Puts or takes an item. */ - protected boolean tryAcquire(int ignore) { - return getState() != 0; + Object transfer(Object e, boolean timed, long nanos) { + /* + * Basic algorithm is to loop trying one of three actions: + * + * 1. If apparently empty or already containing nodes of same + * mode, try to push node on stack and wait for a match, + * returning it, or null if cancelled. + * + * 2. If apparently containing node of complementary mode, + * try to push a fulfilling node on to stack, match + * with corresponding waiting node, pop both from + * stack, and return matched item. The matching or + * unlinking might not actually be necessary because of + * other threads performing action 3: + * + * 3. If top of stack already holds another fulfilling node, + * help it out by doing its match and/or pop + * operations, and then continue. The code for helping + * is essentially the same as for fulfilling, except + * that it doesn't return the item. + */ + + SNode s = null; // constructed/reused as needed + int mode = (e == null)? REQUEST : DATA; + + for (;;) { + SNode h = head; + if (h == null || h.mode == mode) { // empty or same-mode + if (timed && nanos <= 0) { // can't wait + if (h != null && h.isCancelled()) + casHead(h, h.next); // pop cancelled node + else + return null; + } else if (casHead(h, s = snode(s, e, h, mode))) { + SNode m = awaitFulfill(s, timed, nanos); + if (m == s) { // wait was cancelled + clean(s); + return null; + } + if ((h = head) != null && h.next == s) + casHead(h, s.next); // help s's fulfiller + return mode == REQUEST? m.item : s.item; + } + } else if (!isFulfilling(h.mode)) { // try to fulfill + if (h.isCancelled()) // already cancelled + casHead(h, h.next); // pop and retry + else if (casHead(h, s=snode(s, e, h, FULFILLING|mode))) { + for (;;) { // loop until matched or waiters disappear + SNode m = s.next; // m is s's match + if (m == null) { // all waiters are gone + casHead(s, null); // pop fulfill node + s = null; // use new node next time + break; // restart main loop + } + SNode mn = m.next; + if (m.tryMatch(s)) { + casHead(s, mn); // pop both s and m + return (mode == REQUEST)? m.item : s.item; + } else // lost match + s.casNext(m, mn); // help unlink + } + } + } else { // help a fulfiller + SNode m = h.next; // m is h's match + if (m == null) // waiter is gone + casHead(h, null); // pop fulfilling node + else { + SNode mn = m.next; + if (m.tryMatch(h)) // help match + casHead(h, mn); // pop both h and m + else // lost match + h.casNext(m, mn); // help unlink + } + } + } } /** - * Implements AQS base release to signal if state changed + * Spins/blocks until node s is matched by a fulfill operation. + * + * @param s the waiting node + * @param timed true if timed wait + * @param nanos timeout value + * @return matched node, or s if cancelled */ - protected boolean tryRelease(int newState) { - return compareAndSetState(0, newState); + SNode awaitFulfill(SNode s, boolean timed, long nanos) { + /* + * When a node/thread is about to block, it sets its waiter + * field and then rechecks state at least one more time + * before actually parking, thus covering race vs + * fulfiller noticing that waiter is non-null so should be + * woken. + * + * When invoked by nodes that appear at the point of call + * to be at the head of the stack, calls to park are + * preceded by spins to avoid blocking when producers and + * consumers are arriving very close in time. This can + * happen enough to bother only on multiprocessors. + * + * The order of checks for returning out of main loop + * reflects fact that interrupts have precedence over + * normal returns, which have precedence over + * timeouts. (So, on timeout, one last check for match is + * done before giving up.) Except that calls from untimed + * SynchronousQueue.{poll/offer} don't check interrupts + * and don't wait at all, so are trapped in transfer + * method rather than calling awaitFulfill. + */ + long lastTime = (timed)? System.nanoTime() : 0; + Thread w = Thread.currentThread(); + SNode h = head; + int spins = (shouldSpin(s)? + (timed? maxTimedSpins : maxUntimedSpins) : 0); + for (;;) { + if (w.isInterrupted()) + s.tryCancel(); + SNode m = s.match; + if (m != null) + return m; + if (timed) { + long now = System.nanoTime(); + nanos -= now - lastTime; + lastTime = now; + if (nanos <= 0) { + s.tryCancel(); + continue; + } + } + if (spins > 0) + spins = shouldSpin(s)? (spins-1) : 0; + else if (s.waiter == null) + s.waiter = w; // establish waiter so can park next iter + else if (!timed) + LockSupport.park(); + else if (nanos > spinForTimeoutThreshold) + LockSupport.parkNanos(nanos); + } } /** - * Takes item and nulls out field (for sake of GC) + * Returns true if node s is at head or there is an active + * fulfiller. */ - private Object extract() { - Object x = item; - item = null; - return x; + boolean shouldSpin(SNode s) { + SNode h = head; + return (h == s || h == null || isFulfilling(h.mode)); } /** - * Tries to cancel on interrupt; if so rethrowing, - * else setting interrupt state + * Unlinks s from the stack. */ - private void checkCancellationOnInterrupt(InterruptedException ie) - throws InterruptedException { - if (release(CANCEL)) - throw ie; - Thread.currentThread().interrupt(); + void clean(SNode s) { + s.item = null; // forget item + s.waiter = null; // forget thread + + /* + * At worst we may need to traverse entire stack to unlink + * s. If there are multiple concurrent calls to clean, we + * might not see s if another thread has already removed + * it. But we can stop when we see any node known to + * follow s. We use s.next unless it too is cancelled, in + * which case we try the node one past. We don't check any + * further because we don't want to doubly traverse just to + * find sentinel. + */ + + SNode past = s.next; + if (past != null && past.isCancelled()) + past = past.next; + + // Absorb cancelled nodes at head + SNode p; + while ((p = head) != null && p != past && p.isCancelled()) + casHead(p, p.next); + + // Unsplice embedded nodes + while (p != null && p != past) { + SNode n = p.next; + if (n != null && n.isCancelled()) + p.casNext(n, n.next); + else + p = n; + } } + } + + /** Dual Queue */ + static final class TransferQueue extends Transferer { + /* + * This extends Scherer-Scott dual queue algorithm, differing, + * among other ways, by using modes within nodes rather than + * marked pointers. The algorithm is a little simpler than + * that for stacks because fulfillers do not need explicit + * nodes, and matching is done by CAS'ing QNode.item field + * from non-null to null (for put) or vice versa (for take). + */ + + /** Node class for TransferQueue. */ + static final class QNode { + volatile QNode next; // next node in queue + volatile Object item; // CAS'ed to or from null + volatile Thread waiter; // to control park/unpark + final boolean isData; + + QNode(Object item, boolean isData) { + this.item = item; + this.isData = isData; + } + + static final AtomicReferenceFieldUpdater + nextUpdater = AtomicReferenceFieldUpdater.newUpdater + (QNode.class, QNode.class, "next"); + + boolean casNext(QNode cmp, QNode val) { + return (next == cmp && + nextUpdater.compareAndSet(this, cmp, val)); + } + + static final AtomicReferenceFieldUpdater + itemUpdater = AtomicReferenceFieldUpdater.newUpdater + (QNode.class, Object.class, "item"); + + boolean casItem(Object cmp, Object val) { + return (item == cmp && + itemUpdater.compareAndSet(this, cmp, val)); + } + + /** + * Tries to cancel by CAS'ing ref to this as item. + */ + void tryCancel(Object cmp) { + itemUpdater.compareAndSet(this, cmp, this); + } + + boolean isCancelled() { + return item == this; + } + + /** + * Returns true if this node is known to be off the queue + * because its next pointer has been forgotten due to + * an advanceHead operation. + */ + boolean isOffList() { + return next == this; + } + } + + /** Head of queue */ + transient volatile QNode head; + /** Tail of queue */ + transient volatile QNode tail; + /** + * Reference to a cancelled node that might not yet have been + * unlinked from queue because it was the last inserted node + * when it cancelled. + */ + transient volatile QNode cleanMe; + + TransferQueue() { + QNode h = new QNode(null, false); // initialize to dummy node. + head = h; + tail = h; + } + + static final AtomicReferenceFieldUpdater + headUpdater = AtomicReferenceFieldUpdater.newUpdater + (TransferQueue.class, QNode.class, "head"); /** - * Fills in the slot created by the consumer and signal consumer to - * continue. + * Tries to cas nh as new head; if successful, unlink + * old head's next node to avoid garbage retention. */ - boolean setItem(Object x) { - item = x; // can place in slot even if cancelled - return release(ACK); + void advanceHead(QNode h, QNode nh) { + if (h == head && headUpdater.compareAndSet(this, h, nh)) + h.next = h; // forget old next } + static final AtomicReferenceFieldUpdater + tailUpdater = AtomicReferenceFieldUpdater.newUpdater + (TransferQueue.class, QNode.class, "tail"); + /** - * Removes item from slot created by producer and signal producer - * to continue. + * Tries to cas nt as new tail. */ - Object getItem() { - return (release(ACK))? extract() : null; + void advanceTail(QNode t, QNode nt) { + if (tail == t) + tailUpdater.compareAndSet(this, t, nt); } + static final AtomicReferenceFieldUpdater + cleanMeUpdater = AtomicReferenceFieldUpdater.newUpdater + (TransferQueue.class, QNode.class, "cleanMe"); + /** - * Waits for a consumer to take item placed by producer. + * Tries to CAS cleanMe slot. */ - void waitForTake() throws InterruptedException { - try { - acquireInterruptibly(0); - } catch (InterruptedException ie) { - checkCancellationOnInterrupt(ie); - } + boolean casCleanMe(QNode cmp, QNode val) { + return (cleanMe == cmp && + cleanMeUpdater.compareAndSet(this, cmp, val)); } /** - * Waits for a producer to put item placed by consumer. + * Puts or takes an item. */ - Object waitForPut() throws InterruptedException { - try { - acquireInterruptibly(0); - } catch (InterruptedException ie) { - checkCancellationOnInterrupt(ie); + Object transfer(Object e, boolean timed, long nanos) { + /* Basic algorithm is to loop trying to take either of + * two actions: + * + * 1. If queue apparently empty or holding same-mode nodes, + * try to add node to queue of waiters, wait to be + * fulfilled (or cancelled) and return matching item. + * + * 2. If queue apparently contains waiting items, and this + * call is of complementary mode, try to fulfill by CAS'ing + * item field of waiting node and dequeuing it, and then + * returning matching item. + * + * In each case, along the way, check for and try to help + * advance head and tail on behalf of other stalled/slow + * threads. + * + * The loop starts off with a null check guarding against + * seeing uninitialized head or tail values. This never + * happens in current SynchronousQueue, but could if + * callers held non-volatile/final ref to the + * transferer. The check is here anyway because it places + * null checks at top of loop, which is usually faster + * than having them implicitly interspersed. + */ + + QNode s = null; // constructed/reused as needed + boolean isData = (e != null); + + for (;;) { + QNode t = tail; + QNode h = head; + if (t == null || h == null) // saw uninitialized value + continue; // spin + + if (h == t || t.isData == isData) { // empty or same-mode + QNode tn = t.next; + if (t != tail) // inconsistent read + continue; + if (tn != null) { // lagging tail + advanceTail(t, tn); + continue; + } + if (timed && nanos <= 0) // can't wait + return null; + if (s == null) + s = new QNode(e, isData); + if (!t.casNext(null, s)) // failed to link in + continue; + + advanceTail(t, s); // swing tail and wait + Object x = awaitFulfill(s, e, timed, nanos); + if (x == s) { // wait was cancelled + clean(t, s); + return null; + } + + if (!s.isOffList()) { // not already unlinked + advanceHead(t, s); // unlink if head + if (x != null) // and forget fields + s.item = s; + s.waiter = null; + } + return (x != null)? x : e; + + } else { // complementary-mode + QNode m = h.next; // node to fulfill + if (t != tail || m == null || h != head) + continue; // inconsistent read + + Object x = m.item; + if (isData == (x != null) || // m already fulfilled + x == m || // m cancelled + !m.casItem(x, e)) { // lost CAS + advanceHead(h, m); // dequeue and retry + continue; + } + + advanceHead(h, m); // successfully fulfilled + LockSupport.unpark(m.waiter); + return (x != null)? x : e; + } } - return extract(); } /** - * Waits for a consumer to take item placed by producer or time out. + * Spins/blocks until node s is fulfilled. + * + * @param s the waiting node + * @param e the comparison value for checking match + * @param timed true if timed wait + * @param nanos timeout value + * @return matched item, or s if cancelled */ - boolean waitForTake(long nanos) throws InterruptedException { - try { - if (!tryAcquireNanos(0, nanos) && - release(CANCEL)) - return false; - } catch (InterruptedException ie) { - checkCancellationOnInterrupt(ie); + Object awaitFulfill(QNode s, Object e, boolean timed, long nanos) { + /* Same idea as TransferStack.awaitFulfill */ + long lastTime = (timed)? System.nanoTime() : 0; + Thread w = Thread.currentThread(); + int spins = ((head.next == s) ? + (timed? maxTimedSpins : maxUntimedSpins) : 0); + for (;;) { + if (w.isInterrupted()) + s.tryCancel(e); + Object x = s.item; + if (x != e) + return x; + if (timed) { + long now = System.nanoTime(); + nanos -= now - lastTime; + lastTime = now; + if (nanos <= 0) { + s.tryCancel(e); + continue; + } + } + if (spins > 0) + --spins; + else if (s.waiter == null) + s.waiter = w; + else if (!timed) + LockSupport.park(); + else if (nanos > spinForTimeoutThreshold) + LockSupport.parkNanos(nanos); } - return true; } /** - * Waits for a producer to put item placed by consumer, or time out. + * Gets rid of cancelled node s with original predecessor pred. */ - Object waitForPut(long nanos) throws InterruptedException { - try { - if (!tryAcquireNanos(0, nanos) && - release(CANCEL)) - return null; - } catch (InterruptedException ie) { - checkCancellationOnInterrupt(ie); + void clean(QNode pred, QNode s) { + s.waiter = null; // forget thread + /* + * At any given time, exactly one node on list cannot be + * deleted -- the last inserted node. To accommodate this, + * if we cannot delete s, we save its predecessor as + * "cleanMe", deleting the previously saved version + * first. At least one of node s or the node previously + * saved can always be deleted, so this always terminates. + */ + while (pred.next == s) { // Return early if already unlinked + QNode h = head; + QNode hn = h.next; // Absorb cancelled first node as head + if (hn != null && hn.isCancelled()) { + advanceHead(h, hn); + continue; + } + QNode t = tail; // Ensure consistent read for tail + if (t == h) + return; + QNode tn = t.next; + if (t != tail) + continue; + if (tn != null) { + advanceTail(t, tn); + continue; + } + if (s != t) { // If not tail, try to unsplice + QNode sn = s.next; + if (sn == s || pred.casNext(s, sn)) + return; + } + QNode dp = cleanMe; + if (dp != null) { // Try unlinking previous cancelled node + QNode d = dp.next; + QNode dn; + if (d == null || // d is gone or + d == dp || // d is off list or + !d.isCancelled() || // d not cancelled or + (d != t && // d not tail and + (dn = d.next) != null && // has successor + dn != d && // that is on list + dp.casNext(d, dn))) // d unspliced + casCleanMe(dp, null); + if (dp == pred) + return; // s is already saved node + } else if (casCleanMe(null, pred)) + return; // Postpone cleaning s } - return extract(); } } + /** + * The transferer. Set only in constructor, but cannot be declared + * as final without further complicating serialization. Since + * this is accessed only at most once per public method, there + * isn't a noticeable performance penalty for using volatile + * instead of final here. + */ + private transient volatile Transferer transferer; + + /** + * Creates a SynchronousQueue with nonfair access policy. + */ + public SynchronousQueue() { + this(false); + } + + /** + * Creates a SynchronousQueue with the specified fairness policy. + * + * @param fair if true, waiting threads contend in FIFO order for + * access; otherwise the order is unspecified. + */ + public SynchronousQueue(boolean fair) { + transferer = (fair)? new TransferQueue() : new TransferStack(); + } + /** * Adds the specified element to this queue, waiting if necessary for * another thread to receive it. - * @param o the element to add - * @throws InterruptedException if interrupted while waiting. - * @throws NullPointerException if the specified element is null. + * + * @throws InterruptedException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} */ public void put(E o) throws InterruptedException { if (o == null) throw new NullPointerException(); - final ReentrantLock qlock = this.qlock; - - for (;;) { - Node node; - boolean mustWait; - if (Thread.interrupted()) throw new InterruptedException(); - qlock.lock(); - try { - node = waitingConsumers.deq(); - if ( (mustWait = (node == null)) ) - node = waitingProducers.enq(o); - } finally { - qlock.unlock(); - } - - if (mustWait) { - node.waitForTake(); - return; - } - - else if (node.setItem(o)) - return; - - // else consumer cancelled, so retry + if (transferer.transfer(o, false, 0) == null) { + Thread.interrupted(); + throw new InterruptedException(); } } /** * Inserts the specified element into this queue, waiting if necessary * up to the specified wait time for another thread to receive it. - * @param o the element to add - * @param timeout how long to wait before giving up, in units of - * unit - * @param unit a TimeUnit determining how to interpret the - * timeout parameter - * @return true if successful, or false if - * the specified waiting time elapses before a consumer appears. - * @throws InterruptedException if interrupted while waiting. - * @throws NullPointerException if the specified element is null. + * + * @return true if successful, or false if the + * specified waiting time elapses before a consumer appears. + * @throws InterruptedException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} */ - public boolean offer(E o, long timeout, TimeUnit unit) throws InterruptedException { + public boolean offer(E o, long timeout, TimeUnit unit) + throws InterruptedException { if (o == null) throw new NullPointerException(); - long nanos = unit.toNanos(timeout); - final ReentrantLock qlock = this.qlock; - for (;;) { - Node node; - boolean mustWait; - if (Thread.interrupted()) throw new InterruptedException(); - qlock.lock(); - try { - node = waitingConsumers.deq(); - if ( (mustWait = (node == null)) ) - node = waitingProducers.enq(o); - } finally { - qlock.unlock(); - } - - if (mustWait) - return node.waitForTake(nanos); - - else if (node.setItem(o)) - return true; + if (transferer.transfer(o, true, unit.toNanos(timeout)) != null) + return true; + if (!Thread.interrupted()) + return false; + throw new InterruptedException(); + } - // else consumer cancelled, so retry - } + /** + * Inserts the specified element into this queue, if another thread is + * waiting to receive it. + * + * @param e the element to add + * @return true if the element was added to this queue, else + * false + * @throws NullPointerException if the specified element is null + */ + public boolean offer(E e) { + if (e == null) throw new NullPointerException(); + return transferer.transfer(e, true, 0) != null; } /** * Retrieves and removes the head of this queue, waiting if necessary * for another thread to insert it. - * @throws InterruptedException if interrupted while waiting. + * * @return the head of this queue + * @throws InterruptedException {@inheritDoc} */ public E take() throws InterruptedException { - final ReentrantLock qlock = this.qlock; - for (;;) { - Node node; - boolean mustWait; - - if (Thread.interrupted()) throw new InterruptedException(); - qlock.lock(); - try { - node = waitingProducers.deq(); - if ( (mustWait = (node == null)) ) - node = waitingConsumers.enq(null); - } finally { - qlock.unlock(); - } - - if (mustWait) { - Object x = node.waitForPut(); - return (E)x; - } - else { - Object x = node.getItem(); - if (x != null) - return (E)x; - // else cancelled, so retry - } - } + Object e = transferer.transfer(null, false, 0); + if (e != null) + return (E)e; + Thread.interrupted(); + throw new InterruptedException(); } /** * Retrieves and removes the head of this queue, waiting * if necessary up to the specified wait time, for another thread * to insert it. - * @param timeout how long to wait before giving up, in units of - * unit - * @param unit a TimeUnit determining how to interpret the - * timeout parameter + * * @return the head of this queue, or null if the - * specified waiting time elapses before an element is present. - * @throws InterruptedException if interrupted while waiting. + * specified waiting time elapses before an element is present. + * @throws InterruptedException {@inheritDoc} */ public E poll(long timeout, TimeUnit unit) throws InterruptedException { - long nanos = unit.toNanos(timeout); - final ReentrantLock qlock = this.qlock; - - for (;;) { - Node node; - boolean mustWait; - - if (Thread.interrupted()) throw new InterruptedException(); - qlock.lock(); - try { - node = waitingProducers.deq(); - if ( (mustWait = (node == null)) ) - node = waitingConsumers.enq(null); - } finally { - qlock.unlock(); - } - - if (mustWait) { - Object x = node.waitForPut(nanos); - return (E)x; - } - else { - Object x = node.getItem(); - if (x != null) - return (E)x; - // else cancelled, so retry - } - } - } - - // Untimed nonblocking versions - - /** - * Inserts the specified element into this queue, if another thread is - * waiting to receive it. - * - * @param o the element to add. - * @return true if it was possible to add the element to - * this queue, else false - * @throws NullPointerException if the specified element is null - */ - public boolean offer(E o) { - if (o == null) throw new NullPointerException(); - final ReentrantLock qlock = this.qlock; - - for (;;) { - Node node; - qlock.lock(); - try { - node = waitingConsumers.deq(); - } finally { - qlock.unlock(); - } - if (node == null) - return false; - - else if (node.setItem(o)) - return true; - // else retry - } + Object e = transferer.transfer(null, true, unit.toNanos(timeout)); + if (e != null || !Thread.interrupted()) + return (E)e; + throw new InterruptedException(); } /** @@ -490,30 +885,13 @@ public class SynchronousQueue extends AbstractQueue * element is available. */ public E poll() { - final ReentrantLock qlock = this.qlock; - for (;;) { - Node node; - qlock.lock(); - try { - node = waitingProducers.deq(); - } finally { - qlock.unlock(); - } - if (node == null) - return null; - - else { - Object x = node.getItem(); - if (x != null) - return (E)x; - // else retry - } - } + return (E)transferer.transfer(null, true, 0); } /** - * Always returns true. + * Always returns true. * A SynchronousQueue has no internal capacity. + * * @return true */ public boolean isEmpty() { @@ -523,6 +901,7 @@ public class SynchronousQueue extends AbstractQueue /** * Always returns zero. * A SynchronousQueue has no internal capacity. + * * @return zero. */ public int size() { @@ -532,6 +911,7 @@ public class SynchronousQueue extends AbstractQueue /** * Always returns zero. * A SynchronousQueue has no internal capacity. + * * @return zero. */ public int remainingCapacity() { @@ -542,11 +922,13 @@ public class SynchronousQueue extends AbstractQueue * Does nothing. * A SynchronousQueue has no internal capacity. */ - public void clear() {} + public void clear() { + } /** * Always returns false. * A SynchronousQueue has no internal capacity. + * * @param o the element * @return false */ @@ -566,8 +948,9 @@ public class SynchronousQueue extends AbstractQueue } /** - * Returns false unless given collection is empty. + * Returns false unless the given collection is empty. * A SynchronousQueue has no internal capacity. + * * @param c the collection * @return false unless given collection is empty */ @@ -578,6 +961,7 @@ public class SynchronousQueue extends AbstractQueue /** * Always returns false. * A SynchronousQueue has no internal capacity. + * * @param c the collection * @return false */ @@ -588,6 +972,7 @@ public class SynchronousQueue extends AbstractQueue /** * Always returns false. * A SynchronousQueue has no internal capacity. + * * @param c the collection * @return false */ @@ -596,9 +981,10 @@ public class SynchronousQueue extends AbstractQueue } /** - * Always returns null. + * Always returns null. * A SynchronousQueue does not return elements * unless actively waited on. + * * @return null */ public E peek() { @@ -628,7 +1014,6 @@ public class SynchronousQueue extends AbstractQueue return new EmptyIterator(); } - /** * Returns a zero-length array. * @return a zero-length array @@ -640,8 +1025,10 @@ public class SynchronousQueue extends AbstractQueue /** * Sets the zeroeth element of the specified array to null * (if the array has non-zero length) and returns it. + * * @param a the array * @return the specified array + * @throws NullPointerException if the specified array is null */ public T[] toArray(T[] a) { if (a.length > 0) @@ -649,7 +1036,12 @@ public class SynchronousQueue extends AbstractQueue return a; } - + /** + * @throws UnsupportedOperationException {@inheritDoc} + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + */ public int drainTo(Collection c) { if (c == null) throw new NullPointerException(); @@ -664,6 +1056,12 @@ public class SynchronousQueue extends AbstractQueue return n; } + /** + * @throws UnsupportedOperationException {@inheritDoc} + * @throws ClassCastException {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + * @throws IllegalArgumentException {@inheritDoc} + */ public int drainTo(Collection c, int maxElements) { if (c == null) throw new NullPointerException(); @@ -677,9 +1075,54 @@ public class SynchronousQueue extends AbstractQueue } return n; } -} + /* + * To cope with serialization strategy in the 1.5 version of + * SynchronousQueue, we declare some unused classes and fields + * that exist solely to enable serializability across versions. + * These fields are never used, so are initialized only if this + * object is ever serialized or deserialized. + */ + static class WaitQueue implements java.io.Serializable { } + static class LifoWaitQueue extends WaitQueue { + private static final long serialVersionUID = -3633113410248163686L; + } + static class FifoWaitQueue extends WaitQueue { + private static final long serialVersionUID = -3623113410248163686L; + } + private ReentrantLock qlock; + private WaitQueue waitingProducers; + private WaitQueue waitingConsumers; + /** + * Save the state to a stream (that is, serialize it). + * + * @param s the stream + */ + private void writeObject(java.io.ObjectOutputStream s) + throws java.io.IOException { + boolean fair = transferer instanceof TransferQueue; + if (fair) { + qlock = new ReentrantLock(true); + waitingProducers = new FifoWaitQueue(); + waitingConsumers = new FifoWaitQueue(); + } + else { + qlock = new ReentrantLock(); + waitingProducers = new LifoWaitQueue(); + waitingConsumers = new LifoWaitQueue(); + } + s.defaultWriteObject(); + } + private void readObject(final java.io.ObjectInputStream s) + throws java.io.IOException, ClassNotFoundException { + s.defaultReadObject(); + if (waitingProducers instanceof FifoWaitQueue) + transferer = new TransferQueue(); + else + transferer = new TransferStack(); + } +} diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/ThreadFactory.java b/libcore/concurrent/src/main/java/java/util/concurrent/ThreadFactory.java index 04c63e1af..2f0fb1a96 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/ThreadFactory.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/ThreadFactory.java @@ -11,7 +11,7 @@ package java.util.concurrent; * removes hardwiring of calls to {@link Thread#Thread(Runnable) new Thread}, * enabling applications to use special thread subclasses, priorities, etc. * - *

      + *

      * The simplest implementation of this interface is just: *

        * class SimpleThreadFactory implements ThreadFactory {
      @@ -23,18 +23,19 @@ package java.util.concurrent;
        *
        * The {@link Executors#defaultThreadFactory} method provides a more
        * useful simple implementation, that sets the created thread context
      - * to known values before returning it. 
      + * to known values before returning it.
        * @since 1.5
        * @author Doug Lea
        */
      -public interface ThreadFactory { 
      +public interface ThreadFactory {
       
           /**
      -     * Constructs a new Thread.  Implementations may also initialize
      -     * priority, name, daemon status, ThreadGroup, etc.
      +     * Constructs a new {@code Thread}.  Implementations may also initialize
      +     * priority, name, daemon status, {@code ThreadGroup}, etc.
            *
            * @param r a runnable to be executed by new thread instance
      -     * @return constructed thread
      +     * @return constructed thread, or {@code null} if the request to
      +     *         create a thread is rejected
            */
           Thread newThread(Runnable r);
       }
      diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/ThreadPoolExecutor.java b/libcore/concurrent/src/main/java/java/util/concurrent/ThreadPoolExecutor.java
      index 1aa8e557d..d0c934d58 100644
      --- a/libcore/concurrent/src/main/java/java/util/concurrent/ThreadPoolExecutor.java
      +++ b/libcore/concurrent/src/main/java/java/util/concurrent/ThreadPoolExecutor.java
      @@ -6,13 +6,9 @@
       
       package java.util.concurrent;
       import java.util.concurrent.locks.*;
      +import java.util.concurrent.atomic.*;
       import java.util.*;
       
      -// BEGIN android-note
      -// Altered {@link TimeUnit#NANOSECONDS} because our javadoc tool doesn't
      -// like it.
      -// END android-note
      -
       /**
        * An {@link ExecutorService} that executes each submitted task using
        * one of possibly several pooled threads, normally configured
      @@ -23,7 +19,7 @@ import java.util.*;
        * asynchronous tasks, due to reduced per-task invocation overhead,
        * and they provide a means of bounding and managing the resources,
        * including threads, consumed when executing a collection of tasks.
      - * Each ThreadPoolExecutor also maintains some basic
      + * Each {@code ThreadPoolExecutor} also maintains some basic
        * statistics, such as the number of completed tasks.
        *
        * 

      To be useful across a wide range of contexts, this class @@ -42,61 +38,63 @@ import java.util.*; * *

      Core and maximum pool sizes
      * - *
      A ThreadPoolExecutor will automatically adjust the - * pool size - * (see {@link ThreadPoolExecutor#getPoolSize}) - * according to the bounds set by corePoolSize - * (see {@link ThreadPoolExecutor#getCorePoolSize}) - * and - * maximumPoolSize - * (see {@link ThreadPoolExecutor#getMaximumPoolSize}). - * When a new task is submitted in method {@link - * ThreadPoolExecutor#execute}, and fewer than corePoolSize threads - * are running, a new thread is created to handle the request, even if - * other worker threads are idle. If there are more than - * corePoolSize but less than maximumPoolSize threads running, a new - * thread will be created only if the queue is full. By setting - * corePoolSize and maximumPoolSize the same, you create a fixed-size - * thread pool. By setting maximumPoolSize to an essentially unbounded - * value such as Integer.MAX_VALUE, you allow the pool to - * accommodate an arbitrary number of concurrent tasks. Most typically, - * core and maximum pool sizes are set only upon construction, but they - * may also be changed dynamically using {@link - * ThreadPoolExecutor#setCorePoolSize} and {@link - * ThreadPoolExecutor#setMaximumPoolSize}.
      + *
      A {@code ThreadPoolExecutor} will automatically adjust the + * pool size (see {@link #getPoolSize}) + * according to the bounds set by + * corePoolSize (see {@link #getCorePoolSize}) and + * maximumPoolSize (see {@link #getMaximumPoolSize}). + * + * When a new task is submitted in method {@link #execute}, and fewer + * than corePoolSize threads are running, a new thread is created to + * handle the request, even if other worker threads are idle. If + * there are more than corePoolSize but less than maximumPoolSize + * threads running, a new thread will be created only if the queue is + * full. By setting corePoolSize and maximumPoolSize the same, you + * create a fixed-size thread pool. By setting maximumPoolSize to an + * essentially unbounded value such as {@code Integer.MAX_VALUE}, you + * allow the pool to accommodate an arbitrary number of concurrent + * tasks. Most typically, core and maximum pool sizes are set only + * upon construction, but they may also be changed dynamically using + * {@link #setCorePoolSize} and {@link #setMaximumPoolSize}.
      * - *
      On-demand construction + *
      On-demand construction
      * *
      By default, even core threads are initially created and - * started only when needed by new tasks, but this can be overridden - * dynamically using method {@link - * ThreadPoolExecutor#prestartCoreThread} or - * {@link ThreadPoolExecutor#prestartAllCoreThreads}.
      + * started only when new tasks arrive, but this can be overridden + * dynamically using method {@link #prestartCoreThread} or {@link + * #prestartAllCoreThreads}. You probably want to prestart threads if + * you construct the pool with a non-empty queue. * *
      Creating new threads
      * - *
      New threads are created using a {@link - * java.util.concurrent.ThreadFactory}. If not otherwise specified, a - * {@link Executors#defaultThreadFactory} is used, that creates threads to all - * be in the same {@link ThreadGroup} and with the same - * NORM_PRIORITY priority and non-daemon status. By supplying - * a different ThreadFactory, you can alter the thread's name, thread - * group, priority, daemon status, etc.
      + *
      New threads are created using a {@link ThreadFactory}. If not + * otherwise specified, a {@link Executors#defaultThreadFactory} is + * used, that creates threads to all be in the same {@link + * ThreadGroup} and with the same {@code NORM_PRIORITY} priority and + * non-daemon status. By supplying a different ThreadFactory, you can + * alter the thread's name, thread group, priority, daemon status, + * etc. If a {@code ThreadFactory} fails to create a thread when asked + * by returning null from {@code newThread}, the executor will + * continue, but might not be able to execute any tasks. Threads + * should possess the "modifyThread" {@code RuntimePermission}. If + * worker threads or other threads using the pool do not possess this + * permission, service may be degraded: configuration changes may not + * take effect in a timely manner, and a shutdown pool may remain in a + * state in which termination is possible but not completed.
      * *
      Keep-alive times
      * *
      If the pool currently has more than corePoolSize threads, * excess threads will be terminated if they have been idle for more - * than the keepAliveTime (see {@link - * ThreadPoolExecutor#getKeepAliveTime}). This provides a means of - * reducing resource consumption when the pool is not being actively - * used. If the pool becomes more active later, new threads will be - * constructed. This parameter can also be changed dynamically - * using method {@link ThreadPoolExecutor#setKeepAliveTime}. Using - * a value of Long.MAX_VALUE TimeUnit.NANOSECONDS - * effectively disables idle threads from ever terminating prior - * to shut down. - *
      + * than the keepAliveTime (see {@link #getKeepAliveTime}). This + * provides a means of reducing resource consumption when the pool is + * not being actively used. If the pool becomes more active later, new + * threads will be constructed. This parameter can also be changed + * dynamically using method {@link #setKeepAliveTime}. Using a value + * of {@code Long.MAX_VALUE} {@link TimeUnit#NANOSECONDS} effectively + * disables idle threads from ever terminating prior to shut down. The + * keep-alive policy applies only when there are more than + * corePoolSizeThreads. * *
      Queuing
      * @@ -112,7 +110,7 @@ import java.util.*; *
    8. If corePoolSize or more threads are running, the Executor * always prefers queuing a request rather than adding a new * thread.
    9. - * + * *
    10. If a request cannot be queued, a new thread is created unless * this would exceed maximumPoolSize, in which case, the task will be * rejected.
    11. @@ -135,7 +133,7 @@ import java.util.*; * *
    12. Unbounded queues. Using an unbounded queue (for * example a {@link LinkedBlockingQueue} without a predefined - * capacity) will cause new tasks to be queued in cases where all + * capacity) will cause new tasks to wait in the queue when all * corePoolSize threads are busy. Thus, no more than corePoolSize * threads will ever be created. (And the value of the maximumPoolSize * therefore doesn't have any effect.) This may be appropriate when @@ -165,35 +163,33 @@ import java.util.*; * *
      Rejected tasks
      * - *
      New tasks submitted in method {@link - * ThreadPoolExecutor#execute} will be rejected when the - * Executor has been shut down, and also when the Executor uses finite - * bounds for both maximum threads and work queue capacity, and is - * saturated. In either case, the execute method invokes the - * {@link RejectedExecutionHandler#rejectedExecution} method of its - * {@link RejectedExecutionHandler}. Four predefined handler policies - * are provided: + *
      New tasks submitted in method {@link #execute} will be + * rejected when the Executor has been shut down, and also + * when the Executor uses finite bounds for both maximum threads and + * work queue capacity, and is saturated. In either case, the {@code + * execute} method invokes the {@link + * RejectedExecutionHandler#rejectedExecution} method of its {@link + * RejectedExecutionHandler}. Four predefined handler policies are + * provided: * *
        * - *
      1. In the - * default {@link ThreadPoolExecutor.AbortPolicy}, the handler throws a - * runtime {@link RejectedExecutionException} upon rejection.
      2. - * - *
      3. In {@link - * ThreadPoolExecutor.CallerRunsPolicy}, the thread that invokes - * execute itself runs the task. This provides a simple - * feedback control mechanism that will slow down the rate that new - * tasks are submitted.
      4. + *
      5. In the default {@link ThreadPoolExecutor.AbortPolicy}, the + * handler throws a runtime {@link RejectedExecutionException} upon + * rejection.
      6. * - *
      7. In {@link ThreadPoolExecutor.DiscardPolicy}, - * a task that cannot be executed is simply dropped.
      8. + *
      9. In {@link ThreadPoolExecutor.CallerRunsPolicy}, the thread + * that invokes {@code execute} itself runs the task. This provides a + * simple feedback control mechanism that will slow down the rate that + * new tasks are submitted.
      10. * - *
      11. In {@link - * ThreadPoolExecutor.DiscardOldestPolicy}, if the executor is not - * shut down, the task at the head of the work queue is dropped, and - * then execution is retried (which can fail again, causing this to be - * repeated.)
      12. + *
      13. In {@link ThreadPoolExecutor.DiscardPolicy}, a task that + * cannot be executed is simply dropped.
      14. + * + *
      15. In {@link ThreadPoolExecutor.DiscardOldestPolicy}, if the + * executor is not shut down, the task at the head of the work queue + * is dropped, and then execution is retried (which can fail again, + * causing this to be repeated.)
      16. * *
      * @@ -204,50 +200,62 @@ import java.util.*; * *
      Hook methods
      * - *
      This class provides protected overridable {@link - * ThreadPoolExecutor#beforeExecute} and {@link - * ThreadPoolExecutor#afterExecute} methods that are called before and - * after execution of each task. These can be used to manipulate the - * execution environment, for example, reinitializing ThreadLocals, - * gathering statistics, or adding log entries. Additionally, method - * {@link ThreadPoolExecutor#terminated} can be overridden to perform - * any special processing that needs to be done once the Executor has - * fully terminated.
      + *
      This class provides {@code protected} overridable {@link + * #beforeExecute} and {@link #afterExecute} methods that are called + * before and after execution of each task. These can be used to + * manipulate the execution environment; for example, reinitializing + * ThreadLocals, gathering statistics, or adding log + * entries. Additionally, method {@link #terminated} can be overridden + * to perform any special processing that needs to be done once the + * Executor has fully terminated. + * + *

      If hook or callback methods throw exceptions, internal worker + * threads may in turn fail and abruptly terminate.

      * *
      Queue maintenance
      * - *
      Method {@link ThreadPoolExecutor#getQueue} allows access to - * the work queue for purposes of monitoring and debugging. Use of - * this method for any other purpose is strongly discouraged. Two - * supplied methods, {@link ThreadPoolExecutor#remove} and {@link - * ThreadPoolExecutor#purge} are available to assist in storage - * reclamation when large numbers of queued tasks become - * cancelled.
      + *
      Method {@link #getQueue} allows access to the work queue for + * purposes of monitoring and debugging. Use of this method for any + * other purpose is strongly discouraged. Two supplied methods, + * {@link #remove} and {@link #purge} are available to assist in + * storage reclamation when large numbers of queued tasks become + * cancelled.
      + * + *
      Finalization
      + * + *
      A pool that is no longer referenced in a program AND + * has no remaining threads will be {@code shutdown} automatically. If + * you would like to ensure that unreferenced pools are reclaimed even + * if users forget to call {@link #shutdown}, then you must arrange + * that unused threads eventually die, by setting appropriate + * keep-alive times using a lower bound of zero core threads.
      + * + * * *

      Extension example. Most extensions of this class * override one or more of the protected hook methods. For example, * here is a subclass that adds a simple pause/resume feature: * - *

      + *  
       {@code
        * class PausableThreadPoolExecutor extends ThreadPoolExecutor {
        *   private boolean isPaused;
        *   private ReentrantLock pauseLock = new ReentrantLock();
        *   private Condition unpaused = pauseLock.newCondition();
        *
        *   public PausableThreadPoolExecutor(...) { super(...); }
      - * 
      + *
        *   protected void beforeExecute(Thread t, Runnable r) {
        *     super.beforeExecute(t, r);
        *     pauseLock.lock();
        *     try {
        *       while (isPaused) unpaused.await();
      - *     } catch(InterruptedException ie) {
      + *     } catch (InterruptedException ie) {
        *       t.interrupt();
        *     } finally {
        *       pauseLock.unlock();
        *     }
        *   }
      - * 
      + *
        *   public void pause() {
        *     pauseLock.lock();
        *     try {
      @@ -256,7 +264,7 @@ import java.util.*;
        *       pauseLock.unlock();
        *     }
        *   }
      - * 
      + *
        *   public void resume() {
        *     pauseLock.lock();
        *     try {
      @@ -266,86 +274,196 @@ import java.util.*;
        *       pauseLock.unlock();
        *     }
        *   }
      - * }
      - * 
      + * }}
      + * * @since 1.5 * @author Doug Lea */ public class ThreadPoolExecutor extends AbstractExecutorService { /** - * Only used to force toArray() to produce a Runnable[]. + * The main pool control state, ctl, is an atomic integer packing + * two conceptual fields + * workerCount, indicating the effective number of threads + * runState, indicating whether running, shutting down etc + * + * In order to pack them into one int, we limit workerCount to + * (2^29)-1 (about 500 million) threads rather than (2^31)-1 (2 + * billion) otherwise representable. If this is ever an issue in + * the future, the variable can be changed to be an AtomicLong, + * and the shift/mask constants below adjusted. But until the need + * arises, this code is a bit faster and simpler using an int. + * + * The workerCount is the number of workers that have been + * permitted to start and not permitted to stop. The value may be + * transiently different from the actual number of live threads, + * for example when a ThreadFactory fails to create a thread when + * asked, and when exiting threads are still performing + * bookkeeping before terminating. The user-visible pool size is + * reported as the current size of the workers set. + * + * The runState provides the main lifecyle control, taking on values: + * + * RUNNING: Accept new tasks and process queued tasks + * SHUTDOWN: Don't accept new tasks, but process queued tasks + * STOP: Don't accept new tasks, don't process queued tasks, + * and interrupt in-progress tasks + * TIDYING: All tasks have terminated, workerCount is zero, + * the thread transitioning to state TIDYING + * will run the terminated() hook method + * TERMINATED: terminated() has completed + * + * The numerical order among these values matters, to allow + * ordered comparisons. The runState monotonically increases over + * time, but need not hit each state. The transitions are: + * + * RUNNING -> SHUTDOWN + * On invocation of shutdown(), perhaps implicitly in finalize() + * (RUNNING or SHUTDOWN) -> STOP + * On invocation of shutdownNow() + * SHUTDOWN -> TIDYING + * When both queue and pool are empty + * STOP -> TIDYING + * When pool is empty + * TIDYING -> TERMINATED + * When the terminated() hook method has completed + * + * Threads waiting in awaitTermination() will return when the + * state reaches TERMINATED. + * + * Detecting the transition from SHUTDOWN to TIDYING is less + * straightforward than you'd like because the queue may become + * empty after non-empty and vice versa during SHUTDOWN state, but + * we can only terminate if, after seeing that it is empty, we see + * that workerCount is 0 (which sometimes entails a recheck -- see + * below). */ - private static final Runnable[] EMPTY_RUNNABLE_ARRAY = new Runnable[0]; + private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); + private static final int COUNT_BITS = Integer.SIZE - 3; + private static final int CAPACITY = (1 << COUNT_BITS) - 1; + + // runState is stored in the high-order bits + private static final int RUNNING = -1 << COUNT_BITS; + private static final int SHUTDOWN = 0 << COUNT_BITS; + private static final int STOP = 1 << COUNT_BITS; + private static final int TIDYING = 2 << COUNT_BITS; + private static final int TERMINATED = 3 << COUNT_BITS; + + // Packing and unpacking ctl + private static int runStateOf(int c) { return c & ~CAPACITY; } + private static int workerCountOf(int c) { return c & CAPACITY; } + private static int ctlOf(int rs, int wc) { return rs | wc; } + + /* + * Bit field accessors that don't require unpacking ctl. + * These depend on the bit layout and on workerCount being never negative. + */ + + private static boolean runStateLessThan(int c, int s) { + return c < s; + } + + private static boolean runStateAtLeast(int c, int s) { + return c >= s; + } + + private static boolean isRunning(int c) { + return c < SHUTDOWN; + } /** - * Permission for checking shutdown + * Attempt to CAS-increment the workerCount field of ctl. */ - private static final RuntimePermission shutdownPerm = - new RuntimePermission("modifyThread"); + private boolean compareAndIncrementWorkerCount(int expect) { + return ctl.compareAndSet(expect, expect + 1); + } /** - * Queue used for holding tasks and handing off to worker threads. + * Attempt to CAS-decrement the workerCount field of ctl. */ - private final BlockingQueue workQueue; + private boolean compareAndDecrementWorkerCount(int expect) { + return ctl.compareAndSet(expect, expect - 1); + } /** - * Lock held on updates to poolSize, corePoolSize, maximumPoolSize, and - * workers set. + * Decrements the workerCount field of ctl. This is called only on + * abrupt termination of a thread (see processWorkerExit). Other + * decrements are performed within getTask. */ - private final ReentrantLock mainLock = new ReentrantLock(); + private void decrementWorkerCount() { + do {} while (! compareAndDecrementWorkerCount(ctl.get())); + } /** - * Wait condition to support awaitTermination + * The queue used for holding tasks and handing off to worker + * threads. We do not require that workQueue.poll() returning + * null necessarily means that workQueue.isEmpty(), so rely + * solely on isEmpty to see if the queue is empty (which we must + * do for example when deciding whether to transition from + * SHUTDOWN to TIDYING). This accommodates special-purpose + * queues such as DelayQueues for which poll() is allowed to + * return null even if it may later return non-null when delays + * expire. */ - private final Condition termination = mainLock.newCondition(); + private final BlockingQueue workQueue; /** - * Set containing all worker threads in pool. + * Lock held on access to workers set and related bookkeeping. + * While we could use a concurrent set of some sort, it turns out + * to be generally preferable to use a lock. Among the reasons is + * that this serializes interruptIdleWorkers, which avoids + * unnecessary interrupt storms, especially during shutdown. + * Otherwise exiting threads would concurrently interrupt those + * that have not yet interrupted. It also simplifies some of the + * associated statistics bookkeeping of largestPoolSize etc. We + * also hold mainLock on shutdown and shutdownNow, for the sake of + * ensuring workers set is stable while separately checking + * permission to interrupt and actually interrupting. */ - private final HashSet workers = new HashSet(); + private final ReentrantLock mainLock = new ReentrantLock(); /** - * Timeout in nanoseconds for idle threads waiting for work. - * Threads use this timeout only when there are more than - * corePoolSize present. Otherwise they wait forever for new work. + * Set containing all worker threads in pool. Accessed only when + * holding mainLock. */ - private volatile long keepAliveTime; + private final HashSet workers = new HashSet(); /** - * Core pool size, updated only while holding mainLock, - * but volatile to allow concurrent readability even - * during updates. + * Wait condition to support awaitTermination */ - private volatile int corePoolSize; + private final Condition termination = mainLock.newCondition(); /** - * Maximum pool size, updated only while holding mainLock - * but volatile to allow concurrent readability even - * during updates. + * Tracks largest attained pool size. Accessed only under + * mainLock. */ - private volatile int maximumPoolSize; + private int largestPoolSize; /** - * Current pool size, updated only while holding mainLock - * but volatile to allow concurrent readability even - * during updates. + * Counter for completed tasks. Updated only on termination of + * worker threads. Accessed only under mainLock. */ - private volatile int poolSize; + private long completedTaskCount; - /** - * Lifecycle state + /* + * All user control parameters are declared as volatiles so that + * ongoing actions are based on freshest values, but without need + * for locking, since no internal invariants depend on them + * changing synchronously with respect to other actions. */ - volatile int runState; - // Special values for runState - /** Normal, not-shutdown mode */ - static final int RUNNING = 0; - /** Controlled shutdown mode */ - static final int SHUTDOWN = 1; - /** Immediate shutdown mode */ - static final int STOP = 2; - /** Final state */ - static final int TERMINATED = 3; + /** + * Factory for new threads. All threads are created using this + * factory (via method addWorker). All callers must be prepared + * for addWorker to fail, which may reflect a system or user's + * policy limiting the number of threads. Even though it is not + * treated as an error, failure to create threads may result in + * new tasks being rejected or existing ones remaining stuck in + * the queue. On the other hand, no special precautions exist to + * handle OutOfMemoryErrors that might be thrown while trying to + * create threads, since there is generally no recourse from + * within this class. + */ + private volatile ThreadFactory threadFactory; /** * Handler called when saturated or shutdown in execute. @@ -353,21 +471,24 @@ public class ThreadPoolExecutor extends AbstractExecutorService { private volatile RejectedExecutionHandler handler; /** - * Factory for new threads. + * Timeout in nanoseconds for idle threads waiting for work. + * Threads use this timeout when there are more than corePoolSize + * present. Otherwise they wait forever for new work. */ - private volatile ThreadFactory threadFactory; + private volatile long keepAliveTime; /** - * Tracks largest attained pool size. + * Core pool size is the minimum number of workers to keep alive + * (and not allow to time out etc). */ - private int largestPoolSize; + private volatile int corePoolSize; /** - * Counter for completed tasks. Updated only on termination of - * worker threads. + * Maximum pool size. Note that the actual maximum is internally + * bounded by CAPACITY. */ - private long completedTaskCount; - + private volatile int maximumPoolSize; + /** * The default rejected execution handler */ @@ -375,336 +496,622 @@ public class ThreadPoolExecutor extends AbstractExecutorService { new AbortPolicy(); /** - * Invoke the rejected execution handler for the given command. + * Permission required for callers of shutdown and shutdownNow. + * We additionally require (see checkShutdownAccess) that callers + * have permission to actually interrupt threads in the worker set + * (as governed by Thread.interrupt, which relies on + * ThreadGroup.checkAccess, which in turn relies on + * SecurityManager.checkAccess). Shutdowns are attempted only if + * these checks pass. + * + * All actual invocations of Thread.interrupt (see + * interruptIdleWorkers and interruptWorkers) ignore + * SecurityExceptions, meaning that the attempted interrupts + * silently fail. In the case of shutdown, they should not fail + * unless the SecurityManager has inconsistent policies, sometimes + * allowing access to a thread and sometimes not. In such cases, + * failure to actually interrupt threads may disable or delay full + * termination. Other uses of interruptIdleWorkers are advisory, + * and failure to actually interrupt will merely delay response to + * configuration changes so is not handled exceptionally. */ - void reject(Runnable command) { - handler.rejectedExecution(command, this); + private static final RuntimePermission shutdownPerm = + new RuntimePermission("modifyThread"); + + /** + * Class Worker mainly maintains interrupt control state for + * threads running tasks, along with other minor bookkeeping. + * This class opportunistically extends AbstractQueuedSynchronizer + * to simplify acquiring and releasing a lock surrounding each + * task execution. This protects against interrupts that are + * intended to wake up a worker thread waiting for a task from + * instead interrupting a task being run. We implement a simple + * non-reentrant mutual exclusion lock rather than use ReentrantLock + * because we do not want worker tasks to be able to reacquire the + * lock when they invoke pool control methods like setCorePoolSize. + */ + private final class Worker + extends AbstractQueuedSynchronizer + implements Runnable + { + /** + * This class will never be serialized, but we provide a + * serialVersionUID to suppress a javac warning. + */ + private static final long serialVersionUID = 6138294804551838833L; + + /** Thread this worker is running in. Null if factory fails. */ + final Thread thread; + /** Initial task to run. Possibly null. */ + Runnable firstTask; + /** Per-thread task counter */ + volatile long completedTasks; + + /** + * Creates with given first task and thread from ThreadFactory. + * @param firstTask the first task (null if none) + */ + Worker(Runnable firstTask) { + this.firstTask = firstTask; + this.thread = getThreadFactory().newThread(this); + } + + /** Delegates main run loop to outer runWorker */ + public void run() { + runWorker(this); + } + + // Lock methods + // + // The value 0 represents the unlocked state. + // The value 1 represents the locked state. + + protected boolean isHeldExclusively() { + return getState() == 1; + } + + protected boolean tryAcquire(int unused) { + if (compareAndSetState(0, 1)) { + setExclusiveOwnerThread(Thread.currentThread()); + return true; + } + return false; + } + + protected boolean tryRelease(int unused) { + setExclusiveOwnerThread(null); + setState(0); + return true; + } + + public void lock() { acquire(1); } + public boolean tryLock() { return tryAcquire(1); } + public void unlock() { release(1); } + public boolean isLocked() { return isHeldExclusively(); } } + /* + * Methods for setting control state + */ + /** - * Create and return a new thread running firstTask as its first - * task. Call only while holding mainLock - * @param firstTask the task the new thread should run first (or - * null if none) - * @return the new thread + * Transitions runState to given target, or leaves it alone if + * already at least the given target. + * + * @param targetState the desired state, either SHUTDOWN or STOP + * (but not TIDYING or TERMINATED -- use tryTerminate for that) */ - private Thread addThread(Runnable firstTask) { - Worker w = new Worker(firstTask); - Thread t = threadFactory.newThread(w); - w.thread = t; - workers.add(w); - int nt = ++poolSize; - if (nt > largestPoolSize) - largestPoolSize = nt; - return t; + private void advanceRunState(int targetState) { + for (;;) { + int c = ctl.get(); + if (runStateAtLeast(c, targetState) || + ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c)))) + break; + } } /** - * Create and start a new thread running firstTask as its first - * task, only if fewer than corePoolSize threads are running. - * @param firstTask the task the new thread should run first (or - * null if none) - * @return true if successful. + * Transitions to TERMINATED state if either (SHUTDOWN and pool + * and queue empty) or (STOP and pool empty). If otherwise + * eligible to terminate but workerCount is nonzero, interrupts an + * idle worker to ensure that shutdown signals propagate. This + * method must be called following any action that might make + * termination possible -- reducing worker count or removing tasks + * from the queue during shutdown. The method is non-private to + * allow access from ScheduledThreadPoolExecutor. + */ + final void tryTerminate() { + for (;;) { + int c = ctl.get(); + if (isRunning(c) || + runStateAtLeast(c, TIDYING) || + (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty())) + return; + if (workerCountOf(c) != 0) { // Eligible to terminate + interruptIdleWorkers(ONLY_ONE); + return; + } + + final ReentrantLock mainLock = this.mainLock; + mainLock.lock(); + try { + if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) { + try { + terminated(); + } finally { + ctl.set(ctlOf(TERMINATED, 0)); + termination.signalAll(); + } + return; + } + } finally { + mainLock.unlock(); + } + // else retry on failed CAS + } + } + + /* + * Methods for controlling interrupts to worker threads. */ - private boolean addIfUnderCorePoolSize(Runnable firstTask) { - Thread t = null; + + /** + * If there is a security manager, makes sure caller has + * permission to shut down threads in general (see shutdownPerm). + * If this passes, additionally makes sure the caller is allowed + * to interrupt each worker thread. This might not be true even if + * first check passed, if the SecurityManager treats some threads + * specially. + */ + private void checkShutdownAccess() { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkPermission(shutdownPerm); + final ReentrantLock mainLock = this.mainLock; + mainLock.lock(); + try { + for (Worker w : workers) + security.checkAccess(w.thread); + } finally { + mainLock.unlock(); + } + } + } + + /** + * Interrupts all threads, even if active. Ignores SecurityExceptions + * (in which case some threads may remain uninterrupted). + */ + private void interruptWorkers() { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { - if (poolSize < corePoolSize) - t = addThread(firstTask); + for (Worker w : workers) { + try { + w.thread.interrupt(); + } catch (SecurityException ignore) { + } + } } finally { mainLock.unlock(); } - if (t == null) - return false; - t.start(); - return true; } /** - * Create and start a new thread only if fewer than maximumPoolSize - * threads are running. The new thread runs as its first task the - * next task in queue, or if there is none, the given task. - * @param firstTask the task the new thread should run first (or - * null if none) - * @return null on failure, else the first task to be run by new thread. + * Interrupts threads that might be waiting for tasks (as + * indicated by not being locked) so they can check for + * termination or configuration changes. Ignores + * SecurityExceptions (in which case some threads may remain + * uninterrupted). + * + * @param onlyOne If true, interrupt at most one worker. This is + * called only from tryTerminate when termination is otherwise + * enabled but there are still other workers. In this case, at + * most one waiting worker is interrupted to propagate shutdown + * signals in case all threads are currently waiting. + * Interrupting any arbitrary thread ensures that newly arriving + * workers since shutdown began will also eventually exit. + * To guarantee eventual termination, it suffices to always + * interrupt only one idle worker, but shutdown() interrupts all + * idle workers so that redundant workers exit promptly, not + * waiting for a straggler task to finish. */ - private Runnable addIfUnderMaximumPoolSize(Runnable firstTask) { - Thread t = null; - Runnable next = null; + private void interruptIdleWorkers(boolean onlyOne) { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { - if (poolSize < maximumPoolSize) { - next = workQueue.poll(); - if (next == null) - next = firstTask; - t = addThread(next); + for (Worker w : workers) { + Thread t = w.thread; + if (!t.isInterrupted() && w.tryLock()) { + try { + t.interrupt(); + } catch (SecurityException ignore) { + } finally { + w.unlock(); + } + } + if (onlyOne) + break; } } finally { mainLock.unlock(); } - if (t == null) - return null; - t.start(); - return next; } + /** + * Common form of interruptIdleWorkers, to avoid having to + * remember what the boolean argument means. + */ + private void interruptIdleWorkers() { + interruptIdleWorkers(false); + } + + private static final boolean ONLY_ONE = true; /** - * Get the next task for a worker thread to run. - * @return the task - * @throws InterruptedException if interrupted while waiting for task + * Ensures that unless the pool is stopping, the current thread + * does not have its interrupt set. This requires a double-check + * of state in case the interrupt was cleared concurrently with a + * shutdownNow -- if so, the interrupt is re-enabled. */ - Runnable getTask() throws InterruptedException { - for (;;) { - switch(runState) { - case RUNNING: { - if (poolSize <= corePoolSize) // untimed wait if core - return workQueue.take(); - - long timeout = keepAliveTime; - if (timeout <= 0) // die immediately for 0 timeout - return null; - Runnable r = workQueue.poll(timeout, TimeUnit.NANOSECONDS); - if (r != null) - return r; - if (poolSize > corePoolSize) // timed out - return null; - // else, after timeout, pool shrank so shouldn't die, so retry - break; - } + private void clearInterruptsForTaskRun() { + if (runStateLessThan(ctl.get(), STOP) && + Thread.interrupted() && + runStateAtLeast(ctl.get(), STOP)) + Thread.currentThread().interrupt(); + } - case SHUTDOWN: { - // Help drain queue - Runnable r = workQueue.poll(); - if (r != null) - return r; - - // Check if can terminate - if (workQueue.isEmpty()) { - interruptIdleWorkers(); - return null; - } + /* + * Misc utilities, most of which are also exported to + * ScheduledThreadPoolExecutor + */ - // There could still be delayed tasks in queue. - // Wait for one, re-checking state upon interruption - try { - return workQueue.take(); - } catch(InterruptedException ignore) {} - break; - } + /** + * Invokes the rejected execution handler for the given command. + * Package-protected for use by ScheduledThreadPoolExecutor. + */ + final void reject(Runnable command) { + handler.rejectedExecution(command, this); + } - case STOP: - return null; - default: - assert false; + /** + * Performs any further cleanup following run state transition on + * invocation of shutdown. A no-op here, but used by + * ScheduledThreadPoolExecutor to cancel delayed tasks. + */ + void onShutdown() { + } + + /** + * State check needed by ScheduledThreadPoolExecutor to + * enable running tasks during shutdown. + * + * @param shutdownOK true if should return true if SHUTDOWN + */ + final boolean isRunningOrShutdown(boolean shutdownOK) { + int rs = runStateOf(ctl.get()); + return rs == RUNNING || (rs == SHUTDOWN && shutdownOK); + } + + /** + * Drains the task queue into a new list, normally using + * drainTo. But if the queue is a DelayQueue or any other kind of + * queue for which poll or drainTo may fail to remove some + * elements, it deletes them one by one. + */ + private List drainQueue() { + BlockingQueue q = workQueue; + List taskList = new ArrayList(); + q.drainTo(taskList); + if (!q.isEmpty()) { + for (Runnable r : q.toArray(new Runnable[0])) { + if (q.remove(r)) + taskList.add(r); } } + return taskList; } + /* + * Methods for creating, running and cleaning up after workers + */ + /** - * Wake up all threads that might be waiting for tasks. + * Checks if a new worker can be added with respect to current + * pool state and the given bound (either core or maximum). If so, + * the worker count is adjusted accordingly, and, if possible, a + * new worker is created and started running firstTask as its + * first task. This method returns false if the pool is stopped or + * eligible to shut down. It also returns false if the thread + * factory fails to create a thread when asked, which requires a + * backout of workerCount, and a recheck for termination, in case + * the existence of this worker was holding up termination. + * + * @param firstTask the task the new thread should run first (or + * null if none). Workers are created with an initial first task + * (in method execute()) to bypass queuing when there are fewer + * than corePoolSize threads (in which case we always start one), + * or when the queue is full (in which case we must bypass queue). + * Initially idle threads are usually created via + * prestartCoreThread or to replace other dying workers. + * + * @param core if true use corePoolSize as bound, else + * maximumPoolSize. (A boolean indicator is used here rather than a + * value to ensure reads of fresh values after checking other pool + * state). + * @return true if successful */ - void interruptIdleWorkers() { + private boolean addWorker(Runnable firstTask, boolean core) { + retry: + for (;;) { + int c = ctl.get(); + int rs = runStateOf(c); + + // Check if queue empty only if necessary. + if (rs >= SHUTDOWN && + ! (rs == SHUTDOWN && + firstTask == null && + ! workQueue.isEmpty())) + return false; + + for (;;) { + int wc = workerCountOf(c); + if (wc >= CAPACITY || + wc >= (core ? corePoolSize : maximumPoolSize)) + return false; + if (compareAndIncrementWorkerCount(c)) + break retry; + c = ctl.get(); // Re-read ctl + if (runStateOf(c) != rs) + continue retry; + // else CAS failed due to workerCount change; retry inner loop + } + } + + Worker w = new Worker(firstTask); + Thread t = w.thread; + final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { - for (Worker w : workers) - w.interruptIfIdle(); + // Recheck while holding lock. + // Back out on ThreadFactory failure or if + // shut down before lock acquired. + int c = ctl.get(); + int rs = runStateOf(c); + + if (t == null || + (rs >= SHUTDOWN && + ! (rs == SHUTDOWN && + firstTask == null))) { + decrementWorkerCount(); + tryTerminate(); + return false; + } + + workers.add(w); + + int s = workers.size(); + if (s > largestPoolSize) + largestPoolSize = s; } finally { mainLock.unlock(); } + + t.start(); + // It is possible (but unlikely) for a thread to have been + // added to workers, but not yet started, during transition to + // STOP, which could result in a rare missed interrupt, + // because Thread.interrupt is not guaranteed to have any effect + // on a non-yet-started Thread (see Thread#interrupt). + if (runStateOf(ctl.get()) == STOP && ! t.isInterrupted()) + t.interrupt(); + + return true; } /** - * Perform bookkeeping for a terminated worker thread. + * Performs cleanup and bookkeeping for a dying worker. Called + * only from worker threads. Unless completedAbruptly is set, + * assumes that workerCount has already been adjusted to account + * for exit. This method removes thread from worker set, and + * possibly terminates the pool or replaces the worker if either + * it exited due to user task exception or if fewer than + * corePoolSize workers are running or queue is non-empty but + * there are no workers. + * * @param w the worker + * @param completedAbruptly if the worker died due to user exception */ - void workerDone(Worker w) { + private void processWorkerExit(Worker w, boolean completedAbruptly) { + if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted + decrementWorkerCount(); + final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { completedTaskCount += w.completedTasks; workers.remove(w); - if (--poolSize > 0) - return; - - // Else, this is the last thread. Deal with potential shutdown. - - int state = runState; - assert state != TERMINATED; - - if (state != STOP) { - // If there are queued tasks but no threads, create - // replacement. - Runnable r = workQueue.poll(); - if (r != null) { - addThread(r).start(); - return; - } - - // If there are some (presumably delayed) tasks but - // none pollable, create an idle replacement to wait. - if (!workQueue.isEmpty()) { - addThread(null).start(); - return; - } - - // Otherwise, we can exit without replacement - if (state == RUNNING) - return; - } - - // Either state is STOP, or state is SHUTDOWN and there is - // no work to do. So we can terminate. - termination.signalAll(); - runState = TERMINATED; - // fall through to call terminate() outside of lock. } finally { mainLock.unlock(); } - assert runState == TERMINATED; - terminated(); + tryTerminate(); + + int c = ctl.get(); + if (runStateLessThan(c, STOP)) { + if (!completedAbruptly) { + int min = corePoolSize; + if (min == 0 && ! workQueue.isEmpty()) + min = 1; + if (workerCountOf(c) >= min) + return; // replacement not needed + } + addWorker(null, false); + } } /** - * Worker threads + * Performs blocking or timed wait for a task, depending on + * current configuration settings, or returns null if this worker + * must exit because of any of: + * 1. There are more than maximumPoolSize workers (due to + * a call to setMaximumPoolSize). + * 2. The pool is stopped. + * 3. The pool is shutdown and the queue is empty. + * 4. This worker timed out waiting for a task, and timed-out + * workers are subject to termination (that is, + * {@code workerCount > corePoolSize}) + * both before and after the timed wait. + * + * @return task, or null if the worker must exit, in which case + * workerCount is decremented */ - private class Worker implements Runnable { + private Runnable getTask() { + boolean timedOut = false; // Did the last poll() time out? - /** - * The runLock is acquired and released surrounding each task - * execution. It mainly protects against interrupts that are - * intended to cancel the worker thread from instead - * interrupting the task being run. - */ - private final ReentrantLock runLock = new ReentrantLock(); - - /** - * Initial task to run before entering run loop - */ - private Runnable firstTask; - - /** - * Per thread completed task counter; accumulated - * into completedTaskCount upon termination. - */ - volatile long completedTasks; + retry: + for (;;) { + int c = ctl.get(); + int rs = runStateOf(c); - /** - * Thread this worker is running in. Acts as a final field, - * but cannot be set until thread is created. - */ - Thread thread; + // Check if queue empty only if necessary. + if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) { + decrementWorkerCount(); + return null; + } - Worker(Runnable firstTask) { - this.firstTask = firstTask; - } + boolean timed; // Are workers subject to culling? - boolean isActive() { - return runLock.isLocked(); - } + for (;;) { + int wc = workerCountOf(c); + timed = wc > corePoolSize; - /** - * Interrupt thread if not running a task - */ - void interruptIfIdle() { - final ReentrantLock runLock = this.runLock; - if (runLock.tryLock()) { - try { - thread.interrupt(); - } finally { - runLock.unlock(); - } + if (wc <= maximumPoolSize && ! (timedOut && timed)) + break; + if (compareAndDecrementWorkerCount(c)) + return null; + c = ctl.get(); // Re-read ctl + if (runStateOf(c) != rs) + continue retry; + // else CAS failed due to workerCount change; retry inner loop } - } - - /** - * Cause thread to die even if running a task. - */ - void interruptNow() { - thread.interrupt(); - } - /** - * Run a single task between before/after methods. - */ - private void runTask(Runnable task) { - final ReentrantLock runLock = this.runLock; - runLock.lock(); try { - // Abort now if immediate cancel. Otherwise, we have - // committed to run this task. - if (runState == STOP) - return; - - Thread.interrupted(); // clear interrupt status on entry - boolean ran = false; - beforeExecute(thread, task); - try { - task.run(); - ran = true; - afterExecute(task, null); - ++completedTasks; - } catch(RuntimeException ex) { - if (!ran) - afterExecute(task, ex); - // Else the exception occurred within - // afterExecute itself in which case we don't - // want to call it again. - throw ex; - } - } finally { - runLock.unlock(); + Runnable r = timed ? + workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : + workQueue.take(); + if (r != null) + return r; + timedOut = true; + } catch (InterruptedException retry) { + timedOut = false; } } + } - /** - * Main run loop - */ - public void run() { - try { - Runnable task = firstTask; - firstTask = null; - while (task != null || (task = getTask()) != null) { - runTask(task); - task = null; // unnecessary but can help GC + /** + * Main worker run loop. Repeatedly gets tasks from queue and + * executes them, while coping with a number of issues: + * + * 1. We may start out with an initial task, in which case we + * don't need to get the first one. Otherwise, as long as pool is + * running, we get tasks from getTask. If it returns null then the + * worker exits due to changed pool state or configuration + * parameters. Other exits result from exception throws in + * external code, in which case completedAbruptly holds, which + * usually leads processWorkerExit to replace this thread. + * + * 2. Before running any task, the lock is acquired to prevent + * other pool interrupts while the task is executing, and + * clearInterruptsForTaskRun called to ensure that unless pool is + * stopping, this thread does not have its interrupt set. + * + * 3. Each task run is preceded by a call to beforeExecute, which + * might throw an exception, in which case we cause thread to die + * (breaking loop with completedAbruptly true) without processing + * the task. + * + * 4. Assuming beforeExecute completes normally, we run the task, + * gathering any of its thrown exceptions to send to + * afterExecute. We separately handle RuntimeException, Error + * (both of which the specs guarantee that we trap) and arbitrary + * Throwables. Because we cannot rethrow Throwables within + * Runnable.run, we wrap them within Errors on the way out (to the + * thread's UncaughtExceptionHandler). Any thrown exception also + * conservatively causes thread to die. + * + * 5. After task.run completes, we call afterExecute, which may + * also throw an exception, which will also cause thread to + * die. According to JLS Sec 14.20, this exception is the one that + * will be in effect even if task.run throws. + * + * The net effect of the exception mechanics is that afterExecute + * and the thread's UncaughtExceptionHandler have as accurate + * information as we can provide about any problems encountered by + * user code. + * + * @param w the worker + */ + final void runWorker(Worker w) { + Runnable task = w.firstTask; + w.firstTask = null; + boolean completedAbruptly = true; + try { + while (task != null || (task = getTask()) != null) { + w.lock(); + clearInterruptsForTaskRun(); + try { + beforeExecute(w.thread, task); + Throwable thrown = null; + try { + task.run(); + } catch (RuntimeException x) { + thrown = x; throw x; + } catch (Error x) { + thrown = x; throw x; + } catch (Throwable x) { + thrown = x; throw new Error(x); + } finally { + afterExecute(task, thrown); + } + } finally { + task = null; + w.completedTasks++; + w.unlock(); } - } catch(InterruptedException ie) { - // fall through - } finally { - workerDone(this); } + completedAbruptly = false; + } finally { + processWorkerExit(w, completedAbruptly); } } - // Public methods + // Public constructors and methods /** - * Creates a new ThreadPoolExecutor with the given - * initial parameters and default thread factory and handler. It - * may be more convenient to use one of the {@link Executors} - * factory methods instead of this general purpose constructor. + * Creates a new {@code ThreadPoolExecutor} with the given initial + * parameters and default thread factory and rejected execution handler. + * It may be more convenient to use one of the {@link Executors} factory + * methods instead of this general purpose constructor. * - * @param corePoolSize the number of threads to keep in the - * pool, even if they are idle. + * @param corePoolSize the number of threads to keep in the pool, even + * if they are idle * @param maximumPoolSize the maximum number of threads to allow in the - * pool. + * pool * @param keepAliveTime when the number of threads is greater than - * the core, this is the maximum time that excess idle threads - * will wait for new tasks before terminating. - * @param unit the time unit for the keepAliveTime - * argument. - * @param workQueue the queue to use for holding tasks before they - * are executed. This queue will hold only the Runnable - * tasks submitted by the execute method. - * @throws IllegalArgumentException if corePoolSize, or - * keepAliveTime less than zero, or if maximumPoolSize less than or - * equal to zero, or if corePoolSize greater than maximumPoolSize. - * @throws NullPointerException if workQueue is null + * the core, this is the maximum time that excess idle threads + * will wait for new tasks before terminating. + * @param unit the time unit for the {@code keepAliveTime} argument + * @param workQueue the queue to use for holding tasks before they are + * executed. This queue will hold only the {@code Runnable} + * tasks submitted by the {@code execute} method. + * @throws IllegalArgumentException if one of the following holds:
      + * {@code corePoolSize < 0}
      + * {@code keepAliveTime < 0}
      + * {@code maximumPoolSize <= 0}
      + * {@code maximumPoolSize < corePoolSize} + * @throws NullPointerException if {@code workQueue} is null */ public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, @@ -716,28 +1123,29 @@ public class ThreadPoolExecutor extends AbstractExecutorService { } /** - * Creates a new ThreadPoolExecutor with the given initial - * parameters. + * Creates a new {@code ThreadPoolExecutor} with the given initial + * parameters and default rejected execution handler. * - * @param corePoolSize the number of threads to keep in the - * pool, even if they are idle. + * @param corePoolSize the number of threads to keep in the pool, even + * if they are idle * @param maximumPoolSize the maximum number of threads to allow in the - * pool. + * pool * @param keepAliveTime when the number of threads is greater than - * the core, this is the maximum time that excess idle threads - * will wait for new tasks before terminating. - * @param unit the time unit for the keepAliveTime - * argument. - * @param workQueue the queue to use for holding tasks before they - * are executed. This queue will hold only the Runnable - * tasks submitted by the execute method. + * the core, this is the maximum time that excess idle threads + * will wait for new tasks before terminating. + * @param unit the time unit for the {@code keepAliveTime} argument + * @param workQueue the queue to use for holding tasks before they are + * executed. This queue will hold only the {@code Runnable} + * tasks submitted by the {@code execute} method. * @param threadFactory the factory to use when the executor - * creates a new thread. - * @throws IllegalArgumentException if corePoolSize, or - * keepAliveTime less than zero, or if maximumPoolSize less than or - * equal to zero, or if corePoolSize greater than maximumPoolSize. - * @throws NullPointerException if workQueue - * or threadFactory are null. + * creates a new thread + * @throws IllegalArgumentException if one of the following holds:
      + * {@code corePoolSize < 0}
      + * {@code keepAliveTime < 0}
      + * {@code maximumPoolSize <= 0}
      + * {@code maximumPoolSize < corePoolSize} + * @throws NullPointerException if {@code workQueue} + * or {@code threadFactory} is null */ public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, @@ -750,28 +1158,29 @@ public class ThreadPoolExecutor extends AbstractExecutorService { } /** - * Creates a new ThreadPoolExecutor with the given initial - * parameters. + * Creates a new {@code ThreadPoolExecutor} with the given initial + * parameters and default thread factory. * - * @param corePoolSize the number of threads to keep in the - * pool, even if they are idle. + * @param corePoolSize the number of threads to keep in the pool, even + * if they are idle * @param maximumPoolSize the maximum number of threads to allow in the - * pool. + * pool * @param keepAliveTime when the number of threads is greater than - * the core, this is the maximum time that excess idle threads - * will wait for new tasks before terminating. - * @param unit the time unit for the keepAliveTime - * argument. - * @param workQueue the queue to use for holding tasks before they - * are executed. This queue will hold only the Runnable - * tasks submitted by the execute method. + * the core, this is the maximum time that excess idle threads + * will wait for new tasks before terminating. + * @param unit the time unit for the {@code keepAliveTime} argument + * @param workQueue the queue to use for holding tasks before they are + * executed. This queue will hold only the {@code Runnable} + * tasks submitted by the {@code execute} method. * @param handler the handler to use when execution is blocked - * because the thread bounds and queue capacities are reached. - * @throws IllegalArgumentException if corePoolSize, or - * keepAliveTime less than zero, or if maximumPoolSize less than or - * equal to zero, or if corePoolSize greater than maximumPoolSize. - * @throws NullPointerException if workQueue - * or handler are null. + * because the thread bounds and queue capacities are reached + * @throws IllegalArgumentException if one of the following holds:
      + * {@code corePoolSize < 0}
      + * {@code keepAliveTime < 0}
      + * {@code maximumPoolSize <= 0}
      + * {@code maximumPoolSize < corePoolSize} + * @throws NullPointerException if {@code workQueue} + * or {@code handler} is null */ public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, @@ -784,30 +1193,31 @@ public class ThreadPoolExecutor extends AbstractExecutorService { } /** - * Creates a new ThreadPoolExecutor with the given initial + * Creates a new {@code ThreadPoolExecutor} with the given initial * parameters. * - * @param corePoolSize the number of threads to keep in the - * pool, even if they are idle. + * @param corePoolSize the number of threads to keep in the pool, even + * if they are idle * @param maximumPoolSize the maximum number of threads to allow in the - * pool. + * pool * @param keepAliveTime when the number of threads is greater than - * the core, this is the maximum time that excess idle threads - * will wait for new tasks before terminating. - * @param unit the time unit for the keepAliveTime - * argument. - * @param workQueue the queue to use for holding tasks before they - * are executed. This queue will hold only the Runnable - * tasks submitted by the execute method. + * the core, this is the maximum time that excess idle threads + * will wait for new tasks before terminating. + * @param unit the time unit for the {@code keepAliveTime} argument + * @param workQueue the queue to use for holding tasks before they are + * executed. This queue will hold only the {@code Runnable} + * tasks submitted by the {@code execute} method. * @param threadFactory the factory to use when the executor - * creates a new thread. + * creates a new thread * @param handler the handler to use when execution is blocked - * because the thread bounds and queue capacities are reached. - * @throws IllegalArgumentException if corePoolSize, or - * keepAliveTime less than zero, or if maximumPoolSize less than or - * equal to zero, or if corePoolSize greater than maximumPoolSize. - * @throws NullPointerException if workQueue - * or threadFactory or handler are null. + * because the thread bounds and queue capacities are reached + * @throws IllegalArgumentException if one of the following holds:
      + * {@code corePoolSize < 0}
      + * {@code keepAliveTime < 0}
      + * {@code maximumPoolSize <= 0}
      + * {@code maximumPoolSize < corePoolSize} + * @throws NullPointerException if {@code workQueue} + * or {@code threadFactory} or {@code handler} is null */ public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, @@ -831,181 +1241,140 @@ public class ThreadPoolExecutor extends AbstractExecutorService { this.handler = handler; } - /** * Executes the given task sometime in the future. The task * may execute in a new thread or in an existing pooled thread. * * If the task cannot be submitted for execution, either because this * executor has been shutdown or because its capacity has been reached, - * the task is handled by the current RejectedExecutionHandler. + * the task is handled by the current {@code RejectedExecutionHandler}. * * @param command the task to execute * @throws RejectedExecutionException at discretion of - * RejectedExecutionHandler, if task cannot be accepted - * for execution - * @throws NullPointerException if command is null + * {@code RejectedExecutionHandler}, if the task + * cannot be accepted for execution + * @throws NullPointerException if {@code command} is null */ public void execute(Runnable command) { if (command == null) throw new NullPointerException(); - for (;;) { - if (runState != RUNNING) { - reject(command); - return; - } - if (poolSize < corePoolSize && addIfUnderCorePoolSize(command)) - return; - if (workQueue.offer(command)) - return; - Runnable r = addIfUnderMaximumPoolSize(command); - if (r == command) + /* + * Proceed in 3 steps: + * + * 1. If fewer than corePoolSize threads are running, try to + * start a new thread with the given command as its first + * task. The call to addWorker atomically checks runState and + * workerCount, and so prevents false alarms that would add + * threads when it shouldn't, by returning false. + * + * 2. If a task can be successfully queued, then we still need + * to double-check whether we should have added a thread + * (because existing ones died since last checking) or that + * the pool shut down since entry into this method. So we + * recheck state and if necessary roll back the enqueuing if + * stopped, or start a new thread if there are none. + * + * 3. If we cannot queue task, then we try to add a new + * thread. If it fails, we know we are shut down or saturated + * and so reject the task. + */ + int c = ctl.get(); + if (workerCountOf(c) < corePoolSize) { + if (addWorker(command, true)) return; - if (r == null) { + c = ctl.get(); + } + if (isRunning(c) && workQueue.offer(command)) { + int recheck = ctl.get(); + if (! isRunning(recheck) && remove(command)) reject(command); - return; - } - // else retry + else if (workerCountOf(recheck) == 0) + addWorker(null, false); } + else if (!addWorker(command, false)) + reject(command); } /** * Initiates an orderly shutdown in which previously submitted - * tasks are executed, but no new tasks will be - * accepted. Invocation has no additional effect if already shut - * down. - * @throws SecurityException if a security manager exists and - * shutting down this ExecutorService may manipulate threads that - * the caller is not permitted to modify because it does not hold - * {@link java.lang.RuntimePermission}("modifyThread"), - * or the security manager's checkAccess method denies access. + * tasks are executed, but no new tasks will be accepted. + * Invocation has no additional effect if already shut down. + * + *

      This method does not wait for previously submitted tasks to + * complete execution. Use {@link #awaitTermination awaitTermination} + * to do that. + * + * @throws SecurityException {@inheritDoc} */ public void shutdown() { - // Fail if caller doesn't have modifyThread permission - SecurityManager security = System.getSecurityManager(); - if (security != null) - java.security.AccessController.checkPermission(shutdownPerm); - - boolean fullyTerminated = false; final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { - if (workers.size() > 0) { - // Check if caller can modify worker threads. This - // might not be true even if passed above check, if - // the SecurityManager treats some threads specially. - if (security != null) { - for (Worker w: workers) - security.checkAccess(w.thread); - } - - int state = runState; - if (state == RUNNING) // don't override shutdownNow - runState = SHUTDOWN; - - try { - for (Worker w: workers) - w.interruptIfIdle(); - } catch(SecurityException se) { - // If SecurityManager allows above checks, but - // then unexpectedly throws exception when - // interrupting threads (which it ought not do), - // back out as cleanly as we can. Some threads may - // have been killed but we remain in non-shutdown - // state. - runState = state; - throw se; - } - } - else { // If no workers, trigger full termination now - fullyTerminated = true; - runState = TERMINATED; - termination.signalAll(); - } + checkShutdownAccess(); + advanceRunState(SHUTDOWN); + interruptIdleWorkers(); + onShutdown(); // hook for ScheduledThreadPoolExecutor } finally { mainLock.unlock(); } - if (fullyTerminated) - terminated(); + tryTerminate(); } - /** * Attempts to stop all actively executing tasks, halts the - * processing of waiting tasks, and returns a list of the tasks that were - * awaiting execution. - * - *

      This implementation cancels tasks via {@link - * Thread#interrupt}, so if any tasks mask or fail to respond to - * interrupts, they may never terminate. + * processing of waiting tasks, and returns a list of the tasks + * that were awaiting execution. These tasks are drained (removed) + * from the task queue upon return from this method. * - * @return list of tasks that never commenced execution - * @throws SecurityException if a security manager exists and - * shutting down this ExecutorService may manipulate threads that - * the caller is not permitted to modify because it does not hold - * {@link java.lang.RuntimePermission}("modifyThread"), - * or the security manager's checkAccess method denies access. + *

      This method does not wait for actively executing tasks to + * terminate. Use {@link #awaitTermination awaitTermination} to + * do that. + * + *

      There are no guarantees beyond best-effort attempts to stop + * processing actively executing tasks. This implementation + * cancels tasks via {@link Thread#interrupt}, so any task that + * fails to respond to interrupts may never terminate. + * + * @throws SecurityException {@inheritDoc} */ public List shutdownNow() { - // Almost the same code as shutdown() - SecurityManager security = System.getSecurityManager(); - if (security != null) - java.security.AccessController.checkPermission(shutdownPerm); - - boolean fullyTerminated = false; + List tasks; final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { - if (workers.size() > 0) { - if (security != null) { - for (Worker w: workers) - security.checkAccess(w.thread); - } - - int state = runState; - if (state != TERMINATED) - runState = STOP; - try { - for (Worker w : workers) - w.interruptNow(); - } catch(SecurityException se) { - runState = state; // back out; - throw se; - } - } - else { // If no workers, trigger full termination now - fullyTerminated = true; - runState = TERMINATED; - termination.signalAll(); - } + checkShutdownAccess(); + advanceRunState(STOP); + interruptWorkers(); + tasks = drainQueue(); } finally { mainLock.unlock(); } - if (fullyTerminated) - terminated(); - return Arrays.asList(workQueue.toArray(EMPTY_RUNNABLE_ARRAY)); + tryTerminate(); + return tasks; } public boolean isShutdown() { - return runState != RUNNING; + return ! isRunning(ctl.get()); } - /** + /** * Returns true if this executor is in the process of terminating - * after shutdown or shutdownNow but has not + * after {@link #shutdown} or {@link #shutdownNow} but has not * completely terminated. This method may be useful for - * debugging. A return of true reported a sufficient + * debugging. A return of {@code true} reported a sufficient * period after shutdown may indicate that submitted tasks have * ignored or suppressed interruption, causing this executor not * to properly terminate. - * @return true if terminating but not yet terminated. + * + * @return true if terminating but not yet terminated */ public boolean isTerminating() { - return runState == STOP; + int c = ctl.get(); + return ! isRunning(c) && runStateLessThan(c, TERMINATED); } public boolean isTerminated() { - return runState == TERMINATED; + return runStateAtLeast(ctl.get(), TERMINATED); } public boolean awaitTermination(long timeout, TimeUnit unit) @@ -1015,7 +1384,7 @@ public class ThreadPoolExecutor extends AbstractExecutorService { mainLock.lock(); try { for (;;) { - if (runState == TERMINATED) + if (runStateAtLeast(ctl.get(), TERMINATED)) return true; if (nanos <= 0) return false; @@ -1027,10 +1396,10 @@ public class ThreadPoolExecutor extends AbstractExecutorService { } /** - * Invokes shutdown when this executor is no longer - * referenced. - */ - protected void finalize() { + * Invokes {@code shutdown} when this executor is no longer + * referenced and it has no threads. + */ + protected void finalize() { shutdown(); } @@ -1080,104 +1449,34 @@ public class ThreadPoolExecutor extends AbstractExecutorService { return handler; } - /** - * Returns the task queue used by this executor. Access to the - * task queue is intended primarily for debugging and monitoring. - * This queue may be in active use. Retrieving the task queue - * does not prevent queued tasks from executing. - * - * @return the task queue - */ - public BlockingQueue getQueue() { - return workQueue; - } - - /** - * Removes this task from the executor's internal queue if it is - * present, thus causing it not to be run if it has not already - * started. - * - *

      This method may be useful as one part of a cancellation - * scheme. It may fail to remove tasks that have been converted - * into other forms before being placed on the internal queue. For - * example, a task entered using submit might be - * converted into a form that maintains Future status. - * However, in such cases, method {@link ThreadPoolExecutor#purge} - * may be used to remove those Futures that have been cancelled. - * - * - * @param task the task to remove - * @return true if the task was removed - */ - public boolean remove(Runnable task) { - return getQueue().remove(task); - } - - - /** - * Tries to remove from the work queue all {@link Future} - * tasks that have been cancelled. This method can be useful as a - * storage reclamation operation, that has no other impact on - * functionality. Cancelled tasks are never executed, but may - * accumulate in work queues until worker threads can actively - * remove them. Invoking this method instead tries to remove them now. - * However, this method may fail to remove tasks in - * the presence of interference by other threads. - */ - public void purge() { - // Fail if we encounter interference during traversal - try { - Iterator it = getQueue().iterator(); - while (it.hasNext()) { - Runnable r = it.next(); - if (r instanceof Future) { - Future c = (Future)r; - if (c.isCancelled()) - it.remove(); - } - } - } - catch(ConcurrentModificationException ex) { - return; - } - } - /** * Sets the core number of threads. This overrides any value set * in the constructor. If the new value is smaller than the * current value, excess existing threads will be terminated when - * they next become idle. If larger, new threads will, if needed, + * they next become idle. If larger, new threads will, if needed, * be started to execute any queued tasks. * * @param corePoolSize the new core size - * @throws IllegalArgumentException if corePoolSize - * less than zero + * @throws IllegalArgumentException if {@code corePoolSize < 0} * @see #getCorePoolSize */ public void setCorePoolSize(int corePoolSize) { if (corePoolSize < 0) throw new IllegalArgumentException(); - final ReentrantLock mainLock = this.mainLock; - mainLock.lock(); - try { - int extra = this.corePoolSize - corePoolSize; - this.corePoolSize = corePoolSize; - if (extra < 0) { - Runnable r; - while (extra++ < 0 && poolSize < corePoolSize && - (r = workQueue.poll()) != null) - addThread(r).start(); - } - else if (extra > 0 && poolSize > corePoolSize) { - Iterator it = workers.iterator(); - while (it.hasNext() && - extra-- > 0 && - poolSize > corePoolSize && - workQueue.remainingCapacity() == 0) - it.next().interruptIfIdle(); + int delta = corePoolSize - this.corePoolSize; + this.corePoolSize = corePoolSize; + if (workerCountOf(ctl.get()) > corePoolSize) + interruptIdleWorkers(); + else if (delta > 0) { + // We don't really know how many new threads are "needed". + // As a heuristic, prestart enough new workers (up to new + // core size) to handle the current number of tasks in + // queue, but stop if queue becomes empty while doing so. + int k = Math.min(delta, workQueue.size()); + while (k-- > 0 && addWorker(null, true)) { + if (workQueue.isEmpty()) + break; } - } finally { - mainLock.unlock(); } } @@ -1194,23 +1493,26 @@ public class ThreadPoolExecutor extends AbstractExecutorService { /** * Starts a core thread, causing it to idly wait for work. This * overrides the default policy of starting core threads only when - * new tasks are executed. This method will return false + * new tasks are executed. This method will return {@code false} * if all core threads have already been started. - * @return true if a thread was started - */ + * + * @return {@code true} if a thread was started + */ public boolean prestartCoreThread() { - return addIfUnderCorePoolSize(null); + return workerCountOf(ctl.get()) < corePoolSize && + addWorker(null, true); } /** * Starts all core threads, causing them to idly wait for work. This * overrides the default policy of starting core threads only when - * new tasks are executed. - * @return the number of threads started. - */ + * new tasks are executed. + * + * @return the number of threads started + */ public int prestartAllCoreThreads() { int n = 0; - while (addIfUnderCorePoolSize(null)) + while (addWorker(null, true)) ++n; return n; } @@ -1222,30 +1524,17 @@ public class ThreadPoolExecutor extends AbstractExecutorService { * terminated when they next become idle. * * @param maximumPoolSize the new maximum - * @throws IllegalArgumentException if maximumPoolSize less than zero or - * the {@link #getCorePoolSize core pool size} + * @throws IllegalArgumentException if the new maximum is + * less than or equal to zero, or + * less than the {@linkplain #getCorePoolSize core pool size} * @see #getMaximumPoolSize */ public void setMaximumPoolSize(int maximumPoolSize) { if (maximumPoolSize <= 0 || maximumPoolSize < corePoolSize) throw new IllegalArgumentException(); - final ReentrantLock mainLock = this.mainLock; - mainLock.lock(); - try { - int extra = this.maximumPoolSize - maximumPoolSize; - this.maximumPoolSize = maximumPoolSize; - if (extra > 0 && poolSize > maximumPoolSize) { - Iterator it = workers.iterator(); - while (it.hasNext() && - extra > 0 && - poolSize > maximumPoolSize) { - it.next().interruptIfIdle(); - --extra; - } - } - } finally { - mainLock.unlock(); - } + this.maximumPoolSize = maximumPoolSize; + if (workerCountOf(ctl.get()) > maximumPoolSize) + interruptIdleWorkers(); } /** @@ -1264,21 +1553,27 @@ public class ThreadPoolExecutor extends AbstractExecutorService { * threads currently in the pool, after waiting this amount of * time without processing a task, excess threads will be * terminated. This overrides any value set in the constructor. + * * @param time the time to wait. A time value of zero will cause - * excess threads to terminate immediately after executing tasks. - * @param unit the time unit of the time argument - * @throws IllegalArgumentException if time less than zero + * excess threads to terminate immediately after executing tasks. + * @param unit the time unit of the {@code time} argument + * @throws IllegalArgumentException if {@code time} less than zero or + * if {@code time} is zero and {@code allowsCoreThreadTimeOut} * @see #getKeepAliveTime */ public void setKeepAliveTime(long time, TimeUnit unit) { if (time < 0) throw new IllegalArgumentException(); - this.keepAliveTime = unit.toNanos(time); + long keepAliveTime = unit.toNanos(time); + long delta = keepAliveTime - this.keepAliveTime; + this.keepAliveTime = keepAliveTime; + if (delta < 0) + interruptIdleWorkers(); } /** * Returns the thread keep-alive time, which is the amount of time - * which threads in excess of the core pool size may remain + * that threads in excess of the core pool size may remain * idle before being terminated. * * @param unit the desired time unit of the result @@ -1289,6 +1584,73 @@ public class ThreadPoolExecutor extends AbstractExecutorService { return unit.convert(keepAliveTime, TimeUnit.NANOSECONDS); } + /* User-level queue utilities */ + + /** + * Returns the task queue used by this executor. Access to the + * task queue is intended primarily for debugging and monitoring. + * This queue may be in active use. Retrieving the task queue + * does not prevent queued tasks from executing. + * + * @return the task queue + */ + public BlockingQueue getQueue() { + return workQueue; + } + + /** + * Removes this task from the executor's internal queue if it is + * present, thus causing it not to be run if it has not already + * started. + * + *

      This method may be useful as one part of a cancellation + * scheme. It may fail to remove tasks that have been converted + * into other forms before being placed on the internal queue. For + * example, a task entered using {@code submit} might be + * converted into a form that maintains {@code Future} status. + * However, in such cases, method {@link #purge} may be used to + * remove those Futures that have been cancelled. + * + * @param task the task to remove + * @return true if the task was removed + */ + public boolean remove(Runnable task) { + boolean removed = workQueue.remove(task); + tryTerminate(); // In case SHUTDOWN and now empty + return removed; + } + + /** + * Tries to remove from the work queue all {@link Future} + * tasks that have been cancelled. This method can be useful as a + * storage reclamation operation, that has no other impact on + * functionality. Cancelled tasks are never executed, but may + * accumulate in work queues until worker threads can actively + * remove them. Invoking this method instead tries to remove them now. + * However, this method may fail to remove tasks in + * the presence of interference by other threads. + */ + public void purge() { + final BlockingQueue q = workQueue; + try { + Iterator it = q.iterator(); + while (it.hasNext()) { + Runnable r = it.next(); + if (r instanceof Future && ((Future)r).isCancelled()) + it.remove(); + } + } catch (ConcurrentModificationException fallThrough) { + // Take slow path if we encounter interference during traversal. + // Make copy for traversal and call remove for cancelled entries. + // The slow path is more likely to be O(N*N). + for (Object r : q.toArray()) + if (r instanceof Future && ((Future)r).isCancelled()) + q.remove(r); + } + + tryTerminate(); // In case SHUTDOWN and now empty + } + /* Statistics */ /** @@ -1297,7 +1659,16 @@ public class ThreadPoolExecutor extends AbstractExecutorService { * @return the number of threads */ public int getPoolSize() { - return poolSize; + final ReentrantLock mainLock = this.mainLock; + mainLock.lock(); + try { + // Remove rare and surprising possibility of + // isTerminated() && getPoolSize() > 0 + return runStateAtLeast(ctl.get(), TIDYING) ? 0 + : workers.size(); + } finally { + mainLock.unlock(); + } } /** @@ -1311,10 +1682,9 @@ public class ThreadPoolExecutor extends AbstractExecutorService { mainLock.lock(); try { int n = 0; - for (Worker w : workers) { - if (w.isActive()) + for (Worker w : workers) + if (w.isLocked()) ++n; - } return n; } finally { mainLock.unlock(); @@ -1338,11 +1708,10 @@ public class ThreadPoolExecutor extends AbstractExecutorService { } /** - * Returns the approximate total number of tasks that have been + * Returns the approximate total number of tasks that have ever been * scheduled for execution. Because the states of tasks and * threads may change dynamically during computation, the returned - * value is only an approximation, but one that does not ever - * decrease across successive calls. + * value is only an approximation. * * @return the number of tasks */ @@ -1353,7 +1722,7 @@ public class ThreadPoolExecutor extends AbstractExecutorService { long n = completedTaskCount; for (Worker w : workers) { n += w.completedTasks; - if (w.isActive()) + if (w.isLocked()) ++n; } return n + workQueue.size(); @@ -1384,30 +1753,69 @@ public class ThreadPoolExecutor extends AbstractExecutorService { } } + /* Extension hooks */ + /** * Method invoked prior to executing the given Runnable in the - * given thread. This method is invoked by thread t that - * will execute task r, and may be used to re-initialize - * ThreadLocals, or to perform logging. Note: To properly nest - * multiple overridings, subclasses should generally invoke - * super.beforeExecute at the end of this method. + * given thread. This method is invoked by thread {@code t} that + * will execute task {@code r}, and may be used to re-initialize + * ThreadLocals, or to perform logging. + * + *

      This implementation does nothing, but may be customized in + * subclasses. Note: To properly nest multiple overridings, subclasses + * should generally invoke {@code super.beforeExecute} at the end of + * this method. * - * @param t the thread that will run task r. - * @param r the task that will be executed. + * @param t the thread that will run task {@code r} + * @param r the task that will be executed */ protected void beforeExecute(Thread t, Runnable r) { } /** - * Method invoked upon completion of execution of the given - * Runnable. This method is invoked by the thread that executed - * the task. If non-null, the Throwable is the uncaught exception - * that caused execution to terminate abruptly. Note: To properly - * nest multiple overridings, subclasses should generally invoke - * super.afterExecute at the beginning of this method. + * Method invoked upon completion of execution of the given Runnable. + * This method is invoked by the thread that executed the task. If + * non-null, the Throwable is the uncaught {@code RuntimeException} + * or {@code Error} that caused execution to terminate abruptly. + * + *

      This implementation does nothing, but may be customized in + * subclasses. Note: To properly nest multiple overridings, subclasses + * should generally invoke {@code super.afterExecute} at the + * beginning of this method. * - * @param r the runnable that has completed. + *

      Note: When actions are enclosed in tasks (such as + * {@link FutureTask}) either explicitly or via methods such as + * {@code submit}, these task objects catch and maintain + * computational exceptions, and so they do not cause abrupt + * termination, and the internal exceptions are not + * passed to this method. If you would like to trap both kinds of + * failures in this method, you can further probe for such cases, + * as in this sample subclass that prints either the direct cause + * or the underlying exception if a task has been aborted: + * + *

       {@code
      +     * class ExtendedExecutor extends ThreadPoolExecutor {
      +     *   // ...
      +     *   protected void afterExecute(Runnable r, Throwable t) {
      +     *     super.afterExecute(r, t);
      +     *     if (t == null && r instanceof Future) {
      +     *       try {
      +     *         Object result = ((Future) r).get();
      +     *       } catch (CancellationException ce) {
      +     *           t = ce;
      +     *       } catch (ExecutionException ee) {
      +     *           t = ee.getCause();
      +     *       } catch (InterruptedException ie) {
      +     *           Thread.currentThread().interrupt(); // ignore/reset
      +     *       }
      +     *     }
      +     *     if (t != null)
      +     *       System.out.println(t);
      +     *   }
      +     * }}
      + * + * @param r the runnable that has completed * @param t the exception that caused termination, or null if - * execution completed normally. + * execution completed normally */ protected void afterExecute(Runnable r, Throwable t) { } @@ -1415,25 +1823,28 @@ public class ThreadPoolExecutor extends AbstractExecutorService { * Method invoked when the Executor has terminated. Default * implementation does nothing. Note: To properly nest multiple * overridings, subclasses should generally invoke - * super.terminated within this method. + * {@code super.terminated} within this method. */ protected void terminated() { } + /* Predefined RejectedExecutionHandlers */ + /** * A handler for rejected tasks that runs the rejected task - * directly in the calling thread of the execute method, + * directly in the calling thread of the {@code execute} method, * unless the executor has been shut down, in which case the task * is discarded. */ - public static class CallerRunsPolicy implements RejectedExecutionHandler { + public static class CallerRunsPolicy implements RejectedExecutionHandler { /** - * Creates a CallerRunsPolicy. + * Creates a {@code CallerRunsPolicy}. */ public CallerRunsPolicy() { } /** * Executes task r in the caller's thread, unless the executor * has been shut down, in which case the task is discarded. + * * @param r the runnable task requested to be executed * @param e the executor attempting to execute this task */ @@ -1446,16 +1857,17 @@ public class ThreadPoolExecutor extends AbstractExecutorService { /** * A handler for rejected tasks that throws a - * RejectedExecutionException. + * {@code RejectedExecutionException}. */ public static class AbortPolicy implements RejectedExecutionHandler { /** - * Creates an AbortPolicy. + * Creates an {@code AbortPolicy}. */ public AbortPolicy() { } /** * Always throws RejectedExecutionException. + * * @param r the runnable task requested to be executed * @param e the executor attempting to execute this task * @throws RejectedExecutionException always. @@ -1471,12 +1883,13 @@ public class ThreadPoolExecutor extends AbstractExecutorService { */ public static class DiscardPolicy implements RejectedExecutionHandler { /** - * Creates a DiscardPolicy. + * Creates a {@code DiscardPolicy}. */ public DiscardPolicy() { } /** * Does nothing, which has the effect of discarding task r. + * * @param r the runnable task requested to be executed * @param e the executor attempting to execute this task */ @@ -1486,12 +1899,12 @@ public class ThreadPoolExecutor extends AbstractExecutorService { /** * A handler for rejected tasks that discards the oldest unhandled - * request and then retries execute, unless the executor + * request and then retries {@code execute}, unless the executor * is shut down, in which case the task is discarded. */ public static class DiscardOldestPolicy implements RejectedExecutionHandler { /** - * Creates a DiscardOldestPolicy for the given executor. + * Creates a {@code DiscardOldestPolicy} for the given executor. */ public DiscardOldestPolicy() { } @@ -1500,6 +1913,7 @@ public class ThreadPoolExecutor extends AbstractExecutorService { * would otherwise execute, if one is immediately available, * and then retries execution of task r, unless the executor * is shut down, in which case task r is instead discarded. + * * @param r the runnable task requested to be executed * @param e the executor attempting to execute this task */ diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/TimeUnit.java b/libcore/concurrent/src/main/java/java/util/concurrent/TimeUnit.java index b186aebed..412d28a21 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/TimeUnit.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/TimeUnit.java @@ -12,7 +12,11 @@ package java.util.concurrent; * and to perform timing and delay operations in these units. A * TimeUnit does not maintain time information, but only * helps organize and use time representations that may be maintained - * separately across various contexts. + * separately across various contexts. A nanosecond is defined as one + * thousandth of a microsecond, a microsecond as one thousandth of a + * millisecond, a millisecond as one thousandth of a second, a minute + * as sixty seconds, an hour as sixty minutes, and a day as twenty four + * hours. * *

      A TimeUnit is mainly used to inform time-based methods * how a given timing parameter should be interpreted. For example, @@ -37,61 +41,80 @@ package java.util.concurrent; */ public enum TimeUnit { /** TimeUnit which represents one nanosecond. */ - NANOSECONDS(0), + NANOSECONDS { + public long toNanos(long d) { return d; } + public long toMicros(long d) { return d/(C1/C0); } + public long toMillis(long d) { return d/(C2/C0); } + public long toSeconds(long d) { return d/(C3/C0); } + public long toMinutes(long d) { return d/(C4/C0); } + public long toHours(long d) { return d/(C5/C0); } + public long toDays(long d) { return d/(C6/C0); } + public long convert(long d, TimeUnit u) { return u.toNanos(d); } + int excessNanos(long d, long m) { return (int)(d - (m*C2)); } + }, /** TimeUnit which represents one microsecond. */ - MICROSECONDS(1), + MICROSECONDS { + public long toNanos(long d) { return x(d, C1/C0, MAX/(C1/C0)); } + public long toMicros(long d) { return d; } + public long toMillis(long d) { return d/(C2/C1); } + public long toSeconds(long d) { return d/(C3/C1); } + public long toMinutes(long d) { return d/(C4/C1); } + public long toHours(long d) { return d/(C5/C1); } + public long toDays(long d) { return d/(C6/C1); } + public long convert(long d, TimeUnit u) { return u.toMicros(d); } + int excessNanos(long d, long m) { return (int)((d*C1) - (m*C2)); } + }, /** TimeUnit which represents one millisecond. */ - MILLISECONDS(2), + MILLISECONDS { + public long toNanos(long d) { return x(d, C2/C0, MAX/(C2/C0)); } + public long toMicros(long d) { return x(d, C2/C1, MAX/(C2/C1)); } + public long toMillis(long d) { return d; } + public long toSeconds(long d) { return d/(C3/C2); } + public long toMinutes(long d) { return d/(C4/C2); } + public long toHours(long d) { return d/(C5/C2); } + public long toDays(long d) { return d/(C6/C2); } + public long convert(long d, TimeUnit u) { return u.toMillis(d); } + int excessNanos(long d, long m) { return 0; } + }, /** TimeUnit which represents one second. */ - SECONDS(3); + SECONDS { + public long toNanos(long d) { return x(d, C3/C0, MAX/(C3/C0)); } + public long toMicros(long d) { return x(d, C3/C1, MAX/(C3/C1)); } + public long toMillis(long d) { return x(d, C3/C2, MAX/(C3/C2)); } + public long toSeconds(long d) { return d; } + public long toMinutes(long d) { return d/(C4/C3); } + public long toHours(long d) { return d/(C5/C3); } + public long toDays(long d) { return d/(C6/C3); } + public long convert(long d, TimeUnit u) { return u.toSeconds(d); } + int excessNanos(long d, long m) { return 0; } + }; - /** the index of this unit */ - private final int index; + // Handy constants for conversion methods + static final long C0 = 1L; + static final long C1 = C0 * 1000L; + static final long C2 = C1 * 1000L; + static final long C3 = C2 * 1000L; + static final long C4 = C3 * 60L; + static final long C5 = C4 * 60L; + static final long C6 = C5 * 24L; - /** Internal constructor */ - TimeUnit(int index) { - this.index = index; - } - - /** Lookup table for conversion factors */ - private static final int[] multipliers = { - 1, - 1000, - 1000 * 1000, - 1000 * 1000 * 1000 - }; - - /** - * Lookup table to check saturation. Note that because we are - * dividing these down, we don't have to deal with asymmetry of - * MIN/MAX values. - */ - private static final long[] overflows = { - 0, // unused - Long.MAX_VALUE / 1000, - Long.MAX_VALUE / (1000 * 1000), - Long.MAX_VALUE / (1000 * 1000 * 1000) - }; + static final long MAX = Long.MAX_VALUE; /** - * Perform conversion based on given delta representing the - * difference between units - * @param delta the difference in index values of source and target units - * @param duration the duration - * @return converted duration or saturated value + * Scale d by m, checking for overflow. + * This has a short name to make above code more readable. */ - private static long doConvert(int delta, long duration) { - if (delta == 0) - return duration; - if (delta < 0) - return duration / multipliers[-delta]; - if (duration > overflows[delta]) - return Long.MAX_VALUE; - if (duration < -overflows[delta]) - return Long.MIN_VALUE; - return duration * multipliers[delta]; + static long x(long d, long m, long over) { + if (d > over) return Long.MAX_VALUE; + if (d < -over) return Long.MIN_VALUE; + return d * m; } + // To maintain full signature compatibility with 1.5, and to improve the + // clarity of the generated javadoc (see 6287639: Abstract methods in + // enum classes should not be listed as abstract), method convert + // etc. are not declared abstract but otherwise act as abstract methods. + /** * Convert the given time duration in the given unit to this * unit. Conversions from finer to coarser granularities @@ -102,14 +125,17 @@ public enum TimeUnit { * Long.MIN_VALUE if negative or Long.MAX_VALUE * if positive. * - * @param duration the time duration in the given unit - * @param unit the unit of the duration argument + *

      For example, to convert 10 minutes to milliseconds, use: + * TimeUnit.MILLISECONDS.convert(10L, TimeUnit.MINUTES) + * + * @param sourceDuration the time duration in the given sourceUnit + * @param sourceUnit the unit of the sourceDuration argument * @return the converted duration in this unit, * or Long.MIN_VALUE if conversion would negatively * overflow, or Long.MAX_VALUE if it would positively overflow. */ - public long convert(long duration, TimeUnit unit) { - return doConvert(unit.index - index, duration); + public long convert(long sourceDuration, TimeUnit sourceUnit) { + throw new AbstractMethodError(); } /** @@ -121,7 +147,7 @@ public enum TimeUnit { * @see #convert */ public long toNanos(long duration) { - return doConvert(index, duration); + throw new AbstractMethodError(); } /** @@ -133,7 +159,7 @@ public enum TimeUnit { * @see #convert */ public long toMicros(long duration) { - return doConvert(index - MICROSECONDS.index, duration); + throw new AbstractMethodError(); } /** @@ -145,34 +171,66 @@ public enum TimeUnit { * @see #convert */ public long toMillis(long duration) { - return doConvert(index - MILLISECONDS.index, duration); + throw new AbstractMethodError(); } /** * Equivalent to SECONDS.convert(duration, this). * @param duration the duration - * @return the converted duration. + * @return the converted duration, + * or Long.MIN_VALUE if conversion would negatively + * overflow, or Long.MAX_VALUE if it would positively overflow. * @see #convert */ public long toSeconds(long duration) { - return doConvert(index - SECONDS.index, duration); + throw new AbstractMethodError(); + } + + /** + * Equivalent to MINUTES.convert(duration, this). + * @param duration the duration + * @return the converted duration, + * or Long.MIN_VALUE if conversion would negatively + * overflow, or Long.MAX_VALUE if it would positively overflow. + * @see #convert + */ + long toMinutes(long duration) { + throw new AbstractMethodError(); } + /** + * Equivalent to HOURS.convert(duration, this). + * @param duration the duration + * @return the converted duration, + * or Long.MIN_VALUE if conversion would negatively + * overflow, or Long.MAX_VALUE if it would positively overflow. + * @see #convert + */ + long toHours(long duration) { + throw new AbstractMethodError(); + } /** - * Utility method to compute the excess-nanosecond argument to - * wait, sleep, join. + * Equivalent to DAYS.convert(duration, this). + * @param duration the duration + * @return the converted duration + * @see #convert */ - private int excessNanos(long time, long ms) { - if (this == NANOSECONDS) - return (int) (time - (ms * 1000 * 1000)); - if (this == MICROSECONDS) - return (int) ((time * 1000) - (ms * 1000 * 1000)); - return 0; + long toDays(long duration) { + throw new AbstractMethodError(); } /** - * Perform a timed Object.wait using this time unit. + * Utility to compute the excess-nanosecond argument to wait, + * sleep, join. + * @param d the duration + * @param m the number of milliseconds + * @return the number of nanoseconds + */ + abstract int excessNanos(long d, long m); + + /** + * Performs a timed Object.wait using this time unit. * This is a convenience method that converts timeout arguments * into the form required by the Object.wait method. * @@ -180,7 +238,7 @@ public enum TimeUnit { * method (see {@link BlockingQueue#poll BlockingQueue.poll}) * using: * - *

        public synchronized  Object poll(long timeout, TimeUnit unit) throws InterruptedException {
      +     * 
        public synchronized Object poll(long timeout, TimeUnit unit) throws InterruptedException {
            *    while (empty) {
            *      unit.timedWait(this, timeout);
            *      ...
      @@ -188,12 +246,13 @@ public enum TimeUnit {
            *  }
      * * @param obj the object to wait on - * @param timeout the maximum time to wait. + * @param timeout the maximum time to wait. If less than + * or equal to zero, do not wait at all. * @throws InterruptedException if interrupted while waiting. * @see Object#wait(long, int) */ public void timedWait(Object obj, long timeout) - throws InterruptedException { + throws InterruptedException { if (timeout > 0) { long ms = toMillis(timeout); int ns = excessNanos(timeout, ms); @@ -202,16 +261,17 @@ public enum TimeUnit { } /** - * Perform a timed Thread.join using this time unit. + * Performs a timed Thread.join using this time unit. * This is a convenience method that converts time arguments into the * form required by the Thread.join method. * @param thread the thread to wait for - * @param timeout the maximum time to wait + * @param timeout the maximum time to wait. If less than + * or equal to zero, do not wait at all. * @throws InterruptedException if interrupted while waiting. * @see Thread#join(long, int) */ public void timedJoin(Thread thread, long timeout) - throws InterruptedException { + throws InterruptedException { if (timeout > 0) { long ms = toMillis(timeout); int ns = excessNanos(timeout, ms); @@ -220,10 +280,11 @@ public enum TimeUnit { } /** - * Perform a Thread.sleep using this unit. + * Performs a Thread.sleep using this unit. * This is a convenience method that converts time arguments into the * form required by the Thread.sleep method. - * @param timeout the minimum time to sleep + * @param timeout the minimum time to sleep. If less than + * or equal to zero, do not sleep at all. * @throws InterruptedException if interrupted while sleeping. * @see Thread#sleep */ diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicBoolean.java b/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicBoolean.java index fdd3d49a6..ba6055672 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicBoolean.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicBoolean.java @@ -8,10 +8,10 @@ package java.util.concurrent.atomic; import sun.misc.Unsafe; /** - * A boolean value that may be updated atomically. See the + * A {@code boolean} value that may be updated atomically. See the * {@link java.util.concurrent.atomic} package specification for * description of the properties of atomic variables. An - * AtomicBoolean is used in applications such as atomically + * {@code AtomicBoolean} is used in applications such as atomically * updated flags, and cannot be used as a replacement for a * {@link java.lang.Boolean}. * @@ -37,7 +37,7 @@ public class AtomicBoolean implements java.io.Serializable { private volatile int value; /** - * Creates a new AtomicBoolean with the given initial value. + * Creates a new {@code AtomicBoolean} with the given initial value. * * @param initialValue the initial value */ @@ -46,7 +46,7 @@ public class AtomicBoolean implements java.io.Serializable { } /** - * Creates a new AtomicBoolean with initial value false. + * Creates a new {@code AtomicBoolean} with initial value {@code false}. */ public AtomicBoolean() { } @@ -61,16 +61,13 @@ public class AtomicBoolean implements java.io.Serializable { } /** - * Atomically sets the value to the given update value if the - * current value is equal to the expected value. Any given - * invocation of this operation may fail (return - * false) spuriously, but repeated invocation when - * the current value holds the expected value and no other thread - * is also attempting to set the value will eventually succeed. + * Atomically sets the value to the given updated value + * if the current value {@code ==} the expected value. * * @param expect the expected value * @param update the new value - * @return true if successful + * @return true if successful. False return indicates that + * the actual value was not equal to the expected value. */ public final boolean compareAndSet(boolean expect, boolean update) { int e = expect ? 1 : 0; @@ -79,9 +76,13 @@ public class AtomicBoolean implements java.io.Serializable { } /** - * Atomically set the value to the given updated value - * if the current value == the expected value. - * May fail spuriously. + * Atomically sets the value to the given updated value + * if the current value {@code ==} the expected value. + * + *

      May fail spuriously + * and does not provide ordering guarantees, so is only rarely an + * appropriate alternative to {@code compareAndSet}. + * * @param expect the expected value * @param update the new value * @return true if successful. @@ -102,7 +103,7 @@ public class AtomicBoolean implements java.io.Serializable { } /** - * Sets to the given value and returns the previous value. + * Atomically sets to the given value and returns the previous value. * * @param newValue the new value * @return the previous value diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicInteger.java b/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicInteger.java index 2b9d15c00..1aa32f436 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicInteger.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicInteger.java @@ -8,20 +8,19 @@ package java.util.concurrent.atomic; import sun.misc.Unsafe; /** - * An int value that may be updated atomically. See the + * An {@code int} value that may be updated atomically. See the * {@link java.util.concurrent.atomic} package specification for * description of the properties of atomic variables. An - * AtomicInteger is used in applications such as atomically + * {@code AtomicInteger} is used in applications such as atomically * incremented counters, and cannot be used as a replacement for an * {@link java.lang.Integer}. However, this class does extend - * Number to allow uniform access by tools and utilities that + * {@code Number} to allow uniform access by tools and utilities that * deal with numerically-based classes. - * * * @since 1.5 * @author Doug Lea */ -public class AtomicInteger extends Number implements java.io.Serializable { +public class AtomicInteger extends Number implements java.io.Serializable { private static final long serialVersionUID = 6214790243416807050L; // setup to use Unsafe.compareAndSwapInt for updates @@ -34,13 +33,13 @@ public class AtomicInteger extends Number implements java.io.Serializable { try { valueOffset = unsafe.objectFieldOffset (AtomicInteger.class.getDeclaredField("value")); - } catch(Exception ex) { throw new Error(ex); } + } catch (Exception ex) { throw new Error(ex); } } private volatile int value; /** - * Create a new AtomicInteger with the given initial value. + * Creates a new AtomicInteger with the given initial value. * * @param initialValue the initial value */ @@ -49,22 +48,22 @@ public class AtomicInteger extends Number implements java.io.Serializable { } /** - * Create a new AtomicInteger with initial value 0. + * Creates a new AtomicInteger with initial value {@code 0}. */ public AtomicInteger() { } /** - * Get the current value. + * Gets the current value. * * @return the current value */ public final int get() { return value; } - + /** - * Set to the given value. + * Sets to the given value. * * @param newValue the new value */ @@ -73,7 +72,7 @@ public class AtomicInteger extends Number implements java.io.Serializable { } /** - * Set to the give value and return the old value. + * Atomically sets to the given value and returns the old value. * * @param newValue the new value * @return the previous value @@ -85,35 +84,39 @@ public class AtomicInteger extends Number implements java.io.Serializable { return current; } } - - + /** - * Atomically set the value to the given updated value - * if the current value == the expected value. + * Atomically sets the value to the given updated value + * if the current value {@code ==} the expected value. + * * @param expect the expected value * @param update the new value * @return true if successful. False return indicates that * the actual value was not equal to the expected value. */ public final boolean compareAndSet(int expect, int update) { - return unsafe.compareAndSwapInt(this, valueOffset, expect, update); + return unsafe.compareAndSwapInt(this, valueOffset, expect, update); } /** - * Atomically set the value to the given updated value - * if the current value == the expected value. - * May fail spuriously. + * Atomically sets the value to the given updated value + * if the current value {@code ==} the expected value. + * + *

      May fail spuriously + * and does not provide ordering guarantees, so is only rarely an + * appropriate alternative to {@code compareAndSet}. + * * @param expect the expected value * @param update the new value * @return true if successful. */ public final boolean weakCompareAndSet(int expect, int update) { - return unsafe.compareAndSwapInt(this, valueOffset, expect, update); + return unsafe.compareAndSwapInt(this, valueOffset, expect, update); } - /** - * Atomically increment by one the current value. + * Atomically increments by one the current value. + * * @return the previous value */ public final int getAndIncrement() { @@ -124,10 +127,10 @@ public class AtomicInteger extends Number implements java.io.Serializable { return current; } } - - + /** - * Atomically decrement by one the current value. + * Atomically decrements by one the current value. + * * @return the previous value */ public final int getAndDecrement() { @@ -138,10 +141,10 @@ public class AtomicInteger extends Number implements java.io.Serializable { return current; } } - - + /** - * Atomically add the given value to current value. + * Atomically adds the given value to the current value. + * * @param delta the value to add * @return the previous value */ @@ -155,7 +158,8 @@ public class AtomicInteger extends Number implements java.io.Serializable { } /** - * Atomically increment by one the current value. + * Atomically increments by one the current value. + * * @return the updated value */ public final int incrementAndGet() { @@ -166,9 +170,10 @@ public class AtomicInteger extends Number implements java.io.Serializable { return next; } } - + /** - * Atomically decrement by one the current value. + * Atomically decrements by one the current value. + * * @return the updated value */ public final int decrementAndGet() { @@ -179,10 +184,10 @@ public class AtomicInteger extends Number implements java.io.Serializable { return next; } } - - + /** - * Atomically add the given value to current value. + * Atomically adds the given value to the current value. + * * @param delta the value to add * @return the updated value */ diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicIntegerArray.java b/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicIntegerArray.java index 95807f3f8..ee21b13ba 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicIntegerArray.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicIntegerArray.java @@ -9,14 +9,14 @@ import sun.misc.Unsafe; import java.util.*; /** - * An int array in which elements may be updated atomically. + * An {@code int} array in which elements may be updated atomically. * See the {@link java.util.concurrent.atomic} package * specification for description of the properties of atomic * variables. * @since 1.5 * @author Doug Lea */ -public class AtomicIntegerArray implements java.io.Serializable { +public class AtomicIntegerArray implements java.io.Serializable { private static final long serialVersionUID = 2862133569453604235L; // setup to use Unsafe.compareAndSwapInt for updates @@ -34,7 +34,7 @@ public class AtomicIntegerArray implements java.io.Serializable { } /** - * Create a new AtomicIntegerArray of given length. + * Creates a new AtomicIntegerArray of given length. * * @param length the length of the array */ @@ -46,14 +46,14 @@ public class AtomicIntegerArray implements java.io.Serializable { } /** - * Create a new AtomicIntegerArray with the same length as, and + * Creates a new AtomicIntegerArray with the same length as, and * all elements copied from, the given array. * * @param array the array to copy elements from * @throws NullPointerException if array is null */ public AtomicIntegerArray(int[] array) { - if (array == null) + if (array == null) throw new NullPointerException(); int length = array.length; this.array = new int[length]; @@ -76,7 +76,7 @@ public class AtomicIntegerArray implements java.io.Serializable { } /** - * Get the current value at position i. + * Gets the current value at position {@code i}. * * @param i the index * @return the current value @@ -84,9 +84,9 @@ public class AtomicIntegerArray implements java.io.Serializable { public final int get(int i) { return unsafe.getIntVolatile(array, rawIndex(i)); } - + /** - * Set the element at position i to the given value. + * Sets the element at position {@code i} to the given value. * * @param i the index * @param newValue the new value @@ -94,10 +94,10 @@ public class AtomicIntegerArray implements java.io.Serializable { public final void set(int i, int newValue) { unsafe.putIntVolatile(array, rawIndex(i), newValue); } - + /** - * Set the element at position i to the given value and return the - * old value. + * Atomically sets the element at position {@code i} to the given + * value and returns the old value. * * @param i the index * @param newValue the new value @@ -110,10 +110,10 @@ public class AtomicIntegerArray implements java.io.Serializable { return current; } } - + /** - * Atomically set the value to the given updated value - * if the current value == the expected value. + * Atomically sets the element at position {@code i} to the given + * updated value if the current value {@code ==} the expected value. * * @param i the index * @param expect the expected value @@ -122,14 +122,17 @@ public class AtomicIntegerArray implements java.io.Serializable { * the actual value was not equal to the expected value. */ public final boolean compareAndSet(int i, int expect, int update) { - return unsafe.compareAndSwapInt(array, rawIndex(i), + return unsafe.compareAndSwapInt(array, rawIndex(i), expect, update); } /** - * Atomically set the value to the given updated value - * if the current value == the expected value. - * May fail spuriously. + * Atomically sets the element at position {@code i} to the given + * updated value if the current value {@code ==} the expected value. + * + *

      May fail spuriously + * and does not provide ordering guarantees, so is only rarely an + * appropriate alternative to {@code compareAndSet}. * * @param i the index * @param expect the expected value @@ -141,10 +144,10 @@ public class AtomicIntegerArray implements java.io.Serializable { } /** - * Atomically increment by one the element at index i. + * Atomically increments by one the element at index {@code i}. * * @param i the index - * @return the previous value; + * @return the previous value */ public final int getAndIncrement(int i) { while (true) { @@ -154,12 +157,12 @@ public class AtomicIntegerArray implements java.io.Serializable { return current; } } - + /** - * Atomically decrement by one the element at index i. + * Atomically decrements by one the element at index {@code i}. * * @param i the index - * @return the previous value; + * @return the previous value */ public final int getAndDecrement(int i) { while (true) { @@ -169,13 +172,13 @@ public class AtomicIntegerArray implements java.io.Serializable { return current; } } - + /** - * Atomically add the given value to element at index i. + * Atomically adds the given value to the element at index {@code i}. * * @param i the index * @param delta the value to add - * @return the previous value; + * @return the previous value */ public final int getAndAdd(int i, int delta) { while (true) { @@ -187,10 +190,10 @@ public class AtomicIntegerArray implements java.io.Serializable { } /** - * Atomically increment by one the element at index i. + * Atomically increments by one the element at index {@code i}. * * @param i the index - * @return the updated value; + * @return the updated value */ public final int incrementAndGet(int i) { while (true) { @@ -200,12 +203,12 @@ public class AtomicIntegerArray implements java.io.Serializable { return next; } } - + /** - * Atomically decrement by one the element at index i. + * Atomically decrements by one the element at index {@code i}. * * @param i the index - * @return the updated value; + * @return the updated value */ public final int decrementAndGet(int i) { while (true) { @@ -215,13 +218,13 @@ public class AtomicIntegerArray implements java.io.Serializable { return next; } } - + /** - * Atomically add the given value to element at index i. + * Atomically adds the given value to the element at index {@code i}. * * @param i the index * @param delta the value to add - * @return the updated value; + * @return the updated value */ public final int addAndGet(int i, int delta) { while (true) { @@ -231,7 +234,7 @@ public class AtomicIntegerArray implements java.io.Serializable { return next; } } - + /** * Returns the String representation of the current values of array. * @return the String representation of the current values of array. diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java b/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java index 5f289c2ff..f8d2c8199 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java @@ -8,35 +8,40 @@ package java.util.concurrent.atomic; import sun.misc.Unsafe; import java.lang.reflect.*; +import org.apache.harmony.kernel.vm.VM; +import dalvik.system.VMStack; + /** * A reflection-based utility that enables atomic updates to - * designated volatile int fields of designated classes. + * designated {@code volatile int} fields of designated classes. * This class is designed for use in atomic data structures in which * several fields of the same node are independently subject to atomic * updates. * - *

      Note that the guarantees of the compareAndSet method - * in this class are weaker than in other atomic classes. Because this - * class cannot ensure that all uses of the field are appropriate for - * purposes of atomic access, it can guarantee atomicity and volatile - * semantics only with respect to other invocations of - * compareAndSet and set. + *

      Note that the guarantees of the {@code compareAndSet} + * method in this class are weaker than in other atomic classes. + * Because this class cannot ensure that all uses of the field + * are appropriate for purposes of atomic access, it can + * guarantee atomicity only with respect to other invocations of + * {@code compareAndSet} and {@code set} on the same updater. + * * @since 1.5 * @author Doug Lea * @param The type of the object holding the updatable field */ public abstract class AtomicIntegerFieldUpdater { /** - * Creates an updater for objects with the given field. The Class - * argument is needed to check that reflective types and generic - * types match. + * Creates and returns an updater for objects with the given field. + * The Class argument is needed to check that reflective types and + * generic types match. + * * @param tclass the class of the objects holding the field - * @param fieldName the name of the field to be updated. + * @param fieldName the name of the field to be updated * @return the updater * @throws IllegalArgumentException if the field is not a - * volatile integer type. + * volatile integer type * @throws RuntimeException with a nested reflection-based - * exception if the class does not hold field or is the wrong type. + * exception if the class does not hold field or is the wrong type */ public static AtomicIntegerFieldUpdater newUpdater(Class tclass, String fieldName) { return new AtomicIntegerFieldUpdaterImpl(tclass, fieldName); @@ -49,57 +54,63 @@ public abstract class AtomicIntegerFieldUpdater { } /** - * Atomically set the value of the field of the given object managed - * by this Updater to the given updated value if the current value - * == the expected value. This method is guaranteed to be - * atomic with respect to other calls to compareAndSet and - * set, but not necessarily with respect to other - * changes in the field. + * Atomically sets the field of the given object managed by this updater + * to the given updated value if the current value {@code ==} the + * expected value. This method is guaranteed to be atomic with respect to + * other calls to {@code compareAndSet} and {@code set}, but not + * necessarily with respect to other changes in the field. + * * @param obj An object whose field to conditionally set * @param expect the expected value * @param update the new value - * @return true if successful. - * @throws ClassCastException if obj is not an instance - * of the class possessing the field established in the constructor. + * @return true if successful + * @throws ClassCastException if {@code obj} is not an instance + * of the class possessing the field established in the constructor */ - public abstract boolean compareAndSet(T obj, int expect, int update); /** - * Atomically set the value of the field of the given object managed - * by this Updater to the given updated value if the current value - * == the expected value. This method is guaranteed to be - * atomic with respect to other calls to compareAndSet and - * set, but not necessarily with respect to other - * changes in the field, and may fail spuriously. + * Atomically sets the field of the given object managed by this updater + * to the given updated value if the current value {@code ==} the + * expected value. This method is guaranteed to be atomic with respect to + * other calls to {@code compareAndSet} and {@code set}, but not + * necessarily with respect to other changes in the field. + * + *

      May fail spuriously + * and does not provide ordering guarantees, so is only rarely an + * appropriate alternative to {@code compareAndSet}. + * * @param obj An object whose field to conditionally set * @param expect the expected value * @param update the new value - * @return true if successful. - * @throws ClassCastException if obj is not an instance - * of the class possessing the field established in the constructor. + * @return true if successful + * @throws ClassCastException if {@code obj} is not an instance + * of the class possessing the field established in the constructor */ - public abstract boolean weakCompareAndSet(T obj, int expect, int update); /** - * Set the field of the given object managed by this updater. This - * operation is guaranteed to act as a volatile store with respect - * to subsequent invocations of compareAndSet. + * Sets the field of the given object managed by this updater to the + * given updated value. This operation is guaranteed to act as a volatile + * store with respect to subsequent invocations of {@code compareAndSet}. + * * @param obj An object whose field to set * @param newValue the new value */ public abstract void set(T obj, int newValue); /** - * Get the current value held in the field by the given object. + * Gets the current value held in the field of the given object managed + * by this updater. + * * @param obj An object whose field to get * @return the current value */ public abstract int get(T obj); /** - * Set to the given value and return the old value. + * Atomically sets the field of the given object managed by this updater + * to the given value and returns the old value. * * @param obj An object whose field to get and set * @param newValue the new value @@ -114,9 +125,11 @@ public abstract class AtomicIntegerFieldUpdater { } /** - * Atomically increment by one the current value. + * Atomically increments by one the current value of the field of the + * given object managed by this updater. + * * @param obj An object whose field to get and set - * @return the previous value; + * @return the previous value */ public int getAndIncrement(T obj) { for (;;) { @@ -127,11 +140,12 @@ public abstract class AtomicIntegerFieldUpdater { } } - /** - * Atomically decrement by one the current value. + * Atomically decrements by one the current value of the field of the + * given object managed by this updater. + * * @param obj An object whose field to get and set - * @return the previous value; + * @return the previous value */ public int getAndDecrement(T obj) { for (;;) { @@ -142,12 +156,13 @@ public abstract class AtomicIntegerFieldUpdater { } } - /** - * Atomically add the given value to current value. + * Atomically adds the given value to the current value of the field of + * the given object managed by this updater. + * * @param obj An object whose field to get and set * @param delta the value to add - * @return the previous value; + * @return the previous value */ public int getAndAdd(T obj, int delta) { for (;;) { @@ -159,9 +174,11 @@ public abstract class AtomicIntegerFieldUpdater { } /** - * Atomically increment by one the current value. + * Atomically increments by one the current value of the field of the + * given object managed by this updater. + * * @param obj An object whose field to get and set - * @return the updated value; + * @return the updated value */ public int incrementAndGet(T obj) { for (;;) { @@ -172,11 +189,12 @@ public abstract class AtomicIntegerFieldUpdater { } } - /** - * Atomically decrement by one the current value. + * Atomically decrements by one the current value of the field of the + * given object managed by this updater. + * * @param obj An object whose field to get and set - * @return the updated value; + * @return the updated value */ public int decrementAndGet(T obj) { for (;;) { @@ -187,12 +205,13 @@ public abstract class AtomicIntegerFieldUpdater { } } - /** - * Atomically add the given value to current value. + * Atomically adds the given value to the current value of the field of + * the given object managed by this updater. + * * @param obj An object whose field to get and set * @param delta the value to add - * @return the updated value; + * @return the updated value */ public int addAndGet(T obj, int delta) { for (;;) { @@ -212,49 +231,83 @@ public abstract class AtomicIntegerFieldUpdater { // END android-changed private final long offset; private final Class tclass; + private final Class cclass; AtomicIntegerFieldUpdaterImpl(Class tclass, String fieldName) { Field field = null; + Class caller = null; + int modifiers = 0; try { field = tclass.getDeclaredField(fieldName); - } catch(Exception ex) { + // BEGIN android-changed + caller = VMStack.getStackClass2(); + // END android-changed + modifiers = field.getModifiers(); + + SecurityManager smgr = System.getSecurityManager(); + if (smgr != null) { + int type = Modifier.isPublic(modifiers) + ? Member.PUBLIC : Member.DECLARED; + smgr.checkMemberAccess(tclass, type); + smgr.checkPackageAccess(tclass.getPackage().getName()); + } + } catch (Exception ex) { throw new RuntimeException(ex); } - + Class fieldt = field.getType(); if (fieldt != int.class) throw new IllegalArgumentException("Must be integer type"); - - if (!Modifier.isVolatile(field.getModifiers())) + + if (!Modifier.isVolatile(modifiers)) throw new IllegalArgumentException("Must be volatile type"); - + + this.cclass = (Modifier.isProtected(modifiers) && + caller != tclass) ? caller : null; this.tclass = tclass; offset = unsafe.objectFieldOffset(field); } - public boolean compareAndSet(T obj, int expect, int update) { + private void fullCheck(T obj) { if (!tclass.isInstance(obj)) throw new ClassCastException(); + if (cclass != null) + ensureProtectedAccess(obj); + } + + public boolean compareAndSet(T obj, int expect, int update) { + if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj); return unsafe.compareAndSwapInt(obj, offset, expect, update); } public boolean weakCompareAndSet(T obj, int expect, int update) { - if (!tclass.isInstance(obj)) - throw new ClassCastException(); + if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj); return unsafe.compareAndSwapInt(obj, offset, expect, update); } public void set(T obj, int newValue) { - if (!tclass.isInstance(obj)) - throw new ClassCastException(); + if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj); unsafe.putIntVolatile(obj, offset, newValue); } public final int get(T obj) { - if (!tclass.isInstance(obj)) - throw new ClassCastException(); + if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj); return unsafe.getIntVolatile(obj, offset); } + + private void ensureProtectedAccess(T obj) { + if (cclass.isInstance(obj)) { + return; + } + throw new RuntimeException( + new IllegalAccessException("Class " + + cclass.getName() + + " can not access a protected member of class " + + tclass.getName() + + " using an instance of " + + obj.getClass().getName() + ) + ); + } } } - diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLong.java b/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLong.java index 7f413646c..9b5600228 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLong.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLong.java @@ -8,19 +8,19 @@ package java.util.concurrent.atomic; import sun.misc.Unsafe; /** - * A long value that may be updated atomically. See the + * A {@code long} value that may be updated atomically. See the * {@link java.util.concurrent.atomic} package specification for * description of the properties of atomic variables. An - * AtomicLong is used in applications such as atomically + * {@code AtomicLong} is used in applications such as atomically * incremented sequence numbers, and cannot be used as a replacement * for a {@link java.lang.Long}. However, this class does extend - * Number to allow uniform access by tools and utilities that + * {@code Number} to allow uniform access by tools and utilities that * deal with numerically-based classes. * * @since 1.5 * @author Doug Lea */ -public class AtomicLong extends Number implements java.io.Serializable { +public class AtomicLong extends Number implements java.io.Serializable { private static final long serialVersionUID = 1927816293512124184L; // setup to use Unsafe.compareAndSwapLong for updates @@ -30,12 +30,14 @@ public class AtomicLong extends Number implements java.io.Serializable { private static final long valueOffset; /** - * Record whether the underlying JVM supports lockless - * CompareAndSet for longs. While the unsafe.CompareAndSetLong + * Records whether the underlying JVM supports lockless + * compareAndSwap for longs. While the Unsafe.compareAndSwapLong * method works in either case, some constructions should be * handled at Java level to avoid locking user-visible locks. + * + * Initialised in the static block. */ - static final boolean VM_SUPPORTS_LONG_CAS = VMSupportsCS8(); + static final boolean VM_SUPPORTS_LONG_CAS; /** * Returns whether underlying JVM supports lockless CompareAndSet @@ -47,13 +49,22 @@ public class AtomicLong extends Number implements java.io.Serializable { try { valueOffset = unsafe.objectFieldOffset (AtomicLong.class.getDeclaredField("value")); - } catch(Exception ex) { throw new Error(ex); } + } catch (Exception ex) { throw new Error(ex); } + + boolean longCASSupport; + try { + longCASSupport = VMSupportsCS8(); + } catch (UnsatisfiedLinkError e) { + // assume there's support if the native isn't provided by the VM + longCASSupport = true; + } + VM_SUPPORTS_LONG_CAS = longCASSupport; } private volatile long value; /** - * Create a new AtomicLong with the given initial value. + * Creates a new AtomicLong with the given initial value. * * @param initialValue the initial value */ @@ -62,31 +73,31 @@ public class AtomicLong extends Number implements java.io.Serializable { } /** - * Create a new AtomicLong with initial value 0. + * Creates a new AtomicLong with initial value {@code 0}. */ public AtomicLong() { } - + /** - * Get the current value. + * Gets the current value. * * @return the current value */ public final long get() { return value; } - + /** - * Set to the given value. + * Sets to the given value. * * @param newValue the new value */ public final void set(long newValue) { value = newValue; } - + /** - * Set to the give value and return the old value. + * Atomically sets to the given value and returns the old value. * * @param newValue the new value * @return the previous value @@ -98,33 +109,39 @@ public class AtomicLong extends Number implements java.io.Serializable { return current; } } - + /** - * Atomically set the value to the given updated value - * if the current value == the expected value. + * Atomically sets the value to the given updated value + * if the current value {@code ==} the expected value. + * * @param expect the expected value * @param update the new value * @return true if successful. False return indicates that * the actual value was not equal to the expected value. */ public final boolean compareAndSet(long expect, long update) { - return unsafe.compareAndSwapLong(this, valueOffset, expect, update); + return unsafe.compareAndSwapLong(this, valueOffset, expect, update); } /** - * Atomically set the value to the given updated value - * if the current value == the expected value. - * May fail spuriously. + * Atomically sets the value to the given updated value + * if the current value {@code ==} the expected value. + * + *

      May fail spuriously + * and does not provide ordering guarantees, so is only rarely an + * appropriate alternative to {@code compareAndSet}. + * * @param expect the expected value * @param update the new value * @return true if successful. */ public final boolean weakCompareAndSet(long expect, long update) { - return unsafe.compareAndSwapLong(this, valueOffset, expect, update); + return unsafe.compareAndSwapLong(this, valueOffset, expect, update); } - + /** - * Atomically increment by one the current value. + * Atomically increments by one the current value. + * * @return the previous value */ public final long getAndIncrement() { @@ -135,10 +152,10 @@ public class AtomicLong extends Number implements java.io.Serializable { return current; } } - - + /** - * Atomically decrement by one the current value. + * Atomically decrements by one the current value. + * * @return the previous value */ public final long getAndDecrement() { @@ -149,10 +166,10 @@ public class AtomicLong extends Number implements java.io.Serializable { return current; } } - - + /** - * Atomically add the given value to current value. + * Atomically adds the given value to the current value. + * * @param delta the value to add * @return the previous value */ @@ -164,9 +181,10 @@ public class AtomicLong extends Number implements java.io.Serializable { return current; } } - + /** - * Atomically increment by one the current value. + * Atomically increments by one the current value. + * * @return the updated value */ public final long incrementAndGet() { @@ -177,9 +195,10 @@ public class AtomicLong extends Number implements java.io.Serializable { return next; } } - + /** - * Atomically decrement by one the current value. + * Atomically decrements by one the current value. + * * @return the updated value */ public final long decrementAndGet() { @@ -190,10 +209,10 @@ public class AtomicLong extends Number implements java.io.Serializable { return next; } } - - + /** - * Atomically add the given value to current value. + * Atomically adds the given value to the current value. + * * @param delta the value to add * @return the updated value */ @@ -205,7 +224,7 @@ public class AtomicLong extends Number implements java.io.Serializable { return next; } } - + /** * Returns the String representation of the current value. * @return the String representation of the current value. @@ -220,7 +239,7 @@ public class AtomicLong extends Number implements java.io.Serializable { } public long longValue() { - return (long)get(); + return get(); } public float floatValue() { @@ -230,5 +249,5 @@ public class AtomicLong extends Number implements java.io.Serializable { public double doubleValue() { return (double)get(); } - + } diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLongArray.java b/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLongArray.java index a0e76b499..d96f4c27a 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLongArray.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLongArray.java @@ -9,13 +9,13 @@ import sun.misc.Unsafe; import java.util.*; /** - * A long array in which elements may be updated atomically. + * A {@code long} array in which elements may be updated atomically. * See the {@link java.util.concurrent.atomic} package specification * for description of the properties of atomic variables. * @since 1.5 * @author Doug Lea */ -public class AtomicLongArray implements java.io.Serializable { +public class AtomicLongArray implements java.io.Serializable { private static final long serialVersionUID = -2308431214976778248L; // setup to use Unsafe.compareAndSwapInt for updates @@ -33,25 +33,26 @@ public class AtomicLongArray implements java.io.Serializable { } /** - * Create a new AtomicLongArray of given length. + * Creates a new AtomicLongArray of given length. + * * @param length the length of the array */ public AtomicLongArray(int length) { array = new long[length]; // must perform at least one volatile write to conform to JMM - if (length > 0) + if (length > 0) unsafe.putLongVolatile(array, rawIndex(0), 0); } /** - * Create a new AtomicLongArray with the same length as, and + * Creates a new AtomicLongArray with the same length as, and * all elements copied from, the given array. * * @param array the array to copy elements from * @throws NullPointerException if array is null */ public AtomicLongArray(long[] array) { - if (array == null) + if (array == null) throw new NullPointerException(); int length = array.length; this.array = new long[length]; @@ -74,7 +75,7 @@ public class AtomicLongArray implements java.io.Serializable { } /** - * Get the current value at position i. + * Gets the current value at position {@code i}. * * @param i the index * @return the current value @@ -82,9 +83,9 @@ public class AtomicLongArray implements java.io.Serializable { public final long get(int i) { return unsafe.getLongVolatile(array, rawIndex(i)); } - + /** - * Set the element at position i to the given value. + * Sets the element at position {@code i} to the given value. * * @param i the index * @param newValue the new value @@ -92,10 +93,10 @@ public class AtomicLongArray implements java.io.Serializable { public final void set(int i, long newValue) { unsafe.putLongVolatile(array, rawIndex(i), newValue); } - + /** - * Set the element at position i to the given value and return the - * old value. + * Atomically sets the element at position {@code i} to the given value + * and returns the old value. * * @param i the index * @param newValue the new value @@ -108,10 +109,11 @@ public class AtomicLongArray implements java.io.Serializable { return current; } } - + /** - * Atomically set the value to the given updated value - * if the current value == the expected value. + * Atomically sets the value to the given updated value + * if the current value {@code ==} the expected value. + * * @param i the index * @param expect the expected value * @param update the new value @@ -119,14 +121,18 @@ public class AtomicLongArray implements java.io.Serializable { * the actual value was not equal to the expected value. */ public final boolean compareAndSet(int i, long expect, long update) { - return unsafe.compareAndSwapLong(array, rawIndex(i), + return unsafe.compareAndSwapLong(array, rawIndex(i), expect, update); } /** - * Atomically set the value to the given updated value - * if the current value == the expected value. - * May fail spuriously. + * Atomically sets the value to the given updated value + * if the current value {@code ==} the expected value. + * + *

      May fail spuriously + * and does not provide ordering guarantees, so is only rarely an + * appropriate alternative to {@code compareAndSet}. + * * @param i the index * @param expect the expected value * @param update the new value @@ -137,10 +143,10 @@ public class AtomicLongArray implements java.io.Serializable { } /** - * Atomically increment by one the element at index i. + * Atomically increments by one the element at index {@code i}. * * @param i the index - * @return the previous value; + * @return the previous value */ public final long getAndIncrement(int i) { while (true) { @@ -150,12 +156,12 @@ public class AtomicLongArray implements java.io.Serializable { return current; } } - + /** - * Atomically decrement by one the element at index i. + * Atomically decrements by one the element at index {@code i}. * * @param i the index - * @return the previous value; + * @return the previous value */ public final long getAndDecrement(int i) { while (true) { @@ -165,13 +171,13 @@ public class AtomicLongArray implements java.io.Serializable { return current; } } - + /** - * Atomically add the given value to element at index i. + * Atomically adds the given value to the element at index {@code i}. * * @param i the index * @param delta the value to add - * @return the previous value; + * @return the previous value */ public final long getAndAdd(int i, long delta) { while (true) { @@ -181,13 +187,12 @@ public class AtomicLongArray implements java.io.Serializable { return current; } } - /** - * Atomically increment the element at index i. + * Atomically increments by one the element at index {@code i}. * * @param i the index - * @return the updated value; + * @return the updated value */ public final long incrementAndGet(int i) { while (true) { @@ -197,12 +202,12 @@ public class AtomicLongArray implements java.io.Serializable { return next; } } - + /** - * Atomically decrement the element at index i. + * Atomically decrements by one the element at index {@code i}. * * @param i the index - * @return the updated value; + * @return the updated value */ public final long decrementAndGet(int i) { while (true) { @@ -212,13 +217,13 @@ public class AtomicLongArray implements java.io.Serializable { return next; } } - + /** - * Atomically add the given value to element at index i. + * Atomically adds the given value to the element at index {@code i}. * * @param i the index * @param delta the value to add - * @return the updated value; + * @return the updated value */ public long addAndGet(int i, long delta) { while (true) { @@ -238,5 +243,5 @@ public class AtomicLongArray implements java.io.Serializable { get(0); return Arrays.toString(array); } - + } diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLongFieldUpdater.java b/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLongFieldUpdater.java index dcb8b2e52..d97c2e47b 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLongFieldUpdater.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicLongFieldUpdater.java @@ -8,19 +8,22 @@ package java.util.concurrent.atomic; import sun.misc.Unsafe; import java.lang.reflect.*; +import org.apache.harmony.kernel.vm.VM; +import dalvik.system.VMStack; + /** * A reflection-based utility that enables atomic updates to - * designated volatile long fields of designated classes. + * designated {@code volatile long} fields of designated classes. * This class is designed for use in atomic data structures in which * several fields of the same node are independently subject to atomic * updates. * - *

      Note that the guarantees of the compareAndSet method - * in this class are weaker than in other atomic classes. Because this - * class cannot ensure that all uses of the field are appropriate for - * purposes of atomic access, it can guarantee atomicity and volatile - * semantics only with respect to other invocations of - * compareAndSet and set. + *

      Note that the guarantees of the {@code compareAndSet} + * method in this class are weaker than in other atomic classes. + * Because this class cannot ensure that all uses of the field + * are appropriate for purposes of atomic access, it can + * guarantee atomicity only with respect to other invocations of + * {@code compareAndSet} and {@code set} on the same updater. * * @since 1.5 * @author Doug Lea @@ -28,9 +31,10 @@ import java.lang.reflect.*; */ public abstract class AtomicLongFieldUpdater { /** - * Creates an updater for objects with the given field. The Class - * argument is needed to check that reflective types and generic - * types match. + * Creates and returns an updater for objects with the given field. + * The Class argument is needed to check that reflective types and + * generic types match. + * * @param tclass the class of the objects holding the field * @param fieldName the name of the field to be updated. * @return the updater @@ -53,57 +57,63 @@ public abstract class AtomicLongFieldUpdater { } /** - * Atomically set the value of the field of the given object managed - * by this Updater to the given updated value if the current value - * == the expected value. This method is guaranteed to be - * atomic with respect to other calls to compareAndSet and - * set, but not necessarily with respect to other - * changes in the field. + * Atomically sets the field of the given object managed by this updater + * to the given updated value if the current value {@code ==} the + * expected value. This method is guaranteed to be atomic with respect to + * other calls to {@code compareAndSet} and {@code set}, but not + * necessarily with respect to other changes in the field. + * * @param obj An object whose field to conditionally set * @param expect the expected value * @param update the new value * @return true if successful. - * @throws ClassCastException if obj is not an instance + * @throws ClassCastException if {@code obj} is not an instance * of the class possessing the field established in the constructor. */ - public abstract boolean compareAndSet(T obj, long expect, long update); /** - * Atomically set the value of the field of the given object managed - * by this Updater to the given updated value if the current value - * == the expected value. This method is guaranteed to be - * atomic with respect to other calls to compareAndSet and - * set, but not necessarily with respect to other - * changes in the field, and may fail spuriously. + * Atomically sets the field of the given object managed by this updater + * to the given updated value if the current value {@code ==} the + * expected value. This method is guaranteed to be atomic with respect to + * other calls to {@code compareAndSet} and {@code set}, but not + * necessarily with respect to other changes in the field. + * + *

      May fail spuriously + * and does not provide ordering guarantees, so is only rarely an + * appropriate alternative to {@code compareAndSet}. + * * @param obj An object whose field to conditionally set * @param expect the expected value * @param update the new value * @return true if successful. - * @throws ClassCastException if obj is not an instance + * @throws ClassCastException if {@code obj} is not an instance * of the class possessing the field established in the constructor. */ - public abstract boolean weakCompareAndSet(T obj, long expect, long update); /** - * Set the field of the given object managed by this updater. This - * operation is guaranteed to act as a volatile store with respect - * to subsequent invocations of compareAndSet. + * Sets the field of the given object managed by this updater to the + * given updated value. This operation is guaranteed to act as a volatile + * store with respect to subsequent invocations of {@code compareAndSet}. + * * @param obj An object whose field to set * @param newValue the new value */ public abstract void set(T obj, long newValue); /** - * Get the current value held in the field by the given object. + * Gets the current value held in the field of the given object managed + * by this updater. + * * @param obj An object whose field to get * @return the current value */ public abstract long get(T obj); /** - * Set to the given value and return the old value. + * Atomically sets the field of the given object managed by this updater + * to the given value and returns the old value. * * @param obj An object whose field to get and set * @param newValue the new value @@ -118,9 +128,11 @@ public abstract class AtomicLongFieldUpdater { } /** - * Atomically increment by one the current value. + * Atomically increments by one the current value of the field of the + * given object managed by this updater. + * * @param obj An object whose field to get and set - * @return the previous value; + * @return the previous value */ public long getAndIncrement(T obj) { for (;;) { @@ -131,11 +143,12 @@ public abstract class AtomicLongFieldUpdater { } } - /** - * Atomically decrement by one the current value. + * Atomically decrements by one the current value of the field of the + * given object managed by this updater. + * * @param obj An object whose field to get and set - * @return the previous value; + * @return the previous value */ public long getAndDecrement(T obj) { for (;;) { @@ -146,12 +159,13 @@ public abstract class AtomicLongFieldUpdater { } } - /** - * Atomically add the given value to current value. + * Atomically adds the given value to the current value of the field of + * the given object managed by this updater. + * * @param obj An object whose field to get and set * @param delta the value to add - * @return the previous value; + * @return the previous value */ public long getAndAdd(T obj, long delta) { for (;;) { @@ -163,9 +177,11 @@ public abstract class AtomicLongFieldUpdater { } /** - * Atomically increment by one the current value. + * Atomically increments by one the current value of the field of the + * given object managed by this updater. + * * @param obj An object whose field to get and set - * @return the updated value; + * @return the updated value */ public long incrementAndGet(T obj) { for (;;) { @@ -176,11 +192,12 @@ public abstract class AtomicLongFieldUpdater { } } - /** - * Atomically decrement by one the current value. + * Atomically decrements by one the current value of the field of the + * given object managed by this updater. + * * @param obj An object whose field to get and set - * @return the updated value; + * @return the updated value */ public long decrementAndGet(T obj) { for (;;) { @@ -191,12 +208,13 @@ public abstract class AtomicLongFieldUpdater { } } - /** - * Atomically add the given value to current value. + * Atomically adds the given value to the current value of the field of + * the given object managed by this updater. + * * @param obj An object whose field to get and set * @param delta the value to add - * @return the updated value; + * @return the updated value */ public long addAndGet(T obj, long delta) { for (;;) { @@ -213,49 +231,83 @@ public abstract class AtomicLongFieldUpdater { // END android-changed private final long offset; private final Class tclass; + private final Class cclass; CASUpdater(Class tclass, String fieldName) { Field field = null; + Class caller = null; + int modifiers = 0; try { field = tclass.getDeclaredField(fieldName); - } catch(Exception ex) { + // BEGIN android-changed + caller = VMStack.getStackClass2(); + // END android-changed + modifiers = field.getModifiers(); + SecurityManager smgr = System.getSecurityManager(); + if (smgr != null) { + int type = Modifier.isPublic(modifiers) + ? Member.PUBLIC : Member.DECLARED; + smgr.checkMemberAccess(tclass, type); + smgr.checkPackageAccess(tclass.getPackage().getName()); + } + } catch (Exception ex) { throw new RuntimeException(ex); } - + Class fieldt = field.getType(); if (fieldt != long.class) throw new IllegalArgumentException("Must be long type"); - - if (!Modifier.isVolatile(field.getModifiers())) + + if (!Modifier.isVolatile(modifiers)) throw new IllegalArgumentException("Must be volatile type"); - + + this.cclass = (Modifier.isProtected(modifiers) && + caller != tclass) ? caller : null; this.tclass = tclass; offset = unsafe.objectFieldOffset(field); } - public boolean compareAndSet(T obj, long expect, long update) { + private void fullCheck(T obj) { if (!tclass.isInstance(obj)) throw new ClassCastException(); + if (cclass != null) + ensureProtectedAccess(obj); + } + + public boolean compareAndSet(T obj, long expect, long update) { + if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj); return unsafe.compareAndSwapLong(obj, offset, expect, update); } public boolean weakCompareAndSet(T obj, long expect, long update) { - if (!tclass.isInstance(obj)) - throw new ClassCastException(); + if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj); return unsafe.compareAndSwapLong(obj, offset, expect, update); } public void set(T obj, long newValue) { - if (!tclass.isInstance(obj)) - throw new ClassCastException(); + if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj); unsafe.putLongVolatile(obj, offset, newValue); } public long get(T obj) { - if (!tclass.isInstance(obj)) - throw new ClassCastException(); + if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj); return unsafe.getLongVolatile(obj, offset); } + + private void ensureProtectedAccess(T obj) { + if (cclass.isInstance(obj)) { + return; + } + throw new RuntimeException ( + new IllegalAccessException("Class " + + cclass.getName() + + " can not access a protected member of class " + + tclass.getName() + + " using an instance of " + + obj.getClass().getName() + ) + ); + } } @@ -265,32 +317,54 @@ public abstract class AtomicLongFieldUpdater { // END android-changed private final long offset; private final Class tclass; + private final Class cclass; LockedUpdater(Class tclass, String fieldName) { Field field = null; + Class caller = null; + int modifiers = 0; try { field = tclass.getDeclaredField(fieldName); - } catch(Exception ex) { + // BEGIN android-changed + caller = VMStack.getStackClass2(); + // END android-changed + modifiers = field.getModifiers(); + SecurityManager smgr = System.getSecurityManager(); + if (smgr != null) { + int type = Modifier.isPublic(modifiers) + ? Member.PUBLIC : Member.DECLARED; + smgr.checkMemberAccess(tclass, type); + smgr.checkPackageAccess(tclass.getPackage().getName()); + } + } catch (Exception ex) { throw new RuntimeException(ex); } - + Class fieldt = field.getType(); if (fieldt != long.class) throw new IllegalArgumentException("Must be long type"); - - if (!Modifier.isVolatile(field.getModifiers())) + + if (!Modifier.isVolatile(modifiers)) throw new IllegalArgumentException("Must be volatile type"); - + + this.cclass = (Modifier.isProtected(modifiers) && + caller != tclass) ? caller : null; this.tclass = tclass; offset = unsafe.objectFieldOffset(field); } - public boolean compareAndSet(T obj, long expect, long update) { + private void fullCheck(T obj) { if (!tclass.isInstance(obj)) throw new ClassCastException(); + if (cclass != null) + ensureProtectedAccess(obj); + } + + public boolean compareAndSet(T obj, long expect, long update) { + if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj); synchronized(this) { long v = unsafe.getLong(obj, offset); - if (v != expect) + if (v != expect) return false; unsafe.putLong(obj, offset, update); return true; @@ -302,20 +376,36 @@ public abstract class AtomicLongFieldUpdater { } public void set(T obj, long newValue) { - if (!tclass.isInstance(obj)) - throw new ClassCastException(); + if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj); synchronized(this) { unsafe.putLong(obj, offset, newValue); } } + public void lazySet(T obj, long newValue) { + set(obj, newValue); + } + public long get(T obj) { - if (!tclass.isInstance(obj)) - throw new ClassCastException(); + if (obj == null || obj.getClass() != tclass || cclass != null) fullCheck(obj); synchronized(this) { return unsafe.getLong(obj, offset); } } + + private void ensureProtectedAccess(T obj) { + if (cclass.isInstance(obj)) { + return; + } + throw new RuntimeException ( + new IllegalAccessException("Class " + + cclass.getName() + + " can not access a protected member of class " + + tclass.getName() + + " using an instance of " + + obj.getClass().getName() + ) + ); + } } } - diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicMarkableReference.java b/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicMarkableReference.java index 11c91ba54..4877dc49c 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicMarkableReference.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicMarkableReference.java @@ -7,7 +7,7 @@ package java.util.concurrent.atomic; /** - * An AtomicMarkableReference maintains an object reference + * An {@code AtomicMarkableReference} maintains an object reference * along with a mark bit, that can be updated atomically. *

      *

      Implementation note. This implementation maintains markable @@ -31,7 +31,7 @@ public class AtomicMarkableReference { private final AtomicReference> atomicRef; /** - * Creates a new AtomicMarkableReference with the given + * Creates a new {@code AtomicMarkableReference} with the given * initial values. * * @param initialRef the initial reference @@ -61,10 +61,10 @@ public class AtomicMarkableReference { /** * Returns the current values of both the reference and the mark. - * Typical usage is boolean[1] holder; ref = v.get(holder); . + * Typical usage is {@code boolean[1] holder; ref = v.get(holder); }. * * @param markHolder an array of size of at least one. On return, - * markholder[0] will hold the value of the mark. + * {@code markholder[0]} will hold the value of the mark. * @return the current value of the reference */ public V get(boolean[] markHolder) { @@ -76,12 +76,12 @@ public class AtomicMarkableReference { /** * Atomically sets the value of both the reference and mark * to the given update values if the - * current reference is == to the expected reference - * and the current mark is equal to the expected mark. Any given - * invocation of this operation may fail (return - * false) spuriously, but repeated invocation when - * the current value holds the expected value and no other thread - * is also attempting to set the value will eventually succeed. + * current reference is {@code ==} to the expected reference + * and the current mark is equal to the expected mark. + * + *

      May fail spuriously + * and does not provide ordering guarantees, so is only rarely an + * appropriate alternative to {@code compareAndSet}. * * @param expectedReference the expected value of the reference * @param newReference the new value for the reference @@ -93,7 +93,7 @@ public class AtomicMarkableReference { V newReference, boolean expectedMark, boolean newMark) { - ReferenceBooleanPair current = atomicRef.get(); + ReferenceBooleanPair current = atomicRef.get(); return expectedReference == current.reference && expectedMark == current.bit && ((newReference == current.reference && newMark == current.bit) || @@ -105,8 +105,8 @@ public class AtomicMarkableReference { /** * Atomically sets the value of both the reference and mark * to the given update values if the - * current reference is == to the expected reference - * and the current mark is equal to the expected mark. + * current reference is {@code ==} to the expected reference + * and the current mark is equal to the expected mark. * * @param expectedReference the expected value of the reference * @param newReference the new value for the reference @@ -118,7 +118,7 @@ public class AtomicMarkableReference { V newReference, boolean expectedMark, boolean newMark) { - ReferenceBooleanPair current = atomicRef.get(); + ReferenceBooleanPair current = atomicRef.get(); return expectedReference == current.reference && expectedMark == current.bit && ((newReference == current.reference && newMark == current.bit) || @@ -134,16 +134,16 @@ public class AtomicMarkableReference { * @param newMark the new value for the mark */ public void set(V newReference, boolean newMark) { - ReferenceBooleanPair current = atomicRef.get(); + ReferenceBooleanPair current = atomicRef.get(); if (newReference != current.reference || newMark != current.bit) atomicRef.set(new ReferenceBooleanPair(newReference, newMark)); } /** * Atomically sets the value of the mark to the given update value - * if the current reference is == to the expected + * if the current reference is {@code ==} to the expected * reference. Any given invocation of this operation may fail - * (return false) spuriously, but repeated invocation + * (return {@code false}) spuriously, but repeated invocation * when the current value holds the expected value and no other * thread is also attempting to set the value will eventually * succeed. @@ -153,7 +153,7 @@ public class AtomicMarkableReference { * @return true if successful */ public boolean attemptMark(V expectedReference, boolean newMark) { - ReferenceBooleanPair current = atomicRef.get(); + ReferenceBooleanPair current = atomicRef.get(); return expectedReference == current.reference && (newMark == current.bit || atomicRef.compareAndSet diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReference.java b/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReference.java index 89c050c49..1de9b1ccd 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReference.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReference.java @@ -15,7 +15,7 @@ import sun.misc.Unsafe; * @author Doug Lea * @param The type of object referred to by this reference */ -public class AtomicReference implements java.io.Serializable { +public class AtomicReference implements java.io.Serializable { private static final long serialVersionUID = -1848883965231344442L; // BEGIN android-changed @@ -27,13 +27,13 @@ public class AtomicReference implements java.io.Serializable { try { valueOffset = unsafe.objectFieldOffset (AtomicReference.class.getDeclaredField("value")); - } catch(Exception ex) { throw new Error(ex); } + } catch (Exception ex) { throw new Error(ex); } } private volatile V value; /** - * Create a new AtomicReference with the given initial value. + * Creates a new AtomicReference with the given initial value. * * @param initialValue the initial value */ @@ -42,55 +42,59 @@ public class AtomicReference implements java.io.Serializable { } /** - * Create a new AtomicReference with null initial value. + * Creates a new AtomicReference with null initial value. */ public AtomicReference() { } - + /** - * Get the current value. + * Gets the current value. * * @return the current value */ public final V get() { return value; } - + /** - * Set to the given value. + * Sets to the given value. * * @param newValue the new value */ public final void set(V newValue) { value = newValue; } - + /** - * Atomically set the value to the given updated value - * if the current value == the expected value. + * Atomically sets the value to the given updated value + * if the current value {@code ==} the expected value. * @param expect the expected value * @param update the new value * @return true if successful. False return indicates that * the actual value was not equal to the expected value. */ public final boolean compareAndSet(V expect, V update) { - return unsafe.compareAndSwapObject(this, valueOffset, expect, update); + return unsafe.compareAndSwapObject(this, valueOffset, expect, update); } /** - * Atomically set the value to the given updated value - * if the current value == the expected value. - * May fail spuriously. + * Atomically sets the value to the given updated value + * if the current value {@code ==} the expected value. + * + *

      May fail spuriously + * and does not provide ordering guarantees, so is only rarely an + * appropriate alternative to {@code compareAndSet}. + * * @param expect the expected value * @param update the new value * @return true if successful. */ public final boolean weakCompareAndSet(V expect, V update) { - return unsafe.compareAndSwapObject(this, valueOffset, expect, update); + return unsafe.compareAndSwapObject(this, valueOffset, expect, update); } /** - * Set to the given value and return the old value. + * Atomically sets to the given value and returns the old value. * * @param newValue the new value * @return the previous value diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReferenceArray.java b/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReferenceArray.java index 9ad800ce7..9a484e614 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReferenceArray.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReferenceArray.java @@ -17,7 +17,7 @@ import java.util.*; * @author Doug Lea * @param The base class of elements held in this array */ -public class AtomicReferenceArray implements java.io.Serializable { +public class AtomicReferenceArray implements java.io.Serializable { private static final long serialVersionUID = -6209656149925076980L; // BEGIN android-changed @@ -34,25 +34,25 @@ public class AtomicReferenceArray implements java.io.Serializable { } /** - * Create a new AtomicReferenceArray of given length. + * Creates a new AtomicReferenceArray of given length. * @param length the length of the array */ public AtomicReferenceArray(int length) { array = new Object[length]; // must perform at least one volatile write to conform to JMM - if (length > 0) + if (length > 0) unsafe.putObjectVolatile(array, rawIndex(0), null); } /** - * Create a new AtomicReferenceArray with the same length as, and + * Creates a new AtomicReferenceArray with the same length as, and * all elements copied from, the given array. * * @param array the array to copy elements from * @throws NullPointerException if array is null */ public AtomicReferenceArray(E[] array) { - if (array == null) + if (array == null) throw new NullPointerException(); int length = array.length; this.array = new Object[length]; @@ -76,7 +76,7 @@ public class AtomicReferenceArray implements java.io.Serializable { } /** - * Get the current value at position i. + * Gets the current value at position {@code i}. * * @param i the index * @return the current value @@ -84,9 +84,9 @@ public class AtomicReferenceArray implements java.io.Serializable { public final E get(int i) { return (E) unsafe.getObjectVolatile(array, rawIndex(i)); } - + /** - * Set the element at position i to the given value. + * Sets the element at position {@code i} to the given value. * * @param i the index * @param newValue the new value @@ -94,10 +94,10 @@ public class AtomicReferenceArray implements java.io.Serializable { public final void set(int i, E newValue) { unsafe.putObjectVolatile(array, rawIndex(i), newValue); } - + /** - * Set the element at position i to the given value and return the - * old value. + * Atomically sets the element at position {@code i} to the given + * value and returns the old value. * * @param i the index * @param newValue the new value @@ -110,10 +110,10 @@ public class AtomicReferenceArray implements java.io.Serializable { return current; } } - + /** - * Atomically set the value to the given updated value - * if the current value == the expected value. + * Atomically sets the element at position {@code i} to the given + * updated value if the current value {@code ==} the expected value. * @param i the index * @param expect the expected value * @param update the new value @@ -121,14 +121,18 @@ public class AtomicReferenceArray implements java.io.Serializable { * the actual value was not equal to the expected value. */ public final boolean compareAndSet(int i, E expect, E update) { - return unsafe.compareAndSwapObject(array, rawIndex(i), + return unsafe.compareAndSwapObject(array, rawIndex(i), expect, update); } /** - * Atomically set the value to the given updated value - * if the current value == the expected value. - * May fail spuriously. + * Atomically sets the element at position {@code i} to the given + * updated value if the current value {@code ==} the expected value. + * + *

      May fail spuriously + * and does not provide ordering guarantees, so is only rarely an + * appropriate alternative to {@code compareAndSet}. + * * @param i the index * @param expect the expected value * @param update the new value diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java b/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java index f20661d03..fae134f96 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java @@ -8,9 +8,12 @@ package java.util.concurrent.atomic; import sun.misc.Unsafe; import java.lang.reflect.*; +import org.apache.harmony.kernel.vm.VM; +import dalvik.system.VMStack; + /** * A reflection-based utility that enables atomic updates to - * designated volatile reference fields of designated + * designated {@code volatile} reference fields of designated * classes. This class is designed for use in atomic data structures * in which several reference fields of the same node are * independently subject to atomic updates. For example, a tree node @@ -33,12 +36,13 @@ import java.lang.reflect.*; * } *

      * - *

      Note that the guarantees of the compareAndSet - * method in this class are weaker than in other atomic classes. Because this - * class cannot ensure that all uses of the field are appropriate for - * purposes of atomic access, it can guarantee atomicity and volatile - * semantics only with respect to other invocations of - * compareAndSet and set. + *

      Note that the guarantees of the {@code compareAndSet} + * method in this class are weaker than in other atomic classes. + * Because this class cannot ensure that all uses of the field + * are appropriate for purposes of atomic access, it can + * guarantee atomicity only with respect to other invocations of + * {@code compareAndSet} and {@code set} on the same updater. + * * @since 1.5 * @author Doug Lea * @param The type of the object holding the updatable field @@ -47,9 +51,10 @@ import java.lang.reflect.*; public abstract class AtomicReferenceFieldUpdater { /** - * Creates an updater for objects with the given field. The Class - * arguments are needed to check that reflective types and generic - * types match. + * Creates and returns an updater for objects with the given field. + * The Class arguments are needed to check that reflective types and + * generic types match. + * * @param tclass the class of the objects holding the field. * @param vclass the class of the field * @param fieldName the name of the field to be updated. @@ -59,9 +64,8 @@ public abstract class AtomicReferenceFieldUpdater { * exception if the class does not hold field or is the wrong type. */ public static AtomicReferenceFieldUpdater newUpdater(Class tclass, Class vclass, String fieldName) { - // Currently rely on standard intrinsics implementation - return new AtomicReferenceFieldUpdaterImpl(tclass, - vclass, + return new AtomicReferenceFieldUpdaterImpl(tclass, + vclass, fieldName); } @@ -72,27 +76,30 @@ public abstract class AtomicReferenceFieldUpdater { } /** - * Atomically set the value of the field of the given object managed - * by this Updater to the given updated value if the current value - * == the expected value. This method is guaranteed to be - * atomic with respect to other calls to compareAndSet and - * set, but not necessarily with respect to other - * changes in the field. + * Atomically sets the field of the given object managed by this updater + * to the given updated value if the current value {@code ==} the + * expected value. This method is guaranteed to be atomic with respect to + * other calls to {@code compareAndSet} and {@code set}, but not + * necessarily with respect to other changes in the field. + * * @param obj An object whose field to conditionally set * @param expect the expected value * @param update the new value * @return true if successful. */ - public abstract boolean compareAndSet(T obj, V expect, V update); /** - * Atomically set the value of the field of the given object managed - * by this Updater to the given updated value if the current value - * == the expected value. This method is guaranteed to be - * atomic with respect to other calls to compareAndSet and - * set, but not necessarily with respect to other - * changes in the field, and may fail spuriously. + * Atomically sets the field of the given object managed by this updater + * to the given updated value if the current value {@code ==} the + * expected value. This method is guaranteed to be atomic with respect to + * other calls to {@code compareAndSet} and {@code set}, but not + * necessarily with respect to other changes in the field. + * + *

      May fail spuriously + * and does not provide ordering guarantees, so is only rarely an + * appropriate alternative to {@code compareAndSet}. + * * @param obj An object whose field to conditionally set * @param expect the expected value * @param update the new value @@ -101,23 +108,27 @@ public abstract class AtomicReferenceFieldUpdater { public abstract boolean weakCompareAndSet(T obj, V expect, V update); /** - * Set the field of the given object managed by this updater. This - * operation is guaranteed to act as a volatile store with respect - * to subsequent invocations of compareAndSet. + * Sets the field of the given object managed by this updater to the + * given updated value. This operation is guaranteed to act as a volatile + * store with respect to subsequent invocations of {@code compareAndSet}. + * * @param obj An object whose field to set * @param newValue the new value */ public abstract void set(T obj, V newValue); /** - * Get the current value held in the field by the given object. + * Gets the current value held in the field of the given object managed + * by this updater. + * * @param obj An object whose field to get * @return the current value */ public abstract V get(T obj); /** - * Set to the given value and return the old value. + * Atomically sets the field of the given object managed by this updater + * to the given value and returns the old value. * * @param obj An object whose field to get and set * @param newValue the new value @@ -131,67 +142,128 @@ public abstract class AtomicReferenceFieldUpdater { } } - /** - * Standard hotspot implementation using intrinsics - */ - private static class AtomicReferenceFieldUpdaterImpl extends AtomicReferenceFieldUpdater { + private static final class AtomicReferenceFieldUpdaterImpl + extends AtomicReferenceFieldUpdater { // BEGIN android-changed private static final Unsafe unsafe = UnsafeAccess.THE_ONE; // END android-changed private final long offset; private final Class tclass; private final Class vclass; + private final Class cclass; - AtomicReferenceFieldUpdaterImpl(Class tclass, Class vclass, String fieldName) { + /* + * Internal type checks within all update methods contain + * internal inlined optimizations checking for the common + * cases where the class is final (in which case a simple + * getClass comparison suffices) or is of type Object (in + * which case no check is needed because all objects are + * instances of Object). The Object case is handled simply by + * setting vclass to null in constructor. The targetCheck and + * updateCheck methods are invoked when these faster + * screenings fail. + */ + + AtomicReferenceFieldUpdaterImpl(Class tclass, + Class vclass, + String fieldName) { Field field = null; Class fieldClass = null; + Class caller = null; + int modifiers = 0; try { field = tclass.getDeclaredField(fieldName); + // BEGIN android-changed + caller = VMStack.getStackClass2(); + // END android-changed + modifiers = field.getModifiers(); + SecurityManager smgr = System.getSecurityManager(); + if (smgr != null) { + int type = Modifier.isPublic(modifiers) + ? Member.PUBLIC : Member.DECLARED; + smgr.checkMemberAccess(tclass, type); + smgr.checkPackageAccess(tclass.getPackage().getName()); + } fieldClass = field.getType(); - } catch(Exception ex) { + } catch (Exception ex) { throw new RuntimeException(ex); } - + if (vclass != fieldClass) throw new ClassCastException(); - - if (!Modifier.isVolatile(field.getModifiers())) + + if (!Modifier.isVolatile(modifiers)) throw new IllegalArgumentException("Must be volatile type"); + this.cclass = (Modifier.isProtected(modifiers) && + caller != tclass) ? caller : null; this.tclass = tclass; - this.vclass = vclass; + if (vclass == Object.class) + this.vclass = null; + else + this.vclass = vclass; offset = unsafe.objectFieldOffset(field); } - - public boolean compareAndSet(T obj, V expect, V update) { + void targetCheck(T obj) { + if (!tclass.isInstance(obj)) + throw new ClassCastException(); + if (cclass != null) + ensureProtectedAccess(obj); + } + + void updateCheck(T obj, V update) { if (!tclass.isInstance(obj) || - (update != null && !vclass.isInstance(update))) + (update != null && vclass != null && !vclass.isInstance(update))) throw new ClassCastException(); + if (cclass != null) + ensureProtectedAccess(obj); + } + + public boolean compareAndSet(T obj, V expect, V update) { + if (obj == null || obj.getClass() != tclass || cclass != null || + (update != null && vclass != null && + vclass != update.getClass())) + updateCheck(obj, update); return unsafe.compareAndSwapObject(obj, offset, expect, update); } public boolean weakCompareAndSet(T obj, V expect, V update) { // same implementation as strong form for now - if (!tclass.isInstance(obj) || - (update != null && !vclass.isInstance(update))) - throw new ClassCastException(); + if (obj == null || obj.getClass() != tclass || cclass != null || + (update != null && vclass != null && + vclass != update.getClass())) + updateCheck(obj, update); return unsafe.compareAndSwapObject(obj, offset, expect, update); } - public void set(T obj, V newValue) { - if (!tclass.isInstance(obj) || - (newValue != null && !vclass.isInstance(newValue))) - throw new ClassCastException(); + if (obj == null || obj.getClass() != tclass || cclass != null || + (newValue != null && vclass != null && + vclass != newValue.getClass())) + updateCheck(obj, newValue); unsafe.putObjectVolatile(obj, offset, newValue); } public V get(T obj) { - if (!tclass.isInstance(obj)) - throw new ClassCastException(); + if (obj == null || obj.getClass() != tclass || cclass != null) + targetCheck(obj); return (V)unsafe.getObjectVolatile(obj, offset); } + + private void ensureProtectedAccess(T obj) { + if (cclass.isInstance(obj)) { + return; + } + throw new RuntimeException ( + new IllegalAccessException("Class " + + cclass.getName() + + " can not access a protected member of class " + + tclass.getName() + + " using an instance of " + + obj.getClass().getName() + ) + ); + } } } - diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicStampedReference.java b/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicStampedReference.java index b0a02c23a..a1d535f8b 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicStampedReference.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/atomic/AtomicStampedReference.java @@ -7,8 +7,8 @@ package java.util.concurrent.atomic; /** - * An AtomicStampedReference maintains an object reference - * along with an integer "stamp", that can be updated atomically. + * An {@code AtomicStampedReference} maintains an object reference + * along with an integer "stamp", that can be updated atomically. * *

      Implementation note. This implementation maintains stamped * references by creating internal objects representing "boxed" @@ -31,7 +31,7 @@ public class AtomicStampedReference { private final AtomicReference> atomicRef; /** - * Creates a new AtomicStampedReference with the given + * Creates a new {@code AtomicStampedReference} with the given * initial values. * * @param initialRef the initial reference @@ -62,10 +62,10 @@ public class AtomicStampedReference { /** * Returns the current values of both the reference and the stamp. - * Typical usage is int[1] holder; ref = v.get(holder); . + * Typical usage is {@code int[1] holder; ref = v.get(holder); }. * * @param stampHolder an array of size of at least one. On return, - * stampholder[0] will hold the value of the stamp. + * {@code stampholder[0]} will hold the value of the stamp. * @return the current value of the reference */ public V get(int[] stampHolder) { @@ -77,12 +77,12 @@ public class AtomicStampedReference { /** * Atomically sets the value of both the reference and stamp * to the given update values if the - * current reference is == to the expected reference - * and the current stamp is equal to the expected stamp. Any given - * invocation of this operation may fail (return - * false) spuriously, but repeated invocation when - * the current value holds the expected value and no other thread - * is also attempting to set the value will eventually succeed. + * current reference is {@code ==} to the expected reference + * and the current stamp is equal to the expected stamp. + * + *

      May fail spuriously + * and does not provide ordering guarantees, so is only rarely an + * appropriate alternative to {@code compareAndSet}. * * @param expectedReference the expected value of the reference * @param newReference the new value for the reference @@ -94,7 +94,7 @@ public class AtomicStampedReference { V newReference, int expectedStamp, int newStamp) { - ReferenceIntegerPair current = atomicRef.get(); + ReferenceIntegerPair current = atomicRef.get(); return expectedReference == current.reference && expectedStamp == current.integer && ((newReference == current.reference && @@ -107,8 +107,8 @@ public class AtomicStampedReference { /** * Atomically sets the value of both the reference and stamp * to the given update values if the - * current reference is == to the expected reference - * and the current stamp is equal to the expected stamp. + * current reference is {@code ==} to the expected reference + * and the current stamp is equal to the expected stamp. * * @param expectedReference the expected value of the reference * @param newReference the new value for the reference @@ -120,7 +120,7 @@ public class AtomicStampedReference { V newReference, int expectedStamp, int newStamp) { - ReferenceIntegerPair current = atomicRef.get(); + ReferenceIntegerPair current = atomicRef.get(); return expectedReference == current.reference && expectedStamp == current.integer && ((newReference == current.reference && @@ -138,16 +138,16 @@ public class AtomicStampedReference { * @param newStamp the new value for the stamp */ public void set(V newReference, int newStamp) { - ReferenceIntegerPair current = atomicRef.get(); + ReferenceIntegerPair current = atomicRef.get(); if (newReference != current.reference || newStamp != current.integer) atomicRef.set(new ReferenceIntegerPair(newReference, newStamp)); } /** * Atomically sets the value of the stamp to the given update value - * if the current reference is == to the expected + * if the current reference is {@code ==} to the expected * reference. Any given invocation of this operation may fail - * (return false) spuriously, but repeated invocation + * (return {@code false}) spuriously, but repeated invocation * when the current value holds the expected value and no other * thread is also attempting to set the value will eventually * succeed. @@ -157,7 +157,7 @@ public class AtomicStampedReference { * @return true if successful */ public boolean attemptStamp(V expectedReference, int newStamp) { - ReferenceIntegerPair current = atomicRef.get(); + ReferenceIntegerPair current = atomicRef.get(); return expectedReference == current.reference && (newStamp == current.integer || atomicRef.compareAndSet(current, diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/atomic/package-info.java b/libcore/concurrent/src/main/java/java/util/concurrent/atomic/package-info.java new file mode 100644 index 000000000..2252e5ce7 --- /dev/null +++ b/libcore/concurrent/src/main/java/java/util/concurrent/atomic/package-info.java @@ -0,0 +1,163 @@ +/* + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/licenses/publicdomain + */ + +/** + * A small toolkit of classes that support lock-free thread-safe + * programming on single variables. In essence, the classes in this + * package extend the notion of {@code volatile} values, fields, and + * array elements to those that also provide an atomic conditional update + * operation of the form: + * + *

      + *   boolean compareAndSet(expectedValue, updateValue);
      + * 
      + * + *

      This method (which varies in argument types across different + * classes) atomically sets a variable to the {@code updateValue} if it + * currently holds the {@code expectedValue}, reporting {@code true} on + * success. The classes in this package also contain methods to get and + * unconditionally set values, as well as a weaker conditional atomic + * update operation {@code weakCompareAndSet} described below. + * + *

      The specifications of these methods enable implementations to + * employ efficient machine-level atomic instructions that are available + * on contemporary processors. However on some platforms, support may + * entail some form of internal locking. Thus the methods are not + * strictly guaranteed to be non-blocking -- + * a thread may block transiently before performing the operation. + * + *

      Instances of classes + * {@link java.util.concurrent.atomic.AtomicBoolean}, + * {@link java.util.concurrent.atomic.AtomicInteger}, + * {@link java.util.concurrent.atomic.AtomicLong}, and + * {@link java.util.concurrent.atomic.AtomicReference} + * each provide access and updates to a single variable of the + * corresponding type. Each class also provides appropriate utility + * methods for that type. For example, classes {@code AtomicLong} and + * {@code AtomicInteger} provide atomic increment methods. One + * application is to generate sequence numbers, as in: + * + *

      + * class Sequencer {
      + *   private final AtomicLong sequenceNumber
      + *     = new AtomicLong(0);
      + *   public long next() {
      + *     return sequenceNumber.getAndIncrement();
      + *   }
      + * }
      + * 
      + * + *

      The memory effects for accesses and updates of atomics generally + * follow the rules for volatiles, as stated in + * The Java Language + * Specification, Third Edition (17.4 Memory Model): + * + *

        + * + *
      • {@code get} has the memory effects of reading a + * {@code volatile} variable. + * + *
      • {@code set} has the memory effects of writing (assigning) a + * {@code volatile} variable. + * + *
      • {@code weakCompareAndSet} atomically reads and conditionally + * writes a variable but does not + * create any happens-before orderings, so provides no guarantees + * with respect to previous or subsequent reads and writes of any + * variables other than the target of the {@code weakCompareAndSet}. + * + *
      • {@code compareAndSet} + * and all other read-and-update operations such as {@code getAndIncrement} + * have the memory effects of both reading and + * writing {@code volatile} variables. + *
      + * + *

      In addition to classes representing single values, this package + * contains Updater classes that can be used to obtain + * {@code compareAndSet} operations on any selected {@code volatile} + * field of any selected class. + * + * {@link java.util.concurrent.atomic.AtomicReferenceFieldUpdater}, + * {@link java.util.concurrent.atomic.AtomicIntegerFieldUpdater}, and + * {@link java.util.concurrent.atomic.AtomicLongFieldUpdater} are + * reflection-based utilities that provide access to the associated + * field types. These are mainly of use in atomic data structures in + * which several {@code volatile} fields of the same node (for + * example, the links of a tree node) are independently subject to + * atomic updates. These classes enable greater flexibility in how + * and when to use atomic updates, at the expense of more awkward + * reflection-based setup, less convenient usage, and weaker + * guarantees. + * + *

      The + * {@link java.util.concurrent.atomic.AtomicIntegerArray}, + * {@link java.util.concurrent.atomic.AtomicLongArray}, and + * {@link java.util.concurrent.atomic.AtomicReferenceArray} classes + * further extend atomic operation support to arrays of these types. + * These classes are also notable in providing {@code volatile} access + * semantics for their array elements, which is not supported for + * ordinary arrays. + * + * + *

      The atomic classes also support method {@code weakCompareAndSet}, + * which has limited applicability. On some platforms, the weak version + * may be more efficient than {@code compareAndSet} in the normal case, + * but differs in that any given invocation of the + * {@code weakCompareAndSet} method may return {@code false} + * spuriously (that is, for no apparent reason). A + * {@code false} return means only that the operation may be retried if + * desired, relying on the guarantee that repeated invocation when the + * variable holds {@code expectedValue} and no other thread is also + * attempting to set the variable will eventually succeed. (Such + * spurious failures may for example be due to memory contention effects + * that are unrelated to whether the expected and current values are + * equal.) Additionally {@code weakCompareAndSet} does not provide + * ordering guarantees that are usually needed for synchronization + * control. However, the method may be useful for updating counters and + * statistics when such updates are unrelated to the other + * happens-before orderings of a program. When a thread sees an update + * to an atomic variable caused by a {@code weakCompareAndSet}, it does + * not necessarily see updates to any other variables that + * occurred before the {@code weakCompareAndSet}. This may be + * acceptable when, for example, updating performance statistics, but + * rarely otherwise. + * + *

      The {@link java.util.concurrent.atomic.AtomicMarkableReference} + * class associates a single boolean with a reference. For example, this + * bit might be used inside a data structure to mean that the object + * being referenced has logically been deleted. + * + * The {@link java.util.concurrent.atomic.AtomicStampedReference} + * class associates an integer value with a reference. This may be + * used for example, to represent version numbers corresponding to + * series of updates. + * + *

      Atomic classes are designed primarily as building blocks for + * implementing non-blocking data structures and related infrastructure + * classes. The {@code compareAndSet} method is not a general + * replacement for locking. It applies only when critical updates for an + * object are confined to a single variable. + * + *

      Atomic classes are not general purpose replacements for + * {@code java.lang.Integer} and related classes. They do not + * define methods such as {@code hashCode} and + * {@code compareTo}. (Because atomic variables are expected to be + * mutated, they are poor choices for hash table keys.) Additionally, + * classes are provided only for those types that are commonly useful in + * intended applications. For example, there is no atomic class for + * representing {@code byte}. In those infrequent cases where you would + * like to do so, you can use an {@code AtomicInteger} to hold + * {@code byte} values, and cast appropriately. + * + * You can also hold floats using + * {@link java.lang.Float#floatToIntBits} and + * {@link java.lang.Float#intBitsToFloat} conversions, and doubles using + * {@link java.lang.Double#doubleToLongBits} and + * {@link java.lang.Double#longBitsToDouble} conversions. + * + * @since 1.5 + */ +package java.util.concurrent.atomic; diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/atomic/package.html b/libcore/concurrent/src/main/java/java/util/concurrent/atomic/package.html deleted file mode 100644 index bdb15e87f..000000000 --- a/libcore/concurrent/src/main/java/java/util/concurrent/atomic/package.html +++ /dev/null @@ -1,132 +0,0 @@ - - -Atomics - - - - -A small toolkit of classes that support lock-free thread-safe -programming on single variables. In essence, the classes in this -package extend the notion of volatile values, fields, and -array elements to those that also provide an atomic conditional update -operation of the form: - -

      -  boolean compareAndSet(expectedValue, updateValue);
      -
      - -

      This method (which varies in argument types across different -classes) atomically sets a variable to the updateValue if it -currently holds the expectedValue, reporting true on -success. The classes in this package also contain methods to get and -unconditionally set values, as well as a weaker conditional atomic -update operation weakCompareAndSet. The weak version may be -more efficient in the normal case, but differs in that any given -invocation of weakCompareAndSet method may fail, even -spuriously (that is, for no apparent reason). A false return -means only that the operation may be retried if desired, relying on -the guarantee that repeated invocation when the variable holds -expectedValue and no other thread is also attempting to set -the variable will eventually succeed. - -

      The specifications of these methods enable implementations to -employ efficient machine-level atomic instructions that are available -on contemporary processors. However on some platforms, support may -entail some form of internal locking. Thus the methods are not -strictly guaranteed to be non-blocking -- -a thread may block transiently before performing the operation. - -

      Instances of classes {@link -java.util.concurrent.atomic.AtomicBoolean}, {@link -java.util.concurrent.atomic.AtomicInteger}, {@link -java.util.concurrent.atomic.AtomicLong}, and {@link -java.util.concurrent.atomic.AtomicReference} each provide access and -updates to a single variable of the corresponding type. Each class -also provides appropriate utility methods for that type. For example, -classes AtomicLong and AtomicInteger provide atomic -increment methods. One application is to generate sequence numbers, -as in: - -

      -class Sequencer {
      -  private AtomicLong sequenceNumber = new AtomicLong(0);
      -  public long next() { return sequenceNumber.getAndIncrement(); }
      -}
      -
      - -

      The memory effects for accesses and updates of atomics generally follow the -rules for volatiles: - -

        - -
      • get has the memory effects of reading a -volatile variable. - -
      • set has the memory effects of writing (assigning) a -volatile variable. - -
      • weakCompareAndSet atomically reads and conditionally - writes a variable, is ordered with respect to other - memory operations on that variable, but otherwise acts as an - ordinary non-volatile memory operation. - -
      • compareAndSet - and all other read-and-update operations such as getAndIncrement - have the memory effects of both reading and - writing volatile variables. -
      - -

      In addition to classes representing single values, this package -contains Updater classes that can be used to obtain -compareAndSet operations on any selected volatile -field of any selected class. {@link -java.util.concurrent.atomic.AtomicReferenceFieldUpdater}, {@link -java.util.concurrent.atomic.AtomicIntegerFieldUpdater}, and {@link -java.util.concurrent.atomic.AtomicLongFieldUpdater} are -reflection-based utilities that provide access to the associated field -types. These are mainly of use in atomic data structures in which -several volatile fields of the same node (for example, the -links of a tree node) are independently subject to atomic -updates. These classes enable greater flexibility in how and when to -use atomic updates, at the expense of more awkward reflection-based -setup, less convenient usage, and weaker guarantees. - -

      The {@link java.util.concurrent.atomic.AtomicIntegerArray}, {@link -java.util.concurrent.atomic.AtomicLongArray}, and {@link -java.util.concurrent.atomic.AtomicReferenceArray} classes further -extend atomic operation support to arrays of these types. These -classes are also notable in providing volatile access -semantics for their array elements, which is not supported for -ordinary arrays. - -

      The {@link java.util.concurrent.atomic.AtomicMarkableReference} -class associates a single boolean with a reference. For example, this -bit might be used inside a data structure to mean that the object -being referenced has logically been deleted. The {@link -java.util.concurrent.atomic.AtomicStampedReference} class associates -an integer value with a reference. This may be used for example, to -represent version numbers corresponding to series of updates. - -

      Atomic classes are designed primarily as building blocks for -implementing non-blocking data structures and related infrastructure -classes. The compareAndSet method is not a general -replacement for locking. It applies only when critical updates for an -object are confined to a single variable. - -

      Atomic classes are not general purpose replacements for -java.lang.Integer and related classes. They do not -define methods such as hashCode and -compareTo. (Because atomic variables are expected to be -mutated, they are poor choices for hash table keys.) Additionally, -classes are provided only for those types that are commonly useful in -intended applications. For example, there is no atomic class for -representing byte. In those infrequent cases where you would -like to do so, you can use an AtomicInteger to hold -byte values, and cast appropriately. You can also hold floats -using Float.floatToIntBits and Float.intBitstoFloat -conversions, and doubles using Double.doubleToLongBits and -Double.longBitsToDouble conversions. - -@since Android 1.0 - - diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/locks/AbstractOwnableSynchronizer.java b/libcore/concurrent/src/main/java/java/util/concurrent/locks/AbstractOwnableSynchronizer.java new file mode 100644 index 000000000..bf2fe1618 --- /dev/null +++ b/libcore/concurrent/src/main/java/java/util/concurrent/locks/AbstractOwnableSynchronizer.java @@ -0,0 +1,56 @@ +/* + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/licenses/publicdomain + */ + +package java.util.concurrent.locks; + +/** + * A synchronizer that may be exclusively owned by a thread. This + * class provides a basis for creating locks and related synchronizers + * that may entail a notion of ownership. The + * AbstractOwnableSynchronizer class itself does not manage or + * use this information. However, subclasses and tools may use + * appropriately maintained values to help control and monitor access + * and provide diagnostics. + * + * @author Doug Lea + */ +abstract class AbstractOwnableSynchronizer + implements java.io.Serializable { + + /** Use serial ID even though all fields transient. */ + private static final long serialVersionUID = 3737899427754241961L; + + /** + * Empty constructor for use by subclasses. + */ + protected AbstractOwnableSynchronizer() { } + + /** + * The current owner of exclusive mode synchronization. + */ + private transient Thread exclusiveOwnerThread; + + /** + * Sets the thread that currently owns exclusive access. A + * null argument indicates that no thread owns access. + * This method does not otherwise impose any synchronization or + * volatile field accesses. + */ + protected final void setExclusiveOwnerThread(Thread t) { + exclusiveOwnerThread = t; + } + + /** + * Returns the thread last set by + * setExclusiveOwnerThread, or null if never + * set. This method does not otherwise impose any synchronization + * or volatile field accesses. + * @return the owner thread + */ + protected final Thread getExclusiveOwnerThread() { + return exclusiveOwnerThread; + } +} \ No newline at end of file diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/locks/AbstractQueuedSynchronizer.java b/libcore/concurrent/src/main/java/java/util/concurrent/locks/AbstractQueuedSynchronizer.java index 29e72b5d9..8b9821ed4 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/locks/AbstractQueuedSynchronizer.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/locks/AbstractQueuedSynchronizer.java @@ -32,7 +32,7 @@ import sun.misc.Unsafe; * synchronization interface. Instead it defines methods such as * {@link #acquireInterruptibly} that can be invoked as * appropriate by concrete locks and related synchronizers to - * implement their public methods. + * implement their public methods. * *

      This class supports either or both a default exclusive * mode and a shared mode. When acquired in exclusive mode, @@ -59,14 +59,14 @@ import sun.misc.Unsafe; * condition, so if this constraint cannot be met, do not use it. The * behavior of {@link ConditionObject} depends of course on the * semantics of its synchronizer implementation. - * - *

      This class provides inspection, instrumentation, and monitoring + * + *

      This class provides inspection, instrumentation, and monitoring * methods for the internal queue, as well as similar methods for * condition objects. These can be exported as desired into classes * using an AbstractQueuedSynchronizer for their * synchronization mechanics. * - *

      Serialization of this class stores only the underlying atomic + *

      Serialization of this class stores only the underlying atomic * integer maintaining state, so deserialized objects have empty * thread queues. Typical subclasses requiring serializability will * define a readObject method that restores this to a known @@ -74,10 +74,10 @@ import sun.misc.Unsafe; * *

      Usage

      * - *

      To use this class as the basis of a synchronizer, redefine the + *

      To use this class as the basis of a synchronizer, redefine the * following methods, as applicable, by inspecting and/or modifying * the synchronization state using {@link #getState}, {@link - * #setState} and/or {@link #compareAndSetState}: + * #setState} and/or {@link #compareAndSetState}: * *

    13. * @param time the maximum time to wait - * @param unit the time unit of the time argument. - * @return false if the waiting time detectably elapsed - * before return from the method, else true. - * @throws InterruptedException if the current thread is interrupted (and - * interruption of thread suspension is supported). + * @param unit the time unit of the {@code time} argument + * @return {@code false} if the waiting time detectably elapsed + * before return from the method, else {@code true} + * @throws InterruptedException if the current thread is interrupted + * (and interruption of thread suspension is supported) */ boolean await(long time, TimeUnit unit) throws InterruptedException; - + /** * Causes the current thread to wait until it is signalled or interrupted, * or the specified deadline elapses. * - *

      The lock associated with this condition is atomically - * released and the current thread becomes disabled for thread scheduling + *

      The lock associated with this condition is atomically + * released and the current thread becomes disabled for thread scheduling * purposes and lies dormant until one of five things happens: *

        - *
      • Some other thread invokes the {@link #signal} method for this - * Condition and the current thread happens to be chosen as the - * thread to be awakened; or - *
      • Some other thread invokes the {@link #signalAll} method for this - * Condition; or - *
      • Some other thread {@link Thread#interrupt interrupts} the current - * thread, and interruption of thread suspension is supported; or + *
      • Some other thread invokes the {@link #signal} method for this + * {@code Condition} and the current thread happens to be chosen as the + * thread to be awakened; or + *
      • Some other thread invokes the {@link #signalAll} method for this + * {@code Condition}; or + *
      • Some other thread {@linkplain Thread#interrupt interrupts} the + * current thread, and interruption of thread suspension is supported; or *
      • The specified deadline elapses; or *
      • A "spurious wakeup" occurs. *
      @@ -362,11 +367,11 @@ public interface Condition { * *

      If the current thread: *

        - *
      • has its interrupted status set on entry to this method; or - *
      • is {@link Thread#interrupt interrupted} while waiting - * and interruption of thread suspension is supported, + *
      • has its interrupted status set on entry to this method; or + *
      • is {@linkplain Thread#interrupt interrupted} while waiting + * and interruption of thread suspension is supported, *
      - * then {@link InterruptedException} is thrown and the current thread's + * then {@link InterruptedException} is thrown and the current thread's * interrupted status is cleared. It is not specified, in the first * case, whether or not the test for interruption occurs before the lock * is released. @@ -378,20 +383,21 @@ public interface Condition { * synchronized boolean aMethod(Date deadline) { * boolean stillWaiting = true; * while (!conditionBeingWaitedFor) { - * if (stillwaiting) + * if (stillWaiting) * stillWaiting = theCondition.awaitUntil(deadline); * else * return false; * } - * // ... + * // ... * } *
      * *

      Implementation Considerations + * *

      The current thread is assumed to hold the lock associated with this - * Condition when this method is called. + * {@code Condition} when this method is called. * It is up to the implementation to determine if this is - * the case and if not, how to respond. Typically, an exception will be + * the case and if not, how to respond. Typically, an exception will be * thrown (such as {@link IllegalMonitorStateException}) and the * implementation must document that fact. * @@ -401,13 +407,11 @@ public interface Condition { * must ensure that the signal is redirected to another waiting thread, if * there is one. * - * * @param deadline the absolute time to wait until - * @return false if the deadline has - * elapsed upon return, else true. - * - * @throws InterruptedException if the current thread is interrupted (and - * interruption of thread suspension is supported). + * @return {@code false} if the deadline has elapsed upon return, else + * {@code true} + * @throws InterruptedException if the current thread is interrupted + * (and interruption of thread suspension is supported) */ boolean awaitUntil(Date deadline) throws InterruptedException; @@ -416,8 +420,8 @@ public interface Condition { * *

      If any threads are waiting on this condition then one * is selected for waking up. That thread must then re-acquire the - * lock before returning from await. - **/ + * lock before returning from {@code await}. + */ void signal(); /** @@ -425,15 +429,7 @@ public interface Condition { * *

      If any threads are waiting on this condition then they are * all woken up. Each thread must re-acquire the lock before it can - * return from await. - **/ + * return from {@code await}. + */ void signalAll(); - } - - - - - - - diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/locks/Lock.java b/libcore/concurrent/src/main/java/java/util/concurrent/locks/Lock.java index f07b72ec7..4b9abd665 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/locks/Lock.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/locks/Lock.java @@ -8,8 +8,8 @@ package java.util.concurrent.locks; import java.util.concurrent.TimeUnit; /** - * Lock implementations provide more extensive locking - * operations than can be obtained using synchronized methods + * {@code Lock} implementations provide more extensive locking + * operations than can be obtained using {@code synchronized} methods * and statements. They allow more flexible structuring, may have * quite different properties, and may support multiple associated * {@link Condition} objects. @@ -19,17 +19,16 @@ import java.util.concurrent.TimeUnit; * shared resource: only one thread at a time can acquire the lock and * all access to the shared resource requires that the lock be * acquired first. However, some locks may allow concurrent access to - * a shared resource, such as the read lock of a {@link - * ReadWriteLock}. + * a shared resource, such as the read lock of a {@link ReadWriteLock}. * - *

      The use of synchronized methods or statements provides + *

      The use of {@code synchronized} methods or statements provides * access to the implicit monitor lock associated with every object, but * forces all lock acquisition and release to occur in a block-structured way: * when multiple locks are acquired they must be released in the opposite * order, and all locks must be released in the same lexical scope in which * they were acquired. * - *

      While the scoping mechanism for synchronized methods + *

      While the scoping mechanism for {@code synchronized} methods * and statements makes it much easier to program with monitor locks, * and helps avoid many common programming errors involving locks, * there are occasions where you need to work with locks in a more @@ -38,18 +37,18 @@ import java.util.concurrent.TimeUnit; * "hand-over-hand" or "chain locking": you * acquire the lock of node A, then node B, then release A and acquire * C, then release B and acquire D and so on. Implementations of the - * Lock interface enable the use of such techniques by + * {@code Lock} interface enable the use of such techniques by * allowing a lock to be acquired and released in different scopes, * and allowing multiple locks to be acquired and released in any * order. * *

      With this increased flexibility comes additional * responsibility. The absence of block-structured locking removes the - * automatic release of locks that occurs with synchronized + * automatic release of locks that occurs with {@code synchronized} * methods and statements. In most cases, the following idiom * should be used: * - *

           Lock l = ...; 
      + * 
           Lock l = ...;
        *     l.lock();
        *     try {
        *         // access the resource protected by this lock
      @@ -63,39 +62,42 @@ import java.util.concurrent.TimeUnit;
        * held is protected by try-finally or try-catch to ensure that the
        * lock is released when necessary.
        *
      - * 

      Lock implementations provide additional functionality - * over the use of synchronized methods and statements by + *

      {@code Lock} implementations provide additional functionality + * over the use of {@code synchronized} methods and statements by * providing a non-blocking attempt to acquire a lock ({@link * #tryLock()}), an attempt to acquire the lock that can be * interrupted ({@link #lockInterruptibly}, and an attempt to acquire * the lock that can timeout ({@link #tryLock(long, TimeUnit)}). * - *

      A Lock class can also provide behavior and semantics + *

      A {@code Lock} class can also provide behavior and semantics * that is quite different from that of the implicit monitor lock, * such as guaranteed ordering, non-reentrant usage, or deadlock * detection. If an implementation provides such specialized semantics * then the implementation must document those semantics. * - *

      Note that Lock instances are just normal objects and can - * themselves be used as the target in a synchronized statement. + *

      Note that {@code Lock} instances are just normal objects and can + * themselves be used as the target in a {@code synchronized} statement. * Acquiring the - * monitor lock of a Lock instance has no specified relationship - * with invoking any of the {@link #lock} methods of that instance. - * It is recommended that to avoid confusion you never use Lock + * monitor lock of a {@code Lock} instance has no specified relationship + * with invoking any of the {@link #lock} methods of that instance. + * It is recommended that to avoid confusion you never use {@code Lock} * instances in this way, except within their own implementation. * - *

      Except where noted, passing a null value for any + *

      Except where noted, passing a {@code null} value for any * parameter will result in a {@link NullPointerException} being * thrown. * *

      Memory Synchronization

      - *

      All Lock implementations must enforce the same - * memory synchronization semantics as provided by the built-in monitor lock: + * + *

      All {@code Lock} implementations must enforce the same + * memory synchronization semantics as provided by the built-in monitor + * lock, as described in + * The Java Language Specification, Third Edition (17.4 Memory Model): *

        - *
      • A successful lock operation acts like a successful - * monitorEnter action - *
      • A successful unlock operation acts like a successful - * monitorExit action + *
      • A successful {@code lock} operation has the same memory + * synchronization effects as a successful Lock action. + *
      • A successful {@code unlock} operation has the same + * memory synchronization effects as a successful Unlock action. *
      * * Unsuccessful locking and unlocking operations, and reentrant @@ -108,7 +110,7 @@ import java.util.concurrent.TimeUnit; * non-interruptible, and timed) may differ in their performance * characteristics, ordering guarantees, or other implementation * qualities. Further, the ability to interrupt the ongoing - * acquisition of a lock may not be available in a given Lock + * acquisition of a lock may not be available in a given {@code Lock} * class. Consequently, an implementation is not required to define * exactly the same guarantees or semantics for all three forms of * lock acquisition, nor is it required to support interruption of an @@ -119,12 +121,11 @@ import java.util.concurrent.TimeUnit; * acquisition is supported: which is either totally, or only on * method entry. * - *

      As interruption generally implies cancellation, and checks for + *

      As interruption generally implies cancellation, and checks for * interruption are often infrequent, an implementation can favor responding * to an interrupt over normal method return. This is true even if it can be * shown that the interrupt occurred after another action may have unblocked - * the thread. An implementation should document this behavior. - * + * the thread. An implementation should document this behavior. * * @see ReentrantLock * @see Condition @@ -132,45 +133,50 @@ import java.util.concurrent.TimeUnit; * * @since 1.5 * @author Doug Lea - * - **/ + */ public interface Lock { /** * Acquires the lock. - *

      If the lock is not available then - * the current thread becomes disabled for thread scheduling - * purposes and lies dormant until the lock has been acquired. + * + *

      If the lock is not available then the current thread becomes + * disabled for thread scheduling purposes and lies dormant until the + * lock has been acquired. + * *

      Implementation Considerations - *

      A Lock implementation may be able to detect - * erroneous use of the lock, such as an invocation that would cause - * deadlock, and may throw an (unchecked) exception in such circumstances. - * The circumstances and the exception type must be documented by that - * Lock implementation. * - **/ + *

      A {@code Lock} implementation may be able to detect erroneous use + * of the lock, such as an invocation that would cause deadlock, and + * may throw an (unchecked) exception in such circumstances. The + * circumstances and the exception type must be documented by that + * {@code Lock} implementation. + */ void lock(); /** - * Acquires the lock unless the current thread is - * {@link Thread#interrupt interrupted}. + * Acquires the lock unless the current thread is + * {@linkplain Thread#interrupt interrupted}. + * *

      Acquires the lock if it is available and returns immediately. - *

      If the lock is not available then - * the current thread becomes disabled for thread scheduling - * purposes and lies dormant until one of two things happens: + * + *

      If the lock is not available then the current thread becomes + * disabled for thread scheduling purposes and lies dormant until + * one of two things happens: + * *

        *
      • The lock is acquired by the current thread; or - *
      • Some other thread {@link Thread#interrupt interrupts} the current - * thread, and interruption of lock acquisition is supported. + *
      • Some other thread {@linkplain Thread#interrupt interrupts} the + * current thread, and interruption of lock acquisition is supported. *
      + * *

      If the current thread: *

        - *
      • has its interrupted status set on entry to this method; or - *
      • is {@link Thread#interrupt interrupted} while acquiring - * the lock, and interruption of lock acquisition is supported, + *
      • has its interrupted status set on entry to this method; or + *
      • is {@linkplain Thread#interrupt interrupted} while acquiring the + * lock, and interruption of lock acquisition is supported, *
      - * then {@link InterruptedException} is thrown and the current thread's - * interrupted status is cleared. + * then {@link InterruptedException} is thrown and the current thread's + * interrupted status is cleared. * *

      Implementation Considerations * @@ -183,28 +189,26 @@ public interface Lock { *

      An implementation can favor responding to an interrupt over * normal method return. * - *

      A Lock implementation may be able to detect + *

      A {@code Lock} implementation may be able to detect * erroneous use of the lock, such as an invocation that would * cause deadlock, and may throw an (unchecked) exception in such * circumstances. The circumstances and the exception type must - * be documented by that Lock implementation. - * - * @throws InterruptedException if the current thread is interrupted - * while acquiring the lock (and interruption of lock acquisition is - * supported). - * - * @see Thread#interrupt + * be documented by that {@code Lock} implementation. * - **/ + * @throws InterruptedException if the current thread is + * interrupted while acquiring the lock (and interruption + * of lock acquisition is supported). + */ void lockInterruptibly() throws InterruptedException; - /** * Acquires the lock only if it is free at the time of invocation. + * *

      Acquires the lock if it is available and returns immediately - * with the value true. - * If the lock is not available then this method will return - * immediately with the value false. + * with the value {@code true}. + * If the lock is not available then this method will return + * immediately with the value {@code false}. + * *

      A typical usage idiom for this method would be: *

            *      Lock lock = ...;
      @@ -221,109 +225,103 @@ public interface Lock {
            * This usage ensures that the lock is unlocked if it was acquired, and
            * doesn't try to unlock if the lock was not acquired.
            *
      -     * @return true if the lock was acquired and false
      -     * otherwise.
      -     **/
      +     * @return {@code true} if the lock was acquired and
      +     *         {@code false} otherwise
      +     */
           boolean tryLock();
       
           /**
            * Acquires the lock if it is free within the given waiting time and the
      -     * current thread has not been {@link Thread#interrupt interrupted}.
      +     * current thread has not been {@linkplain Thread#interrupt interrupted}.
            *
            * 

      If the lock is available this method returns immediately - * with the value true. + * with the value {@code true}. * If the lock is not available then - * the current thread becomes disabled for thread scheduling + * the current thread becomes disabled for thread scheduling * purposes and lies dormant until one of three things happens: *

        *
      • The lock is acquired by the current thread; or - *
      • Some other thread {@link Thread#interrupt interrupts} the current - * thread, and interruption of lock acquisition is supported; or + *
      • Some other thread {@linkplain Thread#interrupt interrupts} the + * current thread, and interruption of lock acquisition is supported; or *
      • The specified waiting time elapses *
      - *

      If the lock is acquired then the value true is returned. + * + *

      If the lock is acquired then the value {@code true} is returned. + * *

      If the current thread: *

        - *
      • has its interrupted status set on entry to this method; or - *
      • is {@link Thread#interrupt interrupted} while acquiring - * the lock, and interruption of lock acquisition is supported, + *
      • has its interrupted status set on entry to this method; or + *
      • is {@linkplain Thread#interrupt interrupted} while acquiring + * the lock, and interruption of lock acquisition is supported, *
      - * then {@link InterruptedException} is thrown and the current thread's - * interrupted status is cleared. - *

      If the specified waiting time elapses then the value false + * then {@link InterruptedException} is thrown and the current thread's + * interrupted status is cleared. + * + *

      If the specified waiting time elapses then the value {@code false} * is returned. - * If the time is + * If the time is * less than or equal to zero, the method will not wait at all. * *

      Implementation Considerations + * *

      The ability to interrupt a lock acquisition in some implementations - * may not be possible, and if possible may - * be an expensive operation. + * may not be possible, and if possible may + * be an expensive operation. * The programmer should be aware that this may be the case. An * implementation should document when this is the case. - *

      An implementation can favor responding to an interrupt over normal + * + *

      An implementation can favor responding to an interrupt over normal * method return, or reporting a timeout. - *

      A Lock implementation may be able to detect - * erroneous use of the lock, such as an invocation that would cause - * deadlock, and may throw an (unchecked) exception in such circumstances. - * The circumstances and the exception type must be documented by that - * Lock implementation. + * + *

      A {@code Lock} implementation may be able to detect + * erroneous use of the lock, such as an invocation that would cause + * deadlock, and may throw an (unchecked) exception in such circumstances. + * The circumstances and the exception type must be documented by that + * {@code Lock} implementation. * * @param time the maximum time to wait for the lock - * @param unit the time unit of the time argument. - * @return true if the lock was acquired and false - * if the waiting time elapsed before the lock was acquired. + * @param unit the time unit of the {@code time} argument + * @return {@code true} if the lock was acquired and {@code false} + * if the waiting time elapsed before the lock was acquired * * @throws InterruptedException if the current thread is interrupted - * while acquiring the lock (and interruption of lock acquisition is - * supported). - * - * @see Thread#interrupt - * - **/ + * while acquiring the lock (and interruption of lock + * acquisition is supported) + */ boolean tryLock(long time, TimeUnit unit) throws InterruptedException; /** * Releases the lock. + * *

      Implementation Considerations - *

      A Lock implementation will usually impose + * + *

      A {@code Lock} implementation will usually impose * restrictions on which thread can release a lock (typically only the * holder of the lock can release it) and may throw * an (unchecked) exception if the restriction is violated. * Any restrictions and the exception - * type must be documented by that Lock implementation. - **/ + * type must be documented by that {@code Lock} implementation. + */ void unlock(); /** - * Returns a new {@link Condition} instance that is bound to this - * Lock instance. - *

      Before waiting on the condition the lock must be held by the - * current thread. - * A call to {@link Condition#await()} will atomically release the lock + * Returns a new {@link Condition} instance that is bound to this + * {@code Lock} instance. + * + *

      Before waiting on the condition the lock must be held by the + * current thread. + * A call to {@link Condition#await()} will atomically release the lock * before waiting and re-acquire the lock before the wait returns. + * *

      Implementation Considerations - *

      The exact operation of the {@link Condition} instance depends on the - * Lock implementation and must be documented by that + * + *

      The exact operation of the {@link Condition} instance depends on + * the {@code Lock} implementation and must be documented by that * implementation. - * - * @return A new {@link Condition} instance for this Lock - * instance. - * @throws UnsupportedOperationException if this Lock - * implementation does not support conditions. - **/ + * + * @return A new {@link Condition} instance for this {@code Lock} instance + * @throws UnsupportedOperationException if this {@code Lock} + * implementation does not support conditions + */ Condition newCondition(); - } - - - - - - - - - - - - diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/locks/LockSupport.java b/libcore/concurrent/src/main/java/java/util/concurrent/locks/LockSupport.java index 8603785c1..b55b874d7 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/locks/LockSupport.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/locks/LockSupport.java @@ -13,48 +13,56 @@ import sun.misc.Unsafe; * Basic thread blocking primitives for creating locks and other * synchronization classes. * - *

      This class associates with each thread that uses it, a permit + *

      This class associates, with each thread that uses it, a permit * (in the sense of the {@link java.util.concurrent.Semaphore - * Semaphore} class). A call to park will return immediately + * Semaphore} class). A call to {@code park} will return immediately * if the permit is available, consuming it in the process; otherwise - * it may block. A call to unpark makes the permit + * it may block. A call to {@code unpark} makes the permit * available, if it was not already available. (Unlike with Semaphores * though, permits do not accumulate. There is at most one.) * - *

      Methods park and unpark provide efficient + *

      Methods {@code park} and {@code unpark} provide efficient * means of blocking and unblocking threads that do not encounter the - * problems that cause the deprecated methods Thread.suspend - * and Thread.resume to be unusable for such purposes: Races - * between one thread invoking park and another thread trying - * to unpark it will preserve liveness, due to the - * permit. Additionally, park will return if the caller's + * problems that cause the deprecated methods {@code Thread.suspend} + * and {@code Thread.resume} to be unusable for such purposes: Races + * between one thread invoking {@code park} and another thread trying + * to {@code unpark} it will preserve liveness, due to the + * permit. Additionally, {@code park} will return if the caller's * thread was interrupted, and timeout versions are supported. The - * park method may also return at any other time, for "no + * {@code park} method may also return at any other time, for "no * reason", so in general must be invoked within a loop that rechecks - * conditions upon return. In this sense park serves as an + * conditions upon return. In this sense {@code park} serves as an * optimization of a "busy wait" that does not waste as much time - * spinning, but must be paired with an unpark to be + * spinning, but must be paired with an {@code unpark} to be * effective. * *

      These methods are designed to be used as tools for creating * higher-level synchronization utilities, and are not in themselves - * useful for most concurrency control applications. + * useful for most concurrency control applications. The {@code park} + * method is designed for use only in constructions of the form: + *

      while (!canProceed()) { ... LockSupport.park(this); }
      + * where neither {@code canProceed} nor any other actions prior to the + * call to {@code park} entail locking or blocking. Because only one + * permit is associated with each thread, any intermediary uses of + * {@code park} could interfere with its intended effects. * - *

      Sample Usage. Here is a sketch of a First-in-first-out - * non-reentrant lock class. - *

      - *   private AtomicBoolean locked = new AtomicBoolean(false);
      - *   private Queue<Thread> waiters = new ConcurrentLinkedQueue<Thread>();
      + * 

      Sample Usage. Here is a sketch of a first-in-first-out + * non-reentrant lock class: + *

      {@code
      + * class FIFOMutex {
      + *   private final AtomicBoolean locked = new AtomicBoolean(false);
      + *   private final Queue waiters
      + *     = new ConcurrentLinkedQueue();
        *
      - *   public void lock() { 
      + *   public void lock() {
        *     boolean wasInterrupted = false;
        *     Thread current = Thread.currentThread();
        *     waiters.add(current);
        *
        *     // Block while not first in queue or cannot acquire lock
      - *     while (waiters.peek() != current || 
      - *            !locked.compareAndSet(false, true)) { 
      - *        LockSupport.park();
      + *     while (waiters.peek() != current ||
      + *            !locked.compareAndSet(false, true)) {
      + *        LockSupport.park(this);
        *        if (Thread.interrupted()) // ignore interrupts while waiting
        *          wasInterrupted = true;
        *     }
      @@ -67,9 +75,8 @@ import sun.misc.Unsafe;
        *   public void unlock() {
        *     locked.set(false);
        *     LockSupport.unpark(waiters.peek());
      - *   } 
      - * }
      - * 
      + * } + * }}
      */ @SuppressWarnings("all") public class LockSupport { @@ -80,14 +87,15 @@ public class LockSupport { // END android-changed /** - * Make available the permit for the given thread, if it + * Makes available the permit for the given thread, if it * was not already available. If the thread was blocked on - * park then it will unblock. Otherwise, its next call - * to park is guaranteed not to block. This operation + * {@code park} then it will unblock. Otherwise, its next call + * to {@code park} is guaranteed not to block. This operation * is not guaranteed to have any effect at all if the given * thread has not been started. - * @param thread the thread to unpark, or null, in which case - * this operation has no effect. + * + * @param thread the thread to unpark, or {@code null}, in which case + * this operation has no effect */ public static void unpark(Thread thread) { if (thread != null) @@ -97,20 +105,26 @@ public class LockSupport { /** * Disables the current thread for thread scheduling purposes unless the * permit is available. - *

      If the permit is available then it is consumed and the call returns - * immediately; otherwise - * the current thread becomes disabled for thread scheduling - * purposes and lies dormant until one of three things happens: + * + *

      If the permit is available then it is consumed and the call + * returns immediately; otherwise the current thread becomes disabled + * for thread scheduling purposes and lies dormant until one of three + * things happens: + * *

        - *
      • Some other thread invokes unpark with the current thread - * as the target; or - *
      • Some other thread {@link Thread#interrupt interrupts} the current - * thread; or + * + *
      • Some other thread invokes {@link #unpark unpark} with the + * current thread as the target; or + * + *
      • Some other thread {@linkplain Thread#interrupt interrupts} + * the current thread; or + * *
      • The call spuriously (that is, for no reason) returns. *
      - *

      This method does not report which of these caused the - * method to return. Callers should re-check the conditions which caused - * the thread to park in the first place. Callers may also determine, + * + *

      This method does not report which of these caused the + * method to return. Callers should re-check the conditions which caused + * the thread to park in the first place. Callers may also determine, * for example, the interrupt status of the thread upon return. */ public static void park() { @@ -120,21 +134,27 @@ public class LockSupport { /** * Disables the current thread for thread scheduling purposes, for up to * the specified waiting time, unless the permit is available. - *

      If the permit is available then it is consumed and the call returns - * immediately; otherwise - * the current thread becomes disabled for thread scheduling - * purposes and lies dormant until one of four things happens: + * + *

      If the permit is available then it is consumed and the call + * returns immediately; otherwise the current thread becomes disabled + * for thread scheduling purposes and lies dormant until one of four + * things happens: + * *

        - *
      • Some other thread invokes unpark with the current thread - * as the target; or - *
      • Some other thread {@link Thread#interrupt interrupts} the current - * thread; or + *
      • Some other thread invokes {@link #unpark unpark} with the + * current thread as the target; or + * + *
      • Some other thread {@linkplain Thread#interrupt interrupts} + * the current thread; or + * *
      • The specified waiting time elapses; or + * *
      • The call spuriously (that is, for no reason) returns. *
      - *

      This method does not report which of these caused the - * method to return. Callers should re-check the conditions which caused - * the thread to park in the first place. Callers may also determine, + * + *

      This method does not report which of these caused the + * method to return. Callers should re-check the conditions which caused + * the thread to park in the first place. Callers may also determine, * for example, the interrupt status of the thread, or the elapsed time * upon return. * @@ -142,37 +162,40 @@ public class LockSupport { */ public static void parkNanos(long nanos) { if (nanos > 0) - unsafe.park(false, nanos); + unsafe.park(false, nanos); } /** * Disables the current thread for thread scheduling purposes, until * the specified deadline, unless the permit is available. - *

      If the permit is available then it is consumed and the call returns - * immediately; otherwise - * the current thread becomes disabled for thread scheduling - * purposes and lies dormant until one of four things happens: + * + *

      If the permit is available then it is consumed and the call + * returns immediately; otherwise the current thread becomes disabled + * for thread scheduling purposes and lies dormant until one of four + * things happens: + * *

        - *
      • Some other thread invokes unpark with the current thread - * as the target; or - *
      • Some other thread {@link Thread#interrupt interrupts} the current - * thread; or + *
      • Some other thread invokes {@link #unpark unpark} with the + * current thread as the target; or + * + *
      • Some other thread {@linkplain Thread#interrupt interrupts} + * the current thread; or + * *
      • The specified deadline passes; or + * *
      • The call spuriously (that is, for no reason) returns. *
      - *

      This method does not report which of these caused the - * method to return. Callers should re-check the conditions which caused - * the thread to park in the first place. Callers may also determine, + * + *

      This method does not report which of these caused the + * method to return. Callers should re-check the conditions which caused + * the thread to park in the first place. Callers may also determine, * for example, the interrupt status of the thread, or the current time * upon return. * - * @param deadline the absolute time, in milliseconds from the Epoch, to - * wait until + * @param deadline the absolute time, in milliseconds from the Epoch, + * to wait until */ public static void parkUntil(long deadline) { - unsafe.park(true, deadline); + unsafe.park(true, deadline); } - } - - diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/locks/ReadWriteLock.java b/libcore/concurrent/src/main/java/java/util/concurrent/locks/ReadWriteLock.java index c7116d5d4..484f68d15 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/locks/ReadWriteLock.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/locks/ReadWriteLock.java @@ -12,11 +12,18 @@ package java.util.concurrent.locks; * The {@link #readLock read lock} may be held simultaneously by * multiple reader threads, so long as there are no writers. The * {@link #writeLock write lock} is exclusive. - * + * + *

      All ReadWriteLock implementations must guarantee that + * the memory synchronization effects of writeLock operations + * (as specified in the {@link Lock} interface) also hold with respect + * to the associated readLock. That is, a thread successfully + * acquiring the read lock will see all updates made upon previous + * release of the write lock. + * *

      A read-write lock allows for a greater level of concurrency in - * accessing shared data, than that permitted by a mutual exclusion lock. + * accessing shared data than that permitted by a mutual exclusion lock. * It exploits the fact that while only a single thread at a time (a - * writer thread) can modify the shared data, in many cases any + * writer thread) can modify the shared data, in many cases any * number of threads can concurrently read the data (hence reader * threads). * In theory, the increase in concurrency permitted by the use of a read-write @@ -27,7 +34,7 @@ package java.util.concurrent.locks; * *

      Whether or not a read-write lock will improve performance over the use * of a mutual exclusion lock depends on the frequency that the data is - * read compared to being modified, the duration of the read and write + * read compared to being modified, the duration of the read and write * operations, and the contention for the data - that is, the number of * threads that will try to read or write the data at the same time. * For example, a collection that is initially populated with data and @@ -56,14 +63,14 @@ package java.util.concurrent.locks; * lengthy delays for a write if the readers are frequent and long-lived as * expected. Fair, or "in-order" implementations are also possible. * - *

    14. Determining whether readers that request the read lock while a + *
    15. Determining whether readers that request the read lock while a * reader is active and a writer is waiting, are granted the read lock. * Preference to the reader can delay the writer indefinitely, while - * preference to the write can reduce the potential for concurrency. + * preference to the writer can reduce the potential for concurrency. * *
    16. Determining whether the locks are reentrant: can a thread with the - * write lock reacquire it? can it acquire a read lock while holding the - * write lock? is the read lock itself reentrant? + * write lock reacquire it? Can it acquire a read lock while holding the + * write lock? Is the read lock itself reentrant? * *
    17. Can the write lock be downgraded to a read lock without allowing * an intervening writer? Can a read lock be upgraded to a write lock, diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/locks/ReentrantLock.java b/libcore/concurrent/src/main/java/java/util/concurrent/locks/ReentrantLock.java index bf3d65322..bf2ac38cc 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/locks/ReentrantLock.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/locks/ReentrantLock.java @@ -12,19 +12,19 @@ import java.util.concurrent.atomic.*; /** * A reentrant mutual exclusion {@link Lock} with the same basic * behavior and semantics as the implicit monitor lock accessed using - * synchronized methods and statements, but with extended + * {@code synchronized} methods and statements, but with extended * capabilities. * - *

      A ReentrantLock is owned by the thread last + *

      A {@code ReentrantLock} is owned by the thread last * successfully locking, but not yet unlocking it. A thread invoking - * lock will return, successfully acquiring the lock, when + * {@code lock} will return, successfully acquiring the lock, when * the lock is not owned by another thread. The method will return * immediately if the current thread already owns the lock. This can * be checked using methods {@link #isHeldByCurrentThread}, and {@link - * #getHoldCount}. + * #getHoldCount}. * - *

      The constructor for this class accepts an optional - * fairness parameter. When set true, under + *

      The constructor for this class accepts an optional + * fairness parameter. When set {@code true}, under * contention, locks favor granting access to the longest-waiting * thread. Otherwise this lock does not guarantee any particular * access order. Programs using fair locks accessed by many threads @@ -36,9 +36,12 @@ import java.util.concurrent.atomic.*; * fair lock may obtain it multiple times in succession while other * active threads are not progressing and not currently holding the * lock. + * Also note that the untimed {@link #tryLock() tryLock} method does not + * honor the fairness setting. It will succeed if the lock + * is available even if other threads are waiting. * - *

      It is recommended practice to always immediately - * follow a call to lock with a try block, most + *

      It is recommended practice to always immediately + * follow a call to {@code lock} with a {@code try} block, most * typically in a before/after construction such as: * *

      @@ -46,7 +49,7 @@ import java.util.concurrent.atomic.*;
        *   private final ReentrantLock lock = new ReentrantLock();
        *   // ...
        *
      - *   public void m() { 
      + *   public void m() {
        *     lock.lock();  // block until condition holds
        *     try {
        *       // ... method body
      @@ -58,21 +61,21 @@ import java.util.concurrent.atomic.*;
        * 
      * *

      In addition to implementing the {@link Lock} interface, this - * class defines methods isLocked and - * getLockQueueLength, as well as some associated - * protected access methods that may be useful for + * class defines methods {@code isLocked} and + * {@code getLockQueueLength}, as well as some associated + * {@code protected} access methods that may be useful for * instrumentation and monitoring. * - *

      Serialization of this class behaves in the same way as built-in + *

      Serialization of this class behaves in the same way as built-in * locks: a deserialized lock is in the unlocked state, regardless of * its state when serialized. * - *

      This lock supports a maximum of 2147483648 recursive locks by - * the same thread. + *

      This lock supports a maximum of 2147483647 recursive locks by + * the same thread. Attempts to exceed this limit result in + * {@link Error} throws from locking methods. * * @since 1.5 * @author Doug Lea - * */ public class ReentrantLock implements Lock, java.io.Serializable { private static final long serialVersionUID = 7373984872572414699L; @@ -84,32 +87,34 @@ public class ReentrantLock implements Lock, java.io.Serializable { * into fair and nonfair versions below. Uses AQS state to * represent the number of holds on the lock. */ - static abstract class Sync extends AbstractQueuedSynchronizer { - /** Current owner thread */ - transient Thread owner; + static abstract class Sync extends AbstractQueuedSynchronizer { + private static final long serialVersionUID = -5179523762034025860L; /** - * Perform {@link Lock#lock}. The main reason for subclassing + * Performs {@link Lock#lock}. The main reason for subclassing * is to allow fast path for nonfair version. */ abstract void lock(); - /** - * Perform non-fair tryLock. tryAcquire is + /** + * Performs non-fair tryLock. tryAcquire is * implemented in subclasses, but both need nonfair - * try for trylock method + * try for trylock method. */ - final boolean nonfairTryAcquire(int acquires) { + final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (compareAndSetState(0, acquires)) { - owner = current; + setExclusiveOwnerThread(current); return true; } } - else if (current == owner) { - setState(c+acquires); + else if (current == getExclusiveOwnerThread()) { + int nextc = c + acquires; + if (nextc < 0) // overflow + throw new Error("Maximum lock count exceeded"); + setState(nextc); return true; } return false; @@ -117,19 +122,21 @@ public class ReentrantLock implements Lock, java.io.Serializable { protected final boolean tryRelease(int releases) { int c = getState() - releases; - if (Thread.currentThread() != owner) + if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; if (c == 0) { free = true; - owner = null; + setExclusiveOwnerThread(null); } setState(c); return free; } protected final boolean isHeldExclusively() { - return getState() != 0 && owner == Thread.currentThread(); + // While we must in general read state before owner, + // we don't need to do so to check if current thread is owner + return getExclusiveOwnerThread() == Thread.currentThread(); } final ConditionObject newCondition() { @@ -139,23 +146,19 @@ public class ReentrantLock implements Lock, java.io.Serializable { // Methods relayed from outer class final Thread getOwner() { - int c = getState(); - Thread o = owner; - return (c == 0)? null : o; + return getState() == 0 ? null : getExclusiveOwnerThread(); } - + final int getHoldCount() { - int c = getState(); - Thread o = owner; - return (o == Thread.currentThread())? c : 0; + return isHeldExclusively() ? getState() : 0; } - + final boolean isLocked() { return getState() != 0; } /** - * Reconstitute this lock instance from a stream + * Reconstitutes this lock instance from a stream. * @param s the stream */ private void readObject(java.io.ObjectInputStream s) @@ -169,18 +172,20 @@ public class ReentrantLock implements Lock, java.io.Serializable { * Sync object for non-fair locks */ final static class NonfairSync extends Sync { + private static final long serialVersionUID = 7316153563782823691L; + /** - * Perform lock. Try immediate barge, backing up to normal + * Performs lock. Try immediate barge, backing up to normal * acquire on failure. */ final void lock() { if (compareAndSetState(0, 1)) - owner = Thread.currentThread(); + setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); } - protected final boolean tryAcquire(int acquires) { + protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } } @@ -188,28 +193,32 @@ public class ReentrantLock implements Lock, java.io.Serializable { /** * Sync object for fair locks */ - final static class FairSync extends Sync { - final void lock() { - acquire(1); + final static class FairSync extends Sync { + private static final long serialVersionUID = -3000897897090466540L; + + final void lock() { + acquire(1); } /** * Fair version of tryAcquire. Don't grant access unless * recursive call or no waiters or is first. */ - protected final boolean tryAcquire(int acquires) { + protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { - Thread first = getFirstQueuedThread(); - if ((first == null || first == current) && + if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) { - owner = current; + setExclusiveOwnerThread(current); return true; } } - else if (current == owner) { - setState(c+acquires); + else if (current == getExclusiveOwnerThread()) { + int nextc = c + acquires; + if (nextc < 0) + throw new Error("Maximum lock count exceeded"); + setState(nextc); return true; } return false; @@ -217,89 +226,88 @@ public class ReentrantLock implements Lock, java.io.Serializable { } /** - * Creates an instance of ReentrantLock. - * This is equivalent to using ReentrantLock(false). + * Creates an instance of {@code ReentrantLock}. + * This is equivalent to using {@code ReentrantLock(false)}. */ - public ReentrantLock() { + public ReentrantLock() { sync = new NonfairSync(); } /** - * Creates an instance of ReentrantLock with the + * Creates an instance of {@code ReentrantLock} with the * given fairness policy. - * @param fair true if this lock will be fair; else false + * + * @param fair {@code true} if this lock should use a fair ordering policy */ - public ReentrantLock(boolean fair) { + public ReentrantLock(boolean fair) { sync = (fair)? new FairSync() : new NonfairSync(); } /** - * Acquires the lock. + * Acquires the lock. * - *

      Acquires the lock if it is not held by another thread and returns + *

      Acquires the lock if it is not held by another thread and returns * immediately, setting the lock hold count to one. * - *

      If the current thread - * already holds the lock then the hold count is incremented by one and - * the method returns immediately. + *

      If the current thread already holds the lock then the hold + * count is incremented by one and the method returns immediately. * *

      If the lock is held by another thread then the - * current thread becomes disabled for thread scheduling + * current thread becomes disabled for thread scheduling * purposes and lies dormant until the lock has been acquired, - * at which time the lock hold count is set to one. + * at which time the lock hold count is set to one. */ public void lock() { sync.lock(); } /** - * Acquires the lock unless the current thread is - * {@link Thread#interrupt interrupted}. + * Acquires the lock unless the current thread is + * {@linkplain Thread#interrupt interrupted}. * - *

      Acquires the lock if it is not held by another thread and returns + *

      Acquires the lock if it is not held by another thread and returns * immediately, setting the lock hold count to one. * - *

      If the current thread already holds this lock then the hold count + *

      If the current thread already holds this lock then the hold count * is incremented by one and the method returns immediately. * *

      If the lock is held by another thread then the - * current thread becomes disabled for thread scheduling + * current thread becomes disabled for thread scheduling * purposes and lies dormant until one of two things happens: * *

        * *
      • The lock is acquired by the current thread; or * - *
      • Some other thread {@link Thread#interrupt interrupts} the current - * thread. + *
      • Some other thread {@linkplain Thread#interrupt interrupts} the + * current thread. * *
      * - *

      If the lock is acquired by the current thread then the lock hold + *

      If the lock is acquired by the current thread then the lock hold * count is set to one. * *

      If the current thread: * *

        * - *
      • has its interrupted status set on entry to this method; or + *
      • has its interrupted status set on entry to this method; or * - *
      • is {@link Thread#interrupt interrupted} while acquiring + *
      • is {@linkplain Thread#interrupt interrupted} while acquiring * the lock, * *
      * - * then {@link InterruptedException} is thrown and the current thread's - * interrupted status is cleared. + * then {@link InterruptedException} is thrown and the current thread's + * interrupted status is cleared. * - *

      In this implementation, as this method is an explicit interruption - * point, preference is - * given to responding to the interrupt over normal or reentrant - * acquisition of the lock. + *

      In this implementation, as this method is an explicit + * interruption point, preference is given to responding to the + * interrupt over normal or reentrant acquisition of the lock. * * @throws InterruptedException if the current thread is interrupted */ - public void lockInterruptibly() throws InterruptedException { + public void lockInterruptibly() throws InterruptedException { sync.acquireInterruptibly(1); } @@ -308,43 +316,42 @@ public class ReentrantLock implements Lock, java.io.Serializable { * of invocation. * *

      Acquires the lock if it is not held by another thread and - * returns immediately with the value true, setting the + * returns immediately with the value {@code true}, setting the * lock hold count to one. Even when this lock has been set to use a - * fair ordering policy, a call to tryLock() will + * fair ordering policy, a call to {@code tryLock()} will * immediately acquire the lock if it is available, whether or not - * other threads are currently waiting for the lock. - * This "barging" behavior can be useful in certain + * other threads are currently waiting for the lock. + * This "barging" behavior can be useful in certain * circumstances, even though it breaks fairness. If you want to honor - * the fairness setting for this lock, then use + * the fairness setting for this lock, then use * {@link #tryLock(long, TimeUnit) tryLock(0, TimeUnit.SECONDS) } * which is almost equivalent (it also detects interruption). * - *

      If the current thread - * already holds this lock then the hold count is incremented by one and - * the method returns true. + *

      If the current thread already holds this lock then the hold + * count is incremented by one and the method returns {@code true}. * - *

      If the lock is held by another thread then this method will return - * immediately with the value false. + *

      If the lock is held by another thread then this method will return + * immediately with the value {@code false}. * - * @return true if the lock was free and was acquired by the - * current thread, or the lock was already held by the current thread; and - * false otherwise. + * @return {@code true} if the lock was free and was acquired by the + * current thread, or the lock was already held by the current + * thread; and {@code false} otherwise */ public boolean tryLock() { return sync.nonfairTryAcquire(1); } /** - * Acquires the lock if it is not held by another thread within the given - * waiting time and the current thread has not been - * {@link Thread#interrupt interrupted}. + * Acquires the lock if it is not held by another thread within the given + * waiting time and the current thread has not been + * {@linkplain Thread#interrupt interrupted}. * - *

      Acquires the lock if it is not held by another thread and returns - * immediately with the value true, setting the lock hold count + *

      Acquires the lock if it is not held by another thread and returns + * immediately with the value {@code true}, setting the lock hold count * to one. If this lock has been set to use a fair ordering policy then * an available lock will not be acquired if any other threads * are waiting for the lock. This is in contrast to the {@link #tryLock()} - * method. If you want a timed tryLock that does permit barging on + * method. If you want a timed {@code tryLock} that does permit barging on * a fair lock then combine the timed and un-timed forms together: * *

      if (lock.tryLock() || lock.tryLock(timeout, unit) ) { ... }
      @@ -352,60 +359,56 @@ public class ReentrantLock implements Lock, java.io.Serializable {
            *
            * 

      If the current thread * already holds this lock then the hold count is incremented by one and - * the method returns true. + * the method returns {@code true}. * *

      If the lock is held by another thread then the - * current thread becomes disabled for thread scheduling + * current thread becomes disabled for thread scheduling * purposes and lies dormant until one of three things happens: * *

        * *
      • The lock is acquired by the current thread; or * - *
      • Some other thread {@link Thread#interrupt interrupts} the current - * thread; or + *
      • Some other thread {@linkplain Thread#interrupt interrupts} + * the current thread; or * *
      • The specified waiting time elapses * *
      * - *

      If the lock is acquired then the value true is returned and + *

      If the lock is acquired then the value {@code true} is returned and * the lock hold count is set to one. * *

      If the current thread: * *

        * - *
      • has its interrupted status set on entry to this method; or + *
      • has its interrupted status set on entry to this method; or * - *
      • is {@link Thread#interrupt interrupted} while acquiring - * the lock, + *
      • is {@linkplain Thread#interrupt interrupted} while + * acquiring the lock, * *
      - * then {@link InterruptedException} is thrown and the current thread's - * interrupted status is cleared. + * then {@link InterruptedException} is thrown and the current thread's + * interrupted status is cleared. * - *

      If the specified waiting time elapses then the value false - * is returned. - * If the time is - * less than or equal to zero, the method will not wait at all. + *

      If the specified waiting time elapses then the value {@code false} + * is returned. If the time is less than or equal to zero, the method + * will not wait at all. * - *

      In this implementation, as this method is an explicit interruption - * point, preference is - * given to responding to the interrupt over normal or reentrant - * acquisition of the lock, and over reporting the elapse of the waiting - * time. + *

      In this implementation, as this method is an explicit + * interruption point, preference is given to responding to the + * interrupt over normal or reentrant acquisition of the lock, and + * over reporting the elapse of the waiting time. * * @param timeout the time to wait for the lock * @param unit the time unit of the timeout argument - * - * @return true if the lock was free and was acquired by the - * current thread, or the lock was already held by the current thread; and - * false if the waiting time elapsed before the lock could be - * acquired. - * + * @return {@code true} if the lock was free and was acquired by the + * current thread, or the lock was already held by the current + * thread; and {@code false} if the waiting time elapsed before + * the lock could be acquired * @throws InterruptedException if the current thread is interrupted - * @throws NullPointerException if unit is null + * @throws NullPointerException if the time unit is null * */ public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException { @@ -413,22 +416,22 @@ public class ReentrantLock implements Lock, java.io.Serializable { } /** - * Attempts to release this lock. + * Attempts to release this lock. + * + *

      If the current thread is the holder of this lock then the hold + * count is decremented. If the hold count is now zero then the lock + * is released. If the current thread is not the holder of this + * lock then {@link IllegalMonitorStateException} is thrown. * - *

      If the current thread is the - * holder of this lock then the hold count is decremented. If the - * hold count is now zero then the lock is released. If the - * current thread is not the holder of this lock then {@link - * IllegalMonitorStateException} is thrown. * @throws IllegalMonitorStateException if the current thread does not - * hold this lock. + * hold this lock */ public void unlock() { sync.release(1); } /** - * Returns a {@link Condition} instance for use with this + * Returns a {@link Condition} instance for use with this * {@link Lock} instance. * *

      The returned {@link Condition} instance supports the same @@ -440,28 +443,28 @@ public class ReentrantLock implements Lock, java.io.Serializable { *

        * *
      • If this lock is not held when any of the {@link Condition} - * {@link Condition#await() waiting} or {@link Condition#signal - * signalling} methods are called, then an {@link + * {@linkplain Condition#await() waiting} or {@linkplain + * Condition#signal signalling} methods are called, then an {@link * IllegalMonitorStateException} is thrown. * - *
      • When the condition {@link Condition#await() waiting} + *
      • When the condition {@linkplain Condition#await() waiting} * methods are called the lock is released and, before they * return, the lock is reacquired and the lock hold count restored * to what it was when the method was called. * - *
      • If a thread is {@link Thread#interrupt interrupted} while - * waiting then the wait will terminate, an {@link + *
      • If a thread is {@linkplain Thread#interrupt interrupted} + * while waiting then the wait will terminate, an {@link * InterruptedException} will be thrown, and the thread's * interrupted status will be cleared. * - *
      • Waiting threads are signalled in FIFO order + *
      • Waiting threads are signalled in FIFO order. * *
      • The ordering of lock reacquisition for threads returning * from waiting methods is the same as for threads initially * acquiring the lock, which is in the default case not specified, * but for fair locks favors those threads that have been * waiting the longest. - * + * *
      * * @return the Condition object @@ -473,7 +476,7 @@ public class ReentrantLock implements Lock, java.io.Serializable { /** * Queries the number of holds on this lock by the current thread. * - *

      A thread has a hold on a lock for each lock action that is not + *

      A thread has a hold on a lock for each lock action that is not * matched by an unlock action. * *

      The hold count information is typically only used for testing and @@ -484,8 +487,8 @@ public class ReentrantLock implements Lock, java.io.Serializable { *

            * class X {
            *   ReentrantLock lock = new ReentrantLock();
      -     *   // ...     
      -     *   public void m() { 
      +     *   // ...
      +     *   public void m() {
            *     assert lock.getHoldCount() == 0;
            *     lock.lock();
            *     try {
      @@ -498,7 +501,7 @@ public class ReentrantLock implements Lock, java.io.Serializable {
            * 
      * * @return the number of holds on this lock by the current thread, - * or zero if this lock is not held by the current thread. + * or zero if this lock is not held by the current thread */ public int getHoldCount() { return sync.getHoldCount(); @@ -517,7 +520,7 @@ public class ReentrantLock implements Lock, java.io.Serializable { * ReentrantLock lock = new ReentrantLock(); * // ... * - * public void m() { + * public void m() { * assert lock.isHeldByCurrentThread(); * // ... method body * } @@ -532,7 +535,7 @@ public class ReentrantLock implements Lock, java.io.Serializable { * ReentrantLock lock = new ReentrantLock(); * // ... * - * public void m() { + * public void m() { * assert !lock.isHeldByCurrentThread(); * lock.lock(); * try { @@ -543,8 +546,9 @@ public class ReentrantLock implements Lock, java.io.Serializable { * } * } *
      - * @return true if current thread holds this lock and - * false otherwise. + * + * @return {@code true} if current thread holds this lock and + * {@code false} otherwise */ public boolean isHeldByCurrentThread() { return sync.isHeldExclusively(); @@ -552,47 +556,53 @@ public class ReentrantLock implements Lock, java.io.Serializable { /** * Queries if this lock is held by any thread. This method is - * designed for use in monitoring of the system state, + * designed for use in monitoring of the system state, * not for synchronization control. - * @return true if any thread holds this lock and - * false otherwise. + * + * @return {@code true} if any thread holds this lock and + * {@code false} otherwise */ public boolean isLocked() { return sync.isLocked(); } /** - * Returns true if this lock has fairness set true. - * @return true if this lock has fairness set true. + * Returns {@code true} if this lock has fairness set true. + * + * @return {@code true} if this lock has fairness set true */ public final boolean isFair() { return sync instanceof FairSync; } /** - * Returns the thread that currently owns the exclusive lock, or - * null if not owned. Note that the owner may be - * momentarily null even if there are threads trying to - * acquire the lock but have not yet done so. This method is - * designed to facilitate construction of subclasses that provide - * more extensive lock monitoring facilities. - * @return the owner, or null if not owned. + * Returns the thread that currently owns this lock, or + * {@code null} if not owned. When this method is called by a + * thread that is not the owner, the return value reflects a + * best-effort approximation of current lock status. For example, + * the owner may be momentarily {@code null} even if there are + * threads trying to acquire the lock but have not yet done so. + * This method is designed to facilitate construction of + * subclasses that provide more extensive lock monitoring + * facilities. + * + * @return the owner, or {@code null} if not owned */ protected Thread getOwner() { return sync.getOwner(); } /** - * Queries whether any threads are waiting to acquire. Note that - * because cancellations may occur at any time, a true + * Queries whether any threads are waiting to acquire this lock. Note that + * because cancellations may occur at any time, a {@code true} * return does not guarantee that any other thread will ever - * acquire. This method is designed primarily for use in + * acquire this lock. This method is designed primarily for use in * monitoring of the system state. * - * @return true if there may be other threads waiting to acquire - * the lock. + * @return {@code true} if there may be other threads waiting to + * acquire the lock */ - public final boolean hasQueuedThreads() { + public final boolean hasQueuedThreads() { return sync.hasQueuedThreads(); } @@ -600,26 +610,27 @@ public class ReentrantLock implements Lock, java.io.Serializable { /** * Queries whether the given thread is waiting to acquire this * lock. Note that because cancellations may occur at any time, a - * true return does not guarantee that this thread - * will ever acquire. This method is designed primarily for use + * {@code true} return does not guarantee that this thread + * will ever acquire this lock. This method is designed primarily for use * in monitoring of the system state. * * @param thread the thread - * @return true if the given thread is queued waiting for this lock. - * @throws NullPointerException if thread is null + * @return {@code true} if the given thread is queued waiting for this lock + * @throws NullPointerException if the thread is null */ - public final boolean hasQueuedThread(Thread thread) { + public final boolean hasQueuedThread(Thread thread) { return sync.isQueued(thread); } /** * Returns an estimate of the number of threads waiting to - * acquire. The value is only an estimate because the number of + * acquire this lock. The value is only an estimate because the number of * threads may change dynamically while this method traverses * internal data structures. This method is designed for use in * monitoring of the system state, not for synchronization * control. + * * @return the estimated number of threads waiting for this lock */ public final int getQueueLength() { @@ -628,12 +639,13 @@ public class ReentrantLock implements Lock, java.io.Serializable { /** * Returns a collection containing threads that may be waiting to - * acquire. Because the actual set of threads may change + * acquire this lock. Because the actual set of threads may change * dynamically while constructing this result, the returned * collection is only a best-effort estimate. The elements of the * returned collection are in no particular order. This method is * designed to facilitate construction of subclasses that provide * more extensive monitoring facilities. + * * @return the collection of threads */ protected Collection getQueuedThreads() { @@ -643,18 +655,18 @@ public class ReentrantLock implements Lock, java.io.Serializable { /** * Queries whether any threads are waiting on the given condition * associated with this lock. Note that because timeouts and - * interrupts may occur at any time, a true return does - * not guarantee that a future signal will awaken any + * interrupts may occur at any time, a {@code true} return does + * not guarantee that a future {@code signal} will awaken any * threads. This method is designed primarily for use in * monitoring of the system state. + * * @param condition the condition - * @return true if there are any waiting threads. - * @throws IllegalMonitorStateException if this lock - * is not held + * @return {@code true} if there are any waiting threads + * @throws IllegalMonitorStateException if this lock is not held * @throws IllegalArgumentException if the given condition is - * not associated with this lock - * @throws NullPointerException if condition null - */ + * not associated with this lock + * @throws NullPointerException if the condition is null + */ public boolean hasWaiters(Condition condition) { if (condition == null) throw new NullPointerException(); @@ -670,14 +682,14 @@ public class ReentrantLock implements Lock, java.io.Serializable { * serves only as an upper bound on the actual number of waiters. * This method is designed for use in monitoring of the system * state, not for synchronization control. + * * @param condition the condition - * @return the estimated number of waiting threads. - * @throws IllegalMonitorStateException if this lock - * is not held + * @return the estimated number of waiting threads + * @throws IllegalMonitorStateException if this lock is not held * @throws IllegalArgumentException if the given condition is - * not associated with this lock - * @throws NullPointerException if condition null - */ + * not associated with this lock + * @throws NullPointerException if the condition is null + */ public int getWaitQueueLength(Condition condition) { if (condition == null) throw new NullPointerException(); @@ -695,13 +707,13 @@ public class ReentrantLock implements Lock, java.io.Serializable { * are in no particular order. This method is designed to * facilitate construction of subclasses that provide more * extensive condition monitoring facilities. + * * @param condition the condition * @return the collection of threads - * @throws IllegalMonitorStateException if this lock - * is not held + * @throws IllegalMonitorStateException if this lock is not held * @throws IllegalArgumentException if the given condition is - * not associated with this lock - * @throws NullPointerException if condition null + * not associated with this lock + * @throws NullPointerException if the condition is null */ protected Collection getWaitingThreads(Condition condition) { if (condition == null) @@ -712,16 +724,17 @@ public class ReentrantLock implements Lock, java.io.Serializable { } /** - * Returns a string identifying this lock, as well as its lock - * state. The state, in brackets, includes either the String - * "Unlocked" or the String "Locked by" - * followed by the {@link Thread#getName} of the owning thread. - * @return a string identifying this lock, as well as its lock state. + * Returns a string identifying this lock, as well as its lock state. + * The state, in brackets, includes either the String {@code "Unlocked"} + * or the String {@code "Locked by"} followed by the + * {@linkplain Thread#getName name} of the owning thread. + * + * @return a string identifying this lock, as well as its lock state */ public String toString() { - Thread owner = sync.getOwner(); - return super.toString() + ((owner == null) ? + Thread o = sync.getOwner(); + return super.toString() + ((o == null) ? "[Unlocked]" : - "[Locked by thread " + owner.getName() + "]"); + "[Locked by thread " + o.getName() + "]"); } } diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/locks/ReentrantReadWriteLock.java b/libcore/concurrent/src/main/java/java/util/concurrent/locks/ReentrantReadWriteLock.java index 95fc7af5b..c9239448c 100644 --- a/libcore/concurrent/src/main/java/java/util/concurrent/locks/ReentrantReadWriteLock.java +++ b/libcore/concurrent/src/main/java/java/util/concurrent/locks/ReentrantReadWriteLock.java @@ -19,32 +19,60 @@ import java.util.*; * *

      This class does not impose a reader or writer preference * ordering for lock access. However, it does support an optional - * fairness policy. When constructed as fair, threads - * contend for entry using an approximately arrival-order policy. When - * the write lock is released either the longest-waiting single writer - * will be assigned the write lock, or if there is a reader waiting - * longer than any writer, the set of readers will be assigned the - * read lock. When constructed as non-fair, the order of entry to the - * lock need not be in arrival order. In either case, if readers are - * active and a writer enters the lock then no subsequent readers will - * be granted the read lock until after that writer has acquired and - * released the write lock. - * + * fairness policy. + * + *

      + *
      Non-fair mode (default) + *
      When constructed as non-fair (the default), the order of entry + * to the read and write lock is unspecified, subject to reentrancy + * constraints. A nonfair lock that is continuously contended may + * indefinitely postpone one or more reader or writer threads, but + * will normally have higher throughput than a fair lock. + *

      + * + *

      Fair mode + *
      When constructed as fair, threads contend for entry using an + * approximately arrival-order policy. When the currently held lock + * is released either the longest-waiting single writer thread will + * be assigned the write lock, or if there is a group of reader threads + * waiting longer than all waiting writer threads, that group will be + * assigned the read lock. + * + *

      A thread that tries to acquire a fair read lock (non-reentrantly) + * will block if either the write lock is held, or there is a waiting + * writer thread. The thread will not acquire the read lock until + * after the oldest currently waiting writer thread has acquired and + * released the write lock. Of course, if a waiting writer abandons + * its wait, leaving one or more reader threads as the longest waiters + * in the queue with the write lock free, then those readers will be + * assigned the read lock. + * + *

      A thread that tries to acquire a fair write lock (non-reentrantly) + * will block unless both the read lock and write lock are free (which + * implies there are no waiting threads). (Note that the non-blocking + * {@link ReadLock#tryLock()} and {@link WriteLock#tryLock()} methods + * do not honor this fair setting and will acquire the lock if it is + * possible, regardless of waiting threads.) + *

      + *

      + * *
    18. Reentrancy + * *

      This lock allows both readers and writers to reacquire read or - * write locks in the style of a {@link ReentrantLock}. Readers are not - * allowed until all write locks held by the writing thread have been - * released. - *

      Additionally, a writer can acquire the read lock - but not vice-versa. - * Among other applications, reentrancy can be useful when - * write locks are held during calls or callbacks to methods that - * perform reads under read locks. - * If a reader tries to acquire the write lock it will never succeed. - * + * write locks in the style of a {@link ReentrantLock}. Non-reentrant + * readers are not allowed until all write locks held by the writing + * thread have been released. + * + *

      Additionally, a writer can acquire the read lock, but not + * vice-versa. Among other applications, reentrancy can be useful + * when write locks are held during calls or callbacks to methods that + * perform reads under read locks. If a reader tries to acquire the + * write lock it will never succeed. + * *

    19. Lock downgrading *

      Reentrancy also allows downgrading from the write lock to a read lock, * by acquiring the write lock, then the read lock and then releasing the - * write lock. However, upgrading from a read lock to the write lock, is + * write lock. However, upgrading from a read lock to the write lock is * not possible. * *

    20. Interruption of lock acquisition @@ -53,96 +81,105 @@ import java.util.*; * *
    21. {@link Condition} support *

      The write lock provides a {@link Condition} implementation that - * behaves in the same way, with respect to the write lock, as the + * behaves in the same way, with respect to the write lock, as the * {@link Condition} implementation provided by * {@link ReentrantLock#newCondition} does for {@link ReentrantLock}. * This {@link Condition} can, of course, only be used with the write lock. + * *

      The read lock does not support a {@link Condition} and - * readLock().newCondition() throws - * UnsupportedOperationException. + * {@code readLock().newCondition()} throws + * {@code UnsupportedOperationException}. * *

    22. Instrumentation - *

      This class supports methods to determine whether locks + *

      This class supports methods to determine whether locks * are held or contended. These methods are designed for monitoring * system state, not for synchronization control. * * - *

      Serialization of this class behaves in the same way as built-in + *

      Serialization of this class behaves in the same way as built-in * locks: a deserialized lock is in the unlocked state, regardless of * its state when serialized. * - *

      Sample usages. Here is a code sketch showing how to exploit - * reentrancy to perform lock downgrading after updating a cache (exception - * handling is elided for simplicity): - *

      + * 

      Sample usages. Here is a code sketch showing how to perform + * lock downgrading after updating a cache (exception handling is + * particularly tricky when handling multiple locks in a non-nested + * fashion): + * + *

       {@code
        * class CachedData {
        *   Object data;
        *   volatile boolean cacheValid;
      - *   ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
      + *   final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
        *
        *   void processCachedData() {
        *     rwl.readLock().lock();
        *     if (!cacheValid) {
      - *        // upgrade lock manually
      - *        rwl.readLock().unlock();   // must unlock first to obtain writelock
      + *        // Must release read lock before acquiring write lock
      + *        rwl.readLock().unlock();
        *        rwl.writeLock().lock();
      - *        if (!cacheValid) { // recheck
      - *          data = ...
      - *          cacheValid = true;
      + *        try {
      + *          // Recheck state because another thread might have
      + *          // acquired write lock and changed state before we did.
      + *          if (!cacheValid) {
      + *            data = ...
      + *            cacheValid = true;
      + *          }
      + *          // Downgrade by acquiring read lock before releasing write lock
      + *          rwl.readLock().lock();
      + *        } finally  {
      + *          rwl.writeLock().unlock(); // Unlock write, still hold read
        *        }
      - *        // downgrade lock
      - *        rwl.readLock().lock();  // reacquire read without giving up write lock
      - *        rwl.writeLock().unlock(); // unlock write, still hold read
        *     }
        *
      - *     use(data);
      - *     rwl.readLock().unlock();
      + *     try {
      + *       use(data);
      + *     } finally {
      + *       rwl.readLock().unlock();
      + *     }
        *   }
      - * }
      - * 
      + * }}
      * * ReentrantReadWriteLocks can be used to improve concurrency in some * uses of some kinds of Collections. This is typically worthwhile * only when the collections are expected to be large, accessed by * more reader threads than writer threads, and entail operations with * overhead that outweighs synchronization overhead. For example, here - * is a class using a TreeMap that is expected to be large and + * is a class using a TreeMap that is expected to be large and * concurrently accessed. * - *
      + * 
      {@code
        * class RWDictionary {
      - *    private final Map<String, Data>  m = new TreeMap<String, Data>();
      + *    private final Map m = new TreeMap();
        *    private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
        *    private final Lock r = rwl.readLock();
        *    private final Lock w = rwl.writeLock();
        *
        *    public Data get(String key) {
      - *        r.lock(); try { return m.get(key); } finally { r.unlock(); }
      + *        r.lock();
      + *        try { return m.get(key); }
      + *        finally { r.unlock(); }
        *    }
        *    public String[] allKeys() {
      - *        r.lock(); try { return m.keySet().toArray(); } finally { r.unlock(); }
      + *        r.lock();
      + *        try { return m.keySet().toArray(); }
      + *        finally { r.unlock(); }
        *    }
        *    public Data put(String key, Data value) {
      - *        w.lock(); try { return m.put(key, value); } finally { w.unlock(); }
      + *        w.lock();
      + *        try { return m.put(key, value); }
      + *        finally { w.unlock(); }
        *    }
        *    public void clear() {
      - *        w.lock(); try { m.clear(); } finally { w.unlock(); }
      + *        w.lock();
      + *        try { m.clear(); }
      + *        finally { w.unlock(); }
        *    }
      - * }
      - * 
      - * + * }}
      * *

      Implementation Notes

      * - *

      A reentrant write lock intrinsically defines an owner and can - * only be released by the thread that acquired it. In contrast, in - * this implementation, the read lock has no concept of ownership, and - * there is no requirement that the thread releasing a read lock is - * the same as the one that acquired it. However, this property is - * not guaranteed to hold in future implementations of this class. - * - *

      This lock supports a maximum of 65536 recursive write locks - * and 65536 read locks. Attempts to exceed these limits result in + *

      This lock supports a maximum of 65535 recursive write locks + * and 65535 read locks. Attempts to exceed these limits result in * {@link Error} throws from locking methods. * * @since 1.5 @@ -156,26 +193,24 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab /** Inner class providing writelock */ private final ReentrantReadWriteLock.WriteLock writerLock; /** Performs all synchronization mechanics */ - private final Sync sync; + final Sync sync; /** - * Creates a new ReentrantReadWriteLock with - * default ordering properties. + * Creates a new {@code ReentrantReadWriteLock} with + * default (nonfair) ordering properties. */ public ReentrantReadWriteLock() { - sync = new NonfairSync(); - readerLock = new ReadLock(this); - writerLock = new WriteLock(this); + this(false); } /** - * Creates a new ReentrantReadWriteLock with + * Creates a new {@code ReentrantReadWriteLock} with * the given fairness policy. * - * @param fair true if this lock should use a fair ordering policy + * @param fair {@code true} if this lock should use a fair ordering policy */ public ReentrantReadWriteLock(boolean fair) { - sync = (fair)? new FairSync() : new NonfairSync(); + sync = fair ? new FairSync() : new NonfairSync(); readerLock = new ReadLock(this); writerLock = new WriteLock(this); } @@ -183,129 +218,409 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; } public ReentrantReadWriteLock.ReadLock readLock() { return readerLock; } - /* - * Read vs write count extraction constants and functions. - * Lock state is logically divided into two shorts: The lower - * one representing the exclusive (writer) lock hold count, - * and the upper the shared (reader) hold count. - */ - - static final int SHARED_SHIFT = 16; - static final int SHARED_UNIT = (1 << SHARED_SHIFT); - static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1; - - /** Returns the number of shared holds represented in count */ - static int sharedCount(int c) { return c >>> SHARED_SHIFT; } - /** Returns the number of exclusive holds represented in count */ - static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; } - - /** + /** * Synchronization implementation for ReentrantReadWriteLock. * Subclassed into fair and nonfair versions. */ - abstract static class Sync extends AbstractQueuedSynchronizer { - /** Current (exclusive) owner thread */ - transient Thread owner; + static abstract class Sync extends AbstractQueuedSynchronizer { + private static final long serialVersionUID = 6317671515068378041L; + + /* + * Read vs write count extraction constants and functions. + * Lock state is logically divided into two unsigned shorts: + * The lower one representing the exclusive (writer) lock hold count, + * and the upper the shared (reader) hold count. + */ + + static final int SHARED_SHIFT = 16; + static final int SHARED_UNIT = (1 << SHARED_SHIFT); + static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1; + static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1; + + /** Returns the number of shared holds represented in count */ + static int sharedCount(int c) { return c >>> SHARED_SHIFT; } + /** Returns the number of exclusive holds represented in count */ + static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; } + + /** + * A counter for per-thread read hold counts. + * Maintained as a ThreadLocal; cached in cachedHoldCounter + */ + static final class HoldCounter { + int count = 0; + // Use id, not reference, to avoid garbage retention + final long tid = Thread.currentThread().getId(); + } + + /** + * ThreadLocal subclass. Easiest to explicitly define for sake + * of deserialization mechanics. + */ + static final class ThreadLocalHoldCounter + extends ThreadLocal { + public HoldCounter initialValue() { + return new HoldCounter(); + } + } + + /** + * The number of reentrant read locks held by current thread. + * Initialized only in constructor and readObject. + * Removed whenever a thread's read hold count drops to 0. + */ + private transient ThreadLocalHoldCounter readHolds; + + /** + * The hold count of the last thread to successfully acquire + * readLock. This saves ThreadLocal lookup in the common case + * where the next thread to release is the last one to + * acquire. This is non-volatile since it is just used + * as a heuristic, and would be great for threads to cache. + * + *

      Can outlive the Thread for which it is caching the read + * hold count, but avoids garbage retention by not retaining a + * reference to the Thread. + * + *

      Accessed via a benign data race; relies on the memory + * model's final field and out-of-thin-air guarantees. + */ + private transient HoldCounter cachedHoldCounter; + + /** + * firstReader is the first thread to have acquired the read lock. + * firstReaderHoldCount is firstReader's hold count. + * + *

      More precisely, firstReader is the unique thread that last + * changed the shared count from 0 to 1, and has not released the + * read lock since then; null if there is no such thread. + * + *

      Cannot cause garbage retention unless the thread terminated + * without relinquishing its read locks, since tryReleaseShared + * sets it to null. + * + *

      Accessed via a benign data race; relies on the memory + * model's out-of-thin-air guarantees for references. + * + *

      This allows tracking of read holds for uncontended read + * locks to be very cheap. + */ + private transient Thread firstReader = null; + private transient int firstReaderHoldCount; + + Sync() { + readHolds = new ThreadLocalHoldCounter(); + setState(getState()); // ensures visibility of readHolds + } + + /* + * Acquires and releases use the same code for fair and + * nonfair locks, but differ in whether/how they allow barging + * when queues are non-empty. + */ /** - * Perform write lock. Allows fast path in non-fair version. + * Returns true if the current thread, when trying to acquire + * the read lock, and otherwise eligible to do so, should block + * because of policy for overtaking other waiting threads. */ - abstract void wlock(); + abstract boolean readerShouldBlock(); - /** - * Perform non-fair tryLock for write. tryAcquire is - * implemented in subclasses, but both versions need nonfair - * try for trylock method + /** + * Returns true if the current thread, when trying to acquire + * the write lock, and otherwise eligible to do so, should block + * because of policy for overtaking other waiting threads. */ - final boolean nonfairTryAcquire(int acquires) { - // mask out readlocks if called from condition methods - acquires = exclusiveCount(acquires); + abstract boolean writerShouldBlock(); + + /* + * Note that tryRelease and tryAcquire can be called by + * Conditions. So it is possible that their arguments contain + * both read and write holds that are all released during a + * condition wait and re-established in tryAcquire. + */ + + protected final boolean tryRelease(int releases) { + if (!isHeldExclusively()) + throw new IllegalMonitorStateException(); + int nextc = getState() - releases; + boolean free = exclusiveCount(nextc) == 0; + if (free) + setExclusiveOwnerThread(null); + setState(nextc); + return free; + } + + protected final boolean tryAcquire(int acquires) { + /* + * Walkthrough: + * 1. If read count nonzero or write count nonzero + * and owner is a different thread, fail. + * 2. If count would saturate, fail. (This can only + * happen if count is already nonzero.) + * 3. Otherwise, this thread is eligible for lock if + * it is either a reentrant acquire or + * queue policy allows it. If so, update state + * and set owner. + */ Thread current = Thread.currentThread(); int c = getState(); int w = exclusiveCount(c); - if (w + acquires >= SHARED_UNIT) - throw new Error("Maximum lock count exceeded"); - if (c != 0 && (w == 0 || current != owner)) - return false; - if (!compareAndSetState(c, c + acquires)) + if (c != 0) { + // (Note: if c != 0 and w == 0 then shared count != 0) + if (w == 0 || current != getExclusiveOwnerThread()) + return false; + if (w + exclusiveCount(acquires) > MAX_COUNT) + throw new Error("Maximum lock count exceeded"); + // Reentrant acquire + setState(c + acquires); + return true; + } + if (writerShouldBlock() || + !compareAndSetState(c, c + acquires)) return false; - owner = current; + setExclusiveOwnerThread(current); return true; } - /** - * Perform nonfair tryLock for read. + protected final boolean tryReleaseShared(int unused) { + Thread current = Thread.currentThread(); + if (firstReader == current) { + // assert firstReaderHoldCount > 0; + if (firstReaderHoldCount == 1) + firstReader = null; + else + firstReaderHoldCount--; + } else { + HoldCounter rh = cachedHoldCounter; + if (rh == null || rh.tid != current.getId()) + rh = readHolds.get(); + int count = rh.count; + if (count <= 1) { + readHolds.remove(); + if (count <= 0) + throw unmatchedUnlockException(); + } + --rh.count; + } + for (;;) { + int c = getState(); + int nextc = c - SHARED_UNIT; + if (compareAndSetState(c, nextc)) + // Releasing the read lock has no effect on readers, + // but it may allow waiting writers to proceed if + // both read and write locks are now free. + return nextc == 0; + } + } + + private IllegalMonitorStateException unmatchedUnlockException() { + return new IllegalMonitorStateException( + "attempt to unlock read lock, not locked by current thread"); + } + + protected final int tryAcquireShared(int unused) { + /* + * Walkthrough: + * 1. If write lock held by another thread, fail. + * 2. Otherwise, this thread is eligible for + * lock wrt state, so ask if it should block + * because of queue policy. If not, try + * to grant by CASing state and updating count. + * Note that step does not check for reentrant + * acquires, which is postponed to full version + * to avoid having to check hold count in + * the more typical non-reentrant case. + * 3. If step 2 fails either because thread + * apparently not eligible or CAS fails or count + * saturated, chain to version with full retry loop. + */ + Thread current = Thread.currentThread(); + int c = getState(); + if (exclusiveCount(c) != 0 && + getExclusiveOwnerThread() != current) + return -1; + int r = sharedCount(c); + if (!readerShouldBlock() && + r < MAX_COUNT && + compareAndSetState(c, c + SHARED_UNIT)) { + if (r == 0) { + firstReader = current; + firstReaderHoldCount = 1; + } else if (firstReader == current) { + firstReaderHoldCount++; + } else { + HoldCounter rh = cachedHoldCounter; + if (rh == null || rh.tid != current.getId()) + cachedHoldCounter = rh = readHolds.get(); + else if (rh.count == 0) + readHolds.set(rh); + rh.count++; + } + return 1; + } + return fullTryAcquireShared(current); + } + + /** + * Full version of acquire for reads, that handles CAS misses + * and reentrant reads not dealt with in tryAcquireShared. */ - final int nonfairTryAcquireShared(int acquires) { + final int fullTryAcquireShared(Thread current) { + /* + * This code is in part redundant with that in + * tryAcquireShared but is simpler overall by not + * complicating tryAcquireShared with interactions between + * retries and lazily reading hold counts. + */ + HoldCounter rh = null; for (;;) { int c = getState(); - int nextc = c + (acquires << SHARED_SHIFT); - if (nextc < c) + if (exclusiveCount(c) != 0) { + if (getExclusiveOwnerThread() != current) + return -1; + // else we hold the exclusive lock; blocking here + // would cause deadlock. + } else if (readerShouldBlock()) { + // Make sure we're not acquiring read lock reentrantly + if (firstReader == current) { + // assert firstReaderHoldCount > 0; + } else { + if (rh == null) { + rh = cachedHoldCounter; + if (rh == null || rh.tid != current.getId()) { + rh = readHolds.get(); + if (rh.count == 0) + readHolds.remove(); + } + } + if (rh.count == 0) + return -1; + } + } + if (sharedCount(c) == MAX_COUNT) throw new Error("Maximum lock count exceeded"); - if (exclusiveCount(c) != 0 && - owner != Thread.currentThread()) - return -1; - if (compareAndSetState(c, nextc)) + if (compareAndSetState(c, c + SHARED_UNIT)) { + if (sharedCount(c) == 0) { + firstReader = current; + firstReaderHoldCount = 1; + } else if (firstReader == current) { + firstReaderHoldCount++; + } else { + if (rh == null) + rh = cachedHoldCounter; + if (rh == null || rh.tid != current.getId()) + rh = readHolds.get(); + else if (rh.count == 0) + readHolds.set(rh); + rh.count++; + cachedHoldCounter = rh; // cache for release + } return 1; - // Recheck count if lost CAS + } } } - protected final boolean tryRelease(int releases) { + /** + * Performs tryLock for write, enabling barging in both modes. + * This is identical in effect to tryAcquire except for lack + * of calls to writerShouldBlock. + */ + final boolean tryWriteLock() { Thread current = Thread.currentThread(); int c = getState(); - if (owner != current) - throw new IllegalMonitorStateException(); - int nextc = c - releases; - boolean free = false; - if (exclusiveCount(c) == releases) { - free = true; - owner = null; + if (c != 0) { + int w = exclusiveCount(c); + if (w == 0 || current != getExclusiveOwnerThread()) + return false; + if (w == MAX_COUNT) + throw new Error("Maximum lock count exceeded"); } - setState(nextc); - return free; + if (!compareAndSetState(c, c + 1)) + return false; + setExclusiveOwnerThread(current); + return true; } - protected final boolean tryReleaseShared(int releases) { + /** + * Performs tryLock for read, enabling barging in both modes. + * This is identical in effect to tryAcquireShared except for + * lack of calls to readerShouldBlock. + */ + final boolean tryReadLock() { + Thread current = Thread.currentThread(); for (;;) { int c = getState(); - int nextc = c - (releases << SHARED_SHIFT); - if (nextc < 0) - throw new IllegalMonitorStateException(); - if (compareAndSetState(c, nextc)) - return nextc == 0; + if (exclusiveCount(c) != 0 && + getExclusiveOwnerThread() != current) + return false; + int r = sharedCount(c); + if (r == MAX_COUNT) + throw new Error("Maximum lock count exceeded"); + if (compareAndSetState(c, c + SHARED_UNIT)) { + if (r == 0) { + firstReader = current; + firstReaderHoldCount = 1; + } else if (firstReader == current) { + firstReaderHoldCount++; + } else { + HoldCounter rh = cachedHoldCounter; + if (rh == null || rh.tid != current.getId()) + cachedHoldCounter = rh = readHolds.get(); + else if (rh.count == 0) + readHolds.set(rh); + rh.count++; + } + return true; + } } } - + protected final boolean isHeldExclusively() { - return exclusiveCount(getState()) != 0 && - owner == Thread.currentThread(); + // While we must in general read state before owner, + // we don't need to do so to check if current thread is owner + return getExclusiveOwnerThread() == Thread.currentThread(); } // Methods relayed to outer class - - final ConditionObject newCondition() { - return new ConditionObject(); + + final ConditionObject newCondition() { + return new ConditionObject(); } final Thread getOwner() { - int c = exclusiveCount(getState()); - Thread o = owner; - return (c == 0)? null : o; + // Must read state before owner to ensure memory consistency + return ((exclusiveCount(getState()) == 0)? + null : + getExclusiveOwnerThread()); } - + final int getReadLockCount() { return sharedCount(getState()); } - + final boolean isWriteLocked() { return exclusiveCount(getState()) != 0; } final int getWriteHoldCount() { - int c = exclusiveCount(getState()); - Thread o = owner; - return (o == Thread.currentThread())? c : 0; + return isHeldExclusively() ? exclusiveCount(getState()) : 0; + } + + final int getReadHoldCount() { + if (getReadLockCount() == 0) + return 0; + + Thread current = Thread.currentThread(); + if (firstReader == current) + return firstReaderHoldCount; + + HoldCounter rh = cachedHoldCounter; + if (rh != null && rh.tid == current.getId()) + return rh.count; + + int count = readHolds.get().count; + if (count == 0) readHolds.remove(); + return count; } /** @@ -315,78 +630,43 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { s.defaultReadObject(); + readHolds = new ThreadLocalHoldCounter(); setState(0); // reset to unlocked state } final int getCount() { return getState(); } } - /** + /** * Nonfair version of Sync */ final static class NonfairSync extends Sync { - protected final boolean tryAcquire(int acquires) { - return nonfairTryAcquire(acquires); + private static final long serialVersionUID = -8159625535654395037L; + final boolean writerShouldBlock() { + return false; // writers can always barge } - - protected final int tryAcquireShared(int acquires) { - return nonfairTryAcquireShared(acquires); - } - - // Use fastpath for main write lock method - final void wlock() { - if (compareAndSetState(0, 1)) - owner = Thread.currentThread(); - else - acquire(1); + final boolean readerShouldBlock() { + /* As a heuristic to avoid indefinite writer starvation, + * block if the thread that momentarily appears to be head + * of queue, if one exists, is a waiting writer. This is + * only a probabilistic effect since a new reader will not + * block if there is a waiting writer behind other enabled + * readers that have not yet drained from the queue. + */ + return apparentlyFirstQueuedIsExclusive(); } } - /** + /** * Fair version of Sync */ final static class FairSync extends Sync { - protected final boolean tryAcquire(int acquires) { - // mask out readlocks if called from condition methods - acquires = exclusiveCount(acquires); - Thread current = Thread.currentThread(); - Thread first; - int c = getState(); - int w = exclusiveCount(c); - if (w + acquires >= SHARED_UNIT) - throw new Error("Maximum lock count exceeded"); - if ((w == 0 || current != owner) && - (c != 0 || - ((first = getFirstQueuedThread()) != null && - first != current))) - return false; - if (!compareAndSetState(c, c + acquires)) - return false; - owner = current; - return true; - } - - protected final int tryAcquireShared(int acquires) { - Thread current = Thread.currentThread(); - for (;;) { - Thread first = getFirstQueuedThread(); - if (first != null && first != current) - return -1; - int c = getState(); - int nextc = c + (acquires << SHARED_SHIFT); - if (nextc < c) - throw new Error("Maximum lock count exceeded"); - if (exclusiveCount(c) != 0 && - owner != Thread.currentThread()) - return -1; - if (compareAndSetState(c, nextc)) - return 1; - // Recheck count if lost CAS - } + private static final long serialVersionUID = -2274990926593161451L; + final boolean writerShouldBlock() { + return hasQueuedPredecessors(); } - - final void wlock() { // no fast path - acquire(1); + final boolean readerShouldBlock() { + return hasQueuedPredecessors(); } } @@ -396,46 +676,47 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab public static class ReadLock implements Lock, java.io.Serializable { private static final long serialVersionUID = -5992448646407690164L; private final Sync sync; - - /** - * Constructor for use by subclasses. + + /** + * Constructor for use by subclasses + * * @param lock the outer lock object - * @throws NullPointerException if lock null + * @throws NullPointerException if the lock is null */ protected ReadLock(ReentrantReadWriteLock lock) { sync = lock.sync; } /** - * Acquires the shared lock. + * Acquires the read lock. * - *

      Acquires the lock if it is not held exclusively by + *

      Acquires the read lock if the write lock is not held by * another thread and returns immediately. * - *

      If the lock is held exclusively by another thread then + *

      If the write lock is held by another thread then * the current thread becomes disabled for thread scheduling - * purposes and lies dormant until the lock has been acquired. + * purposes and lies dormant until the read lock has been acquired. */ - public void lock() { + public void lock() { sync.acquireShared(1); } /** - * Acquires the shared lock unless the current thread is - * {@link Thread#interrupt interrupted}. + * Acquires the read lock unless the current thread is + * {@linkplain Thread#interrupt interrupted}. * - *

      Acquires the shared lock if it is not held exclusively + *

      Acquires the read lock if the write lock is not held * by another thread and returns immediately. * - *

      If the lock is held by another thread then the - * current thread becomes disabled for thread scheduling + *

      If the write lock is held by another thread then the + * current thread becomes disabled for thread scheduling * purposes and lies dormant until one of two things happens: * *

        * - *
      • The lock is acquired by the current thread; or + *
      • The read lock is acquired by the current thread; or * - *
      • Some other thread {@link Thread#interrupt interrupts} + *
      • Some other thread {@linkplain Thread#interrupt interrupts} * the current thread. * *
      @@ -444,10 +725,10 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab * *
        * - *
      • has its interrupted status set on entry to this method; or + *
      • has its interrupted status set on entry to this method; or * - *
      • is {@link Thread#interrupt interrupted} while acquiring - * the lock, + *
      • is {@linkplain Thread#interrupt interrupted} while + * acquiring the read lock, * *
      * @@ -466,83 +747,83 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab } /** - * Acquires the shared lock only if it is not held exclusively by + * Acquires the read lock only if the write lock is not held by * another thread at the time of invocation. * - *

      Acquires the lock if it is not held exclusively by + *

      Acquires the read lock if the write lock is not held by * another thread and returns immediately with the value - * true. Even when this lock has been set to use a - * fair ordering policy, a call to tryLock() - * will immediately acquire the lock if it is + * {@code true}. Even when this lock has been set to use a + * fair ordering policy, a call to {@code tryLock()} + * will immediately acquire the read lock if it is * available, whether or not other threads are currently - * waiting for the lock. This "barging" behavior + * waiting for the read lock. This "barging" behavior * can be useful in certain circumstances, even though it * breaks fairness. If you want to honor the fairness setting * for this lock, then use {@link #tryLock(long, TimeUnit) * tryLock(0, TimeUnit.SECONDS) } which is almost equivalent * (it also detects interruption). * - *

      If the lock is held exclusively by another thread then + *

      If the write lock is held by another thread then * this method will return immediately with the value - * false. + * {@code false}. * - * @return true if the lock was acquired. + * @return {@code true} if the read lock was acquired */ public boolean tryLock() { - return sync.nonfairTryAcquireShared(1) >= 0; + return sync.tryReadLock(); } /** - * Acquires the shared lock if it is not held exclusively by + * Acquires the read lock if the write lock is not held by * another thread within the given waiting time and the - * current thread has not been {@link Thread#interrupt + * current thread has not been {@linkplain Thread#interrupt * interrupted}. * - *

      Acquires the lock if it is not held exclusively by + *

      Acquires the read lock if the write lock is not held by * another thread and returns immediately with the value - * true. If this lock has been set to use a fair + * {@code true}. If this lock has been set to use a fair * ordering policy then an available lock will not be * acquired if any other threads are waiting for the * lock. This is in contrast to the {@link #tryLock()} - * method. If you want a timed tryLock that does + * method. If you want a timed {@code tryLock} that does * permit barging on a fair lock then combine the timed and * un-timed forms together: * *

      if (lock.tryLock() || lock.tryLock(timeout, unit) ) { ... }
                * 
      * - *

      If the lock is held exclusively by another thread then the - * current thread becomes disabled for thread scheduling + *

      If the write lock is held by another thread then the + * current thread becomes disabled for thread scheduling * purposes and lies dormant until one of three things happens: * *

        * - *
      • The lock is acquired by the current thread; or + *
      • The read lock is acquired by the current thread; or * - *
      • Some other thread {@link Thread#interrupt interrupts} the current - * thread; or + *
      • Some other thread {@linkplain Thread#interrupt interrupts} + * the current thread; or * - *
      • The specified waiting time elapses + *
      • The specified waiting time elapses. * *
      * - *

      If the lock is acquired then the value true is + *

      If the read lock is acquired then the value {@code true} is * returned. * *

      If the current thread: * *

        * - *
      • has its interrupted status set on entry to this method; or + *
      • has its interrupted status set on entry to this method; or * - *
      • is {@link Thread#interrupt interrupted} while acquiring - * the lock, + *
      • is {@linkplain Thread#interrupt interrupted} while + * acquiring the read lock, * *
      then {@link InterruptedException} is thrown and the * current thread's interrupted status is cleared. * *

      If the specified waiting time elapses then the value - * false is returned. If the time is less than or + * {@code false} is returned. If the time is less than or * equal to zero, the method will not wait at all. * *

      In this implementation, as this method is an explicit @@ -550,13 +831,11 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab * the interrupt over normal or reentrant acquisition of the * lock, and over reporting the elapse of the waiting time. * - * @param timeout the time to wait for the lock + * @param timeout the time to wait for the read lock * @param unit the time unit of the timeout argument - * - * @return true if the lock was acquired. - * + * @return {@code true} if the read lock was acquired * @throws InterruptedException if the current thread is interrupted - * @throws NullPointerException if unit is null + * @throws NullPointerException if the time unit is null * */ public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException { @@ -564,20 +843,19 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab } /** - * Attempts to release this lock. + * Attempts to release this lock. * *

      If the number of readers is now zero then the lock - * is made available for other lock attempts. + * is made available for write lock attempts. */ public void unlock() { sync.releaseShared(1); } /** - * Throws UnsupportedOperationException because ReadLocks - * do not support conditions. - * @return A new {@link Condition} instance for this Lock - * instance. + * Throws {@code UnsupportedOperationException} because + * {@code ReadLocks} do not support conditions. + * * @throws UnsupportedOperationException always */ public Condition newCondition() { @@ -586,17 +864,16 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab /** * Returns a string identifying this lock, as well as its lock state. - * The state, in brackets, includes the String - * "Read locks =" followed by the number of held - * read locks. - * @return a string identifying this lock, as well as its lock state. + * The state, in brackets, includes the String {@code "Read locks ="} + * followed by the number of held read locks. + * + * @return a string identifying this lock, as well as its lock state */ public String toString() { int r = sync.getReadLockCount(); - return super.toString() + + return super.toString() + "[Read locks = " + r + "]"; } - } /** @@ -605,42 +882,45 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab public static class WriteLock implements Lock, java.io.Serializable { private static final long serialVersionUID = -4992448646407690164L; private final Sync sync; - - /** - * Constructor for use by subclasses. + + /** + * Constructor for use by subclasses + * * @param lock the outer lock object - * @throws NullPointerException if lock null + * @throws NullPointerException if the lock is null */ protected WriteLock(ReentrantReadWriteLock lock) { sync = lock.sync; } /** - * Acquire the lock. + * Acquires the write lock. * - *

      Acquires the lock if it is not held by another thread - * and returns immediately, setting the lock hold count to + *

      Acquires the write lock if neither the read nor write lock + * are held by another thread + * and returns immediately, setting the write lock hold count to * one. * - *

      If the current thread already holds the lock then the + *

      If the current thread already holds the write lock then the * hold count is incremented by one and the method returns * immediately. * *

      If the lock is held by another thread then the current * thread becomes disabled for thread scheduling purposes and - * lies dormant until the lock has been acquired, at which - * time the lock hold count is set to one. + * lies dormant until the write lock has been acquired, at which + * time the write lock hold count is set to one. */ public void lock() { - sync.wlock(); + sync.acquire(1); } /** - * Acquires the lock unless the current thread is {@link - * Thread#interrupt interrupted}. + * Acquires the write lock unless the current thread is + * {@linkplain Thread#interrupt interrupted}. * - *

      Acquires the lock if it is not held by another thread - * and returns immediately, setting the lock hold count to + *

      Acquires the write lock if neither the read nor write lock + * are held by another thread + * and returns immediately, setting the write lock hold count to * one. * *

      If the current thread already holds this lock then the @@ -653,14 +933,14 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab * *

        * - *
      • The lock is acquired by the current thread; or + *
      • The write lock is acquired by the current thread; or * - *
      • Some other thread {@link Thread#interrupt interrupts} + *
      • Some other thread {@linkplain Thread#interrupt interrupts} * the current thread. * *
      * - *

      If the lock is acquired by the current thread then the + *

      If the write lock is acquired by the current thread then the * lock hold count is set to one. * *

      If the current thread: @@ -670,8 +950,8 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab *

    23. has its interrupted status set on entry to this method; * or * - *
    24. is {@link Thread#interrupt interrupted} while acquiring - * the lock, + *
    25. is {@linkplain Thread#interrupt interrupted} while + * acquiring the write lock, * * * @@ -690,16 +970,17 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab } /** - * Acquires the lock only if it is not held by another thread + * Acquires the write lock only if it is not held by another thread * at the time of invocation. * - *

      Acquires the lock if it is not held by another thread - * and returns immediately with the value true, - * setting the lock hold count to one. Even when this lock has + *

      Acquires the write lock if neither the read nor write lock + * are held by another thread + * and returns immediately with the value {@code true}, + * setting the write lock hold count to one. Even when this lock has * been set to use a fair ordering policy, a call to - * tryLock() will immediately acquire the + * {@code tryLock()} will immediately acquire the * lock if it is available, whether or not other threads are - * currently waiting for the lock. This "barging" + * currently waiting for the write lock. This "barging" * behavior can be useful in certain circumstances, even * though it breaks fairness. If you want to honor the * fairness setting for this lock, then use {@link @@ -708,31 +989,32 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab * *

      If the current thread already holds this lock then the * hold count is incremented by one and the method returns - * true. + * {@code true}. * *

      If the lock is held by another thread then this method - * will return immediately with the value false. + * will return immediately with the value {@code false}. * - * @return true if the lock was free and was acquired by the - * current thread, or the lock was already held by the current thread; and - * false otherwise. + * @return {@code true} if the lock was free and was acquired + * by the current thread, or the write lock was already held + * by the current thread; and {@code false} otherwise. */ public boolean tryLock( ) { - return sync.nonfairTryAcquire(1); + return sync.tryWriteLock(); } /** - * Acquires the lock if it is not held by another thread + * Acquires the write lock if it is not held by another thread * within the given waiting time and the current thread has - * not been {@link Thread#interrupt interrupted}. + * not been {@linkplain Thread#interrupt interrupted}. * - *

      Acquires the lock if it is not held by another thread - * and returns immediately with the value true, - * setting the lock hold count to one. If this lock has been + *

      Acquires the write lock if neither the read nor write lock + * are held by another thread + * and returns immediately with the value {@code true}, + * setting the write lock hold count to one. If this lock has been * set to use a fair ordering policy then an available lock * will not be acquired if any other threads are - * waiting for the lock. This is in contrast to the {@link - * #tryLock()} method. If you want a timed tryLock + * waiting for the write lock. This is in contrast to the {@link + * #tryLock()} method. If you want a timed {@code tryLock} * that does permit barging on a fair lock then combine the * timed and un-timed forms together: * @@ -741,7 +1023,7 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab * *

      If the current thread already holds this lock then the * hold count is incremented by one and the method returns - * true. + * {@code true}. * *

      If the lock is held by another thread then the current * thread becomes disabled for thread scheduling purposes and @@ -749,17 +1031,17 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab * *

        * - *
      • The lock is acquired by the current thread; or + *
      • The write lock is acquired by the current thread; or * - *
      • Some other thread {@link Thread#interrupt interrupts} + *
      • Some other thread {@linkplain Thread#interrupt interrupts} * the current thread; or * *
      • The specified waiting time elapses * *
      * - *

      If the lock is acquired then the value true is - * returned and the lock hold count is set to one. + *

      If the write lock is acquired then the value {@code true} is + * returned and the write lock hold count is set to one. * *

      If the current thread: * @@ -768,16 +1050,16 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab *

    26. has its interrupted status set on entry to this method; * or * - *
    27. is {@link Thread#interrupt interrupted} while acquiring - * the lock, + *
    28. is {@linkplain Thread#interrupt interrupted} while + * acquiring the write lock, * - * + * * * then {@link InterruptedException} is thrown and the current * thread's interrupted status is cleared. * *

      If the specified waiting time elapses then the value - * false is returned. If the time is less than or + * {@code false} is returned. If the time is less than or * equal to zero, the method will not wait at all. * *

      In this implementation, as this method is an explicit @@ -785,30 +1067,31 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab * the interrupt over normal or reentrant acquisition of the * lock, and over reporting the elapse of the waiting time. * - * @param timeout the time to wait for the lock + * @param timeout the time to wait for the write lock * @param unit the time unit of the timeout argument * - * @return true if the lock was free and was acquired - * by the current thread, or the lock was already held by the - * current thread; and false if the waiting time + * @return {@code true} if the lock was free and was acquired + * by the current thread, or the write lock was already held by the + * current thread; and {@code false} if the waiting time * elapsed before the lock could be acquired. * * @throws InterruptedException if the current thread is interrupted - * @throws NullPointerException if unit is null + * @throws NullPointerException if the time unit is null * */ public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException { return sync.tryAcquireNanos(1, unit.toNanos(timeout)); } - + /** - * Attempts to release this lock. + * Attempts to release this lock. * *

      If the current thread is the holder of this lock then * the hold count is decremented. If the hold count is now * zero then the lock is released. If the current thread is * not the holder of this lock then {@link * IllegalMonitorStateException} is thrown. + * * @throws IllegalMonitorStateException if the current thread does not * hold this lock. */ @@ -818,7 +1101,7 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab /** * Returns a {@link Condition} instance for use with this - * {@link Lock} instance. + * {@link Lock} instance. *

      The returned {@link Condition} instance supports the same * usages as do the {@link Object} monitor methods ({@link * Object#wait() wait}, {@link Object#notify notify}, and {@link @@ -834,74 +1117,80 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab * affected. However it is essentially always an error to * invoke a condition waiting method when the current thread * has also acquired read locks, since other threads that - * could unblock it will not be able to access the write + * could unblock it will not be able to acquire the write * lock.) * - *

    29. When the condition {@link Condition#await() waiting} + *
    30. When the condition {@linkplain Condition#await() waiting} * methods are called the write lock is released and, before * they return, the write lock is reacquired and the lock hold * count restored to what it was when the method was called. * - *
    31. If a thread is {@link Thread#interrupt interrupted} while + *
    32. If a thread is {@linkplain Thread#interrupt interrupted} while * waiting then the wait will terminate, an {@link * InterruptedException} will be thrown, and the thread's * interrupted status will be cleared. * - *
    33. Waiting threads are signalled in FIFO order + *
    34. Waiting threads are signalled in FIFO order. * *
    35. The ordering of lock reacquisition for threads returning * from waiting methods is the same as for threads initially * acquiring the lock, which is in the default case not specified, * but for fair locks favors those threads that have been * waiting the longest. - * + * * + * * @return the Condition object */ - public Condition newCondition() { + public Condition newCondition() { return sync.newCondition(); } /** * Returns a string identifying this lock, as well as its lock * state. The state, in brackets includes either the String - * "Unlocked" or the String "Locked by" - * followed by the {@link Thread#getName} of the owning thread. - * @return a string identifying this lock, as well as its lock state. + * {@code "Unlocked"} or the String {@code "Locked by"} + * followed by the {@linkplain Thread#getName name} of the owning thread. + * + * @return a string identifying this lock, as well as its lock state */ public String toString() { - Thread owner = sync.getOwner(); - return super.toString() + ((owner == null) ? + Thread o = sync.getOwner(); + return super.toString() + ((o == null) ? "[Unlocked]" : - "[Locked by thread " + owner.getName() + "]"); + "[Locked by thread " + o.getName() + "]"); } } - // Instrumentation and status /** - * Returns true if this lock has fairness set true. - * @return true if this lock has fairness set true. + * Returns {@code true} if this lock has fairness set true. + * + * @return {@code true} if this lock has fairness set true */ public final boolean isFair() { return sync instanceof FairSync; } /** - * Returns the thread that currently owns the exclusive lock, or - * null if not owned. Note that the owner may be - * momentarily null even if there are threads trying to - * acquire the lock but have not yet done so. This method is - * designed to facilitate construction of subclasses that provide - * more extensive lock monitoring facilities. - * @return the owner, or null if not owned. + * Returns the thread that currently owns the write lock, or + * {@code null} if not owned. When this method is called by a + * thread that is not the owner, the return value reflects a + * best-effort approximation of current lock status. For example, + * the owner may be momentarily {@code null} even if there are + * threads trying to acquire the lock but have not yet done so. + * This method is designed to facilitate construction of + * subclasses that provide more extensive lock monitoring + * facilities. + * + * @return the owner, or {@code null} if not owned */ protected Thread getOwner() { return sync.getOwner(); } - + /** * Queries the number of read locks held for this lock. This * method is designed for use in monitoring system state, not for @@ -916,17 +1205,19 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab * Queries if the write lock is held by any thread. This method is * designed for use in monitoring system state, not for * synchronization control. - * @return true if any thread holds write lock and - * false otherwise. + * + * @return {@code true} if any thread holds the write lock and + * {@code false} otherwise */ public boolean isWriteLocked() { return sync.isWriteLocked(); } /** - * Queries if the write lock is held by the current thread. - * @return true if current thread holds this lock and - * false otherwise. + * Queries if the write lock is held by the current thread. + * + * @return {@code true} if the current thread holds the write lock and + * {@code false} otherwise */ public boolean isWriteLockedByCurrentThread() { return sync.isHeldExclusively(); @@ -937,8 +1228,8 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab * current thread. A writer thread has a hold on a lock for * each lock action that is not matched by an unlock action. * - * @return the number of holds on this lock by the current thread, - * or zero if this lock is not held by the current thread. + * @return the number of holds on the write lock by the current thread, + * or zero if the write lock is not held by the current thread */ public int getWriteHoldCount() { return sync.getWriteHoldCount(); @@ -952,6 +1243,7 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab * returned collection are in no particular order. This method is * designed to facilitate construction of subclasses that provide * more extensive lock monitoring facilities. + * * @return the collection of threads */ protected Collection getQueuedWriterThreads() { @@ -966,6 +1258,7 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab * returned collection are in no particular order. This method is * designed to facilitate construction of subclasses that provide * more extensive lock monitoring facilities. + * * @return the collection of threads */ protected Collection getQueuedReaderThreads() { @@ -973,41 +1266,42 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab } /** - * Queries whether any threads are waiting to acquire. Note that - * because cancellations may occur at any time, a true - * return does not guarantee that any other thread will ever - * acquire. This method is designed primarily for use in - * monitoring of the system state. + * Queries whether any threads are waiting to acquire the read or + * write lock. Note that because cancellations may occur at any + * time, a {@code true} return does not guarantee that any other + * thread will ever acquire a lock. This method is designed + * primarily for use in monitoring of the system state. * - * @return true if there may be other threads waiting to acquire - * the lock. + * @return {@code true} if there may be other threads waiting to + * acquire the lock */ - public final boolean hasQueuedThreads() { + public final boolean hasQueuedThreads() { return sync.hasQueuedThreads(); } /** - * Queries whether the given thread is waiting to acquire this - * lock. Note that because cancellations may occur at any time, a - * true return does not guarantee that this thread - * will ever acquire. This method is designed primarily for use - * in monitoring of the system state. + * Queries whether the given thread is waiting to acquire either + * the read or write lock. Note that because cancellations may + * occur at any time, a {@code true} return does not guarantee + * that this thread will ever acquire a lock. This method is + * designed primarily for use in monitoring of the system state. * * @param thread the thread - * @return true if the given thread is queued waiting for this lock. - * @throws NullPointerException if thread is null + * @return {@code true} if the given thread is queued waiting for this lock + * @throws NullPointerException if the thread is null */ - public final boolean hasQueuedThread(Thread thread) { + public final boolean hasQueuedThread(Thread thread) { return sync.isQueued(thread); } /** - * Returns an estimate of the number of threads waiting to - * acquire. The value is only an estimate because the number of - * threads may change dynamically while this method traverses - * internal data structures. This method is designed for use in - * monitoring of the system state, not for synchronization - * control. + * Returns an estimate of the number of threads waiting to acquire + * either the read or write lock. The value is only an estimate + * because the number of threads may change dynamically while this + * method traverses internal data structures. This method is + * designed for use in monitoring of the system state, not for + * synchronization control. + * * @return the estimated number of threads waiting for this lock */ public final int getQueueLength() { @@ -1016,12 +1310,13 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab /** * Returns a collection containing threads that may be waiting to - * acquire. Because the actual set of threads may change - * dynamically while constructing this result, the returned - * collection is only a best-effort estimate. The elements of the - * returned collection are in no particular order. This method is - * designed to facilitate construction of subclasses that provide - * more extensive monitoring facilities. + * acquire either the read or write lock. Because the actual set + * of threads may change dynamically while constructing this + * result, the returned collection is only a best-effort estimate. + * The elements of the returned collection are in no particular + * order. This method is designed to facilitate construction of + * subclasses that provide more extensive monitoring facilities. + * * @return the collection of threads */ protected Collection getQueuedThreads() { @@ -1031,18 +1326,18 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab /** * Queries whether any threads are waiting on the given condition * associated with the write lock. Note that because timeouts and - * interrupts may occur at any time, a true return does - * not guarantee that a future signal will awaken any + * interrupts may occur at any time, a {@code true} return does + * not guarantee that a future {@code signal} will awaken any * threads. This method is designed primarily for use in * monitoring of the system state. + * * @param condition the condition - * @return true if there are any waiting threads. - * @throws IllegalMonitorStateException if this lock - * is not held + * @return {@code true} if there are any waiting threads + * @throws IllegalMonitorStateException if this lock is not held * @throws IllegalArgumentException if the given condition is - * not associated with this lock - * @throws NullPointerException if condition null - */ + * not associated with this lock + * @throws NullPointerException if the condition is null + */ public boolean hasWaiters(Condition condition) { if (condition == null) throw new NullPointerException(); @@ -1058,14 +1353,14 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab * serves only as an upper bound on the actual number of waiters. * This method is designed for use in monitoring of the system * state, not for synchronization control. + * * @param condition the condition - * @return the estimated number of waiting threads. - * @throws IllegalMonitorStateException if this lock - * is not held + * @return the estimated number of waiting threads + * @throws IllegalMonitorStateException if this lock is not held * @throws IllegalArgumentException if the given condition is - * not associated with this lock - * @throws NullPointerException if condition null - */ + * not associated with this lock + * @throws NullPointerException if the condition is null + */ public int getWaitQueueLength(Condition condition) { if (condition == null) throw new NullPointerException(); @@ -1083,13 +1378,13 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab * are in no particular order. This method is designed to * facilitate construction of subclasses that provide more * extensive condition monitoring facilities. + * * @param condition the condition * @return the collection of threads - * @throws IllegalMonitorStateException if this lock - * is not held + * @throws IllegalMonitorStateException if this lock is not held * @throws IllegalArgumentException if the given condition is - * not associated with this lock - * @throws NullPointerException if condition null + * not associated with this lock + * @throws NullPointerException if the condition is null */ protected Collection getWaitingThreads(Condition condition) { if (condition == null) @@ -1101,18 +1396,19 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab /** * Returns a string identifying this lock, as well as its lock state. - * The state, in brackets, includes the String "Write locks =" - * follwed by the number of reentrantly held write locks, and the - * String "Read locks =" followed by the number of held + * The state, in brackets, includes the String {@code "Write locks ="} + * followed by the number of reentrantly held write locks, and the + * String {@code "Read locks ="} followed by the number of held * read locks. - * @return a string identifying this lock, as well as its lock state. + * + * @return a string identifying this lock, as well as its lock state */ public String toString() { int c = sync.getCount(); - int w = exclusiveCount(c); - int r = sharedCount(c); - - return super.toString() + + int w = Sync.exclusiveCount(c); + int r = Sync.sharedCount(c); + + return super.toString() + "[Write locks = " + w + ", Read locks = " + r + "]"; } diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/locks/package-info.java b/libcore/concurrent/src/main/java/java/util/concurrent/locks/package-info.java new file mode 100644 index 000000000..801ee9be6 --- /dev/null +++ b/libcore/concurrent/src/main/java/java/util/concurrent/locks/package-info.java @@ -0,0 +1,44 @@ +/* + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/licenses/publicdomain + */ + +/** + * Interfaces and classes providing a framework for locking and waiting + * for conditions that is distinct from built-in synchronization and + * monitors. The framework permits much greater flexibility in the use of + * locks and conditions, at the expense of more awkward syntax. + * + *

      The {@link java.util.concurrent.locks.Lock} interface supports + * locking disciplines that differ in semantics (reentrant, fair, etc), + * and that can be used in non-block-structured contexts including + * hand-over-hand and lock reordering algorithms. The main implementation + * is {@link java.util.concurrent.locks.ReentrantLock}. + * + *

      The {@link java.util.concurrent.locks.ReadWriteLock} interface + * similarly defines locks that may be shared among readers but are + * exclusive to writers. Only a single implementation, {@link + * java.util.concurrent.locks.ReentrantReadWriteLock}, is provided, since + * it covers most standard usage contexts. But programmers may create + * their own implementations to cover nonstandard requirements. + * + *

      The {@link java.util.concurrent.locks.Condition} interface + * describes condition variables that may be associated with Locks. + * These are similar in usage to the implicit monitors accessed using + * {@code Object.wait}, but offer extended capabilities. + * In particular, multiple {@code Condition} objects may be associated + * with a single {@code Lock}. To avoid compatibility issues, the + * names of {@code Condition} methods are different from the + * corresponding {@code Object} versions. + * + *

      The {@link java.util.concurrent.locks.AbstractQueuedSynchronizer} + * class serves as a useful superclass for defining locks and other + * synchronizers that rely on queuing blocked threads. The + * {@link java.util.concurrent.locks.LockSupport} class provides + * lower-level blocking and unblocking support that is useful for those + * developers implementing their own customized lock classes. + * + * @since 1.5 + */ +package java.util.concurrent.locks; diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/locks/package.html b/libcore/concurrent/src/main/java/java/util/concurrent/locks/package.html deleted file mode 100644 index 1cc156fc8..000000000 --- a/libcore/concurrent/src/main/java/java/util/concurrent/locks/package.html +++ /dev/null @@ -1,44 +0,0 @@ - - -Locks - - - - -Interfaces and classes providing a framework for locking and waiting -for conditions that is distinct from built-in synchronization and -monitors. The framework permits much greater flexibility in the use of -locks and conditions, at the expense of more awkward syntax. - -

      The {@link java.util.concurrent.locks.Lock} interface supports -locking disciplines that differ in semantics (reentrant, fair, etc), -and that can be used in non-block-structured contexts including -hand-over-hand and lock reordering algorithms. The main implementation -is {@link java.util.concurrent.locks.ReentrantLock}. - -

      The {@link java.util.concurrent.locks.ReadWriteLock} interface -similarly defines locks that may be shared among readers but are -exclusive to writers. Only a single implementation, {@link -java.util.concurrent.locks.ReentrantReadWriteLock}, is provided, since -it covers most standard usage contexts. But programmers may create -their own implementations to cover nonstandard requirements. - -

      The {@link java.util.concurrent.locks.Condition} interface -describes condition variables that may be associated with Locks. -These are similar in usage to the implicit monitors accessed using -Object.wait, but offer extended capabilities. In particular, -multiple Condition objects may be associated with a single -Lock. To avoid compatibility issues, the names of -Condition methods are different than the corresponding -Object versions. - -

      The {@link java.util.concurrent.locks.AbstractQueuedSynchronizer} -class serves as a useful superclass for defining locks and other -synchronizers that rely on queuing blocked threads. The {@link -java.util.concurrent.locks.LockSupport} class provides lower-level -blocking and unblocking support that is useful for those developers -implementing their own customized lock classes. - -@since Android 1.0 - - diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/package-info.java b/libcore/concurrent/src/main/java/java/util/concurrent/package-info.java new file mode 100644 index 000000000..d49ef2526 --- /dev/null +++ b/libcore/concurrent/src/main/java/java/util/concurrent/package-info.java @@ -0,0 +1,229 @@ +/* + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/licenses/publicdomain + */ + +/** + * Utility classes commonly useful in concurrent programming. This + * package includes a few small standardized extensible frameworks, as + * well as some classes that provide useful functionality and are + * otherwise tedious or difficult to implement. Here are brief + * descriptions of the main components. See also the + * {@link java.util.concurrent.locks} and + * {@link java.util.concurrent.atomic} packages. + * + *

      Executors

      + * + * Interfaces. + * + * {@link java.util.concurrent.Executor} is a simple standardized + * interface for defining custom thread-like subsystems, including + * thread pools, asynchronous IO, and lightweight task frameworks. + * Depending on which concrete Executor class is being used, tasks may + * execute in a newly created thread, an existing task-execution thread, + * or the thread calling {@link java.util.concurrent.Executor#execute + * execute}, and may execute sequentially or concurrently. + * + * {@link java.util.concurrent.ExecutorService} provides a more + * complete asynchronous task execution framework. An + * ExecutorService manages queuing and scheduling of tasks, + * and allows controlled shutdown. + * + * The {@link java.util.concurrent.ScheduledExecutorService} + * subinterface and associated interfaces add support for + * delayed and periodic task execution. ExecutorServices + * provide methods arranging asynchronous execution of any + * function expressed as {@link java.util.concurrent.Callable}, + * the result-bearing analog of {@link java.lang.Runnable}. + * + * A {@link java.util.concurrent.Future} returns the results of + * a function, allows determination of whether execution has + * completed, and provides a means to cancel execution. + * + *

      + * + * Implementations. + * + * Classes {@link java.util.concurrent.ThreadPoolExecutor} and + * {@link java.util.concurrent.ScheduledThreadPoolExecutor} + * provide tunable, flexible thread pools. + * + * The {@link java.util.concurrent.Executors} class provides + * factory methods for the most common kinds and configurations + * of Executors, as well as a few utility methods for using + * them. Other utilities based on {@code Executors} include the + * concrete class {@link java.util.concurrent.FutureTask} + * providing a common extensible implementation of Futures, and + * {@link java.util.concurrent.ExecutorCompletionService}, that + * assists in coordinating the processing of groups of + * asynchronous tasks. + * + *

      Queues

      + * + * The {@link java.util.concurrent.ConcurrentLinkedQueue} class + * supplies an efficient scalable thread-safe non-blocking FIFO + * queue. + * + *

      Five implementations in {@code java.util.concurrent} support + * the extended {@link java.util.concurrent.BlockingQueue} + * interface, that defines blocking versions of put and take: + * {@link java.util.concurrent.LinkedBlockingQueue}, + * {@link java.util.concurrent.ArrayBlockingQueue}, + * {@link java.util.concurrent.SynchronousQueue}, + * {@link java.util.concurrent.PriorityBlockingQueue}, and + * {@link java.util.concurrent.DelayQueue}. + * The different classes cover the most common usage contexts + * for producer-consumer, messaging, parallel tasking, and + * related concurrent designs. + * + *

      Timing

      + * + * The {@link java.util.concurrent.TimeUnit} class provides + * multiple granularities (including nanoseconds) for + * specifying and controlling time-out based operations. Most + * classes in the package contain operations based on time-outs + * in addition to indefinite waits. In all cases that + * time-outs are used, the time-out specifies the minimum time + * that the method should wait before indicating that it + * timed-out. Implementations make a "best effort" + * to detect time-outs as soon as possible after they occur. + * However, an indefinite amount of time may elapse between a + * time-out being detected and a thread actually executing + * again after that time-out. All methods that accept timeout + * parameters treat values less than or equal to zero to mean + * not to wait at all. To wait "forever", you can use a value + * of {@code Long.MAX_VALUE}. + * + *

      Synchronizers

      + * + * Four classes aid common special-purpose synchronization idioms. + * {@link java.util.concurrent.Semaphore} is a classic concurrency tool. + * {@link java.util.concurrent.CountDownLatch} is a very simple yet very + * common utility for blocking until a given number of signals, events, + * or conditions hold. A {@link java.util.concurrent.CyclicBarrier} is a + * resettable multiway synchronization point useful in some styles of + * parallel programming. An {@link java.util.concurrent.Exchanger} allows + * two threads to exchange objects at a rendezvous point, and is useful + * in several pipeline designs. + * + *

      Concurrent Collections

      + * + * Besides Queues, this package supplies Collection implementations + * designed for use in multithreaded contexts: + * {@link java.util.concurrent.ConcurrentHashMap}, + * {@link java.util.concurrent.CopyOnWriteArrayList}, and + * {@link java.util.concurrent.CopyOnWriteArraySet}. + * When many threads are expected to access a given collection, a + * {@code ConcurrentHashMap} is normally preferable to a synchronized + * {@code HashMap}. A {@code CopyOnWriteArrayList} is preferable to a + * synchronized {@code ArrayList} when the expected number of reads and + * traversals greatly outnumber the number of updates to a list. + + *

      The "Concurrent" prefix used with some classes in this package + * is a shorthand indicating several differences from similar + * "synchronized" classes. For example {@code java.util.Hashtable} and + * {@code Collections.synchronizedMap(new HashMap())} are + * synchronized. But {@link + * java.util.concurrent.ConcurrentHashMap} is "concurrent". A + * concurrent collection is thread-safe, but not governed by a + * single exclusion lock. In the particular case of + * ConcurrentHashMap, it safely permits any number of + * concurrent reads as well as a tunable number of concurrent + * writes. "Synchronized" classes can be useful when you need + * to prevent all access to a collection via a single lock, at + * the expense of poorer scalability. In other cases in which + * multiple threads are expected to access a common collection, + * "concurrent" versions are normally preferable. And + * unsynchronized collections are preferable when either + * collections are unshared, or are accessible only when + * holding other locks. + * + *

      Most concurrent Collection implementations (including most + * Queues) also differ from the usual java.util conventions in that + * their Iterators provide weakly consistent rather than + * fast-fail traversal. A weakly consistent iterator is thread-safe, + * but does not necessarily freeze the collection while iterating, so + * it may (or may not) reflect any updates since the iterator was + * created. + * + *

      Memory Consistency Properties

      + * + * + * Chapter 17 of the Java Language Specification defines the + * happens-before relation on memory operations such as reads and + * writes of shared variables. The results of a write by one thread are + * guaranteed to be visible to a read by another thread only if the write + * operation happens-before the read operation. The + * {@code synchronized} and {@code volatile} constructs, as well as the + * {@code Thread.start()} and {@code Thread.join()} methods, can form + * happens-before relationships. In particular: + * + *
        + *
      • Each action in a thread happens-before every action in that + * thread that comes later in the program's order. + * + *
      • An unlock ({@code synchronized} block or method exit) of a + * monitor happens-before every subsequent lock ({@code synchronized} + * block or method entry) of that same monitor. And because + * the happens-before relation is transitive, all actions + * of a thread prior to unlocking happen-before all actions + * subsequent to any thread locking that monitor. + * + *
      • A write to a {@code volatile} field happens-before every + * subsequent read of that same field. Writes and reads of + * {@code volatile} fields have similar memory consistency effects + * as entering and exiting monitors, but do not entail + * mutual exclusion locking. + * + *
      • A call to {@code start} on a thread happens-before any + * action in the started thread. + * + *
      • All actions in a thread happen-before any other thread + * successfully returns from a {@code join} on that thread. + * + *
      + * + * + * The methods of all classes in {@code java.util.concurrent} and its + * subpackages extend these guarantees to higher-level + * synchronization. In particular: + * + *
        + * + *
      • Actions in a thread prior to placing an object into any concurrent + * collection happen-before actions subsequent to the access or + * removal of that element from the collection in another thread. + * + *
      • Actions in a thread prior to the submission of a {@code Runnable} + * to an {@code Executor} happen-before its execution begins. + * Similarly for {@code Callables} submitted to an {@code ExecutorService}. + * + *
      • Actions taken by the asynchronous computation represented by a + * {@code Future} happen-before actions subsequent to the + * retrieval of the result via {@code Future.get()} in another thread. + * + *
      • Actions prior to "releasing" synchronizer methods such as + * {@code Lock.unlock}, {@code Semaphore.release}, and + * {@code CountDownLatch.countDown} happen-before actions + * subsequent to a successful "acquiring" method such as + * {@code Lock.lock}, {@code Semaphore.acquire}, + * {@code Condition.await}, and {@code CountDownLatch.await} on the + * same synchronizer object in another thread. + * + *
      • For each pair of threads that successfully exchange objects via + * an {@code Exchanger}, actions prior to the {@code exchange()} + * in each thread happen-before those subsequent to the + * corresponding {@code exchange()} in another thread. + * + *
      • Actions prior to calling {@code CyclicBarrier.await} + * happen-before actions performed by the barrier action, and + * actions performed by the barrier action happen-before actions + * subsequent to a successful return from the corresponding {@code await} + * in other threads. + * + *
      + * + * @since 1.5 + */ +package java.util.concurrent; diff --git a/libcore/concurrent/src/main/java/java/util/concurrent/package.html b/libcore/concurrent/src/main/java/java/util/concurrent/package.html deleted file mode 100644 index a5bb8d24a..000000000 --- a/libcore/concurrent/src/main/java/java/util/concurrent/package.html +++ /dev/null @@ -1,125 +0,0 @@ - - -Concurrency Utilities - - - - -

      Utility classes commonly useful in concurrent programming. This -package includes a few small standardized extensible frameworks, as -well as some classes that provide useful functionality and are -otherwise tedious or difficult to implement. Here are brief -descriptions of the main components. See also the locks and -atomic packages. - -

      Executors

      - -Interfaces. {@link java.util.concurrent.Executor} is a simple -standardized interface for defining custom thread-like subsystems, -including thread pools, asynchronous IO, and lightweight task -frameworks. Depending on which concrete Executor class is being used, -tasks may execute in a newly created thread, an existing -task-execution thread, or the thread calling execute(), and -may execute sequentially or concurrently. {@link -java.util.concurrent.ExecutorService} provides a more complete -asynchronous task execution framework. An ExecutorService manages -queuing and scheduling of tasks, and allows controlled shutdown. The -{@link java.util.concurrent.ScheduledExecutorService} subinterface -adds support for delayed and periodic task execution. -ExecutorServices provide methods arranging asynchronous execution of -any function expressed as {@link java.util.concurrent.Callable}, the -result-bearing analog of {@link java.lang.Runnable}. A {@link -java.util.concurrent.Future} returns the results of a function, allows -determination of whether execution has completed, and provides a means to -cancel execution. - -

      - -Implementations. Classes {@link -java.util.concurrent.ThreadPoolExecutor} and {@link -java.util.concurrent.ScheduledThreadPoolExecutor} provide tunable, -flexible thread pools. The {@link java.util.concurrent.Executors} -class provides factory methods for the most common kinds and -configurations of Executors, as well as a few utility methods for -using them. Other utilities based on Executors include the concrete -class {@link java.util.concurrent.FutureTask} providing a common -extensible implementation of Futures, and {@link -java.util.concurrent.ExecutorCompletionService}, that assists in -coordinating the processing of groups of asynchronous tasks. - -

      Queues

      - -The java.util.concurrent {@link -java.util.concurrent.ConcurrentLinkedQueue} class supplies an -efficient scalable thread-safe non-blocking FIFO queue. Five -implementations in java.util.concurrent support the extended {@link -java.util.concurrent.BlockingQueue} interface, that defines blocking -versions of put and take: {@link -java.util.concurrent.LinkedBlockingQueue}, {@link -java.util.concurrent.ArrayBlockingQueue}, {@link -java.util.concurrent.SynchronousQueue}, {@link -java.util.concurrent.PriorityBlockingQueue}, and {@link -java.util.concurrent.DelayQueue}. The different classes cover the most -common usage contexts for producer-consumer, messaging, parallel -tasking, and related concurrent designs. - - -

      Timing

      - -The {@link java.util.concurrent.TimeUnit} class provides multiple -granularities (including nanoseconds) for specifying and controlling -time-out based operations. Most classes in the package contain -operations based on time-outs in addition to indefinite waits. In all -cases that time-outs are used, the time-out specifies the minimum time -that the method should wait before indicating that it -timed-out. Implementations make a "best effort" to detect -time-outs as soon as possible after they occur. However, an indefinite -amount of time may elapse between a time-out being detected and a -thread actually executing again after that time-out. - -

      Synchronizers

      - -Four classes aid common special-purpose synchronization idioms. -{@link java.util.concurrent.Semaphore} is a classic concurrency tool. -{@link java.util.concurrent.CountDownLatch} is a very simple yet very -common utility for blocking until a given number of signals, events, -or conditions hold. A {@link java.util.concurrent.CyclicBarrier} is a -resettable multiway synchronization point useful in some styles of -parallel programming. An {@link java.util.concurrent.Exchanger} allows -two threads to exchange objects at a rendezvous point, and is useful -in several pipeline designs. - -

      Concurrent Collections

      - -Besides Queues, this package supplies a few Collection implementations -designed for use in multithreaded contexts: {@link -java.util.concurrent.ConcurrentHashMap}, {@link -java.util.concurrent.CopyOnWriteArrayList}, and {@link -java.util.concurrent.CopyOnWriteArraySet}. - -

      The "Concurrent" prefix used with some classes in this package is a -shorthand indicating several differences from similar "synchronized" -classes. For example java.util.Hashtable and -Collections.synchronizedMap(new HashMap()) are -synchronized. But {@link java.util.concurrent.ConcurrentHashMap} is -"concurrent". A concurrent collection is thread-safe, but not -governed by a single exclusion lock. In the particular case of -ConcurrentHashMap, it safely permits any number of concurrent reads as -well as a tunable number of concurrent writes. "Synchronized" classes -can be useful when you need to prevent all access to a collection via -a single lock, at the expense of poorer scalability. In other cases in -which multiple threads are expected to access a common collection, -"concurrent" versions are normally preferable. And unsynchronized -collections are preferable when either collections are unshared, or -are accessible only when holding other locks. - -

      Most concurrent Collection implementations (including most Queues) -also differ from the usual java.util conventions in that their Iterators -provide weakly consistent rather than fast-fail traversal. A -weakly consistent iterator is thread-safe, but does not necessarily -freeze the collection while iterating, so it may (or may not) reflect -any updates since the iterator was created. - -@since Android 1.0 - - diff --git a/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/AbstractQueuedSynchronizerTest.java b/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/AbstractQueuedSynchronizerTest.java index 7102c14d1..db89645eb 100644 --- a/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/AbstractQueuedSynchronizerTest.java +++ b/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/AbstractQueuedSynchronizerTest.java @@ -462,7 +462,9 @@ public class AbstractQueuedSynchronizerTest extends JSR166TestCase { Thread t = new Thread(new InterruptedSyncRunnable(sync)); try { t.start(); + Thread.sleep(SHORT_DELAY_MS); t.interrupt(); + Thread.sleep(SHORT_DELAY_MS); sync.release(1); t.join(); } catch(Exception e){ @@ -951,7 +953,6 @@ public class AbstractQueuedSynchronizerTest extends JSR166TestCase { sync.acquire(1); c.signal(); sync.release(1); - assert(t.isInterrupted()); t.join(SHORT_DELAY_MS); assertFalse(t.isAlive()); } diff --git a/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/ArrayBlockingQueueTest.java b/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/ArrayBlockingQueueTest.java index da6988f6b..b650ede1f 100755 --- a/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/ArrayBlockingQueueTest.java +++ b/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/ArrayBlockingQueueTest.java @@ -651,6 +651,7 @@ public class ArrayBlockingQueueTest extends JSR166TestCase { assertEquals(SIZE, q.remainingCapacity()); q.add(one); assertFalse(q.isEmpty()); + assertTrue(q.contains(one)); q.clear(); assertTrue(q.isEmpty()); } @@ -971,6 +972,17 @@ public class ArrayBlockingQueueTest extends JSR166TestCase { assertEquals(l.size(), SIZE); for (int i = 0; i < SIZE; ++i) assertEquals(l.get(i), new Integer(i)); + q.add(zero); + q.add(one); + assertFalse(q.isEmpty()); + assertTrue(q.contains(zero)); + assertTrue(q.contains(one)); + l.clear(); + q.drainTo(l); + assertEquals(q.size(), 0); + assertEquals(l.size(), 2); + for (int i = 0; i < 2; ++i) + assertEquals(l.get(i), new Integer(i)); } /** @@ -1029,15 +1041,18 @@ public class ArrayBlockingQueueTest extends JSR166TestCase { * drainTo(c, n) empties first max {n, size} elements of queue into c */ public void testDrainToN() { + ArrayBlockingQueue q = new ArrayBlockingQueue(SIZE*2); for (int i = 0; i < SIZE + 2; ++i) { - ArrayBlockingQueue q = populatedQueue(SIZE); + for(int j = 0; j < SIZE; j++) + assertTrue(q.offer(new Integer(j))); ArrayList l = new ArrayList(); q.drainTo(l, i); int k = (i < SIZE)? i : SIZE; - assertEquals(q.size(), SIZE-k); assertEquals(l.size(), k); + assertEquals(q.size(), SIZE-k); for (int j = 0; j < k; ++j) assertEquals(l.get(j), new Integer(j)); + while (q.poll() != null) ; } } diff --git a/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicIntegerFieldUpdaterTest.java b/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicIntegerFieldUpdaterTest.java index 03fd2c869..4c3ecb486 100755 --- a/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicIntegerFieldUpdaterTest.java +++ b/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicIntegerFieldUpdaterTest.java @@ -62,6 +62,27 @@ public class AtomicIntegerFieldUpdaterTest extends JSR166TestCase { catch (RuntimeException rt) {} } + static class Base { + protected volatile int f = 0; + } + static class Sub1 extends Base { + AtomicIntegerFieldUpdater fUpdater + = AtomicIntegerFieldUpdater.newUpdater(Base.class, "f"); + } + static class Sub2 extends Base {} + + public void testProtectedFieldOnAnotherSubtype() { + Sub1 sub1 = new Sub1(); + Sub2 sub2 = new Sub2(); + + sub1.fUpdater.set(sub1, 1); + try { + sub1.fUpdater.set(sub2, 2); + shouldThrow(); + } + catch (RuntimeException rt) {} + } + /** * get returns the last value set or assigned */ @@ -80,6 +101,7 @@ public class AtomicIntegerFieldUpdaterTest extends JSR166TestCase { assertEquals(-3,a.get(this)); } + /** * compareAndSet succeeds in changing value if equal to expected else fails */ diff --git a/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicIntegerTest.java b/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicIntegerTest.java index 1771d8017..d48478560 100755 --- a/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicIntegerTest.java +++ b/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicIntegerTest.java @@ -48,6 +48,7 @@ public class AtomicIntegerTest extends JSR166TestCase { assertEquals(-3,ai.get()); } + /** * compareAndSet succeeds in changing value if equal to expected else fails */ diff --git a/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicLongFieldUpdaterTest.java b/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicLongFieldUpdaterTest.java index 8076f7b50..93107954e 100755 --- a/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicLongFieldUpdaterTest.java +++ b/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicLongFieldUpdaterTest.java @@ -29,7 +29,7 @@ public class AtomicLongFieldUpdaterTest extends JSR166TestCase { */ public void testConstructor(){ try{ - AtomicLongFieldUpdater + AtomicLongFieldUpdater a = AtomicLongFieldUpdater.newUpdater (AtomicLongFieldUpdaterTest.class, "y"); shouldThrow(); @@ -42,7 +42,7 @@ public class AtomicLongFieldUpdaterTest extends JSR166TestCase { */ public void testConstructor2(){ try{ - AtomicLongFieldUpdater + AtomicLongFieldUpdater a = AtomicLongFieldUpdater.newUpdater (AtomicLongFieldUpdaterTest.class, "z"); shouldThrow(); @@ -55,7 +55,7 @@ public class AtomicLongFieldUpdaterTest extends JSR166TestCase { */ public void testConstructor3(){ try{ - AtomicLongFieldUpdater + AtomicLongFieldUpdater a = AtomicLongFieldUpdater.newUpdater (AtomicLongFieldUpdaterTest.class, "w"); shouldThrow(); @@ -64,6 +64,27 @@ public class AtomicLongFieldUpdaterTest extends JSR166TestCase { catch (RuntimeException rt) {} } + static class Base { + protected volatile long f = 0; + } + static class Sub1 extends Base { + AtomicLongFieldUpdater fUpdater + = AtomicLongFieldUpdater.newUpdater(Base.class, "f"); + } + static class Sub2 extends Base {} + + public void testProtectedFieldOnAnotherSubtype() { + Sub1 sub1 = new Sub1(); + Sub2 sub2 = new Sub2(); + + sub1.fUpdater.set(sub1, 1); + try { + sub1.fUpdater.set(sub2, 2); + shouldThrow(); + } + catch (RuntimeException rt) {} + } + /** * get returns the last value set or assigned */ @@ -75,12 +96,12 @@ public class AtomicLongFieldUpdaterTest extends JSR166TestCase { return; } x = 1; - assertEquals(1,a.get(this)); - a.set(this,2); - assertEquals(2,a.get(this)); - a.set(this,-3); - assertEquals(-3,a.get(this)); - + assertEquals(1,a.get(this)); + a.set(this,2); + assertEquals(2,a.get(this)); + a.set(this,-3); + assertEquals(-3,a.get(this)); + } /** * compareAndSet succeeds in changing value if equal to expected else fails @@ -134,7 +155,7 @@ public class AtomicLongFieldUpdaterTest extends JSR166TestCase { /** * repeated weakCompareAndSet succeeds in changing value when equal - * to expected + * to expected */ public void testWeakCompareAndSet(){ AtomicLongFieldUpdater a; diff --git a/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicLongTest.java b/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicLongTest.java index 55b8a49cd..143c84a51 100755 --- a/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicLongTest.java +++ b/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicLongTest.java @@ -48,6 +48,7 @@ public class AtomicLongTest extends JSR166TestCase { assertEquals(-3,ai.get()); } + /** * compareAndSet succeeds in changing value if equal to expected else fails */ diff --git a/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicReferenceFieldUpdaterTest.java b/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicReferenceFieldUpdaterTest.java index 183ca2162..feddce759 100755 --- a/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicReferenceFieldUpdaterTest.java +++ b/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicReferenceFieldUpdaterTest.java @@ -64,6 +64,27 @@ public class AtomicReferenceFieldUpdaterTest extends JSR166TestCase{ catch (RuntimeException rt) {} } + static class Base { + protected volatile Object f = null; + } + static class Sub1 extends Base { + AtomicReferenceFieldUpdater fUpdater + = AtomicReferenceFieldUpdater.newUpdater(Base.class, Object.class, "f"); + } + static class Sub2 extends Base {} + + public void testProtectedFieldOnAnotherSubtype() { + Sub1 sub1 = new Sub1(); + Sub2 sub2 = new Sub2(); + + sub1.fUpdater.set(sub1, "f"); + try { + sub1.fUpdater.set(sub2, "g"); + shouldThrow(); + } + catch (RuntimeException rt) {} + } + /** * get returns the last value set or assigned */ @@ -75,14 +96,11 @@ public class AtomicReferenceFieldUpdaterTest extends JSR166TestCase{ return; } x = one; - assertEquals(one,a.get(this)); - a.set(this,two); - assertEquals(two,a.get(this)); - a.set(this,-3); - // BEGIN android-changed - assertEquals(new Integer(-3),a.get(this)); - // END android-changed - + assertEquals(one,a.get(this)); + a.set(this,two); + assertEquals(two,a.get(this)); + a.set(this,m3); + assertEquals(m3,a.get(this)); } /** * compareAndSet succeeds in changing value if equal to expected else fails @@ -135,7 +153,7 @@ public class AtomicReferenceFieldUpdaterTest extends JSR166TestCase{ /** * repeated weakCompareAndSet succeeds in changing value when equal - * to expected + * to expected */ public void testWeakCompareAndSet(){ AtomicReferenceFieldUpdatera; diff --git a/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicReferenceTest.java b/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicReferenceTest.java index 0aa48cd51..33da30d73 100755 --- a/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicReferenceTest.java +++ b/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/AtomicReferenceTest.java @@ -46,7 +46,7 @@ public class AtomicReferenceTest extends JSR166TestCase { assertEquals(two,ai.get()); ai.set(m3); assertEquals(m3,ai.get()); - + } /** * compareAndSet succeeds in changing value if equal to expected else fails @@ -86,7 +86,7 @@ public class AtomicReferenceTest extends JSR166TestCase { /** * repeated weakCompareAndSet succeeds in changing value when equal - * to expected + * to expected */ public void testWeakCompareAndSet(){ AtomicReference ai = new AtomicReference(one); diff --git a/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/ConcurrentHashMapTest.java b/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/ConcurrentHashMapTest.java index 9b3ec5240..d7f2210fe 100755 --- a/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/ConcurrentHashMapTest.java +++ b/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/ConcurrentHashMapTest.java @@ -16,59 +16,16 @@ import java.io.*; public class ConcurrentHashMapTest extends JSR166TestCase{ public static void main(String[] args) { - junit.textui.TestRunner.run (suite()); + junit.textui.TestRunner.run (suite()); } public static Test suite() { return new TestSuite(ConcurrentHashMapTest.class); } - // BEGIN android-added - static class MyConcurrentHashMap extends ConcurrentHashMap - implements Cloneable { - - public MyConcurrentHashMap() { - super(); - } - - public MyConcurrentHashMap(int initialCapacity, float loadFactor, - int concurrencyLevel) { - super(initialCapacity, loadFactor, concurrencyLevel); - } - - public MyConcurrentHashMap(int initialCapacity) { - super(initialCapacity); - } - - public MyConcurrentHashMap(Map t) { - super(t); - } - - @Override - protected Object clone() throws CloneNotSupportedException { - return super.clone(); - } - - } /** * Create a map from Integers 1-5 to Strings "A"-"E". */ - private static MyConcurrentHashMap myMap5() { - MyConcurrentHashMap map = new MyConcurrentHashMap(5); - assertTrue(map.isEmpty()); - map.put(one, "A"); - map.put(two, "B"); - map.put(three, "C"); - map.put(four, "D"); - map.put(five, "E"); - assertFalse(map.isEmpty()); - assertEquals(5, map.size()); - return map; - } - // END android-added - /** - * Create a map from Integers 1-5 to Strings "A"-"E". - */ - private static ConcurrentHashMap map5() { + private static ConcurrentHashMap map5() { ConcurrentHashMap map = new ConcurrentHashMap(5); assertTrue(map.isEmpty()); map.put(one, "A"); @@ -111,7 +68,7 @@ public class ConcurrentHashMapTest extends JSR166TestCase{ assertTrue(map.contains("A")); assertFalse(map.contains("Z")); } - + /** * containsKey returns true for contained key */ @@ -126,8 +83,8 @@ public class ConcurrentHashMapTest extends JSR166TestCase{ */ public void testContainsValue() { ConcurrentHashMap map = map5(); - assertTrue(map.contains("A")); - assertFalse(map.contains("Z")); + assertTrue(map.containsValue("A")); + assertFalse(map.containsValue("Z")); } /** @@ -145,21 +102,6 @@ public class ConcurrentHashMapTest extends JSR166TestCase{ assertEquals(5, count); } - /** - * Clone creates an equal map - */ - public void testClone() { - // BEGIN android-changed - MyConcurrentHashMap map =myMap5(); - try { - MyConcurrentHashMap m2 = (MyConcurrentHashMap)(map.clone()); - assertEquals(map, m2); - } catch (CloneNotSupportedException e) { - fail("clone not supported"); - } - // END android-changed - } - /** * get returns the correct element at the given key, * or null if not present @@ -209,6 +151,49 @@ public class ConcurrentHashMapTest extends JSR166TestCase{ assertTrue(s.contains(five)); } + /** + * keySet.toArray returns contains all keys + */ + public void testKeySetToArray() { + ConcurrentHashMap map = map5(); + Set s = map.keySet(); + Object[] ar = s.toArray(); + assertTrue(s.containsAll(Arrays.asList(ar))); + assertEquals(5, ar.length); + ar[0] = m10; + assertFalse(s.containsAll(Arrays.asList(ar))); + } + + /** + * Values.toArray contains all values + */ + public void testValuesToArray() { + ConcurrentHashMap map = map5(); + Collection v = map.values(); + Object[] ar = v.toArray(); + ArrayList s = new ArrayList(Arrays.asList(ar)); + assertEquals(5, ar.length); + assertTrue(s.contains("A")); + assertTrue(s.contains("B")); + assertTrue(s.contains("C")); + assertTrue(s.contains("D")); + assertTrue(s.contains("E")); + } + + /** + * entrySet.toArray contains all entries + */ + public void testEntrySetToArray() { + ConcurrentHashMap map = map5(); + Set s = map.entrySet(); + Object[] ar = s.toArray(); + assertEquals(5, ar.length); + for (int i = 0; i < 5; ++i) { + assertTrue(map.containsKey(((Map.Entry)(ar[i])).getKey())); + assertTrue(map.containsValue(((Map.Entry)(ar[i])).getValue())); + } + } + /** * values collection contains all values */ @@ -233,7 +218,7 @@ public class ConcurrentHashMapTest extends JSR166TestCase{ Iterator it = s.iterator(); while (it.hasNext()) { Map.Entry e = (Map.Entry) it.next(); - assertTrue( + assertTrue( (e.getKey().equals(one) && e.getValue().equals("A")) || (e.getKey().equals(two) && e.getValue().equals("B")) || (e.getKey().equals(three) && e.getValue().equals("C")) || @@ -357,12 +342,12 @@ public class ConcurrentHashMapTest extends JSR166TestCase{ for (int i = 1; i <= 5; ++i) { assertTrue(s.indexOf(String.valueOf(i)) >= 0); } - } + } // Exception tests - + /** - * Cannot create with negative capacity + * Cannot create with negative capacity */ public void testConstructor1() { try { @@ -560,6 +545,19 @@ public class ConcurrentHashMapTest extends JSR166TestCase{ } catch(NullPointerException e){} } + /** + * remove(x, null) returns false + */ + public void testRemove3() { + try { + ConcurrentHashMap c = new ConcurrentHashMap(5); + c.put("sadsdf", "asdads"); + assertFalse(c.remove("sadsdf", null)); + } catch(NullPointerException e){ + fail(); + } + } + /** * A deserialized map equals original */ diff --git a/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/CyclicBarrierTest.java b/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/CyclicBarrierTest.java index 9eac1a77a..ecd6e45c6 100755 --- a/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/CyclicBarrierTest.java +++ b/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/CyclicBarrierTest.java @@ -11,6 +11,8 @@ package tests.api.java.util.concurrent; import junit.framework.*; import java.util.*; import java.util.concurrent.*; +import java.util.concurrent.locks.*; +import java.util.concurrent.atomic.*; public class CyclicBarrierTest extends JSR166TestCase{ public static void main(String[] args) { @@ -164,7 +166,7 @@ public class CyclicBarrierTest extends JSR166TestCase{ * throw BrokenBarrierException */ public void testAwait2_Interrupted_BrokenBarrier() { - final CyclicBarrier c = new CyclicBarrier(3); + final CyclicBarrier c = new CyclicBarrier(3); Thread t1 = new Thread(new Runnable() { public void run() { try { @@ -229,7 +231,7 @@ public class CyclicBarrierTest extends JSR166TestCase{ * throw BrokenBarrierException */ public void testAwait4_Timeout_BrokenBarrier() { - final CyclicBarrier c = new CyclicBarrier(3); + final CyclicBarrier c = new CyclicBarrier(3); Thread t1 = new Thread(new Runnable() { public void run() { try { @@ -267,7 +269,7 @@ public class CyclicBarrierTest extends JSR166TestCase{ * throw BrokenBarrierException */ public void testAwait5_Timeout_BrokenBarrier() { - final CyclicBarrier c = new CyclicBarrier(3); + final CyclicBarrier c = new CyclicBarrier(3); Thread t1 = new Thread(new Runnable() { public void run() { try { @@ -376,4 +378,249 @@ public class CyclicBarrierTest extends JSR166TestCase{ } } + /** + * All threads block while a barrier is broken. + */ + public void testReset_Leakage() { + try { + final CyclicBarrier c = new CyclicBarrier(2); + final AtomicBoolean done = new AtomicBoolean(); + Thread t = new Thread() { + public void run() { + while (!done.get()) { + try { + while (c.isBroken()) + c.reset(); + + c.await(); + threadFail("await should not return"); + } + catch (BrokenBarrierException e) { + } + catch (InterruptedException ie) { + } + } + } + }; + + t.start(); + for( int i = 0; i < 4; i++) { + Thread.sleep(SHORT_DELAY_MS); + t.interrupt(); + } + done.set(true); + t.interrupt(); + } + catch (Exception ex) { + unexpectedException(); + } + } + + /** + * Reset of a non-broken barrier does not break barrier + */ + public void testResetWithoutBreakage() { + try { + final CyclicBarrier start = new CyclicBarrier(3); + final CyclicBarrier barrier = new CyclicBarrier(3); + for (int i = 0; i < 3; i++) { + Thread t1 = new Thread(new Runnable() { + public void run() { + try { start.await(); } + catch (Exception ie) { + threadFail("start barrier"); + } + try { barrier.await(); } + catch (Throwable thrown) { + unexpectedException(); + }}}); + + Thread t2 = new Thread(new Runnable() { + public void run() { + try { start.await(); } + catch (Exception ie) { + threadFail("start barrier"); + } + try { barrier.await(); } + catch (Throwable thrown) { + unexpectedException(); + }}}); + + + t1.start(); + t2.start(); + try { start.await(); } + catch (Exception ie) { threadFail("start barrier"); } + barrier.await(); + t1.join(); + t2.join(); + assertFalse(barrier.isBroken()); + assertEquals(0, barrier.getNumberWaiting()); + if (i == 1) barrier.reset(); + assertFalse(barrier.isBroken()); + assertEquals(0, barrier.getNumberWaiting()); + } + } + catch (Exception ex) { + unexpectedException(); + } + } + + /** + * Reset of a barrier after interruption reinitializes it. + */ + public void testResetAfterInterrupt() { + try { + final CyclicBarrier start = new CyclicBarrier(3); + final CyclicBarrier barrier = new CyclicBarrier(3); + for (int i = 0; i < 2; i++) { + Thread t1 = new Thread(new Runnable() { + public void run() { + try { start.await(); } + catch (Exception ie) { + threadFail("start barrier"); + } + try { barrier.await(); } + catch(InterruptedException ok) {} + catch (Throwable thrown) { + unexpectedException(); + }}}); + + Thread t2 = new Thread(new Runnable() { + public void run() { + try { start.await(); } + catch (Exception ie) { + threadFail("start barrier"); + } + try { barrier.await(); } + catch(BrokenBarrierException ok) {} + catch (Throwable thrown) { + unexpectedException(); + }}}); + + t1.start(); + t2.start(); + try { start.await(); } + catch (Exception ie) { threadFail("start barrier"); } + t1.interrupt(); + t1.join(); + t2.join(); + assertTrue(barrier.isBroken()); + assertEquals(0, barrier.getNumberWaiting()); + barrier.reset(); + assertFalse(barrier.isBroken()); + assertEquals(0, barrier.getNumberWaiting()); + } + } + catch (Exception ex) { + unexpectedException(); + } + } + + /** + * Reset of a barrier after timeout reinitializes it. + */ + public void testResetAfterTimeout() { + try { + final CyclicBarrier start = new CyclicBarrier(3); + final CyclicBarrier barrier = new CyclicBarrier(3); + for (int i = 0; i < 2; i++) { + Thread t1 = new Thread(new Runnable() { + public void run() { + try { start.await(); } + catch (Exception ie) { + threadFail("start barrier"); + } + try { barrier.await(MEDIUM_DELAY_MS, TimeUnit.MILLISECONDS); } + catch(TimeoutException ok) {} + catch (Throwable thrown) { + unexpectedException(); + }}}); + + Thread t2 = new Thread(new Runnable() { + public void run() { + try { start.await(); } + catch (Exception ie) { + threadFail("start barrier"); + } + try { barrier.await(); } + catch(BrokenBarrierException ok) {} + catch (Throwable thrown) { + unexpectedException(); + }}}); + + t1.start(); + t2.start(); + try { start.await(); } + catch (Exception ie) { threadFail("start barrier"); } + t1.join(); + t2.join(); + assertTrue(barrier.isBroken()); + assertEquals(0, barrier.getNumberWaiting()); + barrier.reset(); + assertFalse(barrier.isBroken()); + assertEquals(0, barrier.getNumberWaiting()); + } + } + catch (Exception ex) { + unexpectedException(); + } + } + + + /** + * Reset of a barrier after a failed command reinitializes it. + */ + public void testResetAfterCommandException() { + try { + final CyclicBarrier start = new CyclicBarrier(3); + final CyclicBarrier barrier = + new CyclicBarrier(3, new Runnable() { + public void run() { + throw new NullPointerException(); }}); + for (int i = 0; i < 2; i++) { + Thread t1 = new Thread(new Runnable() { + public void run() { + try { start.await(); } + catch (Exception ie) { + threadFail("start barrier"); + } + try { barrier.await(); } + catch(BrokenBarrierException ok) {} + catch (Throwable thrown) { + unexpectedException(); + }}}); + + Thread t2 = new Thread(new Runnable() { + public void run() { + try { start.await(); } + catch (Exception ie) { + threadFail("start barrier"); + } + try { barrier.await(); } + catch(BrokenBarrierException ok) {} + catch (Throwable thrown) { + unexpectedException(); + }}}); + + t1.start(); + t2.start(); + try { start.await(); } + catch (Exception ie) { threadFail("start barrier"); } + while (barrier.getNumberWaiting() < 2) { Thread.yield(); } + try { barrier.await(); } + catch (Exception ok) { } + t1.join(); + t2.join(); + assertTrue(barrier.isBroken()); + assertEquals(0, barrier.getNumberWaiting()); + barrier.reset(); + assertFalse(barrier.isBroken()); + assertEquals(0, barrier.getNumberWaiting()); + } + } + catch (Exception ex) { + unexpectedException(); + } + } } diff --git a/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/DelayQueueTest.java b/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/DelayQueueTest.java index e3325131b..978edb460 100755 --- a/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/DelayQueueTest.java +++ b/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/DelayQueueTest.java @@ -14,11 +14,11 @@ import java.util.concurrent.*; public class DelayQueueTest extends JSR166TestCase { public static void main(String[] args) { - junit.textui.TestRunner.run (suite()); + junit.textui.TestRunner.run (suite()); } public static Test suite() { - return new TestSuite(DelayQueueTest.class); + return new TestSuite(DelayQueueTest.class); } private static final int NOCAP = Integer.MAX_VALUE; @@ -27,20 +27,19 @@ public class DelayQueueTest extends JSR166TestCase { * A delayed implementation for testing. * Most tests use Pseudodelays, where delays are all elapsed * (so, no blocking solely for delays) but are still ordered - */ - static class PDelay implements Delayed { + */ + static class PDelay implements Delayed { int pseudodelay; PDelay(int i) { pseudodelay = Integer.MIN_VALUE + i; } - // BEGIN android-changed - public int compareTo(Delayed y) { + public int compareTo(PDelay y) { int i = pseudodelay; int j = ((PDelay)y).pseudodelay; if (i < j) return -1; if (i > j) return 1; return 0; } - // END android-changed - public int compareTo(PDelay y) { + + public int compareTo(Delayed y) { int i = pseudodelay; int j = ((PDelay)y).pseudodelay; if (i < j) return -1; @@ -72,21 +71,20 @@ public class DelayQueueTest extends JSR166TestCase { /** * Delayed implementation that actually delays */ - static class NanoDelay implements Delayed { + static class NanoDelay implements Delayed { long trigger; - NanoDelay(long i) { + NanoDelay(long i) { trigger = System.nanoTime() + i; } - // BEGIN android-changed - public int compareTo(Delayed y) { + public int compareTo(NanoDelay y) { long i = trigger; long j = ((NanoDelay)y).trigger; if (i < j) return -1; if (i > j) return 1; return 0; } - // END android-changed - public int compareTo(NanoDelay y) { + + public int compareTo(Delayed y) { long i = trigger; long j = ((NanoDelay)y).trigger; if (i < j) return -1; @@ -123,16 +121,16 @@ public class DelayQueueTest extends JSR166TestCase { private DelayQueue populatedQueue(int n) { DelayQueue q = new DelayQueue(); assertTrue(q.isEmpty()); - for(int i = n-1; i >= 0; i-=2) - assertTrue(q.offer(new PDelay(i))); - for(int i = (n & 1); i < n; i+=2) - assertTrue(q.offer(new PDelay(i))); + for(int i = n-1; i >= 0; i-=2) + assertTrue(q.offer(new PDelay(i))); + for(int i = (n & 1); i < n; i+=2) + assertTrue(q.offer(new PDelay(i))); assertFalse(q.isEmpty()); assertEquals(NOCAP, q.remainingCapacity()); - assertEquals(n, q.size()); + assertEquals(n, q.size()); return q; } - + /** * A new queue has unbounded capacity */ @@ -229,22 +227,22 @@ public class DelayQueueTest extends JSR166TestCase { * offer(null) throws NPE */ public void testOfferNull() { - try { + try { DelayQueue q = new DelayQueue(); q.offer(null); shouldThrow(); - } catch (NullPointerException success) { } + } catch (NullPointerException success) { } } /** * add(null) throws NPE */ public void testAddNull() { - try { + try { DelayQueue q = new DelayQueue(); q.add(null); shouldThrow(); - } catch (NullPointerException success) { } + } catch (NullPointerException success) { } } /** @@ -342,13 +340,13 @@ public class DelayQueueTest extends JSR166TestCase { * put(null) throws NPE */ public void testPutNull() { - try { + try { DelayQueue q = new DelayQueue(); q.put(null); shouldThrow(); - } + } catch (NullPointerException success){ - } + } } /** @@ -416,7 +414,7 @@ public class DelayQueueTest extends JSR166TestCase { } finally { } } }); - + try { t.start(); Thread.sleep(SMALL_DELAY_MS); @@ -431,14 +429,14 @@ public class DelayQueueTest extends JSR166TestCase { * take retrieves elements in priority order */ public void testTake() { - try { + try { DelayQueue q = populatedQueue(SIZE); for (int i = 0; i < SIZE; ++i) { assertEquals(new PDelay(i), ((PDelay)q.take())); } } catch (InterruptedException e){ - unexpectedException(); - } + unexpectedException(); + } } /** @@ -450,8 +448,8 @@ public class DelayQueueTest extends JSR166TestCase { public void run() { try { q.take(); - threadShouldThrow(); - } catch (InterruptedException success){ } + threadShouldThrow(); + } catch (InterruptedException success){ } } }); try { @@ -478,16 +476,16 @@ public class DelayQueueTest extends JSR166TestCase { q.take(); threadShouldThrow(); } catch (InterruptedException success){ - } + } }}); t.start(); - try { - Thread.sleep(SHORT_DELAY_MS); + try { + Thread.sleep(SHORT_DELAY_MS); t.interrupt(); t.join(); } catch (InterruptedException ie) { - unexpectedException(); + unexpectedException(); } } @@ -500,7 +498,7 @@ public class DelayQueueTest extends JSR166TestCase { for (int i = 0; i < SIZE; ++i) { assertEquals(new PDelay(i), ((PDelay)q.poll())); } - assertNull(q.poll()); + assertNull(q.poll()); } /** @@ -514,8 +512,8 @@ public class DelayQueueTest extends JSR166TestCase { } assertNull(q.poll(0, TimeUnit.MILLISECONDS)); } catch (InterruptedException e){ - unexpectedException(); - } + unexpectedException(); + } } /** @@ -529,8 +527,8 @@ public class DelayQueueTest extends JSR166TestCase { } assertNull(q.poll(SHORT_DELAY_MS, TimeUnit.MILLISECONDS)); } catch (InterruptedException e){ - unexpectedException(); - } + unexpectedException(); + } } /** @@ -547,16 +545,16 @@ public class DelayQueueTest extends JSR166TestCase { } threadAssertNull(q.poll(SHORT_DELAY_MS, TimeUnit.MILLISECONDS)); } catch (InterruptedException success){ - } + } }}); t.start(); - try { - Thread.sleep(SHORT_DELAY_MS); + try { + Thread.sleep(SHORT_DELAY_MS); t.interrupt(); t.join(); } catch (InterruptedException ie) { - unexpectedException(); + unexpectedException(); } } @@ -572,8 +570,8 @@ public class DelayQueueTest extends JSR166TestCase { threadAssertNull(q.poll(SHORT_DELAY_MS, TimeUnit.MILLISECONDS)); q.poll(LONG_DELAY_MS, TimeUnit.MILLISECONDS); q.poll(LONG_DELAY_MS, TimeUnit.MILLISECONDS); - threadFail("Should block"); - } catch (InterruptedException success) { } + threadFail("Should block"); + } catch (InterruptedException success) { } } }); try { @@ -585,7 +583,7 @@ public class DelayQueueTest extends JSR166TestCase { } catch (Exception e){ unexpectedException(); } - } + } /** @@ -596,10 +594,12 @@ public class DelayQueueTest extends JSR166TestCase { for (int i = 0; i < SIZE; ++i) { assertEquals(new PDelay(i), ((PDelay)q.peek())); q.poll(); - assertTrue(q.peek() == null || - i != ((PDelay)q.peek()).intValue()); + if (q.isEmpty()) + assertNull(q.peek()); + else + assertTrue(i != ((PDelay)q.peek()).intValue()); } - assertNull(q.peek()); + assertNull(q.peek()); } /** @@ -630,7 +630,7 @@ public class DelayQueueTest extends JSR166TestCase { q.remove(); shouldThrow(); } catch (NoSuchElementException success){ - } + } } /** @@ -647,7 +647,7 @@ public class DelayQueueTest extends JSR166TestCase { } assertTrue(q.isEmpty()); } - + /** * contains(x) reports true when elements added but not yet removed */ @@ -669,8 +669,10 @@ public class DelayQueueTest extends JSR166TestCase { assertTrue(q.isEmpty()); assertEquals(0, q.size()); assertEquals(NOCAP, q.remainingCapacity()); - q.add(new PDelay(1)); + PDelay x = new PDelay(1); + q.add(x); assertFalse(q.isEmpty()); + assertTrue(q.contains(x)); q.clear(); assertTrue(q.isEmpty()); } @@ -729,14 +731,14 @@ public class DelayQueueTest extends JSR166TestCase { */ public void testToArray() { DelayQueue q = populatedQueue(SIZE); - Object[] o = q.toArray(); + Object[] o = q.toArray(); Arrays.sort(o); - try { - for(int i = 0; i < o.length; i++) - assertEquals(o[i], q.take()); - } catch (InterruptedException e){ - unexpectedException(); - } + try { + for(int i = 0; i < o.length; i++) + assertEquals(o[i], q.take()); + } catch (InterruptedException e){ + unexpectedException(); + } } /** @@ -744,15 +746,15 @@ public class DelayQueueTest extends JSR166TestCase { */ public void testToArray2() { DelayQueue q = populatedQueue(SIZE); - PDelay[] ints = new PDelay[SIZE]; - ints = (PDelay[])q.toArray(ints); + PDelay[] ints = new PDelay[SIZE]; + ints = (PDelay[])q.toArray(ints); Arrays.sort(ints); - try { - for(int i = 0; i < ints.length; i++) - assertEquals(ints[i], q.take()); - } catch (InterruptedException e){ - unexpectedException(); - } + try { + for(int i = 0; i < ints.length; i++) + assertEquals(ints[i], q.take()); + } catch (InterruptedException e){ + unexpectedException(); + } } @@ -760,31 +762,31 @@ public class DelayQueueTest extends JSR166TestCase { * toArray(null) throws NPE */ public void testToArray_BadArg() { - try { + try { DelayQueue q = populatedQueue(SIZE); - Object o[] = q.toArray(null); - shouldThrow(); - } catch(NullPointerException success){} + Object o[] = q.toArray(null); + shouldThrow(); + } catch(NullPointerException success){} } /** * toArray with incompatible array type throws CCE */ public void testToArray1_BadArg() { - try { + try { DelayQueue q = populatedQueue(SIZE); - Object o[] = q.toArray(new String[10] ); - shouldThrow(); - } catch(ArrayStoreException success){} + Object o[] = q.toArray(new String[10] ); + shouldThrow(); + } catch(ArrayStoreException success){} } - + /** * iterator iterates through all elements */ public void testIterator() { DelayQueue q = populatedQueue(SIZE); int i = 0; - Iterator it = q.iterator(); + Iterator it = q.iterator(); while(it.hasNext()) { assertTrue(q.contains(it.next())); ++i; @@ -819,7 +821,7 @@ public class DelayQueueTest extends JSR166TestCase { for (int i = 0; i < SIZE; ++i) { assertTrue(s.indexOf(String.valueOf(Integer.MIN_VALUE+i)) >= 0); } - } + } /** * offer transfers elements across Executor tasks @@ -875,7 +877,7 @@ public class DelayQueueTest extends JSR166TestCase { NanoDelay e = (NanoDelay)(q.take()); long tt = e.getTriggerTime(); assertTrue(tt <= System.nanoTime()); - if (i != 0) + if (i != 0) assertTrue(tt >= last); last = tt; } @@ -885,10 +887,41 @@ public class DelayQueueTest extends JSR166TestCase { } } + /** + * peek of a non-empty queue returns non-null even if not expired + */ + public void testPeekDelayed() { + DelayQueue q = new DelayQueue(); + q.add(new NanoDelay(Long.MAX_VALUE)); + assert(q.peek() != null); + } + + + /** + * poll of a non-empty queue returns null if no expired elements. + */ + public void testPollDelayed() { + DelayQueue q = new DelayQueue(); + q.add(new NanoDelay(Long.MAX_VALUE)); + assertNull(q.poll()); + } + + /** + * timed poll of a non-empty queue returns null if no expired elements. + */ + public void testTimedPollDelayed() { + DelayQueue q = new DelayQueue(); + q.add(new NanoDelay(LONG_DELAY_MS * 1000000L)); + try { + assertNull(q.poll(SHORT_DELAY_MS, TimeUnit.MILLISECONDS)); + } catch (Exception ex) { + unexpectedException(); + } + } /** * drainTo(null) throws NPE - */ + */ public void testDrainToNull() { DelayQueue q = populatedQueue(SIZE); try { @@ -900,7 +933,7 @@ public class DelayQueueTest extends JSR166TestCase { /** * drainTo(this) throws IAE - */ + */ public void testDrainToSelf() { DelayQueue q = populatedQueue(SIZE); try { @@ -912,13 +945,30 @@ public class DelayQueueTest extends JSR166TestCase { /** * drainTo(c) empties queue into another collection c - */ + */ public void testDrainTo() { - DelayQueue q = populatedQueue(SIZE); + DelayQueue q = new DelayQueue(); + PDelay[] elems = new PDelay[SIZE]; + for (int i = 0; i < SIZE; ++i) { + elems[i] = new PDelay(i); + q.add(elems[i]); + } ArrayList l = new ArrayList(); q.drainTo(l); assertEquals(q.size(), 0); - assertEquals(l.size(), SIZE); + for (int i = 0; i < SIZE; ++i) + assertEquals(l.get(i), elems[i]); + q.add(elems[0]); + q.add(elems[1]); + assertFalse(q.isEmpty()); + assertTrue(q.contains(elems[0])); + assertTrue(q.contains(elems[1])); + l.clear(); + q.drainTo(l); + assertEquals(q.size(), 0); + assertEquals(l.size(), 2); + for (int i = 0; i < 2; ++i) + assertEquals(l.get(i), elems[i]); } /** diff --git a/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/ExecutorCompletionServiceTest.java b/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/ExecutorCompletionServiceTest.java index 2e237e256..b1988cc5b 100644 --- a/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/ExecutorCompletionServiceTest.java +++ b/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/ExecutorCompletionServiceTest.java @@ -11,6 +11,7 @@ package tests.api.java.util.concurrent; import junit.framework.*; import java.util.*; import java.util.concurrent.*; +import java.util.concurrent.atomic.*; import java.math.BigInteger; import java.security.*; @@ -88,7 +89,7 @@ public class ExecutorCompletionServiceTest extends JSR166TestCase{ Callable c = new StringTask(); ecs.submit(c); Future f = ecs.take(); - assert(f.isDone()); + assertTrue(f.isDone()); } catch (Exception ex) { unexpectedException(); } finally { @@ -128,7 +129,7 @@ public class ExecutorCompletionServiceTest extends JSR166TestCase{ for (;;) { Future f = ecs.poll(); if (f != null) { - assert(f.isDone()); + assertTrue(f.isDone()); break; } } @@ -151,12 +152,11 @@ public class ExecutorCompletionServiceTest extends JSR166TestCase{ ecs.submit(c); Future f = ecs.poll(SHORT_DELAY_MS, TimeUnit.MILLISECONDS); if (f != null) - assert(f.isDone()); + assertTrue(f.isDone()); } catch (Exception ex) { unexpectedException(); } finally { joinPool(e); } } - } diff --git a/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/ExecutorsTest.java b/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/ExecutorsTest.java index 40e23e4be..e8fc7e504 100755 --- a/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/ExecutorsTest.java +++ b/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/ExecutorsTest.java @@ -16,7 +16,7 @@ import java.security.*; public class ExecutorsTest extends JSR166TestCase{ public static void main(String[] args) { - junit.textui.TestRunner.run (suite()); + junit.textui.TestRunner.run (suite()); } public static Test suite() { return new TestSuite(ExecutorsTest.class); @@ -26,13 +26,13 @@ public class ExecutorsTest extends JSR166TestCase{ private final ExecutorService exec; private final Callable func; private final long msecs; - + TimedCallable(ExecutorService exec, Callable func, long msecs) { this.exec = exec; this.func = func; this.msecs = msecs; } - + public T call() throws Exception { Future ftask = exec.submit(func); try { @@ -295,13 +295,13 @@ public class ExecutorsTest extends JSR166TestCase{ List> tasks = new ArrayList>(N); try { long startTime = System.currentTimeMillis(); - + long i = 0; while (tasks.size() < N) { tasks.add(new TimedCallable(executor, new Fib(i), 1)); i += 10; } - + int iters = 0; BigInteger sum = BigInteger.ZERO; for (Iterator> it = tasks.iterator(); it.hasNext();) { @@ -326,7 +326,7 @@ public class ExecutorsTest extends JSR166TestCase{ } } - + /** * ThreadPoolExecutor using defaultThreadFactory has * specified group, priority, daemon status, and name @@ -335,31 +335,31 @@ public class ExecutorsTest extends JSR166TestCase{ final ThreadGroup egroup = Thread.currentThread().getThreadGroup(); Runnable r = new Runnable() { public void run() { - try { - Thread current = Thread.currentThread(); - threadAssertTrue(!current.isDaemon()); - threadAssertTrue(current.getPriority() == Thread.NORM_PRIORITY); - ThreadGroup g = current.getThreadGroup(); - SecurityManager s = System.getSecurityManager(); - if (s != null) - threadAssertTrue(g == s.getThreadGroup()); - else - threadAssertTrue(g == egroup); - String name = current.getName(); - threadAssertTrue(name.endsWith("thread-1")); - } catch (SecurityException ok) { - // Also pass if not allowed to change setting - } + try { + Thread current = Thread.currentThread(); + threadAssertTrue(!current.isDaemon()); + threadAssertTrue(current.getPriority() <= Thread.NORM_PRIORITY); + ThreadGroup g = current.getThreadGroup(); + SecurityManager s = System.getSecurityManager(); + if (s != null) + threadAssertTrue(g == s.getThreadGroup()); + else + threadAssertTrue(g == egroup); + String name = current.getName(); + threadAssertTrue(name.endsWith("thread-1")); + } catch (SecurityException ok) { + // Also pass if not allowed to change setting + } } }; ExecutorService e = Executors.newSingleThreadExecutor(Executors.defaultThreadFactory()); - + e.execute(r); try { e.shutdown(); } catch(SecurityException ok) { } - + try { Thread.sleep(SHORT_DELAY_MS); } catch (Exception eX) { @@ -390,27 +390,27 @@ public class ExecutorsTest extends JSR166TestCase{ final AccessControlContext thisacc = AccessController.getContext(); Runnable r = new Runnable() { public void run() { - try { - Thread current = Thread.currentThread(); - threadAssertTrue(!current.isDaemon()); - threadAssertTrue(current.getPriority() == Thread.NORM_PRIORITY); - ThreadGroup g = current.getThreadGroup(); - SecurityManager s = System.getSecurityManager(); - if (s != null) - threadAssertTrue(g == s.getThreadGroup()); - else - threadAssertTrue(g == egroup); - String name = current.getName(); - threadAssertTrue(name.endsWith("thread-1")); - threadAssertTrue(thisccl == current.getContextClassLoader()); - threadAssertTrue(thisacc.equals(AccessController.getContext())); - } catch(SecurityException ok) { - // Also pass if not allowed to change settings - } + try { + Thread current = Thread.currentThread(); + threadAssertTrue(!current.isDaemon()); + threadAssertTrue(current.getPriority() <= Thread.NORM_PRIORITY); + ThreadGroup g = current.getThreadGroup(); + SecurityManager s = System.getSecurityManager(); + if (s != null) + threadAssertTrue(g == s.getThreadGroup()); + else + threadAssertTrue(g == egroup); + String name = current.getName(); + threadAssertTrue(name.endsWith("thread-1")); + threadAssertTrue(thisccl == current.getContextClassLoader()); + threadAssertTrue(thisacc.equals(AccessController.getContext())); + } catch(SecurityException ok) { + // Also pass if not allowed to change settings + } } }; ExecutorService e = Executors.newSingleThreadExecutor(Executors.privilegedThreadFactory()); - + Policy.setPolicy(savedPolicy); e.execute(r); try { @@ -460,7 +460,7 @@ public class ExecutorsTest extends JSR166TestCase{ Policy.setPolicy(savedPolicy); return; } catch(AccessControlException ok) { - } + } try { Callable task = Executors.privilegedCallableUsingCurrentClassLoader(new NoOpCallable()); @@ -468,7 +468,7 @@ public class ExecutorsTest extends JSR166TestCase{ } catch(AccessControlException success) { } catch(Exception ex) { unexpectedException(); - } + } finally { Policy.setPolicy(savedPolicy); } @@ -489,13 +489,13 @@ public class ExecutorsTest extends JSR166TestCase{ } catch (AccessControlException ok) { return; } - + try { Callable task = Executors.privilegedCallableUsingCurrentClassLoader(new NoOpCallable()); task.call(); } catch(Exception ex) { unexpectedException(); - } + } finally { Policy.setPolicy(savedPolicy); } @@ -520,7 +520,7 @@ public class ExecutorsTest extends JSR166TestCase{ return; // program has too few permissions to set up test } - // Make sure that program doesn't have too many permissions + // Make sure that program doesn't have too many permissions try { AccessController.doPrivileged(new PrivilegedAction() { public Object run() { @@ -538,7 +538,7 @@ public class ExecutorsTest extends JSR166TestCase{ } catch(AccessControlException success) { } catch(Exception ex) { unexpectedException(); - } + } } /** diff --git a/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/JSR166TestCase.java b/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/JSR166TestCase.java index 70dafbc97..c900616e3 100644 --- a/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/JSR166TestCase.java +++ b/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/JSR166TestCase.java @@ -2,8 +2,8 @@ * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at * http://creativecommons.org/licenses/publicdomain - * Other contributors include Andrew Wright, Jeffrey Hayes, - * Pat Fisher, Mike Judd. + * Other contributors include Andrew Wright, Jeffrey Hayes, + * Pat Fisher, Mike Judd. */ package tests.api.java.util.concurrent; @@ -19,13 +19,13 @@ import java.security.*; * utility methods and classes, as well as a simple framework for * helping to make sure that assertions failing in generated threads * cause the associated test that generated them to itself fail (which - * JUnit doe not otherwise arrange). The rules for creating such + * JUnit does not otherwise arrange). The rules for creating such * tests are: * *

        * *
      1. All assertions in code running in generated threads must use - * the forms {@link #threadFail} , {@link #threadAssertTrue} {@link + * the forms {@link #threadFail}, {@link #threadAssertTrue}, {@link * #threadAssertEquals}, or {@link #threadAssertNull}, (not * fail, assertTrue, etc.) It is OK (but not * particularly recommended) for other code to use these forms too. @@ -46,7 +46,7 @@ import java.security.*; * is always discriminable as larger than SHORT and smaller than * MEDIUM. And so on. These constants are set to conservative values, * but even so, if there is ever any doubt, they can all be increased - * in one spot to rerun tests on slower platforms
      2. + * in one spot to rerun tests on slower platforms. * *
      3. All threads generated must be joined inside each test case * method (or fail to do so) before returning from the @@ -65,7 +65,7 @@ import java.security.*; * "normal" behaviors differ significantly. And sometimes testcases * cover multiple methods when they cannot be tested in * isolation.
      4. - * + * *
      5. The documentation style for testcases is to provide as javadoc * a simple sentence or two describing the property that the testcase * method purports to test. The javadocs do not say anything about how @@ -90,10 +90,10 @@ import java.security.*; public class JSR166TestCase extends TestCase { /** * Runs all JSR166 unit tests using junit.textui.TestRunner - */ + */ public static void main (String[] args) { int iters = 1; - if (args.length > 0) + if (args.length > 0) iters = Integer.parseInt(args[0]); Test s = suite(); for (int i = 0; i < iters; ++i) { @@ -106,7 +106,7 @@ public class JSR166TestCase extends TestCase { /** * Collects all JSR166 unit tests as one suite - */ + */ public static Test suite ( ) { TestSuite suite = tests.TestSuiteFactory.createTestSuite("JSR166 Unit Tests"); // BEGIN android-changed @@ -114,18 +114,18 @@ public class JSR166TestCase extends TestCase { suite.addTest(AbstractQueueTest.suite()); suite.addTest(AbstractQueuedSynchronizerTest.suite()); suite.addTest(ArrayBlockingQueueTest.suite()); - suite.addTest(AtomicBooleanTest.suite()); - suite.addTest(AtomicIntegerArrayTest.suite()); - suite.addTest(AtomicIntegerFieldUpdaterTest.suite()); - suite.addTest(AtomicIntegerTest.suite()); - suite.addTest(AtomicLongArrayTest.suite()); - suite.addTest(AtomicLongFieldUpdaterTest.suite()); - suite.addTest(AtomicLongTest.suite()); - suite.addTest(AtomicMarkableReferenceTest.suite()); - suite.addTest(AtomicReferenceArrayTest.suite()); - suite.addTest(AtomicReferenceFieldUpdaterTest.suite()); - suite.addTest(AtomicReferenceTest.suite()); - suite.addTest(AtomicStampedReferenceTest.suite()); + suite.addTest(AtomicBooleanTest.suite()); + suite.addTest(AtomicIntegerArrayTest.suite()); + suite.addTest(AtomicIntegerFieldUpdaterTest.suite()); + suite.addTest(AtomicIntegerTest.suite()); + suite.addTest(AtomicLongArrayTest.suite()); + suite.addTest(AtomicLongFieldUpdaterTest.suite()); + suite.addTest(AtomicLongTest.suite()); + suite.addTest(AtomicMarkableReferenceTest.suite()); + suite.addTest(AtomicReferenceArrayTest.suite()); + suite.addTest(AtomicReferenceFieldUpdaterTest.suite()); + suite.addTest(AtomicReferenceTest.suite()); + suite.addTest(AtomicStampedReferenceTest.suite()); suite.addTest(ConcurrentHashMapTest.suite()); suite.addTest(ConcurrentLinkedQueueTest.suite()); suite.addTest(CopyOnWriteArrayListTest.suite()); @@ -164,16 +164,16 @@ public class JSR166TestCase extends TestCase { /** - * Return the shortest timed delay. This could + * Returns the shortest timed delay. This could * be reimplemented to use for example a Property. - */ + */ protected long getShortDelay() { return 50; } /** - * Set delays as multiples of SHORT_DELAY. + * Sets delays as multiples of SHORT_DELAY. */ protected void setDelays() { SHORT_DELAY_MS = getShortDelay(); @@ -188,23 +188,23 @@ public class JSR166TestCase extends TestCase { volatile boolean threadFailed; /** - * Initialize test to indicate that no thread assertions have failed + * Initializes test to indicate that no thread assertions have failed */ - public void setUp() { + public void setUp() { setDelays(); - threadFailed = false; + threadFailed = false; } /** - * Trigger test case failure if any thread assertions have failed + * Triggers test case failure if any thread assertions have failed */ - public void tearDown() { - assertFalse(threadFailed); + public void tearDown() { + assertFalse(threadFailed); } /** * Fail, also setting status to indicate current testcase should fail - */ + */ public void threadFail(String reason) { threadFailed = true; fail(reason); @@ -213,7 +213,7 @@ public class JSR166TestCase extends TestCase { /** * If expression not true, set status to indicate current testcase * should fail - */ + */ public void threadAssertTrue(boolean b) { if (!b) { threadFailed = true; @@ -224,7 +224,7 @@ public class JSR166TestCase extends TestCase { /** * If expression not false, set status to indicate current testcase * should fail - */ + */ public void threadAssertFalse(boolean b) { if (b) { threadFailed = true; @@ -235,7 +235,7 @@ public class JSR166TestCase extends TestCase { /** * If argument not null, set status to indicate current testcase * should fail - */ + */ public void threadAssertNull(Object x) { if (x != null) { threadFailed = true; @@ -246,7 +246,7 @@ public class JSR166TestCase extends TestCase { /** * If arguments not equal, set status to indicate current testcase * should fail - */ + */ public void threadAssertEquals(long x, long y) { if (x != y) { threadFailed = true; @@ -257,7 +257,7 @@ public class JSR166TestCase extends TestCase { /** * If arguments not equal, set status to indicate current testcase * should fail - */ + */ public void threadAssertEquals(Object x, Object y) { if (x != y && (x == null || !x.equals(y))) { threadFailed = true; @@ -267,10 +267,15 @@ public class JSR166TestCase extends TestCase { /** * threadFail with message "should throw exception" - */ + */ public void threadShouldThrow() { - threadFailed = true; - fail("should throw exception"); + try { + threadFailed = true; + fail("should throw exception"); + } catch (AssertionFailedError e) { + e.printStackTrace(); + throw e; + } } /** @@ -281,6 +286,14 @@ public class JSR166TestCase extends TestCase { fail("Unexpected exception"); } + /** + * threadFail with message "Unexpected exception", with argument + */ + public void threadUnexpectedException(Throwable ex) { + threadFailed = true; + ex.printStackTrace(); + fail("Unexpected exception: " + ex); + } /** * Wait out termination of a thread pool or fail doing so @@ -299,7 +312,7 @@ public class JSR166TestCase extends TestCase { /** * fail with message "should throw exception" - */ + */ public void shouldThrow() { fail("Should throw exception"); } @@ -334,6 +347,7 @@ public class JSR166TestCase extends TestCase { static final Integer m3 = new Integer(-3); static final Integer m4 = new Integer(-4); static final Integer m5 = new Integer(-5); + static final Integer m6 = new Integer(-6); static final Integer m10 = new Integer(-10); @@ -389,7 +403,7 @@ public class JSR166TestCase extends TestCase { Thread.sleep(SHORT_DELAY_MS); } catch(Exception e) { - threadUnexpectedException(); + threadUnexpectedException(e); } } } @@ -411,7 +425,7 @@ public class JSR166TestCase extends TestCase { Thread.sleep(SMALL_DELAY_MS); } catch(Exception e) { - threadUnexpectedException(); + threadUnexpectedException(e); } } } @@ -432,7 +446,7 @@ public class JSR166TestCase extends TestCase { Thread.sleep(SMALL_DELAY_MS); } catch(Exception e) { - threadUnexpectedException(); + threadUnexpectedException(e); } return Boolean.TRUE; } @@ -456,7 +470,7 @@ public class JSR166TestCase extends TestCase { Thread.sleep(MEDIUM_DELAY_MS); } catch(Exception e) { - threadUnexpectedException(); + threadUnexpectedException(e); } } } @@ -498,7 +512,7 @@ public class JSR166TestCase extends TestCase { static class SimpleThreadFactory implements ThreadFactory{ public Thread newThread(Runnable r){ return new Thread(r); - } + } } static class TrackedShortRunnable implements Runnable { @@ -558,8 +572,8 @@ public class JSR166TestCase extends TestCase { * For use as RejectedExecutionHandler in constructors */ static class NoOpREHandler implements RejectedExecutionHandler{ - public void rejectedExecution(Runnable r, ThreadPoolExecutor executor){} + public void rejectedExecution(Runnable r, ThreadPoolExecutor executor){} } - - + + } diff --git a/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/LinkedBlockingQueueTest.java b/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/LinkedBlockingQueueTest.java index 7496a4a19..6648afb7e 100755 --- a/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/LinkedBlockingQueueTest.java +++ b/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/LinkedBlockingQueueTest.java @@ -16,7 +16,7 @@ import java.io.*; public class LinkedBlockingQueueTest extends JSR166TestCase { public static void main(String[] args) { - junit.textui.TestRunner.run (suite()); + junit.textui.TestRunner.run (suite()); } public static Test suite() { @@ -38,7 +38,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { assertEquals(n, q.size()); return q; } - + /** * A new queue has the indicated capacity, or Integer.MAX_VALUE if * none given @@ -151,7 +151,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { LinkedBlockingQueue q = new LinkedBlockingQueue(1); q.offer(null); shouldThrow(); - } catch (NullPointerException success) { } + } catch (NullPointerException success) { } } /** @@ -162,7 +162,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { LinkedBlockingQueue q = new LinkedBlockingQueue(1); q.add(null); shouldThrow(); - } catch (NullPointerException success) { } + } catch (NullPointerException success) { } } /** @@ -186,7 +186,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { assertEquals(0, q.remainingCapacity()); q.add(new Integer(SIZE)); } catch (IllegalStateException success){ - } + } } /** @@ -280,9 +280,9 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { LinkedBlockingQueue q = new LinkedBlockingQueue(SIZE); q.put(null); shouldThrow(); - } + } catch (NullPointerException success){ - } + } catch (InterruptedException ie) { unexpectedException(); } @@ -323,11 +323,11 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { threadShouldThrow(); } catch (InterruptedException ie){ threadAssertEquals(added, SIZE); - } + } }}); t.start(); - try { - Thread.sleep(SHORT_DELAY_MS); + try { + Thread.sleep(SHORT_DELAY_MS); t.interrupt(); t.join(); } @@ -386,7 +386,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { } catch (InterruptedException success){} } }); - + try { t.start(); Thread.sleep(SMALL_DELAY_MS); @@ -408,7 +408,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { } } catch (InterruptedException e){ unexpectedException(); - } + } } /** @@ -421,7 +421,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { try { q.take(); threadShouldThrow(); - } catch (InterruptedException success){ } + } catch (InterruptedException success){ } } }); try { @@ -448,11 +448,11 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { q.take(); threadShouldThrow(); } catch (InterruptedException success){ - } + } }}); t.start(); - try { - Thread.sleep(SHORT_DELAY_MS); + try { + Thread.sleep(SHORT_DELAY_MS); t.interrupt(); t.join(); } @@ -485,7 +485,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { assertNull(q.poll(0, TimeUnit.MILLISECONDS)); } catch (InterruptedException e){ unexpectedException(); - } + } } /** @@ -500,7 +500,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { assertNull(q.poll(SHORT_DELAY_MS, TimeUnit.MILLISECONDS)); } catch (InterruptedException e){ unexpectedException(); - } + } } /** @@ -517,11 +517,11 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { } threadAssertNull(q.poll(SHORT_DELAY_MS, TimeUnit.MILLISECONDS)); } catch (InterruptedException success){ - } + } }}); t.start(); - try { - Thread.sleep(SHORT_DELAY_MS); + try { + Thread.sleep(SHORT_DELAY_MS); t.interrupt(); t.join(); } @@ -543,7 +543,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { q.poll(LONG_DELAY_MS, TimeUnit.MILLISECONDS); q.poll(LONG_DELAY_MS, TimeUnit.MILLISECONDS); threadShouldThrow(); - } catch (InterruptedException success) { } + } catch (InterruptedException success) { } } }); try { @@ -555,7 +555,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { } catch (Exception e){ unexpectedException(); } - } + } /** * peek returns next element, or null if empty @@ -599,7 +599,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { q.remove(); shouldThrow(); } catch (NoSuchElementException success){ - } + } } /** @@ -616,7 +616,24 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { } assertTrue(q.isEmpty()); } - + + /** + * An add following remove(x) succeeds + */ + public void testRemoveElementAndAdd() { + try { + LinkedBlockingQueue q = new LinkedBlockingQueue(); + assertTrue(q.add(new Integer(1))); + assertTrue(q.add(new Integer(2))); + assertTrue(q.remove(new Integer(1))); + assertTrue(q.remove(new Integer(2))); + assertTrue(q.add(new Integer(3))); + assertTrue(q.take() != null); + } catch (Exception e){ + unexpectedException(); + } + } + /** * contains(x) reports true when elements added but not yet removed */ @@ -640,6 +657,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { assertEquals(SIZE, q.remainingCapacity()); q.add(one); assertFalse(q.isEmpty()); + assertTrue(q.contains(one)); q.clear(); assertTrue(q.isEmpty()); } @@ -704,7 +722,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { assertEquals(o[i], q.take()); } catch (InterruptedException e){ unexpectedException(); - } + } } /** @@ -719,7 +737,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { assertEquals(ints[i], q.take()); } catch (InterruptedException e){ unexpectedException(); - } + } } /** @@ -744,7 +762,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { } catch(ArrayStoreException success){} } - + /** * iterator iterates through all elements */ @@ -757,7 +775,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { } } catch (InterruptedException e){ unexpectedException(); - } + } } /** @@ -772,7 +790,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { Iterator it = q.iterator(); it.next(); it.remove(); - + it = q.iterator(); assertEquals(it.next(), one); assertEquals(it.next(), three); @@ -827,7 +845,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { for (int i = 0; i < SIZE; ++i) { assertTrue(s.indexOf(String.valueOf(i)) >= 0); } - } + } /** @@ -862,7 +880,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { } } }); - + joinPool(executor); } @@ -896,7 +914,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { } } }); - + joinPool(executor); } @@ -916,7 +934,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { ObjectInputStream in = new ObjectInputStream(new BufferedInputStream(bin)); LinkedBlockingQueue r = (LinkedBlockingQueue)in.readObject(); assertEquals(q.size(), r.size()); - while (!q.isEmpty()) + while (!q.isEmpty()) assertEquals(q.remove(), r.remove()); } catch(Exception e){ unexpectedException(); @@ -925,7 +943,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { /** * drainTo(null) throws NPE - */ + */ public void testDrainToNull() { LinkedBlockingQueue q = populatedQueue(SIZE); try { @@ -937,7 +955,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { /** * drainTo(this) throws IAE - */ + */ public void testDrainToSelf() { LinkedBlockingQueue q = populatedQueue(SIZE); try { @@ -949,27 +967,38 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { /** * drainTo(c) empties queue into another collection c - */ + */ public void testDrainTo() { LinkedBlockingQueue q = populatedQueue(SIZE); ArrayList l = new ArrayList(); q.drainTo(l); assertEquals(q.size(), 0); assertEquals(l.size(), SIZE); - for (int i = 0; i < SIZE; ++i) + for (int i = 0; i < SIZE; ++i) + assertEquals(l.get(i), new Integer(i)); + q.add(zero); + q.add(one); + assertFalse(q.isEmpty()); + assertTrue(q.contains(zero)); + assertTrue(q.contains(one)); + l.clear(); + q.drainTo(l); + assertEquals(q.size(), 0); + assertEquals(l.size(), 2); + for (int i = 0; i < 2; ++i) assertEquals(l.get(i), new Integer(i)); } /** * drainTo empties full queue, unblocking a waiting put. - */ + */ public void testDrainToWithActivePut() { final LinkedBlockingQueue q = populatedQueue(SIZE); Thread t = new Thread(new Runnable() { public void run() { try { q.put(new Integer(SIZE+1)); - } catch (InterruptedException ie){ + } catch (InterruptedException ie){ threadUnexpectedException(); } } @@ -979,7 +1008,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { ArrayList l = new ArrayList(); q.drainTo(l); assertTrue(l.size() >= SIZE); - for (int i = 0; i < SIZE; ++i) + for (int i = 0; i < SIZE; ++i) assertEquals(l.get(i), new Integer(i)); t.join(); assertTrue(q.size() + l.size() >= SIZE); @@ -990,7 +1019,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { /** * drainTo(null, n) throws NPE - */ + */ public void testDrainToNullN() { LinkedBlockingQueue q = populatedQueue(SIZE); try { @@ -1002,7 +1031,7 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { /** * drainTo(this, n) throws IAE - */ + */ public void testDrainToSelfN() { LinkedBlockingQueue q = populatedQueue(SIZE); try { @@ -1014,17 +1043,20 @@ public class LinkedBlockingQueueTest extends JSR166TestCase { /** * drainTo(c, n) empties first max {n, size} elements of queue into c - */ + */ public void testDrainToN() { + LinkedBlockingQueue q = new LinkedBlockingQueue(); for (int i = 0; i < SIZE + 2; ++i) { - LinkedBlockingQueue q = populatedQueue(SIZE); + for(int j = 0; j < SIZE; j++) + assertTrue(q.offer(new Integer(j))); ArrayList l = new ArrayList(); q.drainTo(l, i); int k = (i < SIZE)? i : SIZE; - assertEquals(q.size(), SIZE-k); assertEquals(l.size(), k); - for (int j = 0; j < k; ++j) + assertEquals(q.size(), SIZE-k); + for (int j = 0; j < k; ++j) assertEquals(l.get(j), new Integer(j)); + while (q.poll() != null) ; } } diff --git a/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/LockSupportTest.java b/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/LockSupportTest.java index 8d74a3aa6..b39db2eda 100755 --- a/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/LockSupportTest.java +++ b/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/LockSupportTest.java @@ -15,7 +15,7 @@ import java.util.concurrent.locks.*; public class LockSupportTest extends JSR166TestCase{ public static void main(String[] args) { - junit.textui.TestRunner.run (suite()); + junit.textui.TestRunner.run (suite()); } public static Test suite() { return new TestSuite(LockSupportTest.class); @@ -24,7 +24,7 @@ public class LockSupportTest extends JSR166TestCase{ /** * park is released by unpark occurring after park */ - public void testPark() { + public void testPark() { Thread t = new Thread(new Runnable() { public void run() { try { @@ -48,7 +48,7 @@ public class LockSupportTest extends JSR166TestCase{ /** * park is released by unpark occurring before park */ - public void testPark2() { + public void testPark2() { Thread t = new Thread(new Runnable() { public void run() { try { @@ -70,15 +70,14 @@ public class LockSupportTest extends JSR166TestCase{ } /** - * park is released by interrupt + * park is released by interrupt */ - public void testPark3() { - Thread t = new Thread(new Runnable() { - public void run() { - try { - LockSupport.park(); - threadAssertTrue(Thread.interrupted()); - } catch(Exception e){ + public void testPark3() { + Thread t = new Thread(new Runnable() { + public void run() { + try { + LockSupport.park(); + } catch(Exception e){ threadUnexpectedException(); } } @@ -97,7 +96,7 @@ public class LockSupportTest extends JSR166TestCase{ /** * park returns if interrupted before park */ - public void testPark4() { + public void testPark4() { final ReentrantLock lock = new ReentrantLock(); lock.lock(); Thread t = new Thread(new Runnable() { @@ -124,7 +123,7 @@ public class LockSupportTest extends JSR166TestCase{ /** * parkNanos times out if not unparked */ - public void testParkNanos() { + public void testParkNanos() { Thread t = new Thread(new Runnable() { public void run() { try { @@ -147,7 +146,7 @@ public class LockSupportTest extends JSR166TestCase{ /** * parkUntil times out if not unparked */ - public void testParkUntil() { + public void testParkUntil() { Thread t = new Thread(new Runnable() { public void run() { try { diff --git a/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/PriorityBlockingQueueTest.java b/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/PriorityBlockingQueueTest.java index b25896384..3857e0f13 100755 --- a/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/PriorityBlockingQueueTest.java +++ b/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/PriorityBlockingQueueTest.java @@ -628,9 +628,9 @@ public class PriorityBlockingQueueTest extends JSR166TestCase { q.clear(); assertTrue(q.isEmpty()); assertEquals(0, q.size()); - assertEquals(NOCAP, q.remainingCapacity()); - q.add(new Integer(1)); + q.add(one); assertFalse(q.isEmpty()); + assertTrue(q.contains(one)); q.clear(); assertTrue(q.isEmpty()); } @@ -873,6 +873,17 @@ public class PriorityBlockingQueueTest extends JSR166TestCase { assertEquals(l.size(), SIZE); for (int i = 0; i < SIZE; ++i) assertEquals(l.get(i), new Integer(i)); + q.add(zero); + q.add(one); + assertFalse(q.isEmpty()); + assertTrue(q.contains(zero)); + assertTrue(q.contains(one)); + l.clear(); + q.drainTo(l); + assertEquals(q.size(), 0); + assertEquals(l.size(), 2); + for (int i = 0; i < 2; ++i) + assertEquals(l.get(i), new Integer(i)); } /** @@ -927,15 +938,18 @@ public class PriorityBlockingQueueTest extends JSR166TestCase { * drainTo(c, n) empties first max {n, size} elements of queue into c */ public void testDrainToN() { + PriorityBlockingQueue q = new PriorityBlockingQueue(SIZE*2); for (int i = 0; i < SIZE + 2; ++i) { - PriorityBlockingQueue q = populatedQueue(SIZE); + for(int j = 0; j < SIZE; j++) + assertTrue(q.offer(new Integer(j))); ArrayList l = new ArrayList(); q.drainTo(l, i); int k = (i < SIZE)? i : SIZE; - assertEquals(q.size(), SIZE-k); assertEquals(l.size(), k); + assertEquals(q.size(), SIZE-k); for (int j = 0; j < k; ++j) - assertTrue(l.contains(new Integer(j))); + assertEquals(l.get(j), new Integer(j)); + while (q.poll() != null) ; } } diff --git a/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/ReentrantLockTest.java b/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/ReentrantLockTest.java index fdc9f31c0..c50482d1b 100755 --- a/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/ReentrantLockTest.java +++ b/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/ReentrantLockTest.java @@ -16,7 +16,7 @@ import java.io.*; public class ReentrantLockTest extends JSR166TestCase { public static void main(String[] args) { - junit.textui.TestRunner.run (suite()); + junit.textui.TestRunner.run (suite()); } public static Test suite() { return new TestSuite(ReentrantLockTest.class); @@ -56,11 +56,11 @@ public class ReentrantLockTest extends JSR166TestCase { */ static class PublicReentrantLock extends ReentrantLock { PublicReentrantLock() { super(); } - public Collection getQueuedThreads() { - return super.getQueuedThreads(); + public Collection getQueuedThreads() { + return super.getQueuedThreads(); } - public Collection getWaitingThreads(Condition c) { - return super.getWaitingThreads(c); + public Collection getWaitingThreads(Condition c) { + return super.getWaitingThreads(c); } @@ -69,7 +69,7 @@ public class ReentrantLockTest extends JSR166TestCase { /** * Constructor sets given fairness */ - public void testConstructor() { + public void testConstructor() { ReentrantLock rl = new ReentrantLock(); assertFalse(rl.isFair()); ReentrantLock r2 = new ReentrantLock(true); @@ -79,7 +79,7 @@ public class ReentrantLockTest extends JSR166TestCase { /** * locking an unlocked lock succeeds */ - public void testLock() { + public void testLock() { ReentrantLock rl = new ReentrantLock(); rl.lock(); assertTrue(rl.isLocked()); @@ -89,7 +89,7 @@ public class ReentrantLockTest extends JSR166TestCase { /** * locking an unlocked fair lock succeeds */ - public void testFairLock() { + public void testFairLock() { ReentrantLock rl = new ReentrantLock(true); rl.lock(); assertTrue(rl.isLocked()); @@ -99,7 +99,7 @@ public class ReentrantLockTest extends JSR166TestCase { /** * Unlocking an unlocked lock throws IllegalMonitorStateException */ - public void testUnlock_IllegalMonitorStateException() { + public void testUnlock_IllegalMonitorStateException() { ReentrantLock rl = new ReentrantLock(); try { rl.unlock(); @@ -111,7 +111,7 @@ public class ReentrantLockTest extends JSR166TestCase { /** * tryLock on an unlocked lock succeeds */ - public void testTryLock() { + public void testTryLock() { ReentrantLock rl = new ReentrantLock(); assertTrue(rl.tryLock()); assertTrue(rl.isLocked()); @@ -122,7 +122,7 @@ public class ReentrantLockTest extends JSR166TestCase { /** * hasQueuedThreads reports whether there are waiting threads */ - public void testhasQueuedThreads() { + public void testhasQueuedThreads() { final ReentrantLock lock = new ReentrantLock(); Thread t1 = new Thread(new InterruptedLockRunnable(lock)); Thread t2 = new Thread(new InterruptibleLockRunnable(lock)); @@ -146,12 +146,12 @@ public class ReentrantLockTest extends JSR166TestCase { } catch(Exception e){ unexpectedException(); } - } + } /** * getQueueLength reports number of waiting threads */ - public void testGetQueueLength() { + public void testGetQueueLength() { final ReentrantLock lock = new ReentrantLock(); Thread t1 = new Thread(new InterruptedLockRunnable(lock)); Thread t2 = new Thread(new InterruptibleLockRunnable(lock)); @@ -175,12 +175,12 @@ public class ReentrantLockTest extends JSR166TestCase { } catch(Exception e){ unexpectedException(); } - } + } /** * getQueueLength reports number of waiting threads */ - public void testGetQueueLength_fair() { + public void testGetQueueLength_fair() { final ReentrantLock lock = new ReentrantLock(true); Thread t1 = new Thread(new InterruptedLockRunnable(lock)); Thread t2 = new Thread(new InterruptibleLockRunnable(lock)); @@ -204,12 +204,12 @@ public class ReentrantLockTest extends JSR166TestCase { } catch(Exception e){ unexpectedException(); } - } + } /** * hasQueuedThread(null) throws NPE */ - public void testHasQueuedThreadNPE() { + public void testHasQueuedThreadNPE() { final ReentrantLock sync = new ReentrantLock(); try { sync.hasQueuedThread(null); @@ -221,7 +221,7 @@ public class ReentrantLockTest extends JSR166TestCase { /** * hasQueuedThread reports whether a thread is queued. */ - public void testHasQueuedThread() { + public void testHasQueuedThread() { final ReentrantLock sync = new ReentrantLock(); Thread t1 = new Thread(new InterruptedLockRunnable(sync)); Thread t2 = new Thread(new InterruptibleLockRunnable(sync)); @@ -250,13 +250,13 @@ public class ReentrantLockTest extends JSR166TestCase { } catch(Exception e){ unexpectedException(); } - } + } /** * getQueuedThreads includes waiting threads */ - public void testGetQueuedThreads() { + public void testGetQueuedThreads() { final PublicReentrantLock lock = new PublicReentrantLock(); Thread t1 = new Thread(new InterruptedLockRunnable(lock)); Thread t2 = new Thread(new InterruptibleLockRunnable(lock)); @@ -283,13 +283,13 @@ public class ReentrantLockTest extends JSR166TestCase { } catch(Exception e){ unexpectedException(); } - } + } /** * timed tryLock is interruptible. */ - public void testInterruptedException2() { + public void testInterruptedException2() { final ReentrantLock lock = new ReentrantLock(); lock.lock(); Thread t = new Thread(new Runnable() { @@ -312,7 +312,7 @@ public class ReentrantLockTest extends JSR166TestCase { /** * TryLock on a locked lock fails */ - public void testTryLockWhenLocked() { + public void testTryLockWhenLocked() { final ReentrantLock lock = new ReentrantLock(); lock.lock(); Thread t = new Thread(new Runnable() { @@ -327,12 +327,12 @@ public class ReentrantLockTest extends JSR166TestCase { } catch(Exception e){ unexpectedException(); } - } + } /** * Timed tryLock on a locked lock times out */ - public void testTryLock_Timeout() { + public void testTryLock_Timeout() { final ReentrantLock lock = new ReentrantLock(); lock.lock(); Thread t = new Thread(new Runnable() { @@ -351,8 +351,8 @@ public class ReentrantLockTest extends JSR166TestCase { } catch(Exception e){ unexpectedException(); } - } - + } + /** * getHoldCount returns number of recursive holds */ @@ -367,8 +367,8 @@ public class ReentrantLockTest extends JSR166TestCase { assertEquals(i-1,lock.getHoldCount()); } } - - + + /** * isLocked is true when locked and false when not */ @@ -378,7 +378,7 @@ public class ReentrantLockTest extends JSR166TestCase { assertTrue(lock.isLocked()); lock.unlock(); assertFalse(lock.isLocked()); - Thread t = new Thread(new Runnable() { + Thread t = new Thread(new Runnable() { public void run() { lock.lock(); try { @@ -405,25 +405,27 @@ public class ReentrantLockTest extends JSR166TestCase { /** * lockInterruptibly is interruptible. */ - public void testLockInterruptibly1() { + public void testLockInterruptibly1() { final ReentrantLock lock = new ReentrantLock(); lock.lock(); Thread t = new Thread(new InterruptedLockRunnable(lock)); try { t.start(); + Thread.sleep(SHORT_DELAY_MS); t.interrupt(); + Thread.sleep(SHORT_DELAY_MS); lock.unlock(); t.join(); } catch(Exception e){ unexpectedException(); } - } + } /** * lockInterruptibly succeeds when unlocked, else is interruptible */ public void testLockInterruptibly2() { - final ReentrantLock lock = new ReentrantLock(); + final ReentrantLock lock = new ReentrantLock(); try { lock.lockInterruptibly(); } catch(Exception e) { @@ -445,7 +447,7 @@ public class ReentrantLockTest extends JSR166TestCase { * Calling await without holding lock throws IllegalMonitorStateException */ public void testAwait_IllegalMonitor() { - final ReentrantLock lock = new ReentrantLock(); + final ReentrantLock lock = new ReentrantLock(); final Condition c = lock.newCondition(); try { c.await(); @@ -462,7 +464,7 @@ public class ReentrantLockTest extends JSR166TestCase { * Calling signal without holding lock throws IllegalMonitorStateException */ public void testSignal_IllegalMonitor() { - final ReentrantLock lock = new ReentrantLock(); + final ReentrantLock lock = new ReentrantLock(); final Condition c = lock.newCondition(); try { c.signal(); @@ -479,7 +481,7 @@ public class ReentrantLockTest extends JSR166TestCase { * awaitNanos without a signal times out */ public void testAwaitNanos_Timeout() { - final ReentrantLock lock = new ReentrantLock(); + final ReentrantLock lock = new ReentrantLock(); final Condition c = lock.newCondition(); try { lock.lock(); @@ -496,11 +498,11 @@ public class ReentrantLockTest extends JSR166TestCase { * timed await without a signal times out */ public void testAwait_Timeout() { - final ReentrantLock lock = new ReentrantLock(); + final ReentrantLock lock = new ReentrantLock(); final Condition c = lock.newCondition(); try { lock.lock(); - assertFalse(c.await(SHORT_DELAY_MS, TimeUnit.MILLISECONDS)); + c.await(SHORT_DELAY_MS, TimeUnit.MILLISECONDS); lock.unlock(); } catch (Exception ex) { @@ -512,12 +514,12 @@ public class ReentrantLockTest extends JSR166TestCase { * awaitUntil without a signal times out */ public void testAwaitUntil_Timeout() { - final ReentrantLock lock = new ReentrantLock(); + final ReentrantLock lock = new ReentrantLock(); final Condition c = lock.newCondition(); try { lock.lock(); java.util.Date d = new java.util.Date(); - assertFalse(c.awaitUntil(new java.util.Date(d.getTime() + 10))); + c.awaitUntil(new java.util.Date(d.getTime() + 10)); lock.unlock(); } catch (Exception ex) { @@ -529,9 +531,9 @@ public class ReentrantLockTest extends JSR166TestCase { * await returns when signalled */ public void testAwait() { - final ReentrantLock lock = new ReentrantLock(); + final ReentrantLock lock = new ReentrantLock(); final Condition c = lock.newCondition(); - Thread t = new Thread(new Runnable() { + Thread t = new Thread(new Runnable() { public void run() { try { lock.lock(); @@ -670,9 +672,9 @@ public class ReentrantLockTest extends JSR166TestCase { * getWaitingThreads throws IAE if not owned */ public void testGetWaitingThreadsIAE() { - final PublicReentrantLock lock = new PublicReentrantLock(); + final PublicReentrantLock lock = new PublicReentrantLock(); final Condition c = (lock.newCondition()); - final PublicReentrantLock lock2 = new PublicReentrantLock(); + final PublicReentrantLock lock2 = new PublicReentrantLock(); try { lock2.getWaitingThreads(c); shouldThrow(); @@ -686,7 +688,7 @@ public class ReentrantLockTest extends JSR166TestCase { * getWaitingThreads throws IMSE if not locked */ public void testGetWaitingThreadsIMSE() { - final PublicReentrantLock lock = new PublicReentrantLock(); + final PublicReentrantLock lock = new PublicReentrantLock(); final Condition c = (lock.newCondition()); try { lock.getWaitingThreads(c); @@ -703,9 +705,9 @@ public class ReentrantLockTest extends JSR166TestCase { * hasWaiters returns true when a thread is waiting, else false */ public void testHasWaiters() { - final ReentrantLock lock = new ReentrantLock(); + final ReentrantLock lock = new ReentrantLock(); final Condition c = lock.newCondition(); - Thread t = new Thread(new Runnable() { + Thread t = new Thread(new Runnable() { public void run() { try { lock.lock(); @@ -745,9 +747,9 @@ public class ReentrantLockTest extends JSR166TestCase { * getWaitQueueLength returns number of waiting threads */ public void testGetWaitQueueLength() { - final ReentrantLock lock = new ReentrantLock(); + final ReentrantLock lock = new ReentrantLock(); final Condition c = lock.newCondition(); - Thread t1 = new Thread(new Runnable() { + Thread t1 = new Thread(new Runnable() { public void run() { try { lock.lock(); @@ -762,7 +764,7 @@ public class ReentrantLockTest extends JSR166TestCase { } }); - Thread t2 = new Thread(new Runnable() { + Thread t2 = new Thread(new Runnable() { public void run() { try { lock.lock(); @@ -806,9 +808,9 @@ public class ReentrantLockTest extends JSR166TestCase { * getWaitingThreads returns only and all waiting threads */ public void testGetWaitingThreads() { - final PublicReentrantLock lock = new PublicReentrantLock(); + final PublicReentrantLock lock = new PublicReentrantLock(); final Condition c = lock.newCondition(); - Thread t1 = new Thread(new Runnable() { + Thread t1 = new Thread(new Runnable() { public void run() { try { lock.lock(); @@ -822,7 +824,7 @@ public class ReentrantLockTest extends JSR166TestCase { } }); - Thread t2 = new Thread(new Runnable() { + Thread t2 = new Thread(new Runnable() { public void run() { try { lock.lock(); @@ -865,34 +867,61 @@ public class ReentrantLockTest extends JSR166TestCase { } } + /** A helper class for uninterruptible wait tests */ + class UninterruptableThread extends Thread { + private ReentrantLock lock; + private Condition c; + + public volatile boolean canAwake = false; + public volatile boolean interrupted = false; + public volatile boolean lockStarted = false; + + public UninterruptableThread(ReentrantLock lock, Condition c) { + this.lock = lock; + this.c = c; + } + + public synchronized void run() { + lock.lock(); + lockStarted = true; + while (!canAwake) { + c.awaitUninterruptibly(); + } + + interrupted = isInterrupted(); + lock.unlock(); + } + } /** * awaitUninterruptibly doesn't abort on interrupt */ public void testAwaitUninterruptibly() { - final ReentrantLock lock = new ReentrantLock(); + final ReentrantLock lock = new ReentrantLock(); final Condition c = lock.newCondition(); - Thread t = new Thread(new Runnable() { - public void run() { - lock.lock(); - c.awaitUninterruptibly(); - lock.unlock(); - } - }); + UninterruptableThread thread = new UninterruptableThread(lock, c); try { - t.start(); - Thread.sleep(SHORT_DELAY_MS); - t.interrupt(); + thread.start(); + + while (!thread.lockStarted) { + Thread.sleep(100); + } + lock.lock(); - c.signal(); - lock.unlock(); - assert(t.isInterrupted()); - t.join(SHORT_DELAY_MS); - assertFalse(t.isAlive()); - } - catch (Exception ex) { + try { + thread.interrupt(); + thread.canAwake = true; + c.signal(); + } finally { + lock.unlock(); + } + + thread.join(); + assertTrue(thread.interrupted); + assertFalse(thread.isAlive()); + } catch (Exception ex) { unexpectedException(); } } @@ -901,9 +930,9 @@ public class ReentrantLockTest extends JSR166TestCase { * await is interruptible */ public void testAwait_Interrupt() { - final ReentrantLock lock = new ReentrantLock(); + final ReentrantLock lock = new ReentrantLock(); final Condition c = lock.newCondition(); - Thread t = new Thread(new Runnable() { + Thread t = new Thread(new Runnable() { public void run() { try { lock.lock(); @@ -932,9 +961,9 @@ public class ReentrantLockTest extends JSR166TestCase { * awaitNanos is interruptible */ public void testAwaitNanos_Interrupt() { - final ReentrantLock lock = new ReentrantLock(); + final ReentrantLock lock = new ReentrantLock(); final Condition c = lock.newCondition(); - Thread t = new Thread(new Runnable() { + Thread t = new Thread(new Runnable() { public void run() { try { lock.lock(); @@ -963,9 +992,9 @@ public class ReentrantLockTest extends JSR166TestCase { * awaitUntil is interruptible */ public void testAwaitUntil_Interrupt() { - final ReentrantLock lock = new ReentrantLock(); + final ReentrantLock lock = new ReentrantLock(); final Condition c = lock.newCondition(); - Thread t = new Thread(new Runnable() { + Thread t = new Thread(new Runnable() { public void run() { try { lock.lock(); @@ -995,9 +1024,9 @@ public class ReentrantLockTest extends JSR166TestCase { * signalAll wakes up all threads */ public void testSignalAll() { - final ReentrantLock lock = new ReentrantLock(); + final ReentrantLock lock = new ReentrantLock(); final Condition c = lock.newCondition(); - Thread t1 = new Thread(new Runnable() { + Thread t1 = new Thread(new Runnable() { public void run() { try { lock.lock(); @@ -1010,7 +1039,7 @@ public class ReentrantLockTest extends JSR166TestCase { } }); - Thread t2 = new Thread(new Runnable() { + Thread t2 = new Thread(new Runnable() { public void run() { try { lock.lock(); @@ -1040,6 +1069,61 @@ public class ReentrantLockTest extends JSR166TestCase { } } + /** + * await after multiple reentrant locking preserves lock count + */ + public void testAwaitLockCount() { + final ReentrantLock lock = new ReentrantLock(); + final Condition c = lock.newCondition(); + Thread t1 = new Thread(new Runnable() { + public void run() { + try { + lock.lock(); + threadAssertEquals(1, lock.getHoldCount()); + c.await(); + threadAssertEquals(1, lock.getHoldCount()); + lock.unlock(); + } + catch(InterruptedException e) { + threadUnexpectedException(); + } + } + }); + + Thread t2 = new Thread(new Runnable() { + public void run() { + try { + lock.lock(); + lock.lock(); + threadAssertEquals(2, lock.getHoldCount()); + c.await(); + threadAssertEquals(2, lock.getHoldCount()); + lock.unlock(); + lock.unlock(); + } + catch(InterruptedException e) { + threadUnexpectedException(); + } + } + }); + + try { + t1.start(); + t2.start(); + Thread.sleep(SHORT_DELAY_MS); + lock.lock(); + c.signalAll(); + lock.unlock(); + t1.join(SHORT_DELAY_MS); + t2.join(SHORT_DELAY_MS); + assertFalse(t1.isAlive()); + assertFalse(t2.isAlive()); + } + catch (Exception ex) { + unexpectedException(); + } + } + /** * A serialized lock deserializes as unlocked */ diff --git a/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/ReentrantReadWriteLockTest.java b/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/ReentrantReadWriteLockTest.java index 8925b4247..e38165ad4 100644 --- a/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/ReentrantReadWriteLockTest.java +++ b/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/ReentrantReadWriteLockTest.java @@ -16,7 +16,7 @@ import java.util.*; public class ReentrantReadWriteLockTest extends JSR166TestCase { public static void main(String[] args) { - junit.textui.TestRunner.run (suite()); + junit.textui.TestRunner.run (suite()); } public static Test suite() { return new TestSuite(ReentrantReadWriteLockTest.class); @@ -56,18 +56,18 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { */ static class PublicReentrantReadWriteLock extends ReentrantReadWriteLock { PublicReentrantReadWriteLock() { super(); } - public Collection getQueuedThreads() { - return super.getQueuedThreads(); + public Collection getQueuedThreads() { + return super.getQueuedThreads(); } - public Collection getWaitingThreads(Condition c) { - return super.getWaitingThreads(c); + public Collection getWaitingThreads(Condition c) { + return super.getWaitingThreads(c); } } /** * Constructor sets given fairness, and is in unlocked state */ - public void testConstructor() { + public void testConstructor() { ReentrantReadWriteLock rl = new ReentrantReadWriteLock(); assertFalse(rl.isFair()); assertFalse(rl.isWriteLocked()); @@ -81,7 +81,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { /** * write-locking and read-locking an unlocked lock succeed */ - public void testLock() { + public void testLock() { ReentrantReadWriteLock rl = new ReentrantReadWriteLock(); rl.writeLock().lock(); assertTrue(rl.isWriteLocked()); @@ -105,7 +105,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { /** * locking an unlocked fair lock succeeds */ - public void testFairLock() { + public void testFairLock() { ReentrantReadWriteLock rl = new ReentrantReadWriteLock(true); rl.writeLock().lock(); assertTrue(rl.isWriteLocked()); @@ -128,23 +128,23 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { /** * getWriteHoldCount returns number of recursive holds */ - public void testGetHoldCount() { - ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); - for(int i = 1; i <= SIZE; i++) { - lock.writeLock().lock(); - assertEquals(i,lock.getWriteHoldCount()); - } - for(int i = SIZE; i > 0; i--) { - lock.writeLock().unlock(); - assertEquals(i-1,lock.getWriteHoldCount()); - } + public void testGetWriteHoldCount() { + ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + for(int i = 1; i <= SIZE; i++) { + lock.writeLock().lock(); + assertEquals(i,lock.getWriteHoldCount()); + } + for(int i = SIZE; i > 0; i--) { + lock.writeLock().unlock(); + assertEquals(i-1,lock.getWriteHoldCount()); + } } - + /** * write-unlocking an unlocked lock throws IllegalMonitorStateException */ - public void testUnlock_IllegalMonitorStateException() { + public void testUnlock_IllegalMonitorStateException() { ReentrantReadWriteLock rl = new ReentrantReadWriteLock(); try { rl.writeLock().unlock(); @@ -156,7 +156,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { /** * write-lockInterruptibly is interruptible */ - public void testWriteLockInterruptibly_Interrupted() { + public void testWriteLockInterruptibly_Interrupted() { final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); Thread t = new Thread(new Runnable() { public void run() { @@ -171,18 +171,20 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { try { lock.writeLock().lock(); t.start(); + Thread.sleep(SHORT_DELAY_MS); t.interrupt(); + Thread.sleep(SHORT_DELAY_MS); lock.writeLock().unlock(); t.join(); } catch(Exception e){ unexpectedException(); } - } + } /** * timed write-tryLock is interruptible */ - public void testWriteTryLock_Interrupted() { + public void testWriteTryLock_Interrupted() { final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); lock.writeLock().lock(); Thread t = new Thread(new Runnable() { @@ -205,7 +207,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { /** * read-lockInterruptibly is interruptible */ - public void testReadLockInterruptibly_Interrupted() { + public void testReadLockInterruptibly_Interrupted() { final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); lock.writeLock().lock(); Thread t = new Thread(new Runnable() { @@ -217,18 +219,20 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { }); try { t.start(); + Thread.sleep(SHORT_DELAY_MS); t.interrupt(); + Thread.sleep(SHORT_DELAY_MS); lock.writeLock().unlock(); t.join(); } catch(Exception e){ unexpectedException(); } - } + } /** * timed read-tryLock is interruptible */ - public void testReadTryLock_Interrupted() { + public void testReadTryLock_Interrupted() { final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); lock.writeLock().lock(); Thread t = new Thread(new Runnable() { @@ -248,11 +252,11 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { } } - + /** * write-tryLock fails if locked */ - public void testWriteTryLockWhenLocked() { + public void testWriteTryLockWhenLocked() { final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); lock.writeLock().lock(); Thread t = new Thread(new Runnable() { @@ -267,12 +271,12 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { } catch(Exception e){ unexpectedException(); } - } + } /** * read-tryLock fails if locked */ - public void testReadTryLockWhenLocked() { + public void testReadTryLockWhenLocked() { final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); lock.writeLock().lock(); Thread t = new Thread(new Runnable() { @@ -287,12 +291,12 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { } catch(Exception e){ unexpectedException(); } - } + } /** * Multiple threads can hold a read lock when not write-locked */ - public void testMultipleReadLocks() { + public void testMultipleReadLocks() { final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); lock.readLock().lock(); Thread t = new Thread(new Runnable() { @@ -308,12 +312,12 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { } catch(Exception e){ unexpectedException(); } - } + } /** * A writelock succeeds after reading threads unlock */ - public void testWriteAfterMultipleReadLocks() { + public void testWriteAfterMultipleReadLocks() { final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); lock.readLock().lock(); Thread t1 = new Thread(new Runnable() { @@ -338,16 +342,16 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { t2.join(MEDIUM_DELAY_MS); assertTrue(!t1.isAlive()); assertTrue(!t2.isAlive()); - + } catch(Exception e){ unexpectedException(); } - } + } /** * Readlocks succeed after a writing thread unlocks */ - public void testReadAfterWriteLock() { + public void testReadAfterWriteLock() { final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); lock.writeLock().lock(); Thread t1 = new Thread(new Runnable() { @@ -372,17 +376,280 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { t2.join(MEDIUM_DELAY_MS); assertTrue(!t1.isAlive()); assertTrue(!t2.isAlive()); - + + } catch(Exception e){ + unexpectedException(); + } + } + + /** + * Read trylock succeeds if write locked by current thread + */ + public void testReadHoldingWriteLock() { + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + lock.writeLock().lock(); + assertTrue(lock.readLock().tryLock()); + lock.readLock().unlock(); + lock.writeLock().unlock(); + } + + /** + * Read lock succeeds if write locked by current thread even if + * other threads are waiting for readlock + */ + public void testReadHoldingWriteLock2() { + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + lock.writeLock().lock(); + Thread t1 = new Thread(new Runnable() { + public void run() { + lock.readLock().lock(); + lock.readLock().unlock(); + } + }); + Thread t2 = new Thread(new Runnable() { + public void run() { + lock.readLock().lock(); + lock.readLock().unlock(); + } + }); + + try { + t1.start(); + t2.start(); + lock.readLock().lock(); + lock.readLock().unlock(); + Thread.sleep(SHORT_DELAY_MS); + lock.readLock().lock(); + lock.readLock().unlock(); + lock.writeLock().unlock(); + t1.join(MEDIUM_DELAY_MS); + t2.join(MEDIUM_DELAY_MS); + assertTrue(!t1.isAlive()); + assertTrue(!t2.isAlive()); + + } catch(Exception e){ + unexpectedException(); + } + } + + /** + * Read lock succeeds if write locked by current thread even if + * other threads are waiting for writelock + */ + public void testReadHoldingWriteLock3() { + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + lock.writeLock().lock(); + Thread t1 = new Thread(new Runnable() { + public void run() { + lock.writeLock().lock(); + lock.writeLock().unlock(); + } + }); + Thread t2 = new Thread(new Runnable() { + public void run() { + lock.writeLock().lock(); + lock.writeLock().unlock(); + } + }); + + try { + t1.start(); + t2.start(); + lock.readLock().lock(); + lock.readLock().unlock(); + Thread.sleep(SHORT_DELAY_MS); + lock.readLock().lock(); + lock.readLock().unlock(); + lock.writeLock().unlock(); + t1.join(MEDIUM_DELAY_MS); + t2.join(MEDIUM_DELAY_MS); + assertTrue(!t1.isAlive()); + assertTrue(!t2.isAlive()); + + } catch(Exception e){ + unexpectedException(); + } + } + + + /** + * Write lock succeeds if write locked by current thread even if + * other threads are waiting for writelock + */ + public void testWriteHoldingWriteLock4() { + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + lock.writeLock().lock(); + Thread t1 = new Thread(new Runnable() { + public void run() { + lock.writeLock().lock(); + lock.writeLock().unlock(); + } + }); + Thread t2 = new Thread(new Runnable() { + public void run() { + lock.writeLock().lock(); + lock.writeLock().unlock(); + } + }); + + try { + t1.start(); + t2.start(); + lock.writeLock().lock(); + lock.writeLock().unlock(); + Thread.sleep(SHORT_DELAY_MS); + lock.writeLock().lock(); + lock.writeLock().unlock(); + lock.writeLock().unlock(); + t1.join(MEDIUM_DELAY_MS); + t2.join(MEDIUM_DELAY_MS); + assertTrue(!t1.isAlive()); + assertTrue(!t2.isAlive()); + + } catch(Exception e){ + unexpectedException(); + } + } + + + /** + * Fair Read trylock succeeds if write locked by current thread + */ + public void testReadHoldingWriteLockFair() { + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true); + lock.writeLock().lock(); + assertTrue(lock.readLock().tryLock()); + lock.readLock().unlock(); + lock.writeLock().unlock(); + } + + /** + * Fair Read lock succeeds if write locked by current thread even if + * other threads are waiting for readlock + */ + public void testReadHoldingWriteLockFair2() { + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true); + lock.writeLock().lock(); + Thread t1 = new Thread(new Runnable() { + public void run() { + lock.readLock().lock(); + lock.readLock().unlock(); + } + }); + Thread t2 = new Thread(new Runnable() { + public void run() { + lock.readLock().lock(); + lock.readLock().unlock(); + } + }); + + try { + t1.start(); + t2.start(); + lock.readLock().lock(); + lock.readLock().unlock(); + Thread.sleep(SHORT_DELAY_MS); + lock.readLock().lock(); + lock.readLock().unlock(); + lock.writeLock().unlock(); + t1.join(MEDIUM_DELAY_MS); + t2.join(MEDIUM_DELAY_MS); + assertTrue(!t1.isAlive()); + assertTrue(!t2.isAlive()); + + } catch(Exception e){ + unexpectedException(); + } + } + + + /** + * Fair Read lock succeeds if write locked by current thread even if + * other threads are waiting for writelock + */ + public void testReadHoldingWriteLockFair3() { + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true); + lock.writeLock().lock(); + Thread t1 = new Thread(new Runnable() { + public void run() { + lock.writeLock().lock(); + lock.writeLock().unlock(); + } + }); + Thread t2 = new Thread(new Runnable() { + public void run() { + lock.writeLock().lock(); + lock.writeLock().unlock(); + } + }); + + try { + t1.start(); + t2.start(); + lock.readLock().lock(); + lock.readLock().unlock(); + Thread.sleep(SHORT_DELAY_MS); + lock.readLock().lock(); + lock.readLock().unlock(); + lock.writeLock().unlock(); + t1.join(MEDIUM_DELAY_MS); + t2.join(MEDIUM_DELAY_MS); + assertTrue(!t1.isAlive()); + assertTrue(!t2.isAlive()); + } catch(Exception e){ unexpectedException(); } - } + } + + + /** + * Fair Write lock succeeds if write locked by current thread even if + * other threads are waiting for writelock + */ + public void testWriteHoldingWriteLockFair4() { + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true); + lock.writeLock().lock(); + Thread t1 = new Thread(new Runnable() { + public void run() { + lock.writeLock().lock(); + lock.writeLock().unlock(); + } + }); + Thread t2 = new Thread(new Runnable() { + public void run() { + lock.writeLock().lock(); + lock.writeLock().unlock(); + } + }); + + try { + t1.start(); + t2.start(); + Thread.sleep(SHORT_DELAY_MS); + assertTrue(lock.isWriteLockedByCurrentThread()); + assertTrue(lock.getWriteHoldCount() == 1); + lock.writeLock().lock(); + assertTrue(lock.getWriteHoldCount() == 2); + lock.writeLock().unlock(); + lock.writeLock().lock(); + lock.writeLock().unlock(); + lock.writeLock().unlock(); + t1.join(MEDIUM_DELAY_MS); + t2.join(MEDIUM_DELAY_MS); + assertTrue(!t1.isAlive()); + assertTrue(!t2.isAlive()); + + } catch(Exception e){ + unexpectedException(); + } + } /** * Read tryLock succeeds if readlocked but not writelocked */ - public void testTryLockWhenReadLocked() { + public void testTryLockWhenReadLocked() { final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); lock.readLock().lock(); Thread t = new Thread(new Runnable() { @@ -398,14 +665,14 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { } catch(Exception e){ unexpectedException(); } - } + } + - /** * write tryLock fails when readlocked */ - public void testWriteTryLockWhenReadLocked() { + public void testWriteTryLockWhenReadLocked() { final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); lock.readLock().lock(); Thread t = new Thread(new Runnable() { @@ -420,14 +687,58 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { } catch(Exception e){ unexpectedException(); } - } + } + + + /** + * Fair Read tryLock succeeds if readlocked but not writelocked + */ + public void testTryLockWhenReadLockedFair() { + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true); + lock.readLock().lock(); + Thread t = new Thread(new Runnable() { + public void run() { + threadAssertTrue(lock.readLock().tryLock()); + lock.readLock().unlock(); + } + }); + try { + t.start(); + t.join(); + lock.readLock().unlock(); + } catch(Exception e){ + unexpectedException(); + } + } + + + + /** + * Fair write tryLock fails when readlocked + */ + public void testWriteTryLockWhenReadLockedFair() { + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true); + lock.readLock().lock(); + Thread t = new Thread(new Runnable() { + public void run() { + threadAssertFalse(lock.writeLock().tryLock()); + } + }); + try { + t.start(); + t.join(); + lock.readLock().unlock(); + } catch(Exception e){ + unexpectedException(); + } + } + - /** * write timed tryLock times out if locked */ - public void testWriteTryLock_Timeout() { + public void testWriteTryLock_Timeout() { final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); lock.writeLock().lock(); Thread t = new Thread(new Runnable() { @@ -446,12 +757,12 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { } catch(Exception e){ unexpectedException(); } - } + } /** * read timed tryLock times out if write-locked */ - public void testReadTryLock_Timeout() { + public void testReadTryLock_Timeout() { final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); lock.writeLock().lock(); Thread t = new Thread(new Runnable() { @@ -470,7 +781,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { } catch(Exception e){ unexpectedException(); } - } + } /** @@ -483,7 +794,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { } catch(Exception e) { unexpectedException(); } - Thread t = new Thread(new Runnable() { + Thread t = new Thread(new Runnable() { public void run() { try { lock.writeLock().lockInterruptibly(); @@ -495,7 +806,9 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { }); try { t.start(); + Thread.sleep(SHORT_DELAY_MS); t.interrupt(); + Thread.sleep(SHORT_DELAY_MS); t.join(); lock.writeLock().unlock(); } catch(Exception e){ @@ -513,7 +826,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { } catch(Exception e) { unexpectedException(); } - Thread t = new Thread(new Runnable() { + Thread t = new Thread(new Runnable() { public void run() { try { lock.readLock().lockInterruptibly(); @@ -525,6 +838,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { }); try { t.start(); + Thread.sleep(SHORT_DELAY_MS); t.interrupt(); t.join(); lock.writeLock().unlock(); @@ -537,7 +851,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { * Calling await without holding lock throws IllegalMonitorStateException */ public void testAwait_IllegalMonitor() { - final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); final Condition c = lock.writeLock().newCondition(); try { c.await(); @@ -554,7 +868,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { * Calling signal without holding lock throws IllegalMonitorStateException */ public void testSignal_IllegalMonitor() { - final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); final Condition c = lock.writeLock().newCondition(); try { c.signal(); @@ -571,7 +885,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { * awaitNanos without a signal times out */ public void testAwaitNanos_Timeout() { - final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); final Condition c = lock.writeLock().newCondition(); try { lock.writeLock().lock(); @@ -589,11 +903,10 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { * timed await without a signal times out */ public void testAwait_Timeout() { - final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); final Condition c = lock.writeLock().newCondition(); try { lock.writeLock().lock(); - assertFalse(c.await(10, TimeUnit.MILLISECONDS)); lock.writeLock().unlock(); } catch (Exception ex) { @@ -605,12 +918,11 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { * awaitUntil without a signal times out */ public void testAwaitUntil_Timeout() { - final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); final Condition c = lock.writeLock().newCondition(); try { lock.writeLock().lock(); java.util.Date d = new java.util.Date(); - assertFalse(c.awaitUntil(new java.util.Date(d.getTime() + 10))); lock.writeLock().unlock(); } catch (Exception ex) { @@ -622,9 +934,9 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { * await returns when signalled */ public void testAwait() { - final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); final Condition c = lock.writeLock().newCondition(); - Thread t = new Thread(new Runnable() { + Thread t = new Thread(new Runnable() { public void run() { try { lock.writeLock().lock(); @@ -651,32 +963,61 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { } } + /** A helper class for uninterruptible wait tests */ + class UninterruptableThread extends Thread { + private Lock lock; + private Condition c; + + public volatile boolean canAwake = false; + public volatile boolean interrupted = false; + public volatile boolean lockStarted = false; + + public UninterruptableThread(Lock lock, Condition c) { + this.lock = lock; + this.c = c; + } + + public synchronized void run() { + lock.lock(); + lockStarted = true; + + while (!canAwake) { + c.awaitUninterruptibly(); + } + + interrupted = isInterrupted(); + lock.unlock(); + } + } + /** * awaitUninterruptibly doesn't abort on interrupt */ public void testAwaitUninterruptibly() { - final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); final Condition c = lock.writeLock().newCondition(); - Thread t = new Thread(new Runnable() { - public void run() { - lock.writeLock().lock(); - c.awaitUninterruptibly(); - lock.writeLock().unlock(); - } - }); + UninterruptableThread thread = new UninterruptableThread(lock.writeLock(), c); try { - t.start(); - Thread.sleep(SHORT_DELAY_MS); - t.interrupt(); + thread.start(); + + while (!thread.lockStarted) { + Thread.sleep(100); + } + lock.writeLock().lock(); - c.signal(); - lock.writeLock().unlock(); - assert(t.isInterrupted()); - t.join(SHORT_DELAY_MS); - assertFalse(t.isAlive()); - } - catch (Exception ex) { + try { + thread.interrupt(); + thread.canAwake = true; + c.signal(); + } finally { + lock.writeLock().unlock(); + } + + thread.join(); + assertTrue(thread.interrupted); + assertFalse(thread.isAlive()); + } catch (Exception ex) { unexpectedException(); } } @@ -685,9 +1026,9 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { * await is interruptible */ public void testAwait_Interrupt() { - final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); final Condition c = lock.writeLock().newCondition(); - Thread t = new Thread(new Runnable() { + Thread t = new Thread(new Runnable() { public void run() { try { lock.writeLock().lock(); @@ -716,9 +1057,9 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { * awaitNanos is interruptible */ public void testAwaitNanos_Interrupt() { - final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); final Condition c = lock.writeLock().newCondition(); - Thread t = new Thread(new Runnable() { + Thread t = new Thread(new Runnable() { public void run() { try { lock.writeLock().lock(); @@ -747,9 +1088,9 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { * awaitUntil is interruptible */ public void testAwaitUntil_Interrupt() { - final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); final Condition c = lock.writeLock().newCondition(); - Thread t = new Thread(new Runnable() { + Thread t = new Thread(new Runnable() { public void run() { try { lock.writeLock().lock(); @@ -779,9 +1120,9 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { * signalAll wakes up all threads */ public void testSignalAll() { - final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); final Condition c = lock.writeLock().newCondition(); - Thread t1 = new Thread(new Runnable() { + Thread t1 = new Thread(new Runnable() { public void run() { try { lock.writeLock().lock(); @@ -794,7 +1135,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { } }); - Thread t2 = new Thread(new Runnable() { + Thread t2 = new Thread(new Runnable() { public void run() { try { lock.writeLock().lock(); @@ -852,7 +1193,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { /** * hasQueuedThreads reports whether there are waiting threads */ - public void testhasQueuedThreads() { + public void testhasQueuedThreads() { final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); Thread t1 = new Thread(new InterruptedLockRunnable(lock)); Thread t2 = new Thread(new InterruptibleLockRunnable(lock)); @@ -876,12 +1217,12 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { } catch(Exception e){ unexpectedException(); } - } + } /** * hasQueuedThread(null) throws NPE */ - public void testHasQueuedThreadNPE() { + public void testHasQueuedThreadNPE() { final ReentrantReadWriteLock sync = new ReentrantReadWriteLock(); try { sync.hasQueuedThread(null); @@ -893,7 +1234,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { /** * hasQueuedThread reports whether a thread is queued. */ - public void testHasQueuedThread() { + public void testHasQueuedThread() { final ReentrantReadWriteLock sync = new ReentrantReadWriteLock(); Thread t1 = new Thread(new InterruptedLockRunnable(sync)); Thread t2 = new Thread(new InterruptibleLockRunnable(sync)); @@ -922,13 +1263,13 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { } catch(Exception e){ unexpectedException(); } - } + } /** * getQueueLength reports number of waiting threads */ - public void testGetQueueLength() { + public void testGetQueueLength() { final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); Thread t1 = new Thread(new InterruptedLockRunnable(lock)); Thread t2 = new Thread(new InterruptibleLockRunnable(lock)); @@ -952,12 +1293,12 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { } catch(Exception e){ unexpectedException(); } - } + } /** * getQueuedThreads includes waiting threads */ - public void testGetQueuedThreads() { + public void testGetQueuedThreads() { final PublicReentrantReadWriteLock lock = new PublicReentrantReadWriteLock(); Thread t1 = new Thread(new InterruptedLockRunnable(lock)); Thread t2 = new Thread(new InterruptibleLockRunnable(lock)); @@ -984,7 +1325,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { } catch(Exception e){ unexpectedException(); } - } + } /** * hasWaiters throws NPE if null @@ -1097,9 +1438,9 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { * getWaitingThreads throws IAE if not owned */ public void testGetWaitingThreadsIAE() { - final PublicReentrantReadWriteLock lock = new PublicReentrantReadWriteLock(); + final PublicReentrantReadWriteLock lock = new PublicReentrantReadWriteLock(); final Condition c = (lock.writeLock().newCondition()); - final PublicReentrantReadWriteLock lock2 = new PublicReentrantReadWriteLock(); + final PublicReentrantReadWriteLock lock2 = new PublicReentrantReadWriteLock(); try { lock2.getWaitingThreads(c); shouldThrow(); @@ -1113,7 +1454,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { * getWaitingThreads throws IMSE if not locked */ public void testGetWaitingThreadsIMSE() { - final PublicReentrantReadWriteLock lock = new PublicReentrantReadWriteLock(); + final PublicReentrantReadWriteLock lock = new PublicReentrantReadWriteLock(); final Condition c = (lock.writeLock().newCondition()); try { lock.getWaitingThreads(c); @@ -1131,7 +1472,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { public void testHasWaiters() { final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); final Condition c = (lock.writeLock().newCondition()); - Thread t = new Thread(new Runnable() { + Thread t = new Thread(new Runnable() { public void run() { try { lock.writeLock().lock(); @@ -1173,7 +1514,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { public void testGetWaitQueueLength() { final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); final Condition c = (lock.writeLock().newCondition()); - Thread t = new Thread(new Runnable() { + Thread t = new Thread(new Runnable() { public void run() { try { lock.writeLock().lock(); @@ -1214,9 +1555,9 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { * getWaitingThreads returns only and all waiting threads */ public void testGetWaitingThreads() { - final PublicReentrantReadWriteLock lock = new PublicReentrantReadWriteLock(); + final PublicReentrantReadWriteLock lock = new PublicReentrantReadWriteLock(); final Condition c = lock.writeLock().newCondition(); - Thread t1 = new Thread(new Runnable() { + Thread t1 = new Thread(new Runnable() { public void run() { try { lock.writeLock().lock(); @@ -1230,7 +1571,7 @@ public class ReentrantReadWriteLockTest extends JSR166TestCase { } }); - Thread t2 = new Thread(new Runnable() { + Thread t2 = new Thread(new Runnable() { public void run() { try { lock.writeLock().lock(); diff --git a/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/SynchronousQueueTest.java b/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/SynchronousQueueTest.java index 19bdede0b..debce5d6d 100755 --- a/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/SynchronousQueueTest.java +++ b/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/SynchronousQueueTest.java @@ -757,6 +757,7 @@ public class SynchronousQueueTest extends JSR166TestCase { while (!q.isEmpty()) assertEquals(q.remove(), r.remove()); } catch(Exception e){ + e.printStackTrace(); unexpectedException(); } } diff --git a/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/ThreadPoolExecutorTest.java b/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/ThreadPoolExecutorTest.java index ca4ba7880..4f7cc461b 100755 --- a/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/ThreadPoolExecutorTest.java +++ b/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/ThreadPoolExecutorTest.java @@ -9,6 +9,7 @@ package tests.api.java.util.concurrent; import java.util.concurrent.*; +import java.util.concurrent.atomic.*; import junit.framework.*; import java.util.*; @@ -38,6 +39,15 @@ public class ThreadPoolExecutorTest extends JSR166TestCase { } } + static class FailingThreadFactory implements ThreadFactory{ + int calls = 0; + public Thread newThread(Runnable r){ + if (++calls > 1) return null; + return new Thread(r); + } + } + + /** * execute successfully executes a runnable */ @@ -342,6 +352,9 @@ public class ThreadPoolExecutorTest extends JSR166TestCase { assertSame(q, wq); assertFalse(wq.contains(tasks[0])); assertTrue(wq.contains(tasks[4])); + for (int i = 1; i < 5; ++i) + tasks[i].cancel(true); + p1.shutdownNow(); } catch(Exception e) { unexpectedException(); } finally { @@ -1500,5 +1513,58 @@ public class ThreadPoolExecutorTest extends JSR166TestCase { } } + /** + * Execution continues if there is at least one thread even if + * thread factory fails to create more + */ + public void testFailingThreadFactory() { + ExecutorService e = new ThreadPoolExecutor(100, 100, LONG_DELAY_MS, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(), new FailingThreadFactory()); + try { + ArrayList> l = new ArrayList>(); + for (int k = 0; k < 100; ++k) { + e.execute(new NoOpRunnable()); + } + Thread.sleep(LONG_DELAY_MS); + } catch(Exception ex) { + unexpectedException(); + } finally { + joinPool(e); + } + } + /** + * execute allows the same task to be submitted multiple times, even + * if rejected + */ + public void testRejectedRecycledTask() { + final int nTasks = 1000; + final AtomicInteger nRun = new AtomicInteger(0); + final Runnable recycledTask = new Runnable() { + public void run() { + nRun.getAndIncrement(); + } }; + final ThreadPoolExecutor p = + new ThreadPoolExecutor(1, 30, 60, TimeUnit.SECONDS, + new ArrayBlockingQueue(30)); + try { + for (int i = 0; i < nTasks; ++i) { + for (;;) { + try { + p.execute(recycledTask); + break; + } + catch (RejectedExecutionException ignore) { + } + } + } + Thread.sleep(5000); // enough time to run all tasks + assertEquals(nRun.get(), nTasks); + } catch(Exception ex) { + ex.printStackTrace(); + unexpectedException(); + } finally { + p.shutdown(); + } + } + } diff --git a/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/TimeUnitTest.java b/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/TimeUnitTest.java index 1a9a04a55..54fdc6957 100755 --- a/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/TimeUnitTest.java +++ b/libcore/concurrent/src/test/java/tests/api/java/util/concurrent/TimeUnitTest.java @@ -21,54 +21,60 @@ public class TimeUnitTest extends JSR166TestCase { return new TestSuite(TimeUnitTest.class); } + // (loops to 88888 check increments at all time divisions.) + /** - * convert correctly converts sample values across the four units + * convert correctly converts sample values across the units */ public void testConvert() { - for (long t = 0; t < 10; ++t) { - assertEquals(t, + for (long t = 0; t < 88888; ++t) { + assertEquals(t, TimeUnit.SECONDS.convert(t, TimeUnit.SECONDS)); assertEquals(t, - TimeUnit.SECONDS.convert(1000 * t, + TimeUnit.SECONDS.convert(1000L*t, TimeUnit.MILLISECONDS)); assertEquals(t, - TimeUnit.SECONDS.convert(1000000 * t, + TimeUnit.SECONDS.convert(1000000L*t, TimeUnit.MICROSECONDS)); assertEquals(t, - TimeUnit.SECONDS.convert(1000000000 * t, + TimeUnit.SECONDS.convert(1000000000L*t, TimeUnit.NANOSECONDS)); - assertEquals(1000 * t, + + + assertEquals(1000L*t, TimeUnit.MILLISECONDS.convert(t, TimeUnit.SECONDS)); assertEquals(t, TimeUnit.MILLISECONDS.convert(t, TimeUnit.MILLISECONDS)); assertEquals(t, - TimeUnit.MILLISECONDS.convert(1000 * t, + TimeUnit.MILLISECONDS.convert(1000L*t, TimeUnit.MICROSECONDS)); assertEquals(t, - TimeUnit.MILLISECONDS.convert(1000000 * t, + TimeUnit.MILLISECONDS.convert(1000000L*t, TimeUnit.NANOSECONDS)); - assertEquals(1000000 * t, + + assertEquals(1000000L*t, TimeUnit.MICROSECONDS.convert(t, TimeUnit.SECONDS)); - assertEquals(1000 * t, + assertEquals(1000L*t, TimeUnit.MICROSECONDS.convert(t, TimeUnit.MILLISECONDS)); assertEquals(t, TimeUnit.MICROSECONDS.convert(t, TimeUnit.MICROSECONDS)); assertEquals(t, - TimeUnit.MICROSECONDS.convert(1000 * t, + TimeUnit.MICROSECONDS.convert(1000L*t, TimeUnit.NANOSECONDS)); - assertEquals(1000000000 * t, + + assertEquals(1000000000L*t, TimeUnit.NANOSECONDS.convert(t, TimeUnit.SECONDS)); - assertEquals(1000000 * t, + assertEquals(1000000L*t, TimeUnit.NANOSECONDS.convert(t, TimeUnit.MILLISECONDS)); - assertEquals(1000 * t, + assertEquals(1000L*t, TimeUnit.NANOSECONDS.convert(t, TimeUnit.MICROSECONDS)); assertEquals(t, @@ -82,13 +88,12 @@ public class TimeUnitTest extends JSR166TestCase { * nanoseconds */ public void testToNanos() { - for (long t = 0; t < 10; ++t) { - assertEquals(1000000000 * t, + for (long t = 0; t < 88888; ++t) { + assertEquals(1000000000L*t, TimeUnit.SECONDS.toNanos(t)); - - assertEquals(1000000 * t, + assertEquals(1000000L*t, TimeUnit.MILLISECONDS.toNanos(t)); - assertEquals(1000 * t, + assertEquals(1000L*t, TimeUnit.MICROSECONDS.toNanos(t)); assertEquals(t, TimeUnit.NANOSECONDS.toNanos(t)); @@ -100,16 +105,15 @@ public class TimeUnitTest extends JSR166TestCase { * microseconds */ public void testToMicros() { - for (long t = 0; t < 10; ++t) { - assertEquals(1000000 * t, + for (long t = 0; t < 88888; ++t) { + assertEquals(1000000L*t, TimeUnit.SECONDS.toMicros(t)); - - assertEquals(1000 * t, + assertEquals(1000L*t, TimeUnit.MILLISECONDS.toMicros(t)); assertEquals(t, TimeUnit.MICROSECONDS.toMicros(t)); assertEquals(t, - TimeUnit.NANOSECONDS.toMicros(t * 1000)); + TimeUnit.NANOSECONDS.toMicros(t*1000L)); } } @@ -118,16 +122,15 @@ public class TimeUnitTest extends JSR166TestCase { * milliseconds */ public void testToMillis() { - for (long t = 0; t < 10; ++t) { - assertEquals(1000 * t, + for (long t = 0; t < 88888; ++t) { + assertEquals(1000L*t, TimeUnit.SECONDS.toMillis(t)); - assertEquals(t, TimeUnit.MILLISECONDS.toMillis(t)); assertEquals(t, - TimeUnit.MICROSECONDS.toMillis(t * 1000)); + TimeUnit.MICROSECONDS.toMillis(t*1000L)); assertEquals(t, - TimeUnit.NANOSECONDS.toMillis(t * 1000000)); + TimeUnit.NANOSECONDS.toMillis(t*1000000L)); } } @@ -136,20 +139,18 @@ public class TimeUnitTest extends JSR166TestCase { * seconds */ public void testToSeconds() { - for (long t = 0; t < 10; ++t) { - assertEquals(t, + for (long t = 0; t < 88888; ++t) { + assertEquals(t, TimeUnit.SECONDS.toSeconds(t)); - assertEquals(t, - TimeUnit.MILLISECONDS.toSeconds(t * 1000)); + TimeUnit.MILLISECONDS.toSeconds(t*1000L)); assertEquals(t, - TimeUnit.MICROSECONDS.toSeconds(t * 1000000)); + TimeUnit.MICROSECONDS.toSeconds(t*1000000L)); assertEquals(t, - TimeUnit.NANOSECONDS.toSeconds(t * 1000000000)); + TimeUnit.NANOSECONDS.toSeconds(t*1000000000L)); } } - /** * convert saturates positive too-large values to Long.MAX_VALUE * and negative to LONG.MIN_VALUE @@ -161,6 +162,7 @@ public class TimeUnitTest extends JSR166TestCase { assertEquals(Long.MIN_VALUE, TimeUnit.NANOSECONDS.convert(-Long.MAX_VALUE / 4, TimeUnit.SECONDS)); + } /** diff --git a/libcore/crypto/src/main/java/javax/crypto/BadPaddingException.java b/libcore/crypto/src/main/java/javax/crypto/BadPaddingException.java index 19fdaa8a2..5e2dd3882 100644 --- a/libcore/crypto/src/main/java/javax/crypto/BadPaddingException.java +++ b/libcore/crypto/src/main/java/javax/crypto/BadPaddingException.java @@ -22,8 +22,6 @@ import java.security.GeneralSecurityException; /** * The exception that is thrown when a padding mechanism is expected for the * input data, but the input data does not have the proper padding bytes. - * - * @since Android 1.0 */ public class BadPaddingException extends GeneralSecurityException { @@ -37,7 +35,6 @@ public class BadPaddingException extends GeneralSecurityException { * * @param msg * the message - * @since Android 1.0 */ public BadPaddingException(String msg) { super(msg); @@ -45,8 +42,6 @@ public class BadPaddingException extends GeneralSecurityException { /** * Creates a new instance of {@code BadPaddingException} with no message. - * - * @since Android 1.0 */ public BadPaddingException() { } diff --git a/libcore/crypto/src/main/java/javax/crypto/Cipher.java b/libcore/crypto/src/main/java/javax/crypto/Cipher.java index ae7222639..8e084aee9 100644 --- a/libcore/crypto/src/main/java/javax/crypto/Cipher.java +++ b/libcore/crypto/src/main/java/javax/crypto/Cipher.java @@ -61,58 +61,41 @@ import org.apache.harmony.security.fortress.Engine; * to be processed at a time can be optionally specified by appending it to the * mode name. e.g. "AES/CFB8/NoPadding". If no number is specified, a * provider specific default value is used. - *

        - * - * @since Android 1.0 */ public class Cipher { /** * Constant for decryption operation mode. - * - * @since Android 1.0 */ public static final int DECRYPT_MODE = 2; /** * Constant for encryption operation mode. - * - * @since Android 1.0 */ public static final int ENCRYPT_MODE = 1; /** * Constant indicating that the key to be unwrapped is a private key. - * - * @since Android 1.0 */ public static final int PRIVATE_KEY = 2; /** * Constant indicating that the key to be unwrapped is a public key. - * - * @since Android 1.0 */ public static final int PUBLIC_KEY = 1; /** * Constant indicating that the key to be unwrapped is a secret key. - * - * @since Android 1.0 */ public static final int SECRET_KEY = 3; /** * Constant for key unwrapping operation mode. - * - * @since Android 1.0 */ public static final int UNWRAP_MODE = 4; /** * Constant for key wrapping operation mode. - * - * @since Android 1.0 */ public static final int WRAP_MODE = 3; @@ -147,7 +130,7 @@ public class Cipher { /** * Creates a new Cipher instance. - * + * * @param cipherSpi * the implementation delegate of the cipher. * @param provider @@ -157,7 +140,6 @@ public class Cipher { * @throws NullPointerException * if either cipherSpi is {@code null} or provider is {@code * null} and {@code cipherSpi} is a {@code NullCipherSpi}. - * @since Android 1.0 */ protected Cipher(CipherSpi cipherSpi, Provider provider, String transformation) { @@ -178,7 +160,7 @@ public class Cipher { * transformation. The first found provider providing the transformation is * used to create the cipher. If no provider is found an exception is * thrown. - * + * * @param transformation * the name of the transformation to create a cipher for. * @return a cipher for the requested transformation. @@ -189,7 +171,6 @@ public class Cipher { * @throws NoSuchPaddingException * if no installed provider can provide the padding scheme in * the transformation. - * @since Android 1.0 */ public static final Cipher getInstance(String transformation) throws NoSuchAlgorithmException, NoSuchPaddingException { @@ -199,7 +180,7 @@ public class Cipher { /** * Creates a new cipher for the specified transformation provided by the * specified provider. - * + * * @param transformation * the name of the transformation to create a cipher for. * @param provider @@ -216,7 +197,6 @@ public class Cipher { * is not available. * @throws IllegalArgumentException * if the specified provider is {@code null}. - * @since Android 1.0 */ public static final Cipher getInstance(String transformation, String provider) throws NoSuchAlgorithmException, @@ -235,7 +215,7 @@ public class Cipher { /** * Creates a new cipher for the specified transformation. - * + * * @param transformation * the name of the transformation to create a cipher for. * @param provider @@ -250,7 +230,6 @@ public class Cipher { * is not available. * @throws IllegalArgumentException * if the provider is {@code null}. - * @since Android 1.0 */ public static final Cipher getInstance(String transformation, Provider provider) throws NoSuchAlgorithmException, @@ -373,9 +352,8 @@ public class Cipher { /** * Returns the provider of this cipher instance. - * + * * @return the provider of this cipher instance. - * @since Android 1.0 */ public final Provider getProvider() { return provider; @@ -386,10 +364,8 @@ public class Cipher { *

        * This is the name of the transformation argument used in the * {@code getInstance} call creating this object. - *

        - * + * * @return the name of the algorithm of this cipher instance. - * @since Android 1.0. */ public final String getAlgorithm() { return transformation; @@ -397,9 +373,8 @@ public class Cipher { /** * Returns this ciphers block size (in bytes). - * + * * @return this ciphers block size. - * @since Android 1.0 */ public final int getBlockSize() { return spiImpl.engineGetBlockSize(); @@ -408,13 +383,12 @@ public class Cipher { /** * Returns the length in bytes an output buffer needs to be when this cipher * is updated with {@code inputLen} bytes. - * + * * @param inputLen * the number of bytes of the input. * @return the output buffer length for the input length. * @throws IllegalStateException * if this cipher instance is in an invalid state. - * @since Android 1.0 */ public final int getOutputSize(int inputLen) { if (mode == 0) { @@ -426,9 +400,8 @@ public class Cipher { /** * Returns the initialization vector for this cipher instance. - * + * * @return the initialization vector for this cipher instance. - * @since Android 1.0 */ public final byte[] getIV() { return spiImpl.engineGetIV(); @@ -440,11 +413,10 @@ public class Cipher { * These may be a the same parameters that were used to create this cipher * instance, or may be a combination of default and random parameters, * depending on the underlying cipher implementation. - * + * * @return the parameters that where used to create this cipher instance, or * {@code null} if this cipher instance does not have any * parameters. - * @since Android 1.0 */ public final AlgorithmParameters getParameters() { return spiImpl.engineGetParameters(); @@ -452,9 +424,8 @@ public class Cipher { /** * Returns the exemption mechanism associated with this cipher. - * + * * @return currently {@code null} - * @since Android 1.0 */ public final ExemptionMechanism getExemptionMechanism() { //FIXME implement getExemptionMechanism @@ -473,7 +444,7 @@ public class Cipher { * The cipher is initialized for the specified operational mode (one of: * encryption, decryption, key wrapping or key unwrapping) depending on * {@code opmode}. - *

        + *

        * If this cipher instance needs any algorithm parameters or random values * that the specified key can not provide, the underlying implementation of * this cipher is supposed to generate the required parameters (using its @@ -483,8 +454,7 @@ public class Cipher { * init} methods, the state of the instance is overridden, meaning that it * is equivalent to creating a new instance and calling its {@code init} * method. - *

        - * + * * @param opmode * the operation this cipher instance should be initialized for * (one of: {@code ENCRYPT_MODE}, {@code DECRYPT_MODE}, {@code @@ -494,7 +464,6 @@ public class Cipher { * @throws InvalidKeyException * if the specified key can not be used to initialize this * cipher instance. - * @since Android 1.0 */ public final void init(int opmode, Key key) throws InvalidKeyException { if (sec_rand == null) { @@ -513,7 +482,7 @@ public class Cipher { * The cipher is initialized for the specified operational mode (one of: * encryption, decryption, key wrapping or key unwrapping) depending on * {@code opmode}. - *

        + *

        * If this cipher instance needs any algorithm parameters or random values * that the specified key can not provide, the underlying implementation of * this cipher is supposed to generate the required parameters (using its @@ -523,8 +492,7 @@ public class Cipher { * When a cipher instance is initialized by a call to any of the {@code * init} methods, the state of the instance is overridden, means it is * equivalent to creating a new instance and calling it {@code init} method. - *

        - * + * * @param opmode * the operation this cipher instance should be initialized for * (one of: {@code ENCRYPT_MODE}, {@code DECRYPT_MODE}, {@code @@ -538,7 +506,6 @@ public class Cipher { * cipher instance. * @throws InvalidParameterException * if the specified opmode is invalid. - * @since Android 1.0 */ public final void init(int opmode, Key key, SecureRandom random) throws InvalidKeyException { @@ -559,7 +526,7 @@ public class Cipher { *

        * The cipher is initialized for the specified operational mode (one of: * encryption, decryption, key wrapping or key unwrapping). - *

        + *

        * If this cipher instance needs any algorithm parameters and {@code params} * is {@code null}, the underlying implementation of this cipher is supposed * to generate the required parameters (using its provider or random @@ -568,8 +535,7 @@ public class Cipher { * When a cipher instance is initialized by a call to any of the {@code * init} methods, the state of the instance is overridden, means it is * equivalent to creating a new instance and calling it {@code init} method. - *

        - * + * * @param opmode * the operation this cipher instance should be initialized for * (one of: {@code ENCRYPT_MODE}, {@code DECRYPT_MODE}, {@code @@ -584,7 +550,6 @@ public class Cipher { * @throws InvalidAlgorithmParameterException * it the specified parameters are inappropriate for this * cipher. - * @since Android 1.0 */ public final void init(int opmode, Key key, AlgorithmParameterSpec params) throws InvalidKeyException, InvalidAlgorithmParameterException { @@ -611,7 +576,7 @@ public class Cipher { * init} methods, the state of the instance is overridden, meaning that it * is equivalent to creating a new instance and calling it {@code init} * method. - * + * * @param opmode * the operation this cipher instance should be initialized for * (one of: {@code ENCRYPT_MODE}, {@code DECRYPT_MODE}, {@code @@ -630,7 +595,6 @@ public class Cipher { * cipher. * @throws InvalidParameterException * if the specified {@code opmode} is invalid. - * @since Android 1.0 */ public final void init(int opmode, Key key, AlgorithmParameterSpec params, SecureRandom random) throws InvalidKeyException, @@ -656,7 +620,7 @@ public class Cipher { * The cipher is initialized for the specified operation (one of: * encryption, decryption, key wrapping or key unwrapping) depending on * {@code opmode}. - *

        + *

        * If this cipher instance needs any algorithm parameters and {@code params} * is {@code null}, the underlying implementation of this cipher is supposed * to generate the required parameters (using its provider or random @@ -666,8 +630,7 @@ public class Cipher { * init} methods, the state of the instance is overridden, meaning that it * is equivalent to creating a new instance and calling it {@code init} * method. - *

        - * + * * @param opmode * the operation this cipher instance should be initialized for * (one of: {@code ENCRYPT_MODE}, {@code DECRYPT_MODE}, {@code @@ -682,7 +645,6 @@ public class Cipher { * @throws InvalidAlgorithmParameterException * it the specified parameters are inappropriate for this * cipher. - * @since Android 1.0 */ public final void init(int opmode, Key key, AlgorithmParameters params) throws InvalidKeyException, InvalidAlgorithmParameterException { @@ -699,7 +661,7 @@ public class Cipher { * The cipher will be initialized for the specified operation (one of: * encryption, decryption, key wrapping or key unwrapping) depending on * {@code opmode}. - *

        + *

        * If this cipher instance needs any algorithm parameters and {@code params} * is {@code null}, the underlying implementation of this cipher is supposed * to generate the required parameters (using its provider or random @@ -708,8 +670,7 @@ public class Cipher { * When a cipher instance is initialized by a call to any of the {@code * init} methods, the state of the instance is overridden, means it is * equivalent to creating a new instance and calling it {@code init} method. - *

        - * + * * @param opmode * the operation this cipher instance should be initialized for * (one of: {@code ENCRYPT_MODE}, {@code DECRYPT_MODE}, {@code @@ -728,7 +689,6 @@ public class Cipher { * cipher. * @throws InvalidParameterException * if the specified {@code opmode} is invalid. - * @since Android 1.0 */ public final void init(int opmode, Key key, AlgorithmParameters params, SecureRandom random) throws InvalidKeyException, @@ -768,7 +728,7 @@ public class Cipher { * When a cipher instance is initialized by a call to any of the {@code * init} methods, the state of the instance is overridden, means it is * equivalent to creating a new instance and calling it {@code init} method. - * + * * @param opmode * the operation this cipher instance should be initialized for * (one of: {@code ENCRYPT_MODE}, {@code DECRYPT_MODE}, {@code @@ -778,7 +738,6 @@ public class Cipher { * @throws InvalidKeyException * if the public key in the certificate can not be used to * initialize this cipher instance. - * @since Android 1.0 */ public final void init(int opmode, Certificate certificate) throws InvalidKeyException { @@ -795,7 +754,7 @@ public class Cipher { * The cipher will be initialized for the specified operation (one of: * encryption, decryption, key wrapping or key unwrapping) depending on * {@code opmode}. - *

        + *

        * It the type of the certificate is X.509 and the certificate has a key * usage extension field marked as critical, the specified {@code * opmode} has the be enabled for this key, otherwise an {@code @@ -806,11 +765,11 @@ public class Cipher { * cipher is supposed to generate the required parameters (using its * provider or random values). Random values are generated using {@code * random}. - *

        + *

        * When a cipher instance is initialized by a call to any of the {@code * init} methods, the state of the instance is overridden, means it is * equivalent to creating a new instance and calling it {@code init} method. - * + * * @param opmode * the operation this cipher instance should be initialized for * (one of: {@code ENCRYPT_MODE}, {@code DECRYPT_MODE}, {@code @@ -822,7 +781,6 @@ public class Cipher { * @throws InvalidKeyException * if the public key in the certificate can not be used to * initialize this cipher instance. - * @since Android 1.0 */ public final void init(int opmode, Certificate certificate, SecureRandom random) throws InvalidKeyException { @@ -874,7 +832,7 @@ public class Cipher { /** * Continues a multi-part transformation (encryption or decryption). The * transformed bytes are returned. - * + * * @param input * the input bytes to transform. * @return the transformed bytes in a new buffer, or {@code null} if the @@ -884,7 +842,6 @@ public class Cipher { * decryption. * @throws IllegalArgumentException * if the input is {@code null}. - * @since Android 1.0 */ public final byte[] update(byte[] input) { if (mode != ENCRYPT_MODE && mode != DECRYPT_MODE) { @@ -903,7 +860,7 @@ public class Cipher { /** * Continues a multi-part transformation (encryption or decryption). The * transformed bytes are returned. - * + * * @param input * the input bytes to transform. * @param inputOffset @@ -919,7 +876,6 @@ public class Cipher { * if the input is {@code null}, or if {@code inputOffset} and * {@code inputLen} do not specify a valid chunk in the input * buffer. - * @since Android 1.0 */ public final byte[] update(byte[] input, int inputOffset, int inputLen) { if (mode != ENCRYPT_MODE && mode != DECRYPT_MODE) { @@ -949,8 +905,7 @@ public class Cipher { * a {@code ShortBufferException} is thrown. Use * {@link Cipher#getOutputSize getOutputSize} to check for the size of the * output buffer. - *

        - * + * * @param input * the input bytes to transform. * @param inputOffset @@ -969,7 +924,6 @@ public class Cipher { * if the input is {@code null}, the output is {@code null}, or * if {@code inputOffset} and {@code inputLen} do not specify a * valid chunk in the input buffer. - * @since Android 1.0 */ public final int update(byte[] input, int inputOffset, int inputLen, byte[] output) throws ShortBufferException { @@ -984,8 +938,7 @@ public class Cipher { * a {@code ShortBufferException} is thrown. Use * {@link Cipher#getOutputSize getOutputSize} to check for the size of the * output buffer. - *

        - * + * * @param input * the input bytes to transform. * @param inputOffset @@ -1006,7 +959,6 @@ public class Cipher { * if the input is {@code null}, the output is {@code null}, or * if {@code inputOffset} and {@code inputLen} do not specify a * valid chunk in the input buffer. - * @since Android 1.0 */ public final int update(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset) throws ShortBufferException { @@ -1046,8 +998,7 @@ public class Cipher { * bytes a {@code ShortBufferException} is thrown. Use * {@link Cipher#getOutputSize getOutputSize} to check for the size of the * output buffer. - *

        - * + * * @param input * the input buffer to transform. * @param output @@ -1061,7 +1012,6 @@ public class Cipher { * @throws IllegalArgumentException * if the input buffer and the output buffer are the identical * object. - * @since Android 1.0 */ public final int update(ByteBuffer input, ByteBuffer output) throws ShortBufferException { @@ -1081,8 +1031,7 @@ public class Cipher { *

        * Processes any bytes that may have been buffered in previous {@code * update} calls. - *

        - * + * * @return the final bytes from the transformation. * @throws IllegalBlockSizeException * if the size of the resulting bytes is not a multiple of the @@ -1092,7 +1041,6 @@ public class Cipher { * @throws IllegalStateException * if this cipher instance is not initialized for encryption or * decryption. - * @since Android 1.0 */ public final byte[] doFinal() throws IllegalBlockSizeException, BadPaddingException { @@ -1108,9 +1056,9 @@ public class Cipher { *

        * Processes any bytes that may have been buffered in previous {@code * update} calls. - *

        + *

        * The final transformed bytes are stored in the {@code output} buffer. - * + * * @param output * the output buffer. * @param outputOffset @@ -1126,7 +1074,6 @@ public class Cipher { * @throws IllegalStateException * if this cipher instance is not initialized for encryption or * decryption. - * @since Android 1.0 */ public final int doFinal(byte[] output, int outputOffset) throws IllegalBlockSizeException, ShortBufferException, @@ -1147,8 +1094,7 @@ public class Cipher { *

        * Processes the bytes in {@code input} buffer, and any bytes that have been * buffered in previous {@code update} calls. - *

        - * + * * @param input * the input buffer. * @return the final bytes from the transformation. @@ -1160,7 +1106,6 @@ public class Cipher { * @throws IllegalStateException * if this cipher instance is not initialized for encryption or * decryption. - * @since Android 1.0 */ public final byte[] doFinal(byte[] input) throws IllegalBlockSizeException, BadPaddingException { @@ -1177,7 +1122,7 @@ public class Cipher { * Processes the {@code inputLen} bytes in {@code input} buffer at {@code * inputOffset}, and any bytes that have been buffered in previous {@code * update} calls. - * + * * @param input * the input buffer. * @param inputOffset @@ -1196,7 +1141,6 @@ public class Cipher { * @throws IllegalArgumentException * if {@code inputOffset} and {@code inputLen} do not specify an * valid chunk in the input buffer. - * @since Android 1.0 */ public final byte[] doFinal(byte[] input, int inputOffset, int inputLen) throws IllegalBlockSizeException, BadPaddingException { @@ -1218,7 +1162,7 @@ public class Cipher { * Processes the {@code inputLen} bytes in {@code input} buffer at {@code * inputOffset}, and any bytes that have been buffered in previous {@code * update} calls. - * + * * @param input * the input buffer. * @param inputOffset @@ -1241,7 +1185,6 @@ public class Cipher { * @throws IllegalArgumentException * if {@code inputOffset} and {@code inputLen} do not specify an * valid chunk in the input buffer. - * @since Android 1.0 */ public final int doFinal(byte[] input, int inputOffset, int inputLen, byte[] output) throws ShortBufferException, @@ -1255,8 +1198,7 @@ public class Cipher { * Processes the {@code inputLen} bytes in {@code input} buffer at {@code * inputOffset}, and any bytes that have been buffered in previous {@code * update} calls. - *

        - * + * * @param input * the input buffer. * @param inputOffset @@ -1281,7 +1223,6 @@ public class Cipher { * @throws IllegalArgumentException * if {@code inputOffset} and {@code inputLen} do not specify an * valid chunk in the input buffer. - * @since Android 1.0 */ public final int doFinal(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset) throws ShortBufferException, @@ -1306,8 +1247,7 @@ public class Cipher { * {@code input.position()}, and any bytes that have been buffered in * previous {@code update} calls. The transformed bytes are placed into * {@code output} buffer. - *

        - * + * * @param input * the input buffer. * @param output @@ -1326,7 +1266,6 @@ public class Cipher { * @throws IllegalStateException * if this cipher instance is not initialized for encryption or * decryption. - * @since Android 1.0 */ public final int doFinal(ByteBuffer input, ByteBuffer output) throws ShortBufferException, IllegalBlockSizeException, @@ -1344,7 +1283,7 @@ public class Cipher { /** * Wraps a key using this cipher instance. - * + * * @param key * the key to wrap. * @return the wrapped key. @@ -1355,7 +1294,6 @@ public class Cipher { * if this cipher instance can not wrap this key. * @throws IllegalStateException * if this cipher instance is not initialized for wrapping. - * @since Android 1.0 */ public final byte[] wrap(Key key) throws IllegalBlockSizeException, InvalidKeyException { @@ -1368,7 +1306,7 @@ public class Cipher { /** * Unwraps a key using this cipher instance. - * + * * @param wrappedKey * the wrapped key to unwrap. * @param wrappedKeyAlgorithm @@ -1386,7 +1324,6 @@ public class Cipher { * {@code wrappedKeyType} for the {@code wrappedKeyAlgorithm}. * @throws IllegalStateException * if this cipher instance is not initialized for unwrapping. - * @since Android 1.0 */ public final Key unwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, int wrappedKeyType) throws InvalidKeyException, @@ -1401,7 +1338,7 @@ public class Cipher { /** * Returns the maximum key length for the specified transformation. - * + * * @param transformation * the transformation name. * @return the maximum key length, currently {@code Integer.MAX_VALUE}. @@ -1410,7 +1347,6 @@ public class Cipher { * be found. * @throws NullPointerException * if {@code transformation} is {@code null}. - * @since Android 1.0 */ public static final int getMaxAllowedKeyLength(String transformation) throws NoSuchAlgorithmException { @@ -1425,7 +1361,7 @@ public class Cipher { /** * Returns the maximum cipher parameter value for the specified * transformation. If there is no maximum limit, {@code null} is returned. - * + * * @param transformation * the transformation name. * @return a parameter spec holding the maximum value or {@code null}. @@ -1435,7 +1371,6 @@ public class Cipher { * be found. * @throws NullPointerException * if {@code transformation} is {@code null}. - * @since Android 1.0 */ public static final AlgorithmParameterSpec getMaxAllowedParameterSpec( String transformation) throws NoSuchAlgorithmException { diff --git a/libcore/crypto/src/main/java/javax/crypto/CipherInputStream.java b/libcore/crypto/src/main/java/javax/crypto/CipherInputStream.java index ca64c49d6..b2c626d6d 100644 --- a/libcore/crypto/src/main/java/javax/crypto/CipherInputStream.java +++ b/libcore/crypto/src/main/java/javax/crypto/CipherInputStream.java @@ -32,9 +32,6 @@ import java.security.GeneralSecurityException; * by a {@code CipherInputStream}. For example, if a cipher initialized for * decryption is used with a {@code CipherInputStream}, the {@code * CipherInputStream} tries to read the data an decrypt them before returning. - *

        - * - * @since Android 1.0 */ public class CipherInputStream extends FilterInputStream { @@ -48,12 +45,11 @@ public class CipherInputStream extends FilterInputStream { /** * Creates a new {@code CipherInputStream} instance for an {@code * InputStream} and a cipher. - * + * * @param is * the input stream to read data from. * @param c * the cipher to process the data with. - * @since Android 1.0 */ public CipherInputStream(InputStream is, Cipher c) { super(is); @@ -65,11 +61,9 @@ public class CipherInputStream extends FilterInputStream { * InputStream} without a cipher. *

        * A {@code NullCipher} is created and used to process the data. - *

        - * + * * @param is * the input stream to read data from. - * @since Android 1.0 */ protected CipherInputStream(InputStream is) { this(is, new NullCipher()); @@ -77,11 +71,10 @@ public class CipherInputStream extends FilterInputStream { /** * Reads the next byte from this cipher input stream. - * + * * @return the next byte, or {@code -1} if the end of the stream is reached. * @throws IOException * if an error occurs. - * @since Android 1.0 */ @Override public int read() throws IOException { @@ -114,14 +107,13 @@ public class CipherInputStream extends FilterInputStream { /** * Reads the next {@code b.length} bytes from this input stream into buffer * {@code b}. - * + * * @param b * the buffer to be filled with data. * @return the number of bytes filled into buffer {@code b}, or {@code -1} * if the end of the stream is reached. * @throws IOException * if an error occurs. - * @since Android 1.0 */ @Override public int read(byte[] b) throws IOException { @@ -134,8 +126,7 @@ public class CipherInputStream extends FilterInputStream { *

        * if {@code b} is {@code null}, the next {@code len} bytes are read and * discarded. - *

        - * + * * @param b * the buffer to be filled with data. * @param off @@ -148,7 +139,6 @@ public class CipherInputStream extends FilterInputStream { * if an error occurs. * @throws NullPointerException * if the underlying input stream is {@code null}. - * @since Android 1.0 */ @Override public int read(byte[] b, int off, int len) throws IOException { @@ -175,15 +165,12 @@ public class CipherInputStream extends FilterInputStream { * The number of bytes skipped depends on the result of a call to * {@link CipherInputStream#available() available}. The smaller of n and the * result are the number of bytes being skipped. - *

        - * Skipping is (currently) not supported in Android. - * + * * @param n * the number of bytes that should be skipped. * @return the number of bytes actually skipped. * @throws IOException * if an error occurs - * @since Android 1.0 */ @Override public long skip(long n) throws IOException { @@ -199,13 +186,11 @@ public class CipherInputStream extends FilterInputStream { } /** - * Returns the number of bytes available without blocking. It (currently) - * always returns {@code 0} in Android. - * + * Returns the number of bytes available without blocking. + * * @return the number of bytes available, currently zero. * @throws IOException * if an error occurs - * @since Android 1.0 */ @Override public int available() throws IOException { @@ -215,10 +200,9 @@ public class CipherInputStream extends FilterInputStream { /** * Closes this {@code CipherInputStream}, also closes the underlying input * stream and call {@code doFinal} on the cipher object. - * + * * @throws IOException * if an error occurs. - * @since Android 1.0 */ @Override public void close() throws IOException { @@ -232,15 +216,15 @@ public class CipherInputStream extends FilterInputStream { } /** - * Returns whether this input stream supports {@code mark} and {@code reset} - * , which it does not. - * + * Returns whether this input stream supports {@code mark} and + * {@code reset}, which it does not. + * * @return false, since this input stream does not support {@code mark} and * {@code reset}. - * @since Android 1.0 */ @Override public boolean markSupported() { return false; } } + diff --git a/libcore/crypto/src/main/java/javax/crypto/CipherOutputStream.java b/libcore/crypto/src/main/java/javax/crypto/CipherOutputStream.java index 8bce42bbc..1f95b99f3 100644 --- a/libcore/crypto/src/main/java/javax/crypto/CipherOutputStream.java +++ b/libcore/crypto/src/main/java/javax/crypto/CipherOutputStream.java @@ -31,9 +31,6 @@ import javax.crypto.NullCipher; * by a {@code CipherOutputStream}. For example, if a cipher initialized for * encryption is used with a {@code CipherOutputStream}, the {@code * CipherOutputStream} tries to encrypt the data writing it out. - *

        - * - * @since Android 1.0 */ public class CipherOutputStream extends FilterOutputStream { @@ -43,12 +40,11 @@ public class CipherOutputStream extends FilterOutputStream { /** * Creates a new {@code CipherOutputStream} instance for an {@code * OutputStream} and a {@code Cipher}. - * + * * @param os * the output stream to write data to. * @param c * the cipher to process the data with. - * @since Android 1.0 */ public CipherOutputStream(OutputStream os, Cipher c) { super(os); @@ -60,11 +56,9 @@ public class CipherOutputStream extends FilterOutputStream { * OutputStream} without a cipher. *

        * A {@code NullCipher} is created to process the data. - *

        - * + * * @param os * the output stream to write the data to. - * @since Android 1.0 */ protected CipherOutputStream(OutputStream os) { this(os, new NullCipher()); @@ -72,12 +66,11 @@ public class CipherOutputStream extends FilterOutputStream { /** * Writes the single byte to this cipher output stream. - * + * * @param b * the byte to write. * @throws IOException * if an error occurs. - * @since Android 1.0 */ @Override public void write(int b) throws IOException { @@ -91,12 +84,11 @@ public class CipherOutputStream extends FilterOutputStream { /** * Writes the buffer of bytes to this cipher output stream. - * + * * @param b * the buffer of bytes. * @throws IOException * if an error occurs. - * @since Android 1.0 */ @Override public void write(byte[] b) throws IOException { @@ -106,7 +98,7 @@ public class CipherOutputStream extends FilterOutputStream { /** * Writes the {@code len} bytes from buffer {@code b} starting at offset * {@code off} to this cipher output stream. - * + * * @param b * the buffer. * @param off @@ -115,7 +107,6 @@ public class CipherOutputStream extends FilterOutputStream { * the number of bytes. * @throws IOException * if an error occurs. - * @since Android 1.0 */ @Override public void write(byte[] b, int off, int len) throws IOException { @@ -130,7 +121,7 @@ public class CipherOutputStream extends FilterOutputStream { /** * Flushes this cipher output stream. - * + * * @throws IOException * if an error occurs */ @@ -145,7 +136,7 @@ public class CipherOutputStream extends FilterOutputStream { * On the underlying cipher {@code doFinal} will be invoked, and any * buffered bytes from the cipher are also written out, and the cipher is * reset to its initial state. The underlying output stream is also closed. - * + * * @throws IOException * if an error occurs. */ @@ -173,3 +164,4 @@ public class CipherOutputStream extends FilterOutputStream { } } } + diff --git a/libcore/crypto/src/main/java/javax/crypto/CipherSpi.java b/libcore/crypto/src/main/java/javax/crypto/CipherSpi.java index f6da9295e..50fdd497a 100644 --- a/libcore/crypto/src/main/java/javax/crypto/CipherSpi.java +++ b/libcore/crypto/src/main/java/javax/crypto/CipherSpi.java @@ -44,7 +44,7 @@ import org.apache.harmony.crypto.internal.nls.Messages; * * The following behavior should be implemented for obtaining {@code Cipher} * instances. - *

        + *

        * When one of the {@link Cipher#getInstance} factory methods is called with a * transformation that is only an algorithm, check if the provider * defines a {@code CipherSpi} for "algorithm", if so: return it, otherwise @@ -76,53 +76,46 @@ import org.apache.harmony.crypto.internal.nls.Messages; * padding name and return it, otherwise throw a * {@link NoSuchAlgorithmException}. * - *

        - * + * * @see Cipher - * @since Android 1.0 */ public abstract class CipherSpi { /** * Creates a new {@code CipherSpi} instance. - * - * @since Android 1.0 */ public CipherSpi() { } /** * Sets the mode for this cipher. - * + * * @param mode * the name of the cipher mode. * @throws NoSuchAlgorithmException * if the specified cipher mode is not supported by this * provider. - * @since Android 1.0 */ protected abstract void engineSetMode(String mode) throws NoSuchAlgorithmException; /** * Sets the padding method for this cipher. - * + * * @param padding * the name of the padding method. * @throws NoSuchPaddingException * if the specified padding method is not supported by this * cipher. - * @since Android 1.0 */ protected abstract void engineSetPadding(String padding) throws NoSuchPaddingException; /** * Returns the block size of this cipher (in bytes) - * + * * @return the block size of this cipher, or zero if this cipher is not a * block cipher. - * @since Android 1.0 */ protected abstract int engineGetBlockSize(); @@ -133,21 +126,18 @@ public abstract class CipherSpi { *

        * The actual output length of the next call to {@code update} or {@code * doFinal} may be smaller than the length returned by this method. - *

        - * + * * @param inputLen * the length of the input (in bytes). * @return the size for a buffer (in bytes). - * @since Android 1.0 */ protected abstract int engineGetOutputSize(int inputLen); /** * Returns the Initialization Vector (IV) that was used to initialize this * cipher or {@code null} if none was used. - * + * * @return the Initialization Vector (IV), or {@code null} if none was used. - * @since Android 1.0 */ protected abstract byte[] engineGetIV(); @@ -157,12 +147,10 @@ public abstract class CipherSpi { * These may be a the same parameters that were used to create this cipher * instance, or may be a combination of default and random parameters, * depending on the underlying cipher implementation. - *

        - * + * * @return the parameters that where used to create this cipher instance, or * {@code null} if this cipher instance does not have any parameters * at all. - * @since Android 1.0 */ protected abstract AlgorithmParameters engineGetParameters(); @@ -173,7 +161,7 @@ public abstract class CipherSpi { * The cipher will be initialized for the specified operation (one of: * encryption, decryption, key wrapping or key unwrapping) depending on * {@code opmode}. - *

        + *

        * If this cipher instance needs any algorithm parameters or random values * that the specified key cannot provide, the underlying implementation of * this cipher is supposed to generate the required parameters (using its @@ -183,8 +171,7 @@ public abstract class CipherSpi { * When a cipher instance is initialized by a call to any of the {@code * init} methods, the state of the instance is overridden, means it is * equivalent to creating a new instance and calling it {@code init} method. - *

        - * + * * @param opmode * the operation this cipher instance should be initialized for * (one of: {@code ENCRYPT_MODE}, {@code DECRYPT_MODE}, {@code @@ -196,7 +183,6 @@ public abstract class CipherSpi { * @throws InvalidKeyException * if the specified key cannot be used to initialize this cipher * instance. - * @since Android 1.0 */ protected abstract void engineInit(int opmode, Key key, SecureRandom random) throws InvalidKeyException; @@ -208,7 +194,7 @@ public abstract class CipherSpi { * The cipher will be initialized for the specified operation (one of: * encryption, decryption, key wrapping or key unwrapping) depending on * {@code opmode}. - *

        + *

        * If this cipher instance needs any algorithm parameters and {@code params} * is {@code null}, the underlying implementation of this cipher is supposed * to generate the required parameters (using its provider or random @@ -217,8 +203,7 @@ public abstract class CipherSpi { * When a cipher instance is initialized by a call to any of the {@code * init} methods, the state of the instance is overridden, means it is * equivalent to creating a new instance and calling it {@code init} method. - *

        - * + * * @param opmode * the operation this cipher instance should be initialized for * (one of: {@code ENCRYPT_MODE}, {@code DECRYPT_MODE}, {@code @@ -235,7 +220,6 @@ public abstract class CipherSpi { * @throws InvalidAlgorithmParameterException * it the specified parameters are inappropriate for this * cipher. - * @since Android 1.0 */ protected abstract void engineInit(int opmode, Key key, AlgorithmParameterSpec params, SecureRandom random) @@ -248,7 +232,7 @@ public abstract class CipherSpi { * The cipher will be initialized for the specified operation (one of: * encryption, decryption, key wrapping or key unwrapping) depending on * {@code opmode}. - *

        + *

        * If this cipher instance needs any algorithm parameters and {@code params} * is {@code null}, the underlying implementation of this cipher is supposed * to generate the required parameters (using its provider or random @@ -257,8 +241,7 @@ public abstract class CipherSpi { * When a cipher instance is initialized by a call to any of the {@code * init} methods, the state of the instance is overridden, means it is * equivalent to creating a new instance and calling it {@code init} method. - *

        - * + * * @param opmode * the operation this cipher instance should be initialized for * (one of: {@code ENCRYPT_MODE}, {@code DECRYPT_MODE}, {@code @@ -275,7 +258,6 @@ public abstract class CipherSpi { * @throws InvalidAlgorithmParameterException * if the specified parameters are inappropriate for this * cipher. - * @since Android 1.0 */ protected abstract void engineInit(int opmode, Key key, AlgorithmParameters params, SecureRandom random) @@ -284,7 +266,7 @@ public abstract class CipherSpi { /** * Continues a multi-part transformation (encryption or decryption). The * transformed bytes are returned. - * + * * @param input * the input bytes to transform. * @param inputOffset @@ -299,7 +281,6 @@ public abstract class CipherSpi { * @throws IllegalArgumentException * if the input is null, or if {@code inputOffset} and {@code * inputLen} do not specify a valid chunk in the input buffer. - * @since Android 1.0 */ protected abstract byte[] engineUpdate(byte[] input, int inputOffset, int inputLen); @@ -312,8 +293,7 @@ public abstract class CipherSpi { * a {@code ShortBufferException} is thrown. Use * {@link Cipher#getOutputSize getOutputSize} to check for the size of the * output buffer. - *

        - * + * * @param input * the input bytes to transform. * @param inputOffset @@ -327,7 +307,6 @@ public abstract class CipherSpi { * @return the number of bytes placed in output. * @throws ShortBufferException * if the size of the {@code output} buffer is too small. - * @since Android 1.0 */ protected abstract int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset) @@ -342,8 +321,7 @@ public abstract class CipherSpi { * bytes a {@code ShortBufferException} is thrown. Use * {@link Cipher#getOutputSize getOutputSize} to check for the size of the * output buffer. - *

        - * + * * @param input * the input buffer to transform. * @param output @@ -351,7 +329,6 @@ public abstract class CipherSpi { * @return the number of bytes stored in the output buffer. * @throws ShortBufferException * if the size of the {@code output} buffer is too small. - * @since Android 1.0 */ protected int engineUpdate(ByteBuffer input, ByteBuffer output) throws ShortBufferException { @@ -395,8 +372,7 @@ public abstract class CipherSpi { * Processes the {@code inputLen} bytes in {@code input} buffer at {@code * inputOffset}, and any bytes that have been buffered in previous {@code * update} calls. - *

        - * + * * @param input * the input buffer. * @param inputOffset @@ -409,7 +385,6 @@ public abstract class CipherSpi { * cipher block size. * @throws BadPaddingException * if the padding of the data does not match the padding scheme. - * @since Android 1.0 */ protected abstract byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen) throws IllegalBlockSizeException, BadPaddingException; @@ -418,10 +393,9 @@ public abstract class CipherSpi { * Finishes a multi-part transformation (encryption or decryption). *

        * Processes the {@code inputLen} bytes in {@code input} buffer at - * {@code inputOffset}, and any bytes that have been buffered in previous + * {@code inputOffset}, and any bytes that have been buffered in previous * {@code update} calls. - *

        - * + * * @param input * the input buffer. * @param inputOffset @@ -440,7 +414,6 @@ public abstract class CipherSpi { * cipher block size. * @throws BadPaddingException * if the padding of the data does not match the padding scheme. - * @since Android 1.0 */ protected abstract int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset) @@ -454,8 +427,7 @@ public abstract class CipherSpi { * {@code input.position()}, and any bytes that have been buffered in * previous {@code update} calls. The transformed bytes are placed into * {@code output} buffer. - *

        - * + * * @param input * the input buffer. * @param output @@ -519,7 +491,7 @@ public abstract class CipherSpi { * this class (for backwards compatibility, it cannot be abstract). If this * method is not overridden, it throws an {@code * UnsupportedOperationException}. - * + * * @param key * the key to wrap. * @return the wrapped key @@ -528,7 +500,6 @@ public abstract class CipherSpi { * cipher block size. * @throws InvalidKeyException * if this cipher instance cannot wrap this key. - * @since Android 1.0 */ protected byte[] engineWrap(Key key) throws IllegalBlockSizeException, InvalidKeyException { @@ -542,8 +513,7 @@ public abstract class CipherSpi { * This method has been added to this class (for backwards compatibility, it * cannot be abstract). If this method is not overridden, it throws an * {@code UnsupportedOperationException}. - *

        - * + * * @param wrappedKey * the wrapped key to unwrap. * @param wrappedKeyAlgorithm @@ -559,7 +529,6 @@ public abstract class CipherSpi { * @throws NoSuchAlgorithmException * if no provider can be found that can create a key of type * {@code wrappedKeyType} for the {@code wrappedKeyAlgorithm}. - * @since Android 1.0 */ protected Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, int wrappedKeyType) throws InvalidKeyException, @@ -573,17 +542,16 @@ public abstract class CipherSpi { * added to this class (for backwards compatibility, it cannot be abstract). * If this method is not overridden, it throws an {@code * UnsupportedOperationException}. - * + * * @param key * the key to get the size for. * @return the size of a specified key object in bits. * @throws InvalidKeyException * if the size of the key cannot be determined by this * implementation. - * @since Android 1.0 */ protected int engineGetKeySize(Key key) throws InvalidKeyException { throw new UnsupportedOperationException( Messages.getString("crypto.12")); //$NON-NLS-1$ } -} +} \ No newline at end of file diff --git a/libcore/crypto/src/main/java/javax/crypto/EncryptedPrivateKeyInfo.java b/libcore/crypto/src/main/java/javax/crypto/EncryptedPrivateKeyInfo.java index 301cd4924..2d0fd259d 100644 --- a/libcore/crypto/src/main/java/javax/crypto/EncryptedPrivateKeyInfo.java +++ b/libcore/crypto/src/main/java/javax/crypto/EncryptedPrivateKeyInfo.java @@ -46,7 +46,6 @@ import org.apache.harmony.security.x509.AlgorithmIdentifier; * #8 - Private-Key Information Syntax Standard. *

        * The definition of ASN.1 is as follows: - *

        *
        * EncryptedPrivateKeyInfo ::= SEQUENCE { *
        encryptionAlgorithm AlgorithmIdentifier,
        @@ -57,8 +56,6 @@ import org.apache.harmony.security.x509.AlgorithmIdentifier; *
        algorithm OBJECT IDENTIFIER,
        *
        parameters ANY DEFINED BY algorithm OPTIONAL }
        *
        - * - * @since Android 1.0 */ public class EncryptedPrivateKeyInfo { // Encryption algorithm name @@ -75,14 +72,13 @@ public class EncryptedPrivateKeyInfo { /** * Creates an {@code EncryptedPrivateKeyInfo} instance from its encoded * representation by parsing it. - * + * * @param encoded * the encoded representation of this object * @throws IOException * if parsing the encoded representation fails. * @throws NullPointerException * if {@code encoded} is {@code null}. - * @since Android 1.0 */ public EncryptedPrivateKeyInfo(byte[] encoded) throws IOException { @@ -127,7 +123,7 @@ public class EncryptedPrivateKeyInfo { /** * Creates an {@code EncryptedPrivateKeyInfo} instance from an algorithm * name and its encrypted data. - * + * * @param encrAlgName * the name of an algorithm. * @param encryptedData @@ -139,7 +135,6 @@ public class EncryptedPrivateKeyInfo { * null}. * @throws IllegalArgumentException * if {@code encryptedData} is empty. - * @since Android 1.0 */ public EncryptedPrivateKeyInfo(String encrAlgName, byte[] encryptedData) throws NoSuchAlgorithmException { @@ -166,7 +161,7 @@ public class EncryptedPrivateKeyInfo { /** * Creates an {@code EncryptedPrivateKeyInfo} instance from the * encryption algorithm parameters an its encrypted data. - * + * * @param algParams * the encryption algorithm parameters. * @param encryptedData @@ -177,7 +172,6 @@ public class EncryptedPrivateKeyInfo { * @throws NullPointerException * if {@code algParams} or {@code encryptedData} is * {@code null}. - * @since Android 1.0 */ public EncryptedPrivateKeyInfo(AlgorithmParameters algParams, byte[] encryptedData) @@ -204,9 +198,8 @@ public class EncryptedPrivateKeyInfo { /** * Returns the name of the encryption algorithm. - * + * * @return the name of the encryption algorithm. - * @since Android 1.0 */ public String getAlgName() { return algName; @@ -214,9 +207,8 @@ public class EncryptedPrivateKeyInfo { /** * Returns the parameters used by the encryption algorithm. - * + * * @return the parameters used by the encryption algorithm. - * @since Android 1.0 */ public AlgorithmParameters getAlgParameters() { return algParameters; @@ -224,10 +216,9 @@ public class EncryptedPrivateKeyInfo { /** * Returns the encrypted data of this key. - * + * * @return the encrypted data of this key, each time this method is called a * new array is returned. - * @since Android 1.0 */ public byte[] getEncryptedData() { byte[] ret = new byte[encryptedData.length]; @@ -242,8 +233,7 @@ public class EncryptedPrivateKeyInfo { * The cipher must be initialize in either {@code Cipher.DECRYPT_MODE} or * {@code Cipher.UNWRAP_MODE} with the same parameters and key used for * encrypting this. - *

        - * + * * @param cipher * the cipher initialized for decrypting the encrypted data. * @return the extracted {@code PKCS8EncodedKeySpec}. @@ -252,7 +242,6 @@ public class EncryptedPrivateKeyInfo { * encrypted data. * @throws NullPointerException * if {@code cipher} is {@code null}. - * @since Android 1.0 */ public PKCS8EncodedKeySpec getKeySpec(Cipher cipher) throws InvalidKeySpecException { @@ -280,7 +269,7 @@ public class EncryptedPrivateKeyInfo { /** * Returns the {@code PKCS8EncodedKeySpec} object extracted from the * encrypted data. - * + * * @param decryptKey * the key to decrypt the encrypted data with. * @return the extracted {@code PKCS8EncodedKeySpec}. @@ -292,7 +281,6 @@ public class EncryptedPrivateKeyInfo { * data. * @throws NullPointerException * if {@code decryptKey} is {@code null}. - * @since Android 1.0 */ public PKCS8EncodedKeySpec getKeySpec(Key decryptKey) throws NoSuchAlgorithmException, @@ -331,7 +319,7 @@ public class EncryptedPrivateKeyInfo { /** * Returns the {@code PKCS8EncodedKeySpec} object extracted from the * encrypted data. - * + * * @param decryptKey * the key to decrypt the encrypted data with. * @param providerName @@ -349,7 +337,6 @@ public class EncryptedPrivateKeyInfo { * @throws NullPointerException * if {@code decryptKey} or {@code providerName} is {@code null} * . - * @since Android 1.0 */ public PKCS8EncodedKeySpec getKeySpec(Key decryptKey, String providerName) throws NoSuchProviderException, @@ -393,7 +380,7 @@ public class EncryptedPrivateKeyInfo { /** * Returns the {@code PKCS8EncodedKeySpec} object extracted from the * encrypted data. - * + * * @param decryptKey * the key to decrypt the encrypted data with. * @param provider @@ -407,7 +394,6 @@ public class EncryptedPrivateKeyInfo { * data. * @throws NullPointerException * if {@code decryptKey} or {@code provider} is {@code null}. - * @since Android 1.0 */ public PKCS8EncodedKeySpec getKeySpec(Key decryptKey, Provider provider) throws NoSuchAlgorithmException, @@ -448,11 +434,10 @@ public class EncryptedPrivateKeyInfo { /** * Returns the ASN.1 encoded representation of this object. - * + * * @return the ASN.1 encoded representation of this object. * @throws IOException * if encoding this object fails. - * @since Android 1.0 */ public byte[] getEncoded() throws IOException { if (encoded == null) { diff --git a/libcore/crypto/src/main/java/javax/crypto/ExemptionMechanism.java b/libcore/crypto/src/main/java/javax/crypto/ExemptionMechanism.java index 7c68d288f..76c88cbd0 100644 --- a/libcore/crypto/src/main/java/javax/crypto/ExemptionMechanism.java +++ b/libcore/crypto/src/main/java/javax/crypto/ExemptionMechanism.java @@ -34,8 +34,6 @@ import org.apache.harmony.security.fortress.Engine; /** * This class implements the functionality of an exemption mechanism such as * key recovery, key weakening, or key escrow. - * - * @since Android 1.0 */ public class ExemptionMechanism { @@ -62,14 +60,13 @@ public class ExemptionMechanism { /** * Creates a {@code ExemptionMechanism} instance. - * + * * @param exmechSpi * the implementation delegate. * @param provider * the associated provider. * @param mechanism * the name of the mechanism. - * @since Android 1.0 */ protected ExemptionMechanism(ExemptionMechanismSpi exmechSpi, Provider provider, String mechanism) { @@ -81,9 +78,8 @@ public class ExemptionMechanism { /** * Returns the name of this {@code ExemptionMechanism}. - * + * * @return the name of this {@code ExemptionMechanism}. - * @since Android 1.0 */ public final String getName() { return mechanism; @@ -92,7 +88,7 @@ public class ExemptionMechanism { /** * Returns a new {@code ExemptionMechanism} instance that provides the * specified exemption mechanism algorithm. - * + * * @param algorithm * the name of the requested exemption mechanism. * @return the new {@code ExemptionMechanism} instance. @@ -100,7 +96,6 @@ public class ExemptionMechanism { * if the specified algorithm is not available by any provider. * @throws NullPointerException * if the algorithm parameter is {@code null}. - * @since Android 1.0 */ public static final ExemptionMechanism getInstance(String algorithm) throws NoSuchAlgorithmException { @@ -117,7 +112,7 @@ public class ExemptionMechanism { /** * Returns a new {@code ExemptionMechansm} instance that provides the * specified exemption mechanism algorithm from the specified provider. - * + * * @param algorithm * the name of the requested exemption mechanism. * @param provider @@ -132,7 +127,6 @@ public class ExemptionMechanism { * if the algorithm parameter is {@code null}. * @throws IllegalArgumentException * if the provider parameter is {@code null}. - * @since Android 1.0 */ public static final ExemptionMechanism getInstance(String algorithm, String provider) throws NoSuchAlgorithmException, @@ -153,7 +147,7 @@ public class ExemptionMechanism { /** * Returns a new {@code ExemptionMechanism} instance that provides the * specified exemption mechanism algorithm from the specified provider. - * + * * @param algorithm * the name of the requested exemption mechanism. * @param provider @@ -166,7 +160,6 @@ public class ExemptionMechanism { * if the algorithm parameter is {@code null}. * @throws IllegalArgumentException * if the provider parameter is {@code null}. - * @since Android 1.0 */ public static final ExemptionMechanism getInstance(String algorithm, Provider provider) throws NoSuchAlgorithmException { @@ -185,9 +178,8 @@ public class ExemptionMechanism { /** * Returns the provider of this {@code ExemptionMechanism} instance. - * + * * @return the provider of this {@code ExemptionMechanism} instance. - * @since Android 1.0 */ public final Provider getProvider() { return provider; @@ -197,7 +189,7 @@ public class ExemptionMechanism { * Returns whether the result blob for this {@code ExemptionMechanism} * instance has been generated successfully and that the specified key is * the same as the one that was used to initialize and generate. - * + * * @param key * the key to verify. * @return whether the result blob for this {@code ExemptionMechanism} @@ -205,7 +197,6 @@ public class ExemptionMechanism { * @throws ExemptionMechanismException * if an error occurs while determining whether the result blob * has been generated successfully. - * @since Android 1.0 */ public final boolean isCryptoAllowed(Key key) throws ExemptionMechanismException { @@ -222,14 +213,13 @@ public class ExemptionMechanism { * Returns the size in bytes for the output buffer needed to hold the output * of the next {@link #genExemptionBlob} call, given the specified {@code * inputLen} (in bytes). - * + * * @param inputLen * the specified input length (in bytes). * @return the size in bytes for the output buffer. * @throws IllegalStateException * if this {@code ExemptionMechanism} instance is not * initialized. - * @since Android 1.0 */ public final int getOutputSize(int inputLen) throws IllegalStateException { if (!isInit) { @@ -241,14 +231,13 @@ public class ExemptionMechanism { /** * Initializes this {@code ExemptionMechanism} instance with the * specified key. - * + * * @param key * the key to initialize this instance with. * @throws InvalidKeyException * if the key cannot be used to initialize this mechanism. * @throws ExemptionMechanismException * if error(s) occur during initialization. - * @since Android 1.0 */ public final void init(Key key) throws InvalidKeyException, ExemptionMechanismException { @@ -261,7 +250,7 @@ public class ExemptionMechanism { /** * Initializes this {@code ExemptionMechanism} instance with the * specified key and algorithm parameters. - * + * * @param key * the key to initialize this instance with. * @param param @@ -273,7 +262,6 @@ public class ExemptionMechanism { * mechanism. * @throws ExemptionMechanismException * if error(s) occur during initialization. - * @since Android 1.0 */ public final void init(Key key, AlgorithmParameters param) throws InvalidKeyException, InvalidAlgorithmParameterException, @@ -287,7 +275,7 @@ public class ExemptionMechanism { /** * Initializes this {@code ExemptionMechanism} instance with the * specified key and algorithm parameters. - * + * * @param key * the key to initialize this instance with. * @param param @@ -299,7 +287,6 @@ public class ExemptionMechanism { * mechanism. * @throws ExemptionMechanismException * if error(s) occur during initialization. - * @since Android 1.0 */ public final void init(Key key, AlgorithmParameterSpec param) throws InvalidKeyException, InvalidAlgorithmParameterException, @@ -312,14 +299,13 @@ public class ExemptionMechanism { /** * Generates the result key blob for this exemption mechanism. - * + * * @return the result key blob for this exemption mechanism. * @throws IllegalStateException * if this {@code ExemptionMechanism} instance is not * initialized. * @throws ExemptionMechanismException * if error(s) occur during generation. - * @since Android 1.0 */ public final byte[] genExemptionBlob() throws IllegalStateException, ExemptionMechanismException { @@ -335,7 +321,7 @@ public class ExemptionMechanism { /** * Generates the result key blob for this exemption mechanism and stores it * into the {@code output} buffer. - * + * * @param output * the output buffer for the result key blob. * @return the number of bytes written to the {@code output} buffer. @@ -346,7 +332,6 @@ public class ExemptionMechanism { * if the provided buffer is too small for the result key blob. * @throws ExemptionMechanismException * if error(s) occur during generation. - * @since Android 1.0 */ public final int genExemptionBlob(byte[] output) throws IllegalStateException, ShortBufferException, @@ -357,7 +342,7 @@ public class ExemptionMechanism { /** * Generates the result key blob for this exemption mechanism and stores it * into the {@code output} buffer at offset {@code outputOffset}. - * + * * @param output * the output buffer for the result key blob. * @param outputOffset @@ -370,7 +355,6 @@ public class ExemptionMechanism { * if the provided buffer is too small for the result key blob. * @throws ExemptionMechanismException * if error(s) occur during generation. - * @since Android 1.0 */ public final int genExemptionBlob(byte[] output, int outputOffset) throws IllegalStateException, ShortBufferException, @@ -386,8 +370,6 @@ public class ExemptionMechanism { /** * Frees the references to the key used to initialize this instance. - * - * @since Android 1.0 */ @Override protected void finalize() { diff --git a/libcore/crypto/src/main/java/javax/crypto/ExemptionMechanismException.java b/libcore/crypto/src/main/java/javax/crypto/ExemptionMechanismException.java index 3af498b4a..d094ac359 100644 --- a/libcore/crypto/src/main/java/javax/crypto/ExemptionMechanismException.java +++ b/libcore/crypto/src/main/java/javax/crypto/ExemptionMechanismException.java @@ -21,8 +21,6 @@ import java.security.GeneralSecurityException; /** * This is the base class for {@code ExemptionMechanismException}. - * - * @since Android 1.0 */ public class ExemptionMechanismException extends GeneralSecurityException { @@ -37,7 +35,6 @@ public class ExemptionMechanismException extends GeneralSecurityException { * * @param msg * the exception message. - * @since Android 1.0 */ public ExemptionMechanismException(String msg) { super(msg); @@ -45,8 +42,6 @@ public class ExemptionMechanismException extends GeneralSecurityException { /** * Creates a new {@code ExemptionMechanismException} with no message. - * - * @since Android 1.0 */ public ExemptionMechanismException() { } diff --git a/libcore/crypto/src/main/java/javax/crypto/ExemptionMechanismSpi.java b/libcore/crypto/src/main/java/javax/crypto/ExemptionMechanismSpi.java index cef151632..39a27f6ab 100644 --- a/libcore/crypto/src/main/java/javax/crypto/ExemptionMechanismSpi.java +++ b/libcore/crypto/src/main/java/javax/crypto/ExemptionMechanismSpi.java @@ -26,26 +26,21 @@ import java.security.spec.AlgorithmParameterSpec; /** * The Service Provider Interface (SPI) definition for the {@code * ExemptionMechanism} class. - * - * @since Android 1.0 */ public abstract class ExemptionMechanismSpi { /** * Creates a new {@code ExemptionMechanismSpi} instance. - * - * @since Android 1.0 */ public ExemptionMechanismSpi() { } /** * Generates the result key blob for this exemption mechanism. - * + * * @return the result key blob for this exemption mechanism. * @throws ExemptionMechanismException * if error(s) occur during generation. - * @since Android 1.0 */ protected abstract byte[] engineGenExemptionBlob() throws ExemptionMechanismException; @@ -53,7 +48,7 @@ public abstract class ExemptionMechanismSpi { /** * Generates the result key blob for this exemption mechanism and stores it * into the {@code output} buffer at offset {@code outputOffset}. - * + * * @param output * the output buffer for the result key blob. * @param outputOffset @@ -63,7 +58,6 @@ public abstract class ExemptionMechanismSpi { * if the provided buffer is too small for the result key blob. * @throws ExemptionMechanismException * if error(s) occur during generation. - * @since Android 1.0 */ protected abstract int engineGenExemptionBlob(byte[] output, int outputOffset) throws ShortBufferException, @@ -73,7 +67,7 @@ public abstract class ExemptionMechanismSpi { * Returns the size in bytes for the output buffer needed to hold the output * of the next {@link #engineGenExemptionBlob} call, given the specified * {@code inputLen} (in bytes). - * + * * @param inputLen * the specified input length (in bytes). * @return the size in bytes for the output buffer. @@ -83,14 +77,13 @@ public abstract class ExemptionMechanismSpi { /** * Initializes this {@code ExemptionMechanism} instance with the specified * key. - * + * * @param key * the key to initialize this instance with. * @throws InvalidKeyException * if the key cannot be used to initialize this mechanism. * @throws ExemptionMechanismException * if error(s) occur during initialization. - * @since Android 1.0 */ protected abstract void engineInit(Key key) throws InvalidKeyException, ExemptionMechanismException; @@ -98,7 +91,7 @@ public abstract class ExemptionMechanismSpi { /** * Initializes this {@code ExemptionMechanism} instance with the specified * key and algorithm parameters. - * + * * @param key * the key to initialize this instance with. * @param params @@ -110,7 +103,6 @@ public abstract class ExemptionMechanismSpi { * mechanism. * @throws ExemptionMechanismException * if error(s) occur during initialization. - * @since Android 1.0 */ protected abstract void engineInit(Key key, AlgorithmParameters params) throws InvalidKeyException, InvalidAlgorithmParameterException, @@ -119,7 +111,7 @@ public abstract class ExemptionMechanismSpi { /** * Initializes this {@code ExemptionMechanism} instance with the specified * key and algorithm parameters. - * + * * @param key * the key to initialize this instance with. * @param params @@ -131,7 +123,6 @@ public abstract class ExemptionMechanismSpi { * mechanism. * @throws ExemptionMechanismException * if error(s) occur during initialization. - * @since Android 1.0 */ protected abstract void engineInit(Key key, AlgorithmParameterSpec params) throws InvalidKeyException, InvalidAlgorithmParameterException, diff --git a/libcore/crypto/src/main/java/javax/crypto/IllegalBlockSizeException.java b/libcore/crypto/src/main/java/javax/crypto/IllegalBlockSizeException.java index e376b85a1..0766b7cca 100644 --- a/libcore/crypto/src/main/java/javax/crypto/IllegalBlockSizeException.java +++ b/libcore/crypto/src/main/java/javax/crypto/IllegalBlockSizeException.java @@ -22,8 +22,6 @@ import java.security.GeneralSecurityException; /** * The exception, that is thrown when the data length provided to a block cipher * does not match the block size of the cipher. - * - * @since Android 1.0 */ public class IllegalBlockSizeException extends GeneralSecurityException { @@ -38,7 +36,6 @@ public class IllegalBlockSizeException extends GeneralSecurityException { * * @param msg * the message - * @since Android 1.0 */ public IllegalBlockSizeException(String msg) { super(msg); @@ -46,8 +43,6 @@ public class IllegalBlockSizeException extends GeneralSecurityException { /** * Creates a new {@code IllegalBlockSizeException}. - * - * @since Android 1.0 */ public IllegalBlockSizeException() { } diff --git a/libcore/crypto/src/main/java/javax/crypto/KeyAgreement.java b/libcore/crypto/src/main/java/javax/crypto/KeyAgreement.java index ee1f195f7..593aa378d 100644 --- a/libcore/crypto/src/main/java/javax/crypto/KeyAgreement.java +++ b/libcore/crypto/src/main/java/javax/crypto/KeyAgreement.java @@ -34,8 +34,6 @@ import org.apache.harmony.security.fortress.Engine; * This class provides the functionality for a key exchange protocol. This * enables two or more parties to agree on a secret key for symmetric * cryptography. - * - * @since Android 1.0 */ public class KeyAgreement { @@ -56,14 +54,13 @@ public class KeyAgreement { /** * Creates a new {@code KeyAgreement} instance. - * + * * @param keyAgreeSpi * the SPI delegate. * @param provider * the provider providing this KeyAgreement. * @param algorithm * the name of the key agreement algorithm. - * @since Android 1.0 */ protected KeyAgreement(KeyAgreementSpi keyAgreeSpi, Provider provider, String algorithm) { @@ -74,9 +71,8 @@ public class KeyAgreement { /** * Returns the name of the key agreement algorithm. - * + * * @return the name of the key agreement algorithm. - * @since Android 1.0 */ public final String getAlgorithm() { return algorithm; @@ -84,9 +80,8 @@ public class KeyAgreement { /** * Returns the provider for this {@code KeyAgreement} instance. - * + * * @return the provider for this {@code KeyAgreement} instance. - * @since Android 1.0 */ public final Provider getProvider() { return provider; @@ -94,7 +89,7 @@ public class KeyAgreement { /** * Creates a new {@code KeyAgreement} for the specified algorithm. - * + * * @param algorithm * the name of the key agreement algorithm to create. * @return a key agreement for the specified algorithm. @@ -102,7 +97,6 @@ public class KeyAgreement { * if no installed provider can provide the requested algorithm. * @throws NullPointerException * if the specified algorithm is {@code null}. - * @since Android 1.0 */ public static final KeyAgreement getInstance(String algorithm) throws NoSuchAlgorithmException { @@ -119,7 +113,7 @@ public class KeyAgreement { /** * Creates a new {@code KeyAgreement} for the specified algorithm from the * specified provider. - * + * * @param algorithm * the name of the key agreement algorithm to create. * @param provider @@ -134,7 +128,6 @@ public class KeyAgreement { * if the specified provider does not exist. * @throws IllegalArgumentException * if the specified provider name is {@code null} or empty. - * @since Android 1.0 */ public static final KeyAgreement getInstance(String algorithm, String provider) throws NoSuchAlgorithmException, @@ -152,7 +145,7 @@ public class KeyAgreement { /** * Create a new {@code KeyAgreement} for the specified algorithm from the * specified provider. - * + * * @param algorithm * the name of the key agreement algorithm to create. * @param provider @@ -184,13 +177,12 @@ public class KeyAgreement { /** * Initializes this {@code KeyAgreement} with the specified key. - * + * * @param key * the key to initialize this key agreement. * @throws InvalidKeyException * if the specified key cannot be used to initialize this key * agreement. - * @since Android 1.0 */ public final void init(Key key) throws InvalidKeyException { spiImpl.engineInit(key, rndm);//new SecureRandom()); @@ -199,7 +191,7 @@ public class KeyAgreement { /** * Initializes this {@code KeyAgreement} with the specified key and the * specified randomness source. - * + * * @param key * the key to initialize this key agreement. * @param random @@ -207,7 +199,6 @@ public class KeyAgreement { * @throws InvalidKeyException * if the specified key cannot be used to initialize this key * agreement. - * @since Android 1.0 */ public final void init(Key key, SecureRandom random) throws InvalidKeyException { @@ -217,7 +208,7 @@ public class KeyAgreement { /** * Initializes this {@code KeyAgreement} with the specified key and the * algorithm parameters. - * + * * @param key * the key to initialize this key agreement. * @param params @@ -228,7 +219,6 @@ public class KeyAgreement { * @throws InvalidAlgorithmParameterException * if the specified parameters are invalid for this key * agreement algorithm. - * @since Android 1.0 */ public final void init(Key key, AlgorithmParameterSpec params) throws InvalidKeyException, InvalidAlgorithmParameterException { @@ -238,7 +228,7 @@ public class KeyAgreement { /** * Initializes this {@code KeyAgreement} with the specified key, algorithm * parameters and randomness source. - * + * * @param key * the key to initialize this key agreement. * @param params @@ -251,7 +241,6 @@ public class KeyAgreement { * @throws InvalidAlgorithmParameterException * if the specified parameters are invalid for this key * agreement algorithm. - * @since Android 1.0 */ public final void init(Key key, AlgorithmParameterSpec params, SecureRandom random) throws InvalidKeyException, @@ -262,7 +251,7 @@ public class KeyAgreement { /** * Does the next (or the last) phase of the key agreement, using the * specified key. - * + * * @param key * the key received from the other party for this phase. * @param lastPhase @@ -275,7 +264,6 @@ public class KeyAgreement { * this phase, * @throws IllegalStateException * if this instance has not been initialized. - * @since Android 1.0 */ public final Key doPhase(Key key, boolean lastPhase) throws InvalidKeyException, IllegalStateException { @@ -284,11 +272,10 @@ public class KeyAgreement { /** * Generates the shared secret. - * + * * @return the generated shared secret. * @throws IllegalStateException * if this key agreement is not complete. - * @since Android 1.0 */ public final byte[] generateSecret() throws IllegalStateException { return spiImpl.engineGenerateSecret(); @@ -297,7 +284,7 @@ public class KeyAgreement { /** * Generates the shared secret and stores it into the buffer {@code * sharedSecred} at {@code offset}. - * + * * @param sharedSecret * the buffer to store the shared secret. * @param offset @@ -307,7 +294,6 @@ public class KeyAgreement { * if this key agreement is not complete. * @throws ShortBufferException * if the specified buffer is too small for the shared secret. - * @since Android 1.0 */ public final int generateSecret(byte[] sharedSecret, int offset) throws IllegalStateException, ShortBufferException { @@ -316,7 +302,7 @@ public class KeyAgreement { /** * Generates the shared secret. - * + * * @param algorithm * the algorithm to for the {@code SecretKey} * @return the shared secret as a {@code SecretKey} of the specified @@ -329,7 +315,6 @@ public class KeyAgreement { * @throws InvalidKeyException * if a {@code SecretKey} with the specified algorithm cannot be * created using the generated shared secret. - * @since Android 1.0 */ public final SecretKey generateSecret(String algorithm) throws IllegalStateException, NoSuchAlgorithmException, diff --git a/libcore/crypto/src/main/java/javax/crypto/KeyAgreementSpi.java b/libcore/crypto/src/main/java/javax/crypto/KeyAgreementSpi.java index fa9f3772f..50111837c 100644 --- a/libcore/crypto/src/main/java/javax/crypto/KeyAgreementSpi.java +++ b/libcore/crypto/src/main/java/javax/crypto/KeyAgreementSpi.java @@ -27,15 +27,11 @@ import java.security.spec.AlgorithmParameterSpec; /** * The Service Provider Interface (SPI) definition for the * {@code KeyAgreement} class. - * - * @since Android 1.0 */ public abstract class KeyAgreementSpi { - + /** * Creates a new {@code KeyAgreementSpi} instance. - * - * @since Android 1.0 */ public KeyAgreementSpi() { } @@ -43,7 +39,7 @@ public abstract class KeyAgreementSpi { /** * Does the next (or the last) phase of the key agreement, using the * specified key. - * + * * @param key * the key received from the other party for this phase. * @param lastPhase @@ -56,18 +52,16 @@ public abstract class KeyAgreementSpi { * this phase, * @throws IllegalStateException * if this instance has not been initialized. - * @since Android 1.0 */ protected abstract Key engineDoPhase(Key key, boolean lastPhase) throws InvalidKeyException, IllegalStateException; /** * Generates the shared secret. - * + * * @return the generated shared secret. * @throws IllegalStateException * if this key agreement is not complete. - * @since Android 1.0 */ protected abstract byte[] engineGenerateSecret() throws IllegalStateException; @@ -75,7 +69,7 @@ public abstract class KeyAgreementSpi { /** * Generates the shared secret and stores it into the buffer {@code * sharedSecred} at {@code offset}. - * + * * @param sharedSecret * the buffer to store the shared secret. * @param offset @@ -85,14 +79,13 @@ public abstract class KeyAgreementSpi { * if this key agreement is not complete. * @throws ShortBufferException * if the specified buffer is too small for the shared secret. - * @since Android 1.0 */ protected abstract int engineGenerateSecret(byte[] sharedSecret, int offset) throws IllegalStateException, ShortBufferException; /** * Generates the shared secret. - * + * * @param algorithm * the algorithm to for the {@code SecretKey} * @return the shared secret as a {@code SecretKey} of the specified @@ -105,7 +98,6 @@ public abstract class KeyAgreementSpi { * @throws InvalidKeyException * if a {@code SecretKey} with the specified algorithm cannot be * created using the generated shared secret. - * @since Android 1.0 */ protected abstract SecretKey engineGenerateSecret(String algorithm) throws IllegalStateException, NoSuchAlgorithmException, @@ -114,7 +106,7 @@ public abstract class KeyAgreementSpi { /** * Initializes this {@code KeyAgreementSpi} with the specified key and the * specified randomness source. - * + * * @param key * the key to initialize this key agreement. * @param random @@ -122,7 +114,6 @@ public abstract class KeyAgreementSpi { * @throws InvalidKeyException * if the specified key cannot be used to initialize this key * agreement. - * @since Android 1.0 */ protected abstract void engineInit(Key key, SecureRandom random) throws InvalidKeyException; @@ -130,7 +121,7 @@ public abstract class KeyAgreementSpi { /** * Initializes this {@code KeyAgreementSpi} with the specified key, * algorithm parameters and randomness source. - * + * * @param key * the key to initialize this key agreement. * @param params @@ -143,7 +134,6 @@ public abstract class KeyAgreementSpi { * @throws InvalidAlgorithmParameterException * if the specified parameters are invalid for this key * agreement algorithm. - * @since Android 1.0 */ protected abstract void engineInit(Key key, AlgorithmParameterSpec params, SecureRandom random) throws InvalidKeyException, diff --git a/libcore/crypto/src/main/java/javax/crypto/KeyGenerator.java b/libcore/crypto/src/main/java/javax/crypto/KeyGenerator.java index 3243b3947..f1dd3b2fb 100644 --- a/libcore/crypto/src/main/java/javax/crypto/KeyGenerator.java +++ b/libcore/crypto/src/main/java/javax/crypto/KeyGenerator.java @@ -32,8 +32,6 @@ import org.apache.harmony.security.fortress.Engine; /** * This class provides the public API for generating symmetric cryptographic * keys. - * - * @since Android 1.0 */ public class KeyGenerator { @@ -54,14 +52,13 @@ public class KeyGenerator { /** * Creates a new {@code KeyGenerator} instance. - * + * * @param keyGenSpi * the implementation delegate. * @param provider * the implementation provider. * @param algorithm * the name of the algorithm. - * @since Android 1.0 */ protected KeyGenerator(KeyGeneratorSpi keyGenSpi, Provider provider, String algorithm) { @@ -72,9 +69,8 @@ public class KeyGenerator { /** * Returns the name of the key generation algorithm. - * + * * @return the name of the key generation algorithm. - * @since Android 1.0 */ public final String getAlgorithm() { return algorithm; @@ -82,9 +78,8 @@ public class KeyGenerator { /** * Returns the provider of this {@code KeyGenerator} instance. - * + * * @return the provider of this {@code KeyGenerator} instance. - * @since Android 1.0 */ public final Provider getProvider() { return provider; @@ -93,7 +88,7 @@ public class KeyGenerator { /** * Creates a new {@code KeyGenerator} instance that provides the specified * key algorithm, - * + * * @param algorithm * the name of the requested key algorithm * @return the new {@code KeyGenerator} instance. @@ -101,7 +96,6 @@ public class KeyGenerator { * if the specified algorithm is not available by any provider. * @throws NullPointerException * if {@code algorithm} is {@code null}. - * @since Android 1.0 */ public static final KeyGenerator getInstance(String algorithm) throws NoSuchAlgorithmException { @@ -118,7 +112,7 @@ public class KeyGenerator { /** * Creates a new {@code KeyGenerator} instance that provides the specified * key algorithm from the specified provider. - * + * * @param algorithm * the name of the requested key algorithm. * @param provider @@ -133,7 +127,6 @@ public class KeyGenerator { * if the specified provider is name is {@code null} or empty. * @throws NullPointerException * if the specified algorithm name is {@code null}. - * @since Android 1.0 */ public static final KeyGenerator getInstance(String algorithm, String provider) throws NoSuchAlgorithmException, @@ -151,7 +144,7 @@ public class KeyGenerator { /** * Creates a new {@code KeyGenerator} instance that provides the specified * key algorithm from the specified provider. - * + * * @param algorithm * the name of the requested key algorithm. * @param provider @@ -164,7 +157,6 @@ public class KeyGenerator { * if the specified provider is {@code null}. * @throws NullPointerException * if the specified algorithm name is {@code null}. - * @since Android 1.0 */ public static final KeyGenerator getInstance(String algorithm, Provider provider) throws NoSuchAlgorithmException { @@ -183,9 +175,8 @@ public class KeyGenerator { /** * Generates a secret key. - * + * * @return the generated secret key. - * @since Android 1.0 */ public final SecretKey generateKey() { return spiImpl.engineGenerateKey(); @@ -194,13 +185,12 @@ public class KeyGenerator { /** * Initializes this {@code KeyGenerator} instance with the specified * algorithm parameters. - * + * * @param params * the parameters for the key generation algorithm. * @throws InvalidAlgorithmParameterException * if the parameters cannot be used to initialize this key * generator algorithm. - * @since Android 1.0 */ public final void init(AlgorithmParameterSpec params) throws InvalidAlgorithmParameterException { @@ -210,7 +200,7 @@ public class KeyGenerator { /** * Initializes this {@code KeyGenerator} instance with the specified * algorithm parameters and randomness source. - * + * * @param params * the parameters for the key generation algorithm. * @param random @@ -218,7 +208,6 @@ public class KeyGenerator { * @throws InvalidAlgorithmParameterException * if the parameters cannot be uses to initialize this key * generator algorithm. - * @since Android 1.0 */ public final void init(AlgorithmParameterSpec params, SecureRandom random) throws InvalidAlgorithmParameterException { @@ -228,10 +217,9 @@ public class KeyGenerator { /** * Initializes this {@code KeyGenerator} instance for the specified key size * (in bits). - * + * * @param keysize * the size of the key (in bits). - * @since Android 1.0 */ public final void init(int keysize) { spiImpl.engineInit(keysize, rndm);//new SecureRandom()); @@ -240,12 +228,11 @@ public class KeyGenerator { /** * Initializes this {@code KeyGenerator} instance for the specified key size * (in bits) using the specified randomness source. - * + * * @param keysize * the size of the key (in bits). * @param random * the randomness source for any random bytes. - * @since Android 1.0 */ public final void init(int keysize, SecureRandom random) { spiImpl.engineInit(keysize, random); @@ -254,10 +241,9 @@ public class KeyGenerator { /** * Initializes this {@code KeyGenerator} with the specified randomness * source. - * + * * @param random * the randomness source for any random bytes. - * @since Android 1.0 */ public final void init(SecureRandom random) { spiImpl.engineInit(random); diff --git a/libcore/crypto/src/main/java/javax/crypto/KeyGeneratorSpi.java b/libcore/crypto/src/main/java/javax/crypto/KeyGeneratorSpi.java index 165db6986..edbbe4321 100644 --- a/libcore/crypto/src/main/java/javax/crypto/KeyGeneratorSpi.java +++ b/libcore/crypto/src/main/java/javax/crypto/KeyGeneratorSpi.java @@ -26,30 +26,26 @@ import java.security.spec.AlgorithmParameterSpec; * {@code KeyGenerator} class. * * @see KeyGenerator - * @since Android 1.0 */ public abstract class KeyGeneratorSpi { /** * Creates a new {@code KeyGeneratorSpi} instance. - * - * @since Android 1.0 */ public KeyGeneratorSpi() { } /** * Generates a secret key. - * + * * @return the generated secret key. - * @since Android 1.0 */ protected abstract SecretKey engineGenerateKey(); /** * Initializes this {@code KeyGeneratorSpi} instance with the specified * algorithm parameters and randomness source. - * + * * @param params * the parameters for the key generation algorithm. * @param random @@ -57,7 +53,6 @@ public abstract class KeyGeneratorSpi { * @throws InvalidAlgorithmParameterException * if the parameters cannot be uses to initialize this key * generator algorithm. - * @since Android 1.0 */ protected abstract void engineInit(AlgorithmParameterSpec params, SecureRandom random) throws InvalidAlgorithmParameterException; @@ -65,22 +60,20 @@ public abstract class KeyGeneratorSpi { /** * Initializes this {@code KeyGenerator} instance for the specified key * size (in bits) using the specified randomness source. - * + * * @param keysize * the size of the key (in bits). * @param random * the randomness source for any random bytes. - * @since Android 1.0 */ protected abstract void engineInit(int keysize, SecureRandom random); /** * Initializes this {@code KeyGenerator} with the specified randomness * source. - * + * * @param random * the randomness source for any random bytes. - * @since Android 1.0 */ protected abstract void engineInit(SecureRandom random); } \ No newline at end of file diff --git a/libcore/crypto/src/main/java/javax/crypto/Mac.java b/libcore/crypto/src/main/java/javax/crypto/Mac.java index 95f453938..94ea20e25 100644 --- a/libcore/crypto/src/main/java/javax/crypto/Mac.java +++ b/libcore/crypto/src/main/java/javax/crypto/Mac.java @@ -34,8 +34,6 @@ import org.apache.harmony.security.fortress.Engine; /** * This class provides the public API for Message Authentication Code * (MAC) algorithms. - * - * @since Android 1.0 */ public class Mac implements Cloneable { @@ -56,14 +54,13 @@ public class Mac implements Cloneable { /** * Creates a new {@code Mac} instance. - * + * * @param macSpi * the implementation delegate. * @param provider * the implementation provider. * @param algorithm * the name of the MAC algorithm. - * @since Android 1.0 */ protected Mac(MacSpi macSpi, Provider provider, String algorithm) { this.provider = provider; @@ -76,7 +73,6 @@ public class Mac implements Cloneable { * Returns the name of the MAC algorithm. * * @return the name of the MAC algorithm. - * @since Android 1.0 */ public final String getAlgorithm() { return algorithm; @@ -84,9 +80,8 @@ public class Mac implements Cloneable { /** * Returns the provider of this {@code Mac} instance. - * + * * @return the provider of this {@code Mac} instance. - * @since Android 1.0 */ public final Provider getProvider() { return provider; @@ -102,8 +97,8 @@ public class Mac implements Cloneable { * @throws NoSuchAlgorithmException * if the specified algorithm is not available by any provider. * @throws NullPointerException - * if {@code algorithm} is {@code null}. - * @since Android 1.0 + * if {@code algorithm} is {@code null} (instead of + * NoSuchAlgorithmException as in 1.4 release). */ public static final Mac getInstance(String algorithm) throws NoSuchAlgorithmException { @@ -133,8 +128,8 @@ public class Mac implements Cloneable { * @throws IllegalArgumentException * if the specified provider name is {@code null} or empty. * @throws NullPointerException - * if {@code algorithm} is {@code null} - * @since Android 1.0. + * if {@code algorithm} is {@code null} (instead of + * NoSuchAlgorithmException as in 1.4 release). */ public static final Mac getInstance(String algorithm, String provider) throws NoSuchAlgorithmException, NoSuchProviderException { @@ -163,8 +158,8 @@ public class Mac implements Cloneable { * @throws IllegalArgumentException * if {@code provider} is {@code null}. * @throws NullPointerException - * if {@code algorithm} is {@code null}. - * @since Android 1.0 + * if {@code algorithm} is {@code null} (instead of + * NoSuchAlgorithmException as in 1.4 release). */ public static final Mac getInstance(String algorithm, Provider provider) throws NoSuchAlgorithmException { @@ -182,7 +177,7 @@ public class Mac implements Cloneable { /** * Returns the length of this MAC (in bytes). - * + * * @return the length of this MAC (in bytes). */ public final int getMacLength() { @@ -192,7 +187,7 @@ public class Mac implements Cloneable { /** * Initializes this {@code Mac} instance with the specified key and * algorithm parameters. - * + * * @param key * the key to initialize this algorithm. * @param params @@ -203,7 +198,6 @@ public class Mac implements Cloneable { * @throws InvalidAlgorithmParameterException * if the specified parameters cannot be used to initialize this * algorithm. - * @since Android 1.0 */ public final void init(Key key, AlgorithmParameterSpec params) throws InvalidKeyException, InvalidAlgorithmParameterException { @@ -216,7 +210,7 @@ public class Mac implements Cloneable { /** * Initializes this {@code Mac} instance with the specified key. - * + * * @param key * the key to initialize this algorithm. * @throws InvalidKeyException @@ -225,7 +219,6 @@ public class Mac implements Cloneable { * @throws RuntimeException * if the specified key cannot be used to initialize this * algorithm. - * @since Android 1.0 */ public final void init(Key key) throws InvalidKeyException { if (key == null) { @@ -241,12 +234,11 @@ public class Mac implements Cloneable { /** * Updates this {@code Mac} instance with the specified byte. - * + * * @param input * the byte * @throws IllegalStateException * if this MAC is not initialized. - * @since Android 1.0 */ public final void update(byte input) throws IllegalStateException { if (!isInitMac) { @@ -258,7 +250,7 @@ public class Mac implements Cloneable { /** * Updates this {@code Mac} instance with the data from the specified buffer * {@code input} from the specified {@code offset} and length {@code len}. - * + * * @param input * the buffer. * @param offset @@ -270,7 +262,6 @@ public class Mac implements Cloneable { * @throws IllegalArgumentException * if {@code offset} and {@code len} do not specified a valid * chunk in {@code input} buffer. - * @since Android 1.0 */ public final void update(byte[] input, int offset, int len) throws IllegalStateException { @@ -288,12 +279,11 @@ public class Mac implements Cloneable { /** * Copies the buffer provided as input for further processing. - * + * * @param input * the buffer. * @throws IllegalStateException * if this MAC is not initialized. - * @since Android 1.0 */ public final void update(byte[] input) throws IllegalStateException { if (!isInitMac) { @@ -308,12 +298,11 @@ public class Mac implements Cloneable { * Updates this {@code Mac} instance with the data from the specified * buffer, starting at {@link ByteBuffer#position()}, including the next * {@link ByteBuffer#remaining()} bytes. - * + * * @param input * the buffer. * @throws IllegalStateException * if this MAC is not initialized. - * @since Android 1.0 */ public final void update(ByteBuffer input) { if (!isInitMac) { @@ -333,12 +322,10 @@ public class Mac implements Cloneable { * This {@code Mac} instance is reverted to its initial state and can be * used to start the next MAC computation with the same parameters or * initialized with different parameters. - *

        - * + * * @return the generated digest. * @throws IllegalStateException * if this MAC is not initialized. - * @since Android 1.0 */ public final byte[] doFinal() throws IllegalStateException { if (!isInitMac) { @@ -355,8 +342,7 @@ public class Mac implements Cloneable { * This {@code Mac} instance is reverted to its initial state and can be * used to start the next MAC computation with the same parameters or * initialized with different parameters. - *

        - * + * * @param output * the output buffer * @param outOffset @@ -368,7 +354,6 @@ public class Mac implements Cloneable { * of the output buffer. * @throws IllegalStateException * if this MAC is not initialized. - * @since Android 1.0 */ public final void doFinal(byte[] output, int outOffset) throws ShortBufferException, IllegalStateException { @@ -401,14 +386,12 @@ public class Mac implements Cloneable { * This {@code Mac} instance is reverted to its initial state and can be * used to start the next MAC computation with the same parameters or * initialized with different parameters. - *

        - * + * * @param input * the final bytes. * @return the generated digest. * @throws IllegalStateException * if this MAC is not initialized. - * @since Android 1.0 */ public final byte[] doFinal(byte[] input) throws IllegalStateException { if (!isInitMac) { @@ -426,9 +409,6 @@ public class Mac implements Cloneable { * This {@code Mac} instance is reverted to its initial state and can be * used to start the next MAC computation with the same parameters or * initialized with different parameters. - *

        - * - * @since Android 1.0 */ public final void reset() { spiImpl.engineReset(); @@ -436,11 +416,10 @@ public class Mac implements Cloneable { /** * Clones this {@code Mac} instance and the underlying implementation. - * + * * @return the cloned instance. * @throws CloneNotSupportedException * if the underlying implementation does not support cloning. - * @since Android 1.0 */ @Override public final Object clone() throws CloneNotSupportedException { @@ -449,4 +428,4 @@ public class Mac implements Cloneable { mac.isInitMac = this.isInitMac; return mac; } -} +} \ No newline at end of file diff --git a/libcore/crypto/src/main/java/javax/crypto/MacSpi.java b/libcore/crypto/src/main/java/javax/crypto/MacSpi.java index 475618410..b2683d226 100644 --- a/libcore/crypto/src/main/java/javax/crypto/MacSpi.java +++ b/libcore/crypto/src/main/java/javax/crypto/MacSpi.java @@ -28,30 +28,26 @@ import java.nio.ByteBuffer; * Mac} class. * * @see Mac - * @since Android 1.0 */ public abstract class MacSpi { - + /** * Creates a new {@code MacSpi} instance. - * - * @since Android 1.0 */ public MacSpi() { } /** * Returns the length of this MAC (in bytes). - * + * * @return the length of this MAC (in bytes). - * @since Android 1.0 */ protected abstract int engineGetMacLength(); /** * Initializes this {@code MacSpi} instance with the specified key and * algorithm parameters. - * + * * @param key * the key to initialize this algorithm. * @param params @@ -62,17 +58,15 @@ public abstract class MacSpi { * @throws InvalidAlgorithmParameterException * if the specified parameters cannot be used to initialize this * algorithm. - * @since Android 1.0 */ protected abstract void engineInit(Key key, AlgorithmParameterSpec params) throws InvalidKeyException, InvalidAlgorithmParameterException; /** * Updates this {@code MacSpi} instance with the specified byte. - * + * * @param input * the byte. - * @since Android 1.0 */ protected abstract void engineUpdate(byte input); @@ -80,14 +74,13 @@ public abstract class MacSpi { * Updates this {@code MacSpi} instance with the data from the specified * buffer {@code input} from the specified {@code offset} and length {@code * len}. - * + * * @param input * the buffer. * @param offset * the offset in the buffer. * @param len * the length of the data in the buffer. - * @since Android 1.0 */ protected abstract void engineUpdate(byte[] input, int offset, int len); @@ -95,10 +88,9 @@ public abstract class MacSpi { * Updates this {@code MacSpi} instance with the data from the specified * buffer, starting at {@link ByteBuffer#position()}, including the next * {@link ByteBuffer#remaining()} bytes. - * + * * @param input * the buffer. - * @since Android 1.0 */ protected void engineUpdate(ByteBuffer input) { if (!input.hasRemaining()) { @@ -126,10 +118,8 @@ public abstract class MacSpi { * This {@code MacSpi} instance is reverted to its initial state and * can be used to start the next MAC computation with the same parameters or * initialized with different parameters. - *

        - * + * * @return the generated digest. - * @since Android 1.0 */ protected abstract byte[] engineDoFinal(); @@ -139,19 +129,15 @@ public abstract class MacSpi { * This {@code MacSpi} instance is reverted to its initial state and can be * used to start the next MAC computation with the same parameters or * initialized with different parameters. - *

        - * - * @since Android 1.0 */ protected abstract void engineReset(); /** * Clones this {@code MacSpi} instance. - * + * * @return the cloned instance. * @throws CloneNotSupportedException * if cloning is not supported. - * @since Android 1.0 */ @Override public Object clone() throws CloneNotSupportedException { diff --git a/libcore/crypto/src/main/java/javax/crypto/NoSuchPaddingException.java b/libcore/crypto/src/main/java/javax/crypto/NoSuchPaddingException.java index 4afb8aba4..55e1f1e4e 100644 --- a/libcore/crypto/src/main/java/javax/crypto/NoSuchPaddingException.java +++ b/libcore/crypto/src/main/java/javax/crypto/NoSuchPaddingException.java @@ -22,8 +22,6 @@ import java.security.GeneralSecurityException; /** * The exception that is thrown when the requested padding mechanism is not * supported. - * - * @since Android 1.0 */ public class NoSuchPaddingException extends GeneralSecurityException { @@ -38,7 +36,6 @@ public class NoSuchPaddingException extends GeneralSecurityException { * * @param msg * the message. - * @since Android 1.0 */ public NoSuchPaddingException(String msg) { super(msg); @@ -46,8 +43,6 @@ public class NoSuchPaddingException extends GeneralSecurityException { /** * Creates a new {@code NoSuchPaddingException}. - * - * @since Android 1.0 */ public NoSuchPaddingException() { } diff --git a/libcore/crypto/src/main/java/javax/crypto/NullCipher.java b/libcore/crypto/src/main/java/javax/crypto/NullCipher.java index 49f96c220..fadf3aee9 100644 --- a/libcore/crypto/src/main/java/javax/crypto/NullCipher.java +++ b/libcore/crypto/src/main/java/javax/crypto/NullCipher.java @@ -32,8 +32,6 @@ import org.apache.harmony.crypto.internal.NullCipherSpi; /** * This class provides an identity cipher that does not transform the input data * in any way. The encrypted data is identical to the plain text. - * - * @since Android 1.0 */ public class NullCipher extends Cipher { diff --git a/libcore/crypto/src/main/java/javax/crypto/SealedObject.java b/libcore/crypto/src/main/java/javax/crypto/SealedObject.java index 4e71453e5..bf453db77 100644 --- a/libcore/crypto/src/main/java/javax/crypto/SealedObject.java +++ b/libcore/crypto/src/main/java/javax/crypto/SealedObject.java @@ -40,19 +40,13 @@ import org.apache.harmony.crypto.internal.nls.Messages; *

        * Since a {@code SealedObject} instance is a serializable object itself it can * either be stored or transmitted over an insecure channel. - *

        + *

        * The wrapped object can later be decrypted (unsealed) using the corresponding * key and then be deserialized to retrieve the original object.The sealed * object itself keeps track of the cipher and corresponding parameters. - * - * @since Android 1.0 */ public class SealedObject implements Serializable { - // the value of this field was derived by using serialver utility - /** - * @com.intel.drl.spec_ref - */ private static final long serialVersionUID = 4482838265551344752L; /** @@ -76,8 +70,7 @@ public class SealedObject implements Serializable { * and sealing it using the specified cipher. *

        * The cipher must be fully initialized. - *

        - * + * * @param object * the object to seal, can be {@code null}. * @param c @@ -90,7 +83,6 @@ public class SealedObject implements Serializable { * size. * @throws NullPointerException * if the cipher is {@code null}. - * @since Android 1.0 */ public SealedObject(Serializable object, Cipher c) throws IOException, IllegalBlockSizeException { @@ -117,10 +109,9 @@ public class SealedObject implements Serializable { /** * Creates a new {@code SealedObject} instance by copying the data from * the specified object. - * + * * @param so * the object to copy. - * @since Android 1.0 */ protected SealedObject(SealedObject so) { if (so == null) { @@ -134,9 +125,8 @@ public class SealedObject implements Serializable { /** * Returns the algorithm this object was sealed with. - * + * * @return the algorithm this object was sealed with. - * @since Android 1.0 */ public final String getAlgorithm() { return sealAlg; @@ -144,7 +134,7 @@ public class SealedObject implements Serializable { /** * Returns the wrapped object, decrypting it using the specified key. - * + * * @param key * the key to decrypt the data with. * @return the encapsulated object. @@ -156,7 +146,6 @@ public class SealedObject implements Serializable { * if the algorithm to decrypt the data is not available. * @throws InvalidKeyException * if the specified key cannot be used to decrypt the data. - * @since Android 1.0 */ public final Object getObject(Key key) throws IOException, ClassNotFoundException, @@ -207,7 +196,7 @@ public class SealedObject implements Serializable { /** * Returns the wrapped object, decrypting it using the specified * cipher. - * + * * @param c * the cipher to decrypt the data. * @return the encapsulated object. @@ -221,7 +210,6 @@ public class SealedObject implements Serializable { * size. * @throws BadPaddingException * if the padding of the data does not match the padding scheme. - * @since Android 1.0 */ public final Object getObject(Cipher c) throws IOException, ClassNotFoundException, @@ -239,7 +227,7 @@ public class SealedObject implements Serializable { /** * Returns the wrapped object, decrypting it using the specified key. The * specified provider is used to retrieve the cipher algorithm. - * + * * @param key * the key to decrypt the data. * @param provider @@ -255,7 +243,6 @@ public class SealedObject implements Serializable { * if the specified provider is not available. * @throws InvalidKeyException * if the specified key cannot be used to decrypt the data. - * @since Android 1.0 */ public final Object getObject(Key key, String provider) throws IOException, ClassNotFoundException, diff --git a/libcore/crypto/src/main/java/javax/crypto/SecretKey.java b/libcore/crypto/src/main/java/javax/crypto/SecretKey.java index 102f8887f..ac6107443 100644 --- a/libcore/crypto/src/main/java/javax/crypto/SecretKey.java +++ b/libcore/crypto/src/main/java/javax/crypto/SecretKey.java @@ -24,21 +24,18 @@ import java.security.Key; *

        * This interface is a marker interface to group secret keys and to * provide type safety for. - *

        + *

        * Implementations of this interface have to overwrite the * {@link Object#equals(Object) equals} and {@link Object#hashCode() hashCode} * from {@link java.lang.Object} so comparison is done using the actual key data * and not the object reference. - * - * @since Android 1.0 */ public interface SecretKey extends Key { /** * The serialization version identifier. - * + * * @serial - * @since Android 1.0 */ public static final long serialVersionUID = -4795878709595146952L; } \ No newline at end of file diff --git a/libcore/crypto/src/main/java/javax/crypto/SecretKeyFactory.java b/libcore/crypto/src/main/java/javax/crypto/SecretKeyFactory.java index a420dab7a..57bca3ec7 100644 --- a/libcore/crypto/src/main/java/javax/crypto/SecretKeyFactory.java +++ b/libcore/crypto/src/main/java/javax/crypto/SecretKeyFactory.java @@ -40,9 +40,6 @@ import org.apache.harmony.security.fortress.Engine; * * Which key specifications are supported by the {@link #generateSecret} and * {@link #getKeySpec} is provider dependent. - *

        - * - * @since Android 1.0 */ public class SecretKeyFactory { @@ -60,14 +57,13 @@ public class SecretKeyFactory { /** * Creates a new {@code SecretKeyFactory} - * + * * @param keyFacSpi * the SPI delegate. * @param provider * the provider providing this key factory. * @param algorithm * the algorithm name for the secret key. - * @since Android 1.0 */ protected SecretKeyFactory(SecretKeyFactorySpi keyFacSpi, Provider provider, String algorithm) { @@ -78,9 +74,8 @@ public class SecretKeyFactory { /** * Returns the name of the secret key algorithm. - * + * * @return the name of the secret key algorithm. - * @since Android 1.0 */ public final String getAlgorithm() { return algorithm; @@ -88,9 +83,8 @@ public class SecretKeyFactory { /** * Returns the provider for this {@code SecretKeyFactory} instance. - * + * * @return the provider for this {@code SecretKeyFactory} instance. - * @since Android 1.0 */ public final Provider getProvider() { return provider; @@ -99,7 +93,7 @@ public class SecretKeyFactory { /** * Creates a new {@code SecretKeyFactory} instance for the specified key * algorithm. - * + * * @param algorithm * the name of the key algorithm. * @return a secret key factory for the specified key algorithm. @@ -107,7 +101,6 @@ public class SecretKeyFactory { * if no installed provider can provide the requested algorithm. * @throws NullPointerException * if the specified algorithm is {@code null}. - * @since Android 1.0 */ public static final SecretKeyFactory getInstance(String algorithm) throws NoSuchAlgorithmException { @@ -124,7 +117,7 @@ public class SecretKeyFactory { /** * Creates a new {@code SecretKeyFactory} instance for the specified key * algorithm from the specified {@code provider}. - * + * * @param algorithm * the name of the key algorithm. * @param provider @@ -139,7 +132,6 @@ public class SecretKeyFactory { * if the specified provider does not exist. * @throws IllegalArgumentException * if the specified provider name is {@code null} or empty. - * @since Android 1.0 */ public static final SecretKeyFactory getInstance(String algorithm, String provider) throws NoSuchAlgorithmException, @@ -157,7 +149,7 @@ public class SecretKeyFactory { /** * Creates a new {@code SecretKeyFactory} instance for the specified key * algorithm from the specified provider. - * + * * @param algorithm * the name of the key algorithm. * @param provider @@ -171,7 +163,6 @@ public class SecretKeyFactory { * if the specified provider is {@code null}. * @throws NullPointerException * is the specified algorithm name is {@code null}. - * @since Android 1.0 */ public static final SecretKeyFactory getInstance(String algorithm, Provider provider) throws NoSuchAlgorithmException { @@ -190,14 +181,13 @@ public class SecretKeyFactory { /** * Generate a secret key from the specified key specification. - * + * * @param keySpec * the key specification. * @return a secret key. * @throws InvalidKeySpecException * if the specified key specification cannot be used to generate * a secret key. - * @since Android 1.0 */ public final SecretKey generateSecret(KeySpec keySpec) throws InvalidKeySpecException { @@ -206,7 +196,7 @@ public class SecretKeyFactory { /** * Returns the key specification of the specified secret key. - * + * * @param key * the secret key to get the specification from. * @param keySpec @@ -215,7 +205,6 @@ public class SecretKeyFactory { * @throws InvalidKeySpecException * if the specified secret key cannot be transformed into the * requested key specification. - * @since Android 1.0 */ @SuppressWarnings("unchecked") public final KeySpec getKeySpec(SecretKey key, Class keySpec) @@ -226,14 +215,13 @@ public class SecretKeyFactory { /** * Translates the specified secret key into an instance of the corresponding * key from the provider of this key factory. - * + * * @param key * the secret key to translate. * @return the corresponding translated key. * @throws InvalidKeyException * if the specified key cannot be translated using this key * factory. - * @since Android 1.0 */ public final SecretKey translateKey(SecretKey key) throws InvalidKeyException { diff --git a/libcore/crypto/src/main/java/javax/crypto/SecretKeyFactorySpi.java b/libcore/crypto/src/main/java/javax/crypto/SecretKeyFactorySpi.java index f834dbb4b..5d7764ed8 100644 --- a/libcore/crypto/src/main/java/javax/crypto/SecretKeyFactorySpi.java +++ b/libcore/crypto/src/main/java/javax/crypto/SecretKeyFactorySpi.java @@ -24,35 +24,31 @@ import java.security.spec.KeySpec; /** * The Service Provider Interface (SPI) definition for the {@code * SecretKeyFactory} class. - * - * @since Android 1.0 */ public abstract class SecretKeyFactorySpi { /** * Creates a new {@code SecretKeyFactorySpi} instance. - * @since Android 1.0 */ public SecretKeyFactorySpi() { } /** * Generate a secret key from the specified key specification. - * + * * @param keySpec * the key specification. * @return a secret key. * @throws InvalidKeySpecException * if the specified key specification cannot be used to generate * a secret key. - * @since Android 1.0 */ protected abstract SecretKey engineGenerateSecret(KeySpec keySpec) throws InvalidKeySpecException; /** * Returns the key specification of the specified secret key. - * + * * @param key * the secret key to get the specification from. * @param keySpec @@ -61,7 +57,6 @@ public abstract class SecretKeyFactorySpi { * @throws InvalidKeySpecException * if the specified secret key cannot be transformed into the * requested key specification. - * @since Android 1.0 */ @SuppressWarnings("unchecked") protected abstract KeySpec engineGetKeySpec(SecretKey key, Class keySpec) @@ -70,14 +65,13 @@ public abstract class SecretKeyFactorySpi { /** * Translates the specified secret key into an instance of the corresponding * key from the provider of this key factory. - * + * * @param key * the secret key to translate. * @return the corresponding translated key. * @throws InvalidKeyException * if the specified key cannot be translated using this key * factory. - * @since Android 1.0 */ protected abstract SecretKey engineTranslateKey(SecretKey key) throws InvalidKeyException; diff --git a/libcore/crypto/src/main/java/javax/crypto/ShortBufferException.java b/libcore/crypto/src/main/java/javax/crypto/ShortBufferException.java index 593a31ee6..56480a852 100644 --- a/libcore/crypto/src/main/java/javax/crypto/ShortBufferException.java +++ b/libcore/crypto/src/main/java/javax/crypto/ShortBufferException.java @@ -27,8 +27,6 @@ import java.security.GeneralSecurityException; /** * The exception that is thrown when the result of an operation is attempted to * store in a user provided buffer that is too small. - * - * @since Android 1.0 */ public class ShortBufferException extends GeneralSecurityException { @@ -43,7 +41,6 @@ public class ShortBufferException extends GeneralSecurityException { * * @param msg * the exception message. - * @since Android 1.0 */ public ShortBufferException(String msg) { super(msg); @@ -51,8 +48,6 @@ public class ShortBufferException extends GeneralSecurityException { /** * Creates a new instance of {@code ShortBufferException}. - * - * @since Android 1.0 */ public ShortBufferException() { } diff --git a/libcore/crypto/src/main/java/javax/crypto/interfaces/DHKey.java b/libcore/crypto/src/main/java/javax/crypto/interfaces/DHKey.java index f686844d4..6ef17d406 100644 --- a/libcore/crypto/src/main/java/javax/crypto/interfaces/DHKey.java +++ b/libcore/crypto/src/main/java/javax/crypto/interfaces/DHKey.java @@ -21,14 +21,12 @@ import javax.crypto.spec.DHParameterSpec; /** * The interface for a Diffie-Hellman key. - * - * @since Android 1.0 */ public interface DHKey { /** * Returns the parameters for this key. - * + * * @return the parameters for this key. */ public DHParameterSpec getParams(); diff --git a/libcore/crypto/src/main/java/javax/crypto/interfaces/DHPrivateKey.java b/libcore/crypto/src/main/java/javax/crypto/interfaces/DHPrivateKey.java index d39268b81..f63e0cb5e 100644 --- a/libcore/crypto/src/main/java/javax/crypto/interfaces/DHPrivateKey.java +++ b/libcore/crypto/src/main/java/javax/crypto/interfaces/DHPrivateKey.java @@ -22,8 +22,6 @@ import java.security.PrivateKey; /** * The interface for a private key in the Diffie-Hellman key exchange protocol. - * - * @since Android 1.0 */ public interface DHPrivateKey extends DHKey, PrivateKey { diff --git a/libcore/crypto/src/main/java/javax/crypto/interfaces/DHPublicKey.java b/libcore/crypto/src/main/java/javax/crypto/interfaces/DHPublicKey.java index 75201a745..92e8f10f1 100644 --- a/libcore/crypto/src/main/java/javax/crypto/interfaces/DHPublicKey.java +++ b/libcore/crypto/src/main/java/javax/crypto/interfaces/DHPublicKey.java @@ -21,9 +21,7 @@ import java.math.BigInteger; import java.security.PublicKey; /** - * The interface for a public key in the Diffie-Hellman key exchange protocol. - * - * @since Android 1.0 + * The interface for a public key in the Diffie-Hellman key exchange protocol. */ public interface DHPublicKey extends DHKey, PublicKey { diff --git a/libcore/crypto/src/main/java/javax/crypto/interfaces/PBEKey.java b/libcore/crypto/src/main/java/javax/crypto/interfaces/PBEKey.java index 4612ad2d1..c718715e9 100644 --- a/libcore/crypto/src/main/java/javax/crypto/interfaces/PBEKey.java +++ b/libcore/crypto/src/main/java/javax/crypto/interfaces/PBEKey.java @@ -21,8 +21,6 @@ import javax.crypto.SecretKey; /** * The interface to a password-based-encryption key. - * - * @since Android 1.0 */ public interface PBEKey extends SecretKey { @@ -33,21 +31,21 @@ public interface PBEKey extends SecretKey { /** * Returns the iteration count, 0 if not specified. - * + * * @return the iteration count, 0 if not specified. */ public int getIterationCount(); /** * Returns a copy of the salt data or null if not specified. - * + * * @return a copy of the salt data or null if not specified. */ public byte[] getSalt(); /** * Returns a copy to the password. - * + * * @return a copy to the password. */ public char[] getPassword(); diff --git a/libcore/crypto/src/main/java/javax/crypto/spec/DESKeySpec.java b/libcore/crypto/src/main/java/javax/crypto/spec/DESKeySpec.java index 2a994d5ab..372a68d00 100644 --- a/libcore/crypto/src/main/java/javax/crypto/spec/DESKeySpec.java +++ b/libcore/crypto/src/main/java/javax/crypto/spec/DESKeySpec.java @@ -24,8 +24,6 @@ import org.apache.harmony.crypto.internal.nls.Messages; /** * The key specification for a DES key. - * - * @since Android 1.0 */ public class DESKeySpec implements KeySpec { @@ -96,7 +94,7 @@ public class DESKeySpec implements KeySpec { /** * Creates a new DESKeySpec from the first 8 bytes of the * specified key data. - * + * * @param key * the key data. * @throws InvalidKeyException @@ -109,7 +107,7 @@ public class DESKeySpec implements KeySpec { /** * Creates a new DESKeySpec from the first 8 bytes of the * specified key data starting at offset. - * + * * @param key * the key data * @param offset @@ -133,7 +131,7 @@ public class DESKeySpec implements KeySpec { /** * Returns a copy of the key. - * + * * @return a copy of the key. */ public byte[] getKey() { @@ -145,7 +143,7 @@ public class DESKeySpec implements KeySpec { /** * Returns whether the specified key data starting at offset is * parity-adjusted. - * + * * @param key * the key data. * @param offset @@ -185,7 +183,7 @@ public class DESKeySpec implements KeySpec { /** * Returns whether the specified key data starting at offset is * weak or semi-weak. - * + * * @param key * the key data. * @param offset diff --git a/libcore/crypto/src/main/java/javax/crypto/spec/DESedeKeySpec.java b/libcore/crypto/src/main/java/javax/crypto/spec/DESedeKeySpec.java index 186f07dcd..199eba6c0 100644 --- a/libcore/crypto/src/main/java/javax/crypto/spec/DESedeKeySpec.java +++ b/libcore/crypto/src/main/java/javax/crypto/spec/DESedeKeySpec.java @@ -24,8 +24,6 @@ import org.apache.harmony.crypto.internal.nls.Messages; /** * The key specification for a triple-DES (DES-EDE) key. - * - * @since Android 1.0 */ public class DESedeKeySpec implements KeySpec { @@ -39,7 +37,7 @@ public class DESedeKeySpec implements KeySpec { /** * Creates a new DESedeKeySpec instance from the first 24 ( * {@link #DES_EDE_KEY_LEN}) bytes of the specified key data. - * + * * @param key * the key data. * @throws InvalidKeyException @@ -64,7 +62,7 @@ public class DESedeKeySpec implements KeySpec { * Creates a new DESedeKeySpec instance from the first 24 ( * {@link #DES_EDE_KEY_LEN} ) bytes of the specified key data starting at * offset. - * + * * @param key * the key data * @param offset @@ -90,7 +88,7 @@ public class DESedeKeySpec implements KeySpec { /** * Returns a copy of the key. - * + * * @return a copy of the key. */ public byte[] getKey() { @@ -102,7 +100,7 @@ public class DESedeKeySpec implements KeySpec { /** * Returns whether the specified key data starting at offset is * parity-adjusted. - * + * * @param key * the key data. * @param offset diff --git a/libcore/crypto/src/main/java/javax/crypto/spec/DHGenParameterSpec.java b/libcore/crypto/src/main/java/javax/crypto/spec/DHGenParameterSpec.java index a14931801..f6ddc035c 100644 --- a/libcore/crypto/src/main/java/javax/crypto/spec/DHGenParameterSpec.java +++ b/libcore/crypto/src/main/java/javax/crypto/spec/DHGenParameterSpec.java @@ -22,8 +22,6 @@ import java.security.spec.AlgorithmParameterSpec; /** * The algorithm parameter specification for generating Diffie-Hellman * parameters used in Diffie-Hellman key agreement. - * - * @since Android 1.0 */ public class DHGenParameterSpec implements AlgorithmParameterSpec { @@ -33,7 +31,7 @@ public class DHGenParameterSpec implements AlgorithmParameterSpec { /** * Creates a new DHGenParameterSpec instance with the specified * parameters. - * + * * @param primeSize * the size of the prime modulus in bits. * @param exponentSize @@ -46,7 +44,7 @@ public class DHGenParameterSpec implements AlgorithmParameterSpec { /** * Returns the size of the prime modulus in bits. - * + * * @return the size of the prime modulus in bits. */ public int getPrimeSize() { @@ -55,7 +53,7 @@ public class DHGenParameterSpec implements AlgorithmParameterSpec { /** * Returns the size of the random exponent in bits. - * + * * @return the size of the random exponent in bits. */ public int getExponentSize() { diff --git a/libcore/crypto/src/main/java/javax/crypto/spec/DHParameterSpec.java b/libcore/crypto/src/main/java/javax/crypto/spec/DHParameterSpec.java index 9bb94b515..6030b404b 100644 --- a/libcore/crypto/src/main/java/javax/crypto/spec/DHParameterSpec.java +++ b/libcore/crypto/src/main/java/javax/crypto/spec/DHParameterSpec.java @@ -22,8 +22,6 @@ import java.security.spec.AlgorithmParameterSpec; /** * The algorithm parameter specification for the Diffie-Hellman algorithm. - * - * @since Android 1.0 */ public class DHParameterSpec implements AlgorithmParameterSpec { @@ -34,7 +32,7 @@ public class DHParameterSpec implements AlgorithmParameterSpec { /** * Creates a new DHParameterSpec instance with the specified * prime modulus and base generator. - * + * * @param p * the prime modulus. * @param g @@ -50,7 +48,7 @@ public class DHParameterSpec implements AlgorithmParameterSpec { * Creates a new DHParameterSpec instance with the specified * prime modulus, base generator and size (in bits) of the * random exponent. - * + * * @param p * the prime modulus. * @param g @@ -66,7 +64,7 @@ public class DHParameterSpec implements AlgorithmParameterSpec { /** * Returns the prime modulus of this parameter specification. - * + * * @return the prime modulus. */ public BigInteger getP() { @@ -75,7 +73,7 @@ public class DHParameterSpec implements AlgorithmParameterSpec { /** * Returns the base generator of this parameter specification. - * + * * @return the base generator. */ public BigInteger getG() { @@ -84,7 +82,7 @@ public class DHParameterSpec implements AlgorithmParameterSpec { /** * Returns the size (in bits) of the random exponent. - * + * * @return the size (in bits) of the random exponent. */ public int getL() { diff --git a/libcore/crypto/src/main/java/javax/crypto/spec/DHPrivateKeySpec.java b/libcore/crypto/src/main/java/javax/crypto/spec/DHPrivateKeySpec.java index 6652de86f..925a00378 100644 --- a/libcore/crypto/src/main/java/javax/crypto/spec/DHPrivateKeySpec.java +++ b/libcore/crypto/src/main/java/javax/crypto/spec/DHPrivateKeySpec.java @@ -22,8 +22,6 @@ import java.security.spec.KeySpec; /** * The key specification for a Diffie-Hellman private key. - * - * @since Android 1.0 */ public class DHPrivateKeySpec implements KeySpec { @@ -35,7 +33,7 @@ public class DHPrivateKeySpec implements KeySpec { * Creates a new DHPrivateKeySpec with the specified private * value x. prime modulus p and base * generator g. - * + * * @param x * the private value. * @param p @@ -51,7 +49,7 @@ public class DHPrivateKeySpec implements KeySpec { /** * Returns the private value x. - * + * * @return the private value x. */ public BigInteger getX() { @@ -60,7 +58,7 @@ public class DHPrivateKeySpec implements KeySpec { /** * Returns the prime modulus p. - * + * * @return the prime modulus p. */ public BigInteger getP() { @@ -69,10 +67,11 @@ public class DHPrivateKeySpec implements KeySpec { /** * Returns the base generator g. - * + * * @return the base generator g. */ public BigInteger getG() { return g; } } + diff --git a/libcore/crypto/src/main/java/javax/crypto/spec/DHPublicKeySpec.java b/libcore/crypto/src/main/java/javax/crypto/spec/DHPublicKeySpec.java index 68d3267f7..a5d4461a9 100644 --- a/libcore/crypto/src/main/java/javax/crypto/spec/DHPublicKeySpec.java +++ b/libcore/crypto/src/main/java/javax/crypto/spec/DHPublicKeySpec.java @@ -22,8 +22,6 @@ import java.security.spec.KeySpec; /** * The key specification for a Diffie-Hellman public key. - * - * @since Android 1.0 */ public class DHPublicKeySpec implements KeySpec { @@ -35,7 +33,7 @@ public class DHPublicKeySpec implements KeySpec { * Creates a new DHPublicKeySpec instance with the specified * public value y, the prime modulus * p and the base generator g. - * + * * @param y * the public value. * @param p @@ -51,7 +49,7 @@ public class DHPublicKeySpec implements KeySpec { /** * Returns the public value y. - * + * * @return the public value y. */ public BigInteger getY() { @@ -60,7 +58,7 @@ public class DHPublicKeySpec implements KeySpec { /** * Returns the prime modulus p. - * + * * @return the prime modulus p. */ public BigInteger getP() { @@ -69,7 +67,7 @@ public class DHPublicKeySpec implements KeySpec { /** * Returns the base generator g; - * + * * @return the base generator g; */ public BigInteger getG() { diff --git a/libcore/crypto/src/main/java/javax/crypto/spec/IvParameterSpec.java b/libcore/crypto/src/main/java/javax/crypto/spec/IvParameterSpec.java index 2f532a816..ce7f9d3c4 100644 --- a/libcore/crypto/src/main/java/javax/crypto/spec/IvParameterSpec.java +++ b/libcore/crypto/src/main/java/javax/crypto/spec/IvParameterSpec.java @@ -35,7 +35,7 @@ public class IvParameterSpec implements AlgorithmParameterSpec { /** * Creates a new IvParameterSpec instance with the bytes from * the specified buffer iv used as initialization vector. - * + * * @param iv * the buffer used as initialization vector. * @throws NullPointerException @@ -53,7 +53,7 @@ public class IvParameterSpec implements AlgorithmParameterSpec { * Creates a new IvParameterSpec instance with len * bytes from the specified buffer iv starting at * offset. - * + * * @param iv * the buffer used as initialization vector. * @param offset @@ -81,7 +81,7 @@ public class IvParameterSpec implements AlgorithmParameterSpec { /** * Returns a copy of the initialization vector data. - * + * * @return a copy of the initialization vector data. */ public byte[] getIV() { diff --git a/libcore/crypto/src/main/java/javax/crypto/spec/OAEPParameterSpec.java b/libcore/crypto/src/main/java/javax/crypto/spec/OAEPParameterSpec.java index ebb6cce14..29b572d8d 100644 --- a/libcore/crypto/src/main/java/javax/crypto/spec/OAEPParameterSpec.java +++ b/libcore/crypto/src/main/java/javax/crypto/spec/OAEPParameterSpec.java @@ -26,8 +26,6 @@ import javax.crypto.spec.PSource; *

        * This padding algorithm is defined in the PKCS #1 standard. - * - * @since Android 1.0 */ public class OAEPParameterSpec implements AlgorithmParameterSpec { @@ -60,7 +58,7 @@ public class OAEPParameterSpec implements AlgorithmParameterSpec { * message digest algorithm name, mask generation function * (mgf) algorithm name, parameters for the mgf * algorithm and the source of the label L. - * + * * @param mdName * the message digest algorithm name. * @param mgfName @@ -87,7 +85,7 @@ public class OAEPParameterSpec implements AlgorithmParameterSpec { /** * Returns the algorithm name of the message digest. - * + * * @return the algorithm name of the message digest. */ public String getDigestAlgorithm() { @@ -96,7 +94,7 @@ public class OAEPParameterSpec implements AlgorithmParameterSpec { /** * Returns the algorithm name of the mask generation function. - * + * * @return the algorithm name of the mask generation function. */ public String getMGFAlgorithm() { @@ -106,7 +104,7 @@ public class OAEPParameterSpec implements AlgorithmParameterSpec { /** * Returns the algorithm parameter specification for the mask generation * function algorithm. - * + * * @return the algorithm parameter specification for the mask generation * function algorithm. */ @@ -116,7 +114,7 @@ public class OAEPParameterSpec implements AlgorithmParameterSpec { /** * Returns the source of the label L. - * + * * @return the source of the label L. */ public PSource getPSource() { diff --git a/libcore/crypto/src/main/java/javax/crypto/spec/PBEKeySpec.java b/libcore/crypto/src/main/java/javax/crypto/spec/PBEKeySpec.java index c46617e0a..d79fd3c63 100644 --- a/libcore/crypto/src/main/java/javax/crypto/spec/PBEKeySpec.java +++ b/libcore/crypto/src/main/java/javax/crypto/spec/PBEKeySpec.java @@ -27,8 +27,6 @@ import org.apache.harmony.crypto.internal.nls.Messages; *

        * Password based encryption is described in PKCS #5. - * - * @since Android 1.0 */ public class PBEKeySpec implements KeySpec { @@ -39,7 +37,7 @@ public class PBEKeySpec implements KeySpec { /** * Creates a new PBEKeySpec with the specified password. - * + * * @param password * the password. */ @@ -58,7 +56,7 @@ public class PBEKeySpec implements KeySpec { /** * Creates a new PBEKeySpec with the specified password, salt, * iteration count and the desired length of the derived key. - * + * * @param password * the password. * @param salt @@ -104,7 +102,7 @@ public class PBEKeySpec implements KeySpec { /** * Creates a new PBEKeySpec with the specified password, salt * and iteration count. - * + * * @param password * the password. * @param salt @@ -150,7 +148,7 @@ public class PBEKeySpec implements KeySpec { /** * Returns a copy of the password of this key specification. - * + * * @return a copy of the password of this key specification. * @throws IllegalStateException * if the password has been cleared before. @@ -166,7 +164,7 @@ public class PBEKeySpec implements KeySpec { /** * Returns a copy of the salt of this key specification. - * + * * @return a copy of the salt of this key specification or null if none is * specified. */ @@ -181,7 +179,7 @@ public class PBEKeySpec implements KeySpec { /** * Returns the iteration count of this key specification. - * + * * @return the iteration count of this key specification. */ public final int getIterationCount() { @@ -190,7 +188,7 @@ public class PBEKeySpec implements KeySpec { /** * Returns the desired key length of the derived key. - * + * * @return the desired key length of the derived key. */ public final int getKeyLength() { diff --git a/libcore/crypto/src/main/java/javax/crypto/spec/PBEParameterSpec.java b/libcore/crypto/src/main/java/javax/crypto/spec/PBEParameterSpec.java index 190986eff..cce3832ce 100644 --- a/libcore/crypto/src/main/java/javax/crypto/spec/PBEParameterSpec.java +++ b/libcore/crypto/src/main/java/javax/crypto/spec/PBEParameterSpec.java @@ -23,12 +23,10 @@ import org.apache.harmony.crypto.internal.nls.Messages; /** * The algorithm parameter specification for a password based encryption - * algorithm. + * algorithm. *

        * Password based encryption is described in PKCS #5. - * - * @since Android 1.0 * */ public class PBEParameterSpec implements AlgorithmParameterSpec { @@ -39,7 +37,7 @@ public class PBEParameterSpec implements AlgorithmParameterSpec { /** * Creates a new PBEParameterSpec with the specified salt and * iteration count. - * + * * @param salt * the salt. * @param iterationCount @@ -58,7 +56,7 @@ public class PBEParameterSpec implements AlgorithmParameterSpec { /** * Returns a copy to the salt. - * + * * @return a copy to the salt. */ public byte[] getSalt() { @@ -69,7 +67,7 @@ public class PBEParameterSpec implements AlgorithmParameterSpec { /** * Returns the iteration count. - * + * * @return the iteration count. */ public int getIterationCount() { diff --git a/libcore/crypto/src/main/java/javax/crypto/spec/PSource.java b/libcore/crypto/src/main/java/javax/crypto/spec/PSource.java index d5bdf1b15..30fd8af49 100644 --- a/libcore/crypto/src/main/java/javax/crypto/spec/PSource.java +++ b/libcore/crypto/src/main/java/javax/crypto/spec/PSource.java @@ -22,8 +22,6 @@ import org.apache.harmony.crypto.internal.nls.Messages; /** * The source of the label L as specified in PKCS #1. - * - * @since Android 1.0 */ public class PSource { @@ -34,7 +32,7 @@ public class PSource { /** * Creates a new PSource instance with the specified source * algorithm identifier. - * + * * @param pSrcName * the source algorithm identifier. * @throws NullPointerException @@ -49,7 +47,7 @@ public class PSource { /** * Returns the source algorithm identifier. - * + * * @return the source algorithm identifier. */ public String getAlgorithm() { @@ -59,15 +57,14 @@ public class PSource { /** * The explicit specification of the parameter P used in the * source algorithm. - * - * @since Android 1.0 */ public static final class PSpecified extends PSource { private final byte[] p; /** - * The instance of PSpecified with the default value byte[0] for P + * The instance of PSpecified with the default value + * byte[0] for P */ public static final PSpecified DEFAULT = new PSpecified(); @@ -79,7 +76,7 @@ public class PSource { /** * Creates a new instance of PSpecified with the specified * parameter P. - * + * * @param p * the parameter P. * @throws NullPointerException @@ -98,7 +95,7 @@ public class PSource { /** * Returns a copy of the value of the parameter P. - * + * * @return a copy of the value of the parameter P */ public byte[] getValue() { diff --git a/libcore/crypto/src/main/java/javax/crypto/spec/RC2ParameterSpec.java b/libcore/crypto/src/main/java/javax/crypto/spec/RC2ParameterSpec.java index bd76cf45d..bc7a39cab 100644 --- a/libcore/crypto/src/main/java/javax/crypto/spec/RC2ParameterSpec.java +++ b/libcore/crypto/src/main/java/javax/crypto/spec/RC2ParameterSpec.java @@ -25,8 +25,6 @@ import org.apache.harmony.crypto.internal.nls.Messages; /** * The algorithm parameter specification for the RC2 algorithm. - * - * @since Android 1.0 */ public class RC2ParameterSpec implements AlgorithmParameterSpec { @@ -36,7 +34,7 @@ public class RC2ParameterSpec implements AlgorithmParameterSpec { /** * Creates a new RC2ParameterSpec instance with the specified * effective key length (in bits), - * + * * @param effectiveKeyBits * the effective key length (in bits). */ @@ -51,7 +49,7 @@ public class RC2ParameterSpec implements AlgorithmParameterSpec { *

        * The size of the initialization vector must be at least 8 bytes * which are copied to protect them against modification. - * + * * @param effectiveKeyBits * the effective key length (in bits). * @param iv @@ -78,7 +76,7 @@ public class RC2ParameterSpec implements AlgorithmParameterSpec { * The size of the initialization vector starting at * offset must be at least 8 bytes which are copied to protect * them against modification. - * + * * @param effectiveKeyBits * the effective key length (in bits). * @param iv @@ -103,7 +101,7 @@ public class RC2ParameterSpec implements AlgorithmParameterSpec { /** * Returns the effective key length (in bits). - * + * * @return the effective key length (in bits). */ public int getEffectiveKeyBits() { @@ -112,7 +110,7 @@ public class RC2ParameterSpec implements AlgorithmParameterSpec { /** * Returns a copy of the initialization vector. - * + * * @return a copy of the initialization vector, or null if none specified. */ public byte[] getIV() { @@ -127,7 +125,7 @@ public class RC2ParameterSpec implements AlgorithmParameterSpec { /** * Compares the specified object to this RC2ParameterSpec * instance. - * + * * @param obj * the object to compare. * @return true if the effective key length and the initialization vector of @@ -148,7 +146,7 @@ public class RC2ParameterSpec implements AlgorithmParameterSpec { /** * Returns the hash code of this RC2ParameterSpec instance. - * + * * @return the hash code. */ @Override diff --git a/libcore/crypto/src/main/java/javax/crypto/spec/RC5ParameterSpec.java b/libcore/crypto/src/main/java/javax/crypto/spec/RC5ParameterSpec.java index f711f41cd..57010f7d5 100644 --- a/libcore/crypto/src/main/java/javax/crypto/spec/RC5ParameterSpec.java +++ b/libcore/crypto/src/main/java/javax/crypto/spec/RC5ParameterSpec.java @@ -25,8 +25,6 @@ import org.apache.harmony.crypto.internal.nls.Messages; /** * The algorithm parameter specification for the RC5 algorithm. - * - * @since Android 1.0 */ public class RC5ParameterSpec implements AlgorithmParameterSpec { @@ -38,7 +36,7 @@ public class RC5ParameterSpec implements AlgorithmParameterSpec { /** * Creates a new RC5ParameterSpec instance with the specified * version, round count an word size (in bits). - * + * * @param version * the version. * @param rounds @@ -61,7 +59,7 @@ public class RC5ParameterSpec implements AlgorithmParameterSpec { * The size of the initialization vector must be at least * 2 * (wordSize / 8) bytes which are copied to protect them * against modification. - * + * * @param version * the version. * @param rounds @@ -97,7 +95,7 @@ public class RC5ParameterSpec implements AlgorithmParameterSpec { * The size of the initialization vector must be at least * offset + (2 * (wordSize / 8)) bytes. The bytes starting at * offset are copied to protect them against modification. - * + * * @param version * the version. * @param rounds @@ -135,7 +133,7 @@ public class RC5ParameterSpec implements AlgorithmParameterSpec { /** * Returns the version. - * + * * @return the version. */ public int getVersion() { @@ -144,7 +142,7 @@ public class RC5ParameterSpec implements AlgorithmParameterSpec { /** * Returns the round count. - * + * * @return the round count. */ public int getRounds() { @@ -153,7 +151,7 @@ public class RC5ParameterSpec implements AlgorithmParameterSpec { /** * Returns the word size (in bits). - * + * * @return the word size (in bits). */ public int getWordSize() { @@ -162,7 +160,7 @@ public class RC5ParameterSpec implements AlgorithmParameterSpec { /** * Returns a copy of the initialization vector. - * + * * @return a copy of the initialization vector, or null if none specified. */ public byte[] getIV() { @@ -177,7 +175,7 @@ public class RC5ParameterSpec implements AlgorithmParameterSpec { /** * Compares the specified object with this RC5ParameterSpec * instance. - * + * * @param obj * the object to compare. * @return true if version, round count, word size and initializaion vector @@ -200,7 +198,7 @@ public class RC5ParameterSpec implements AlgorithmParameterSpec { /** * Returns the hash code of this RC5ParameterSpec instance. - * + * * @return the hash code. */ @Override diff --git a/libcore/crypto/src/main/java/javax/crypto/spec/SecretKeySpec.java b/libcore/crypto/src/main/java/javax/crypto/spec/SecretKeySpec.java index 897948c3c..d1eba47cd 100644 --- a/libcore/crypto/src/main/java/javax/crypto/spec/SecretKeySpec.java +++ b/libcore/crypto/src/main/java/javax/crypto/spec/SecretKeySpec.java @@ -33,8 +33,6 @@ import org.apache.harmony.crypto.internal.nls.Messages; * A key specification for a SecretKey and also a secret key * implementation that is provider-independent. It can be used for raw secret * keys that can be specified as byte[]. - * - * @since Android 1.0 */ public class SecretKeySpec implements SecretKey, KeySpec, Serializable { @@ -50,7 +48,7 @@ public class SecretKeySpec implements SecretKey, KeySpec, Serializable { /** * Creates a new SecretKeySpec for the specified key data and * algorithm name. - * + * * @param key * the key data. * @param algorithm @@ -79,7 +77,7 @@ public class SecretKeySpec implements SecretKey, KeySpec, Serializable { * Creates a new SecretKeySpec for the key data from the * specified buffer key starting at offset with * length len and the specified algorithm name. - * + * * @param key * the key data. * @param offset @@ -120,7 +118,7 @@ public class SecretKeySpec implements SecretKey, KeySpec, Serializable { /** * Returns the algorithm name. - * + * * @return the algorithm name. */ public String getAlgorithm() { @@ -129,7 +127,7 @@ public class SecretKeySpec implements SecretKey, KeySpec, Serializable { /** * Returns the name of the format used to encode the key. - * + * * @return the format name "RAW". */ public String getFormat() { @@ -138,7 +136,7 @@ public class SecretKeySpec implements SecretKey, KeySpec, Serializable { /** * Returns the encoded form of this secret key. - * + * * @return the encoded form of this secret key. */ public byte[] getEncoded() { @@ -149,7 +147,7 @@ public class SecretKeySpec implements SecretKey, KeySpec, Serializable { /** * Returns the hash code of this SecretKeySpec object. - * + * * @return the hash code. */ @Override @@ -164,7 +162,7 @@ public class SecretKeySpec implements SecretKey, KeySpec, Serializable { /** * Compares the specified object with this SecretKeySpec * instance. - * + * * @param obj * the object to compare. * @return true if the algorithm name and key of both object are equal, diff --git a/libcore/crypto/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/ExemptionMechanismTest.java b/libcore/crypto/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/ExemptionMechanismTest.java index eb2920e86..d93c15610 100644 --- a/libcore/crypto/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/ExemptionMechanismTest.java +++ b/libcore/crypto/src/test/java/org/apache/harmony/crypto/tests/javax/crypto/ExemptionMechanismTest.java @@ -21,6 +21,7 @@ import dalvik.annotation.TestTargetClass; import dalvik.annotation.TestTargets; import dalvik.annotation.TestLevel; import dalvik.annotation.TestTargetNew; +import dalvik.annotation.SideEffect; import java.math.BigInteger; import java.security.AlgorithmParameters; @@ -221,6 +222,7 @@ public class ExemptionMechanismTest extends TestCase { method = "finalize", args = {} ) + @SideEffect("Causes OutOfMemoryError to test finalization") public void test_finalize () { Mock_ExemptionMechanism mem = new Mock_ExemptionMechanism(null, null, "Name"); assertNotNull(mem); diff --git a/libcore/dalvik/src/main/java/dalvik/system/AllocationLimitError.java b/libcore/dalvik/src/main/java/dalvik/system/AllocationLimitError.java index 85f7de66a..9118199c6 100644 --- a/libcore/dalvik/src/main/java/dalvik/system/AllocationLimitError.java +++ b/libcore/dalvik/src/main/java/dalvik/system/AllocationLimitError.java @@ -18,8 +18,9 @@ package dalvik.system; /** * Is thrown when an allocation limit is exceeded. - * - * @since Android 1.0 + * + * @deprecated this is an internal Dalvik class that is not appropriate for + * general use. It will be removed from the public API in a future release. */ public class AllocationLimitError extends VirtualMachineError { /** diff --git a/libcore/dalvik/src/main/java/dalvik/system/DexFile.java b/libcore/dalvik/src/main/java/dalvik/system/DexFile.java index da51e4582..00de31454 100644 --- a/libcore/dalvik/src/main/java/dalvik/system/DexFile.java +++ b/libcore/dalvik/src/main/java/dalvik/system/DexFile.java @@ -38,11 +38,13 @@ public final class DexFile { /** * Opens a DEX file from a given File object. This will usually be a ZIP/JAR - * file with a "classes.dex" inside. The method should not be used for files - * inside the Dalvik cache. - * - * @cts What will happen if we refer to the Dalvik cache? Should be either - * specified or throw an exception... + * file with a "classes.dex" inside. + * + * The VM will generate the name of the coresponding file in + * /data/dalvik-cache and open it, possibly creating or updating + * it first if system permissions allow. Don't pass in the name of + * a file in /data/dalvik-cache, as the named file is expected to be + * in its original (pre-dexopt) state. * * @param file * the File object referencing the actual DEX file @@ -57,11 +59,13 @@ public final class DexFile { /** * Opens a DEX file from a given filename. This will usually be a ZIP/JAR - * file with a "classes.dex" inside. The method should not be used for files - * inside the Dalvik cache. - * - * @cts What will happen if we refer to the Dalvik cache? Should be either - * specified or throw an exception... + * file with a "classes.dex" inside. + * + * The VM will generate the name of the coresponding file in + * /data/dalvik-cache and open it, possibly creating or updating + * it first if system permissions allow. Don't pass in the name of + * a file in /data/dalvik-cache, as the named file is expected to be + * in its original (pre-dexopt) state. * * @param fileName * the filename of the DEX file @@ -190,11 +194,23 @@ public final class DexFile { * @cts Exception comment is a bit cryptic. What exception will be thrown? */ public Class loadClass(String name, ClassLoader loader) { + String slashName = name.replace('.', '/'); + return loadClassBinaryName(slashName, loader); + } + + /** + * See {@link #loadClass(String, ClassLoader)}. + * + * This takes a "binary" class name to better match ClassLoader semantics. + * + * {@hide} + */ + public Class loadClassBinaryName(String name, ClassLoader loader) { return defineClass(name, loader, mCookie, null); //new ProtectionDomain(name) /*DEBUG ONLY*/); } - + native private static Class defineClass(String name, ClassLoader loader, int cookie, ProtectionDomain pd); diff --git a/libcore/dalvik/src/main/java/dalvik/system/PathClassLoader.java b/libcore/dalvik/src/main/java/dalvik/system/PathClassLoader.java index c80aef8e3..597eb5b99 100644 --- a/libcore/dalvik/src/main/java/dalvik/system/PathClassLoader.java +++ b/libcore/dalvik/src/main/java/dalvik/system/PathClassLoader.java @@ -56,8 +56,8 @@ public class PathClassLoader extends ClassLoader { /** * Creates a {@code PathClassLoader} that operates on a given list of files * and directories. This method is equivalent to calling - * {@link #PathClassLoader(String, String, ClassLoader) with a {@code null} - * value for the second argument (see description there). + * {@link #PathClassLoader(String, String, ClassLoader)} with a + * {@code null} value for the second argument (see description there). * * @param path * the list of files and directories @@ -179,8 +179,9 @@ public class PathClassLoader extends ClassLoader { * parent ClassLoader has failed to find a loaded class of the same name. * * @param name - * The name of the class to search for, in a human-readable form - * like "java.lang.String" or "java.net.URLClassLoader$3$1". + * The "binary name" of the class to search for, in a + * human-readable form like "java.lang.String" or + * "java.net.URLClassLoader$3$1". * @return the {@link Class} object representing the class * @throws ClassNotFoundException * if the class cannot be found @@ -199,8 +200,7 @@ public class PathClassLoader extends ClassLoader { //System.out.println("My path is: " + mPaths[i]); if (mDexs[i] != null) { - String slashName = name.replace('.', '/'); - Class clazz = mDexs[i].loadClass(slashName, this); + Class clazz = mDexs[i].loadClassBinaryName(name, this); if (clazz != null) return clazz; } else if (mZips[i] != null) { diff --git a/libcore/dalvik/src/main/java/dalvik/system/PotentialDeadlockError.java b/libcore/dalvik/src/main/java/dalvik/system/PotentialDeadlockError.java index c85caeee1..938a5cd6e 100644 --- a/libcore/dalvik/src/main/java/dalvik/system/PotentialDeadlockError.java +++ b/libcore/dalvik/src/main/java/dalvik/system/PotentialDeadlockError.java @@ -18,8 +18,9 @@ package dalvik.system; /** * Is thrown when the VM identifies a potential deadlock. - * - * @since Android 1.0 + * + * @deprecated this is an internal Dalvik class that is not appropriate for + * general use. It will be removed from the public API in a future release. */ public class PotentialDeadlockError extends VirtualMachineError { /** diff --git a/libcore/dalvik/src/main/java/dalvik/system/SamplingProfiler.java b/libcore/dalvik/src/main/java/dalvik/system/SamplingProfiler.java new file mode 100644 index 000000000..1d88dd170 --- /dev/null +++ b/libcore/dalvik/src/main/java/dalvik/system/SamplingProfiler.java @@ -0,0 +1,346 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dalvik.system; + +import java.util.logging.Logger; +import java.io.DataInputStream; +import java.io.ByteArrayInputStream; +import java.io.IOException; + +/** + * A sampling profiler. + * + * @hide + */ +public class SamplingProfiler { + + private static final Logger logger = Logger.getLogger( + SamplingProfiler.class.getName()); + + static final boolean DEBUG = false; + + enum State { + /** The sampling thread hasn't started or is waiting to resume. */ + PAUSED, + /** The sampling thread is collecting samples. */ + RUNNING, + /** The sampling thread is shutting down. */ + SHUTTING_DOWN + } + + /** Pointer to native state. */ + int pointer = 0; + + /** The thread that collects samples. */ + Thread samplingThread; + + /** Time between samples. */ + volatile int delay; // ms + + /** Number of samples taken (~samples per second * number of threads). */ + int totalThreadsSampled = 0; + + /** Total time spent collecting samples. */ + long totalSampleTime = 0; + + /** The state of the profiler. */ + volatile State state = State.PAUSED; + + private SamplingProfiler() {} + + /** + * Returns true if the profiler is running. + */ + public boolean isRunning() { + return state == State.RUNNING; + } + + /** + * Starts collecting samples. + * + * @param samplesPerSecond number of times to sample each thread per second + * @throws IllegalStateException if the profiler is + * {@linkplain #shutDown()} shutting down} + */ + public synchronized void start(int samplesPerSecond) { + if (samplesPerSecond < 1) { + throw new IllegalArgumentException("samplesPerSecond < 1"); + } + ensureNotShuttingDown(); + delay = 1000 / samplesPerSecond; + if (!isRunning()) { + if (DEBUG) logger.info("Starting profiler..."); + state = State.RUNNING; + if (samplingThread == null) { + // TODO: Priority? + samplingThread = new Thread(new Sampler(), "SamplingProfiler"); + samplingThread.setDaemon(true); + samplingThread.start(); + } else { + notifyAll(); + } + } + } + + /** + * Pauses sample collection. + */ + public synchronized void pause() { + if (isRunning()) { + if (DEBUG) logger.info("Pausing profiler..."); + state = State.PAUSED; + } + } + + /** + * Captures collected samples and clears the sample set. Returns null + * if no data has been captured. + * + *

        Note: The exact format is not documented because it's not set in + * stone yet. + * + * @throws IllegalStateException if the profiler is + * {@linkplain #shutDown()} shutting down} + */ + public synchronized byte[] snapshot() { + ensureNotShuttingDown(); + if (pointer == 0 || totalThreadsSampled == 0) { + return null; + } + + if (DEBUG) { + int size = size(pointer); + int collisions = collisions(pointer); + + long start = System.nanoTime(); + byte[] bytes = snapshot(pointer); + long elapsed = System.nanoTime() - start; + + /* + * We shifted the times by 10 bits in the sampling thread to avoid + * overflow. Undo the shift and then convert from ns to us. + */ + long averageSampleTime = ((totalSampleTime / totalThreadsSampled) + << 10) / 1000; + logger.info("Grabbed snapshot in " + (elapsed / 1000) + "us." + + " Samples collected: " + totalThreadsSampled + + ", Average sample time (per thread): " + + averageSampleTime + "us" + + ", Set size: " + size + + ", Collisions: " + collisions); + totalThreadsSampled = 0; + totalSampleTime = 0; + + return bytes; + } else { + totalThreadsSampled = 0; + return snapshot(pointer); + } + } + + /** + * Identifies the "event thread". For a user-facing application, this + * might be the UI thread. For a background process, this might be the + * thread that processes incoming requests. + * + * @throws IllegalStateException if the profiler is + * {@linkplain #shutDown()} shutting down} + */ + public synchronized void setEventThread(Thread eventThread) { + ensureNotShuttingDown(); + if (pointer == 0) { + pointer = allocate(); + } + setEventThread(pointer, eventThread); + } + + private void ensureNotShuttingDown() { + if (state == State.SHUTTING_DOWN) { + throw new IllegalStateException("Profiler is shutting down."); + } + } + + /** + * Shuts down the profiler thread and frees native memory. The profiler + * will recreate the thread the next time {@link #start(int)} is called. + * + * @throws IllegalStateException if the profiler is already shutting down + * or if it hasn't started yet + * + */ + public void shutDown() { + Thread toStop; + synchronized (this) { + ensureNotShuttingDown(); + + toStop = samplingThread; + if (toStop == null) { + throw new IllegalStateException( + "The profiler was never started."); + } + + state = State.SHUTTING_DOWN; + samplingThread = null; + notifyAll(); + } + + // Release lock to 'this' so background thread can grab it and stop. + // Interrupt the thread in case it's sleeping. + toStop.interrupt(); + while (true) { + try { + toStop.join(); + break; + } catch (InterruptedException e) { /* ignore */ } + } + + synchronized (this) { + if (pointer != 0) { + free(pointer); + pointer = 0; + } + + totalThreadsSampled = 0; + totalSampleTime = 0; + state = State.PAUSED; + } + } + + /** Collects some data. Returns number of threads sampled. */ + private static native int sample(int pointer); + + /** Allocates native state. */ + private static native int allocate(); + + /** Frees native state. */ + private static native void free(int pointer); + + /** Gets the number of methods in the sample set. */ + private static native int size(int pointer); + + /** Gets the number of collisions in the sample set. */ + private static native int collisions(int pointer); + + /** Captures data. */ + private static native byte[] snapshot(int pointer); + + /** Identifies the "event thread". */ + private static native void setEventThread(int pointer, Thread thread); + + /** + * Background thread that collects samples. + */ + class Sampler implements Runnable { + public void run() { + boolean firstSample = true; + while (true) { + synchronized (SamplingProfiler.this) { + if (!isRunning()) { + if (DEBUG) logger.info("Paused profiler."); + while (!isRunning()) { + if (state == State.SHUTTING_DOWN) { + // Stop thread. + return; + } + + try { + SamplingProfiler.this.wait(); + } catch (InterruptedException e) { /* ignore */ } + } + firstSample = true; + } + + if (pointer == 0) { + pointer = allocate(); + } + + if (firstSample) { + if (DEBUG) logger.info("Started profiler."); + firstSample = false; + } + + if (DEBUG) { + long start = System.nanoTime(); + int threadsSampled = sample(pointer); + long elapsed = System.nanoTime() - start; + + totalThreadsSampled += threadsSampled; + totalSampleTime += elapsed >> 10; // avoids overflow. + } else { + totalThreadsSampled += sample(pointer); + } + } + + try { + Thread.sleep(delay); + } catch (InterruptedException e) { /* ignore */ } + } + } + } + + /** + * Dumps a snapshot to the log. Useful for debugging. + */ + public static void logSnapshot(byte[] snapshot) { + DataInputStream in = new DataInputStream( + new ByteArrayInputStream(snapshot)); + try { + int version = in.readUnsignedShort(); + int classCount = in.readUnsignedShort(); + StringBuilder sb = new StringBuilder(); + sb.append("version=").append(version).append(' ') + .append("classes=").append(classCount).append('\n'); + logger.info(sb.toString()); + for (int i = 0; i < classCount; i++) { + sb = new StringBuilder(); + sb.append("class ").append(in.readUTF()).append('\n'); + int methodCount = in.readUnsignedShort(); + for (int m = 0; m < methodCount; m++) { + sb.append(" ").append(in.readUTF()).append(":\n"); + sb.append(" event:\n"); + appendCounts(in, sb); + sb.append(" other:\n"); + appendCounts(in, sb); + } + logger.info(sb.toString()); + } + } catch (IOException e) { + logger.warning(e.toString()); + } + } + + private static void appendCounts(DataInputStream in, StringBuilder sb) + throws IOException { + sb.append(" running:\n"); + sb.append(" caller: ").append(in.readShort()).append('\n'); + sb.append(" leaf: ").append(in.readShort()).append('\n'); + sb.append(" suspended:\n"); + sb.append(" caller: ").append(in.readShort()).append('\n'); + sb.append(" leaf: ").append(in.readShort()).append('\n'); + } + + /** This will be allocated when the user calls getInstance(). */ + private static final SamplingProfiler instance = new SamplingProfiler(); + + /** + * Gets the profiler. The profiler is not running by default. Start it + * with {@link #start(int)}. + */ + public static synchronized SamplingProfiler getInstance() { + return instance; + } +} diff --git a/libcore/dalvik/src/main/java/dalvik/system/StaleDexCacheError.java b/libcore/dalvik/src/main/java/dalvik/system/StaleDexCacheError.java index 44a40a57b..ffde7cb21 100644 --- a/libcore/dalvik/src/main/java/dalvik/system/StaleDexCacheError.java +++ b/libcore/dalvik/src/main/java/dalvik/system/StaleDexCacheError.java @@ -19,6 +19,9 @@ package dalvik.system; /** * Is thrown when the VM determines that a DEX file's cache is out of date, and * that there is no way to recreate it. + * + * @deprecated this is an internal Dalvik class that is not appropriate for + * general use. It will be removed from the public API in a future release. */ public class StaleDexCacheError extends VirtualMachineError { /** diff --git a/libcore/dalvik/src/main/java/dalvik/system/TemporaryDirectory.java b/libcore/dalvik/src/main/java/dalvik/system/TemporaryDirectory.java index 2f909ac21..0e26a70ed 100644 --- a/libcore/dalvik/src/main/java/dalvik/system/TemporaryDirectory.java +++ b/libcore/dalvik/src/main/java/dalvik/system/TemporaryDirectory.java @@ -25,8 +25,9 @@ import java.util.logging.Logger; * call into this class with an appropriate base directory during its * startup, as a reasonably easy way to get the standard property * java.io.tmpdir to point at something useful. - * - * @since Android 1.0 + * + * @deprecated this is an internal Dalvik class that is not appropriate for + * general use. It will be removed from the public API in a future release. */ public class TemporaryDirectory { /** system property name for the temporary directory */ diff --git a/libcore/dalvik/src/main/java/dalvik/system/TouchDex.java b/libcore/dalvik/src/main/java/dalvik/system/TouchDex.java index 2dbc3ea80..04c654656 100644 --- a/libcore/dalvik/src/main/java/dalvik/system/TouchDex.java +++ b/libcore/dalvik/src/main/java/dalvik/system/TouchDex.java @@ -25,13 +25,8 @@ import java.io.FilenameFilter; /** * Induces optimization/verification of a set of DEX files. * - * TODO: This class is public, so SystemServer can access it. This is NOT - * the correct long-term solution; once we have a real installer and/or - * dalvik-cache manager, this class should be removed. - * - * @cts See to-do about removing this class... - * - * @since Android 1.0 + * @deprecated this is an internal Dalvik class that is not appropriate for + * general use. It will be removed from the public API in a future release. */ public class TouchDex { diff --git a/libcore/dalvik/src/main/java/dalvik/system/VMDebug.java b/libcore/dalvik/src/main/java/dalvik/system/VMDebug.java index efc25d6a2..365388aaa 100644 --- a/libcore/dalvik/src/main/java/dalvik/system/VMDebug.java +++ b/libcore/dalvik/src/main/java/dalvik/system/VMDebug.java @@ -27,8 +27,9 @@ import java.io.IOException; * android.os.Debug. * * @cts Please complete the spec. - * - * @since Android 1.0 + * + * @deprecated this is an internal Dalvik class that is not appropriate for + * general use. It will be removed from the public API in a future release. */ public final class VMDebug { /** @@ -275,8 +276,22 @@ public final class VMDebug { */ public static native void dumpHprofData(String fileName) throws IOException; - /* don't ask */ - static native void printThis(Object thisThing, int count, int thing); + /** + * Primes the register map cache. + * + * @hide + */ + public static native boolean cacheRegisterMap(String classAndMethodDesc); + + /** + * Crashes the VM. Seriously. Dumps the stack trace for the current + * thread and then aborts the VM so you can see the native stack trace. + * Useful for figuring out how you got somewhere when lots of native + * code is involved. + * + * @hide + */ + public static native void crash(); /* * Fake method, inserted into dmtrace output when the garbage collector diff --git a/libcore/dalvik/src/main/java/dalvik/system/VMRuntime.java b/libcore/dalvik/src/main/java/dalvik/system/VMRuntime.java index 5d5e60024..7ac0849e9 100644 --- a/libcore/dalvik/src/main/java/dalvik/system/VMRuntime.java +++ b/libcore/dalvik/src/main/java/dalvik/system/VMRuntime.java @@ -20,7 +20,10 @@ package dalvik.system; * Provides an interface to VM-global, Dalvik-specific features. * An application cannot create its own Runtime instance, and must obtain * one from the getRuntime method. - * + * + * @deprecated this is an internal Dalvik class that is not appropriate for + * general use. It will be removed from the public API in a future release. + * * @since Android 1.0 */ public final class VMRuntime { @@ -62,6 +65,8 @@ public final class VMRuntime { * be resized so that (size of live objects) / (size of heap) is * equal to this number. * + *

        This is only a hint to the garbage collector and may be ignored. + * * @param newTarget the new suggested ideal heap utilization. * This value may be adjusted internally. * @return the previous ideal heap utilization @@ -98,7 +103,9 @@ public final class VMRuntime { * size, the maximum size will be used. If size is zero * or negative, the minimum size constraint will be removed. * - * Synchronized to make the order of the exchange reliable. + *

        Synchronized to make the order of the exchange reliable. + * + *

        This is only a hint to the garbage collector and may be ignored. * * @param size the new suggested minimum heap size, in bytes * @return the old minimum heap size value @@ -175,7 +182,7 @@ public final class VMRuntime { /** * Returns the number of externally-allocated bytes being tracked by * trackExternalAllocation/Free(). - * + * * @return the number of bytes */ public native long getExternalBytesAllocated(); diff --git a/libcore/dalvik/src/main/java/dalvik/system/VMStack.java b/libcore/dalvik/src/main/java/dalvik/system/VMStack.java index 9330c683f..b04996290 100644 --- a/libcore/dalvik/src/main/java/dalvik/system/VMStack.java +++ b/libcore/dalvik/src/main/java/dalvik/system/VMStack.java @@ -19,8 +19,9 @@ package dalvik.system; /** * Provides a limited interface to the Dalvik VM stack. This class is mostly * used for implementing security checks. - * - * @since Android 1.0 + * + * @deprecated this is an internal Dalvik class that is not appropriate for + * general use. It will be removed from the public API in a future release. */ public final class VMStack { /** @@ -39,6 +40,14 @@ public final class VMStack { */ native public static ClassLoader getCallingClassLoader2(); + /** + * Returns the class of the caller's caller's caller. + * + * @hide + * @return the requested class, or {@code null}. + */ + native public static Class getStackClass2(); + /** * Creates an array of classes from the methods at the top of the stack. * We continue until we reach the bottom of the stack or exceed the diff --git a/libcore/dalvik/src/main/java/dalvik/system/Zygote.java b/libcore/dalvik/src/main/java/dalvik/system/Zygote.java index 9d98bb24a..13e756114 100644 --- a/libcore/dalvik/src/main/java/dalvik/system/Zygote.java +++ b/libcore/dalvik/src/main/java/dalvik/system/Zygote.java @@ -20,8 +20,9 @@ package dalvik.system; * Provides access to the Dalvik "zygote" feature, which allows a VM instance to * be partially initialized and then fork()'d from the partially initialized * state. - * - * @since Android 1.0 + * + * @deprecated this is an internal Dalvik class that is not appropriate for + * general use. It will be removed from the public API in a future release. */ public class Zygote { /* diff --git a/libcore/icu/src/main/java/com/ibm/icu4jni/charset/CharsetDecoderICU.java b/libcore/icu/src/main/java/com/ibm/icu4jni/charset/CharsetDecoderICU.java index 206f0c8f7..919d8656e 100644 --- a/libcore/icu/src/main/java/com/ibm/icu4jni/charset/CharsetDecoderICU.java +++ b/libcore/icu/src/main/java/com/ibm/icu4jni/charset/CharsetDecoderICU.java @@ -344,18 +344,11 @@ public final class CharsetDecoderICU extends CharsetDecoder{ // ok was there input held in the previous invocation of decodeLoop // that resulted in output in this invocation? - if(data[OUTPUT_OFFSET]>0 && savedInputHeldLen >0){ - int len = in.position() + data[INPUT_OFFSET] + savedInputHeldLen; - in.position(len); - savedInputHeldLen = data[INPUT_HELD]; - }else{ - in.position(in.position() + data[INPUT_OFFSET] + savedInputHeldLen); - savedInputHeldLen = data[INPUT_HELD]; - in.position(in.position() - savedInputHeldLen); - } - // BEGIN android-added + // BEGIN android-changed + in.position(in.position() + data[INPUT_OFFSET] + savedInputHeldLen - data[INPUT_HELD]); + savedInputHeldLen = data[INPUT_HELD]; // release reference to input array, which may not be ours input = null; - // END android-added + // END android-changed } } diff --git a/libcore/icu/src/main/java/com/ibm/icu4jni/util/Resources.java b/libcore/icu/src/main/java/com/ibm/icu4jni/util/Resources.java index 85530acc9..0460fdef6 100644 --- a/libcore/icu/src/main/java/com/ibm/icu4jni/util/Resources.java +++ b/libcore/icu/src/main/java/com/ibm/icu4jni/util/Resources.java @@ -179,20 +179,28 @@ public class Resources { private static String getDefaultLocaleName() { return java.util.Locale.getDefault().toString(); } - - /** - * Name of default locale at the time this class was initialized. - */ - private static final String initialLocale = getDefaultLocaleName(); /** - * Names of time zones for the default locale. + * Initialization holder for default time zone names. This class will + * be preloaded by the zygote to share the time and space costs of setting + * up the list of time zone names, so although it looks like the lazy + * initialization idiom, it's actually the opposite. */ - private static String[][] defaultTimezoneNames = null; + private static class DefaultTimeZones { + /** + * Name of default locale at the time this class was initialized. + */ + private static final String locale = getDefaultLocaleName(); + + /** + * Names of time zones for the default locale. + */ + private static final String[][] names = createTimeZoneNamesFor(locale); + } /** * Creates array of time zone names for the given locale. This method takes - * about 2s to run on a 400mhz ARM11. + * about 2s to run on a 400MHz ARM11. */ private static String[][] createTimeZoneNamesFor(String locale) { long start = System.currentTimeMillis(); @@ -252,22 +260,16 @@ public class Resources { * the TomeZone class. */ public static String[][] getDisplayTimeZones(String locale) { - // Note: Defer loading DefaultTimeZones as long as possible. - - String defaultLocaleName = getDefaultLocaleName(); + String defaultLocale = getDefaultLocaleName(); if (locale == null) { - locale = defaultLocaleName; + locale = defaultLocale; } // If locale == default and the default locale hasn't changed since // DefaultTimeZones loaded, return the cached names. // TODO: We should force a reboot if the default locale changes. - if (defaultLocaleName.equals(locale) - && initialLocale.equals(defaultLocaleName)) { - if (defaultTimezoneNames == null) { - defaultTimezoneNames = createTimeZoneNamesFor(locale); - } - return defaultTimezoneNames; + if (defaultLocale.equals(locale) && DefaultTimeZones.locale.equals(defaultLocale)) { + return DefaultTimeZones.names; } return createTimeZoneNamesFor(locale); diff --git a/libcore/icu/src/main/native/DecimalFormatInterface.cpp b/libcore/icu/src/main/native/DecimalFormatInterface.cpp index 6221826f0..7e37d6cdc 100644 --- a/libcore/icu/src/main/native/DecimalFormatInterface.cpp +++ b/libcore/icu/src/main/native/DecimalFormatInterface.cpp @@ -15,6 +15,7 @@ * the VM-specific behavior isolated in VMThread. */ +#define LOG_TAG "DecimalFormatInterface" #include "JNIHelp.h" #include "AndroidSystemNatives.h" #include "unicode/unum.h" @@ -28,7 +29,6 @@ #include #include "cutils/log.h" -#define LOG_TAG "DecimalFormatInterface" static UBool icuError(JNIEnv *env, UErrorCode errorcode) { diff --git a/libcore/icu/src/main/native/ResourceInterface.cpp b/libcore/icu/src/main/native/ResourceInterface.cpp index 5f9d442f4..a88e15cd6 100644 --- a/libcore/icu/src/main/native/ResourceInterface.cpp +++ b/libcore/icu/src/main/native/ResourceInterface.cpp @@ -943,7 +943,7 @@ static jobjectArray getContentImpl(JNIEnv* env, jclass clazz, - jclass obj_class = env->FindClass("java/lang/Object"); + jclass obj_class = env->FindClass("[Ljava/lang/Object;"); jclass integer_class = env->FindClass("java/lang/Integer"); jmethodID integerInit = env->GetMethodID(integer_class, "", "(I)V"); jobjectArray result; @@ -1207,7 +1207,8 @@ endOfCalendar: intCurrencySymbol = env->NewStringUTF("XXX"); } if(currencySymbol == NULL) { - currencySymbol = env->NewStringUTF("\u00a4"); + // creating a new string explicitly with the UTF-8 encoding of "\u00a4" + currencySymbol = env->NewStringUTF("\xc2\xa4"); } counter += 2; diff --git a/libcore/logging/src/main/java/java/util/logging/ConsoleHandler.java b/libcore/logging/src/main/java/java/util/logging/ConsoleHandler.java index a88cf0ccb..ef365ca4d 100644 --- a/libcore/logging/src/main/java/java/util/logging/ConsoleHandler.java +++ b/libcore/logging/src/main/java/java/util/logging/ConsoleHandler.java @@ -38,19 +38,13 @@ package java.util.logging; * handler will use to encode log messages, defaults to {@code null} if this * property is not found or has an invalid value. * - *

        *

        * This class is not thread-safe. - *

        - * - * @since Android 1.0 */ public class ConsoleHandler extends StreamHandler { /** * Constructs a {@code ConsoleHandler} object. - * - * @since Android 1.0 */ public ConsoleHandler() { super(System.err); @@ -58,8 +52,6 @@ public class ConsoleHandler extends StreamHandler { /** * Closes this handler. The {@code System.err} is flushed but not closed. - * - * @since Android 1.0 */ @Override public void close() { @@ -68,16 +60,13 @@ public class ConsoleHandler extends StreamHandler { /** * Logs a record if necessary. A flush operation will be done. - * + * * @param record * the log record to be logged. - * - * @since Android 1.0 */ @Override public void publish(LogRecord record) { super.publish(record); super.flush(); - } } diff --git a/libcore/logging/src/main/java/java/util/logging/ErrorManager.java b/libcore/logging/src/main/java/java/util/logging/ErrorManager.java index 6f5084c26..708ddfa75 100644 --- a/libcore/logging/src/main/java/java/util/logging/ErrorManager.java +++ b/libcore/logging/src/main/java/java/util/logging/ErrorManager.java @@ -24,51 +24,37 @@ import org.apache.harmony.logging.internal.nls.Messages; * error that may happen during logging. {@code Handlers} should report errors * to an {@code ErrorManager}, instead of throwing exceptions, which would * interfere with the log issuer's execution. - * - * @since Android 1.0 */ public class ErrorManager { /** * The error code indicating a failure that does not fit in any of the * specific types of failures that follow. - * - * @since Android 1.0 */ public static final int GENERIC_FAILURE = 0; /** * The error code indicating a failure when writing to an output stream. - * - * @since Android 1.0 */ public static final int WRITE_FAILURE = 1; /** * The error code indicating a failure when flushing an output stream. - * - * @since Android 1.0 */ public static final int FLUSH_FAILURE = 2; /** * The error code indicating a failure when closing an output stream. - * - * @since Android 1.0 */ public static final int CLOSE_FAILURE = 3; /** * The error code indicating a failure when opening an output stream. - * - * @since Android 1.0 */ public static final int OPEN_FAILURE = 4; /** * The error code indicating a failure when formatting the error messages. - * - * @since Android 1.0 */ public static final int FORMAT_FAILURE = 5; @@ -85,20 +71,16 @@ public class ErrorManager { /** * Constructs an instance of {@code ErrorManager}. - * - * @since Android 1.0 */ public ErrorManager() { super(); } /** - *

        * Reports an error using the given message, exception and error code. This * implementation will write out the message to {@link System#err} on the * first call and all subsequent calls are ignored. A subclass of this class * should override this method. - *

        * * @param message * the error message, which may be {@code null}. @@ -108,8 +90,6 @@ public class ErrorManager { * @param errorCode * the error code that identifies the type of error; see the * constant fields of this class for possible values. - * - * @since Android 1.0 */ public void error(String message, Exception exception, int errorCode) { synchronized (this) { @@ -119,7 +99,7 @@ public class ErrorManager { called = true; } System.err.println(this.getClass().getName() - + ": " + FAILURES[errorCode]); //$NON-NLS-1$ + + ": " + FAILURES[errorCode]); //$NON-NLS-1$ if (message != null) { // logging.1E=Error message - {0} System.err.println(Messages.getString("logging.1E", message)); //$NON-NLS-1$ diff --git a/libcore/logging/src/main/java/java/util/logging/FileHandler.java b/libcore/logging/src/main/java/java/util/logging/FileHandler.java index af71a6dcf..e1eba9ea5 100644 --- a/libcore/logging/src/main/java/java/util/logging/FileHandler.java +++ b/libcore/logging/src/main/java/java/util/logging/FileHandler.java @@ -38,48 +38,45 @@ import org.apache.harmony.logging.internal.nls.Messages; * When a set of files is used and a given amount of data has been written to * one file, then this file is closed and another file is opened. The name of * these files are generated by given name pattern, see below for details. - *

        + * When the files have all been filled the Handler returns to the first and goes + * through the set again. *

        * By default, the I/O buffering mechanism is enabled, but when each log record * is complete, it is flushed out. - *

        *

        * {@code XMLFormatter} is the default formatter for {@code FileHandler}. - *

        *

        * {@code FileHandler} reads the following {@code LogManager} properties for * initialization; if a property is not defined or has an invalid value, a * default value is used. *

          - *
        • java.util.logging.FileHandler.level specifies the level for this - * {@code Handler}, defaults to {@code Level.ALL}.
        • + *
        • java.util.logging.FileHandler.append specifies whether this + * {@code FileHandler} should append onto existing files, defaults to + * {@code false}.
        • + *
        • java.util.logging.FileHandler.count specifies how many output files to + * rotate, defaults to 1.
        • *
        • java.util.logging.FileHandler.filter specifies the {@code Filter} class * name, defaults to no {@code Filter}.
        • *
        • java.util.logging.FileHandler.formatter specifies the {@code Formatter} * class, defaults to {@code java.util.logging.XMLFormatter}.
        • *
        • java.util.logging.FileHandler.encoding specifies the character set * encoding name, defaults to the default platform encoding.
        • + *
        • java.util.logging.FileHandler.level specifies the level for this + * {@code Handler}, defaults to {@code Level.ALL}.
        • *
        • java.util.logging.FileHandler.limit specifies the maximum number of * bytes to write to any one file, defaults to zero, which means no limit.
        • - *
        • java.util.logging.FileHandler.count specifies how many output files to - * rotate, defaults to 1.
        • *
        • java.util.logging.FileHandler.pattern specifies name pattern for the * output files. See below for details. Defaults to "%h/java%u.log".
        • - *
        • java.util.logging.FileHandler.append specifies whether this - * {@code FileHandler} should append onto existing files, defaults to - * {@code false}.
        • *
        - *

        *

        * Name pattern is a string that may include some special substrings, which will * be replaced to generate output files: - *

        *
          *
        • "/" represents the local pathname separator
        • - *
        • "%t" represents the system's temporary directory
        • + *
        • "%g" represents the generation number to distinguish rotated logs
        • *
        • "%h" represents the home directory of the current user, which is * specified by "user.home" system property
        • - *
        • "%g" represents the generation number to distinguish rotated logs
        • + *
        • "%t" represents the system's temporary directory
        • *
        • "%u" represents a unique number to resolve conflicts
        • *
        • "%%" represents the percent sign character '%'
        • *
        @@ -88,7 +85,6 @@ import org.apache.harmony.logging.internal.nls.Messages; * follow the sequence 0, 1, 2.... If the file count is larger than one, but the * generation field("%g") has not been specified in the pattern, then the * generation number after a dot will be added to the end of the file name. - *

        *

        * The "%u" unique field is used to avoid conflicts and is set to 0 at first. If * one {@code FileHandler} tries to open the filename which is currently in use @@ -98,8 +94,6 @@ import org.apache.harmony.logging.internal.nls.Messages; * value will be added to the end of the filename in question immediately to the * right of a dot. The generation of unique IDs for avoiding conflicts is only * guaranteed to work reliably when using a local disk file system. - *

        - * @since Android 1.0 */ public class FileHandler extends StreamHandler { @@ -150,7 +144,7 @@ public class FileHandler extends StreamHandler { /** * Construct a {@code FileHandler} using {@code LogManager} properties or * their default value. - * + * * @throws IOException * if any I/O error occurs. * @throws SecurityException @@ -159,7 +153,6 @@ public class FileHandler extends StreamHandler { * handler; required permissions include * {@code LogPermission("control")}, * {@code FilePermission("write")} etc. - * @since Android 1.0 */ public FileHandler() throws IOException { init(null, null, null, null); @@ -232,21 +225,22 @@ public class FileHandler extends StreamHandler { setOutputStream(output); } + @SuppressWarnings("nls") private void initProperties(String p, Boolean a, Integer l, Integer c) { - super.initProperties("ALL", null, "java.util.logging.XMLFormatter", //$NON-NLS-1$//$NON-NLS-2$ + super.initProperties("ALL", null, "java.util.logging.XMLFormatter", null); String className = this.getClass().getName(); - pattern = (null == p) ? getStringProperty(className + ".pattern", //$NON-NLS-1$ + pattern = (null == p) ? getStringProperty(className + ".pattern", DEFAULT_PATTERN) : p; - if (null == pattern || "".equals(pattern)) { //$NON-NLS-1$ + if (null == pattern || "".equals(pattern)) { // logging.19=Pattern cannot be empty - throw new NullPointerException(Messages.getString("logging.19")); //$NON-NLS-1$ + throw new NullPointerException(Messages.getString("logging.19")); } - append = (null == a) ? getBooleanProperty(className + ".append", //$NON-NLS-1$ + append = (null == a) ? getBooleanProperty(className + ".append", DEFAULT_APPEND) : a.booleanValue(); - count = (null == c) ? getIntProperty(className + ".count", //$NON-NLS-1$ + count = (null == c) ? getIntProperty(className + ".count", DEFAULT_COUNT) : c.intValue(); - limit = (null == l) ? getIntProperty(className + ".limit", //$NON-NLS-1$ + limit = (null == l) ? getIntProperty(className + ".limit", DEFAULT_LIMIT) : l.intValue(); count = count < 1 ? DEFAULT_COUNT : count; limit = limit < 0 ? DEFAULT_LIMIT : limit; @@ -279,7 +273,7 @@ public class FileHandler extends StreamHandler { /** * Transform the pattern to the valid file name, replacing any patterns, and * applying generation and uniqueID if present. - * + * * @param gen * generation of this file * @return transformed filename ready for use. @@ -396,9 +390,9 @@ public class FileHandler extends StreamHandler { * Constructs a new {@code FileHandler}. The given name pattern is used as * output filename, the file limit is set to zero (no limit), the file count * is set to one; the remaining configuration is done using - * {@code LogManager} properties or their default values. This handler write - * to only one file without size limit. - * + * {@code LogManager} properties or their default values. This handler + * writes to only one file with no size limit. + * * @param pattern * the name pattern for the output file. * @throws IOException @@ -413,7 +407,6 @@ public class FileHandler extends StreamHandler { * if the pattern is empty. * @throws NullPointerException * if the pattern is {@code null}. - * @since Android 1.0 */ public FileHandler(String pattern) throws IOException { if (pattern.equals("")) { //$NON-NLS-1$ @@ -429,9 +422,9 @@ public class FileHandler extends StreamHandler { * output filename, the file limit is set to zero (no limit), the file count * is initialized to one and the value of {@code append} becomes the new * instance's append mode. The remaining configuration is done using - * {@code LogManager} properties. This handler write to only one file - * without size limit. - * + * {@code LogManager} properties. This handler writes to only one file + * with no size limit. + * * @param pattern * the name pattern for the output file. * @param append @@ -448,11 +441,10 @@ public class FileHandler extends StreamHandler { * if {@code pattern} is empty. * @throws NullPointerException * if {@code pattern} is {@code null}. - * @since Android 1.0 */ public FileHandler(String pattern, boolean append) throws IOException { if (pattern.equals("")) { //$NON-NLS-1$ - throw new IllegalArgumentException(Messages.getString("logging.19")); //$NON-NLS-1$ + throw new IllegalArgumentException(Messages.getString("logging.19")); //$NON-NLS-1$ } init(pattern, Boolean.valueOf(append), Integer.valueOf(DEFAULT_LIMIT), @@ -466,7 +458,7 @@ public class FileHandler extends StreamHandler { * is done using {@code LogManager} properties. This handler is configured * to write to a rotating set of count files, when the limit of bytes has * been written to one output file, another file will be opened instead. - * + * * @param pattern * the name pattern for the output file. * @param limit @@ -487,11 +479,10 @@ public class FileHandler extends StreamHandler { * {@code count < 1}. * @throws NullPointerException * if {@code pattern} is {@code null}. - * @since Android 1.0 */ public FileHandler(String pattern, int limit, int count) throws IOException { if (pattern.equals("")) { //$NON-NLS-1$ - throw new IllegalArgumentException(Messages.getString("logging.19")); //$NON-NLS-1$ + throw new IllegalArgumentException(Messages.getString("logging.19")); //$NON-NLS-1$ } if (limit < 0 || count < 1) { // logging.1B=The limit and count property must be larger than 0 and @@ -509,7 +500,7 @@ public class FileHandler extends StreamHandler { * {@code LogManager} properties. This handler is configured to write to a * rotating set of count files, when the limit of bytes has been written to * one output file, another file will be opened instead. - * + * * @param pattern * the name pattern for the output file. * @param limit @@ -532,12 +523,11 @@ public class FileHandler extends StreamHandler { * {@code count < 1}. * @throws NullPointerException * if {@code pattern} is {@code null}. - * @since Android 1.0 */ public FileHandler(String pattern, int limit, int count, boolean append) throws IOException { if (pattern.equals("")) { //$NON-NLS-1$ - throw new IllegalArgumentException(Messages.getString("logging.19")); //$NON-NLS-1$ + throw new IllegalArgumentException(Messages.getString("logging.19")); //$NON-NLS-1$ } if (limit < 0 || count < 1) { // logging.1B=The limit and count property must be larger than 0 and @@ -550,14 +540,13 @@ public class FileHandler extends StreamHandler { /** * Flushes and closes all opened files. - * + * * @throws SecurityException * if a security manager exists and it determines that the * caller does not have the required permissions to control this * handler; required permissions include * {@code LogPermission("control")}, * {@code FilePermission("write")} etc. - * @since Android 1.0 */ @Override public void close() { @@ -577,10 +566,9 @@ public class FileHandler extends StreamHandler { /** * Publish a {@code LogRecord}. - * + * * @param record * the log record to publish. - * @since Android 1.0 */ @Override public void publish(LogRecord record) { diff --git a/libcore/logging/src/main/java/java/util/logging/Filter.java b/libcore/logging/src/main/java/java/util/logging/Filter.java index e81f216a5..f5dbd9fed 100644 --- a/libcore/logging/src/main/java/java/util/logging/Filter.java +++ b/libcore/logging/src/main/java/java/util/logging/Filter.java @@ -20,8 +20,6 @@ package java.util.logging; /** * A {@code Filter} provides a mechanism for exercising fine-grained control * over which records get logged. - * - * @since Android 1.0 */ public interface Filter { @@ -32,7 +30,6 @@ public interface Filter { * the {@link LogRecord} to be checked. * @return {@code true} if the supplied log record needs to be logged, * {@code false} otherwise. - * @since Android 1.0 */ boolean isLoggable(LogRecord record); } diff --git a/libcore/logging/src/main/java/java/util/logging/Formatter.java b/libcore/logging/src/main/java/java/util/logging/Formatter.java index 2941c2414..f9b0d25b5 100644 --- a/libcore/logging/src/main/java/java/util/logging/Formatter.java +++ b/libcore/logging/src/main/java/java/util/logging/Formatter.java @@ -15,7 +15,6 @@ * limitations under the License. */ - package java.util.logging; import java.text.MessageFormat; @@ -26,41 +25,24 @@ import java.util.ResourceBundle; * string representation. Head and tail strings are sometimes used to wrap a set * of records. The {@code getHead} and {@code getTail} methods are used for this * purpose. - * - * @since Android 1.0 */ public abstract class Formatter { - /* - * ------------------------------------------------------------------- - * Constructors - * ------------------------------------------------------------------- - */ - /** * Constructs a {@code Formatter} object. - * - * @since Android 1.0 */ protected Formatter() { super(); } - /* - * ------------------------------------------------------------------- - * Methods - * ------------------------------------------------------------------- - */ - /** * Converts a {@link LogRecord} object into a string representation. The * resulted string is usually localized and includes the message field of * the record. - * + * * @param r * the log record to be formatted into a string. * @return the formatted string. - * @since Android 1.0 */ public abstract String format(LogRecord r); @@ -71,16 +53,13 @@ public abstract class Formatter { *

        * The message string is firstly localized using the {@code ResourceBundle} * object associated with the supplied {@code LogRecord}. - *

        *

        * Notice : if message contains "{0", then java.text.MessageFormat is used. * Otherwise no formatting is performed. - *

        - * + * * @param r * the log record to be formatted. * @return the string resulted from the formatting. - * @since Android 1.0 */ public String formatMessage(LogRecord r) { String pattern = r.getMessage(); @@ -114,14 +93,12 @@ public abstract class Formatter { /** * Gets the head string used to wrap a set of log records. This base class * always returns an empty string. - * + * * @param h * the target handler. * @return the head string used to wrap a set of log records, empty in this * implementation. - * @since Android 1.0 */ - @SuppressWarnings("unused") public String getHead(Handler h) { return ""; //$NON-NLS-1$ } @@ -129,17 +106,13 @@ public abstract class Formatter { /** * Gets the tail string used to wrap a set of log records. This base class * always returns the empty string. - * + * * @param h * the target handler. * @return the tail string used to wrap a set of log records, empty in this * implementation. - * @since Android 1.0 */ - @SuppressWarnings("unused") public String getTail(Handler h) { return ""; //$NON-NLS-1$ } - } - diff --git a/libcore/logging/src/main/java/java/util/logging/Handler.java b/libcore/logging/src/main/java/java/util/logging/Handler.java index d28bce055..a5b92a0c7 100644 --- a/libcore/logging/src/main/java/java/util/logging/Handler.java +++ b/libcore/logging/src/main/java/java/util/logging/Handler.java @@ -15,13 +15,12 @@ * limitations under the License. */ - package java.util.logging; +import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; import java.security.AccessController; import java.security.PrivilegedExceptionAction; -import java.io.UnsupportedEncodingException; import org.apache.harmony.logging.internal.nls.Messages; @@ -29,24 +28,11 @@ import org.apache.harmony.logging.internal.nls.Messages; * A {@code Handler} object accepts a logging request and exports the desired * messages to a target, for example, a file, the console, etc. It can be * disabled by setting its logging level to {@code Level.OFF}. - * - * @since Android 1.0 */ public abstract class Handler { - /* - * ------------------------------------------------------------------- - * Constants - * ------------------------------------------------------------------- - */ private static final Level DEFAULT_LEVEL = Level.ALL; - /* - * ------------------------------------------------------------------- - * Instance variables - * ------------------------------------------------------------------- - */ - // the error manager to report errors during logging private ErrorManager errorMan; @@ -65,18 +51,10 @@ public abstract class Handler { // class name, used for property reading private String prefix; - /* - * ------------------------------------------------------------------- - * Constructors - * ------------------------------------------------------------------- - */ - /** * Constructs a {@code Handler} object with a default error manager instance * {@code ErrorManager}, the default encoding, and the default logging * level {@code Level.ALL}. It has no filter and no formatter. - * - * @since Android 1.0 */ protected Handler() { this.errorMan = new ErrorManager(); @@ -87,12 +65,6 @@ public abstract class Handler { this.prefix = this.getClass().getName(); } - /* - * ------------------------------------------------------------------- - * Methods - * ------------------------------------------------------------------- - */ - // get a instance from given class name, using Class.forName() private Object getDefaultInstance(String className) { Object result = null; @@ -102,7 +74,7 @@ public abstract class Handler { try { result = Class.forName(className).newInstance(); } catch (Exception e) { - //ignore + // ignore } return result; } @@ -110,7 +82,8 @@ public abstract class Handler { // get a instance from given class name, using context classloader private Object getCustomizeInstance(final String className) throws Exception { - Class c = AccessController.doPrivileged(new PrivilegedExceptionAction>() { + Class c = AccessController + .doPrivileged(new PrivilegedExceptionAction>() { public Class run() throws Exception { ClassLoader loader = Thread.currentThread() .getContextClassLoader(); @@ -126,22 +99,22 @@ public abstract class Handler { // print error message in some format void printInvalidPropMessage(String key, String value, Exception e) { // logging.12=Invalid property value for - String msg = new StringBuilder().append(Messages.getString("logging.12")) //$NON-NLS-1$ + String msg = new StringBuilder().append( + Messages.getString("logging.12")) //$NON-NLS-1$ .append(prefix).append(":").append(key).append("/").append( //$NON-NLS-1$//$NON-NLS-2$ value).toString(); errorMan.error(msg, e, ErrorManager.GENERIC_FAILURE); } - /* + /** * init the common properties, including filter, level, formatter, and * encoding */ - @SuppressWarnings("unused") void initProperties(String defaultLevel, String defaultFilter, String defaultFormatter, String defaultEncoding) { LogManager manager = LogManager.getLogManager(); - //set filter + // set filter final String filterName = manager.getProperty(prefix + ".filter"); //$NON-NLS-1$ if (null != filterName) { try { @@ -154,7 +127,7 @@ public abstract class Handler { filter = (Filter) getDefaultInstance(defaultFilter); } - //set level + // set level String levelName = manager.getProperty(prefix + ".level"); //$NON-NLS-1$ if (null != levelName) { try { @@ -167,7 +140,7 @@ public abstract class Handler { level = Level.parse(defaultLevel); } - //set formatter + // set formatter final String formatterName = manager.getProperty(prefix + ".formatter"); //$NON-NLS-1$ if (null != formatterName) { try { @@ -180,7 +153,7 @@ public abstract class Handler { formatter = (Formatter) getDefaultInstance(defaultFormatter); } - //set encoding + // set encoding final String encodingName = manager.getProperty(prefix + ".encoding"); //$NON-NLS-1$ try { internalSetEncoding(encodingName); @@ -193,37 +166,31 @@ public abstract class Handler { * Closes this handler. A flush operation will be performed and all the * associated resources will be freed. Client applications should not use * this handler after closing it. - * + * * @throws SecurityException * if a security manager determines that the caller does not * have the required permission. - * - * @since Android 1.0 */ public abstract void close(); /** * Flushes any buffered output. - * - * @since Android 1.0 */ public abstract void flush(); /** * Accepts a logging request and sends it to the the target. - * + * * @param record * the log record to be logged; {@code null} records are ignored. - * @since Android 1.0 */ public abstract void publish(LogRecord record); /** * Gets the character encoding used by this handler, {@code null} for * default encoding. - * + * * @return the character encoding used by this handler. - * @since Android 1.0 */ public String getEncoding() { return this.encoding; @@ -232,12 +199,11 @@ public abstract class Handler { /** * Gets the error manager used by this handler to report errors during * logging. - * + * * @return the error manager used by this handler. * @throws SecurityException * if a security manager determines that the caller does not * have the required permission. - * @since Android 1.0 */ public ErrorManager getErrorManager() { LogManager.getLogManager().checkAccess(); @@ -246,9 +212,8 @@ public abstract class Handler { /** * Gets the filter used by this handler. - * + * * @return the filter used by this handler (possibly {@code null}). - * @since Android 1.0 */ public Filter getFilter() { return this.filter; @@ -256,9 +221,8 @@ public abstract class Handler { /** * Gets the formatter used by this handler to format the logging messages. - * + * * @return the formatter used by this handler (possibly {@code null}). - * @since Android 1.0 */ public Formatter getFormatter() { return this.formatter; @@ -267,9 +231,8 @@ public abstract class Handler { /** * Gets the logging level of this handler, records with levels lower than * this value will be dropped. - * + * * @return the logging level of this handler. - * @since Android 1.0 */ public Level getLevel() { return this.level; @@ -278,12 +241,11 @@ public abstract class Handler { /** * Determines whether the supplied log record needs to be logged. The * logging levels will be checked as well as the filter. - * + * * @param record * the log record to be checked. * @return {@code true} if the supplied log record needs to be logged, * otherwise {@code false}. - * @since Android 1.0 */ public boolean isLoggable(LogRecord record) { if (null == record) { @@ -302,14 +264,13 @@ public abstract class Handler { * {@code ErrorManager} is used for that purpose. No security checks are * done, therefore this is compatible with environments where the caller * is non-privileged. - * + * * @param msg * the error message, may be {@code null}. * @param ex * the associated exception, may be {@code null}. * @param code * an {@code ErrorManager} error code. - * @since Android 1.0 */ protected void reportError(String msg, Exception ex, int code) { this.errorMan.error(msg, ex, code); @@ -319,12 +280,11 @@ public abstract class Handler { * Sets the character encoding used by this handler. A {@code null} value * indicates the use of the default encoding. This internal method does * not check security. - * + * * @param newEncoding * the character encoding to set. * @throws UnsupportedEncodingException * if the specified encoding is not supported by the runtime. - * @since Android 1.0 */ void internalSetEncoding(String newEncoding) throws UnsupportedEncodingException { @@ -347,7 +307,7 @@ public abstract class Handler { /** * Sets the character encoding used by this handler, {@code null} indicates * a default encoding. - * + * * @param encoding * the character encoding to set. * @throws SecurityException @@ -355,7 +315,6 @@ public abstract class Handler { * have the required permission. * @throws UnsupportedEncodingException * if the specified encoding is not supported by the runtime. - * @since Android 1.0 */ public void setEncoding(String encoding) throws SecurityException, UnsupportedEncodingException { @@ -365,7 +324,7 @@ public abstract class Handler { /** * Sets the error manager for this handler. - * + * * @param em * the error manager to set. * @throws NullPointerException @@ -373,7 +332,6 @@ public abstract class Handler { * @throws SecurityException * if a security manager determines that the caller does not * have the required permission. - * @since Android 1.0 */ public void setErrorManager(ErrorManager em) { LogManager.getLogManager().checkAccess(); @@ -385,13 +343,12 @@ public abstract class Handler { /** * Sets the filter to be used by this handler. - * + * * @param newFilter * the filter to set, may be {@code null}. * @throws SecurityException * if a security manager determines that the caller does not * have the required permission. - * @since Android 1.0 */ public void setFilter(Filter newFilter) { LogManager.getLogManager().checkAccess(); @@ -401,7 +358,7 @@ public abstract class Handler { /** * Sets the formatter to be used by this handler. This internal method does * not check security. - * + * * @param newFormatter * the formatter to set. */ @@ -414,7 +371,7 @@ public abstract class Handler { /** * Sets the formatter to be used by this handler. - * + * * @param newFormatter * the formatter to set. * @throws NullPointerException @@ -422,7 +379,6 @@ public abstract class Handler { * @throws SecurityException * if a security manager determines that the caller does not * have the required permission. - * @since Android 1.0 */ public void setFormatter(Formatter newFormatter) { LogManager.getLogManager().checkAccess(); @@ -432,7 +388,7 @@ public abstract class Handler { /** * Sets the logging level of the messages logged by this handler, levels * lower than this value will be dropped. - * + * * @param newLevel * the logging level to set. * @throws NullPointerException @@ -440,7 +396,6 @@ public abstract class Handler { * @throws SecurityException * if a security manager determines that the caller does not * have the required permission. - * @since Android 1.0 */ public void setLevel(Level newLevel) { if (null == newLevel) { @@ -450,4 +405,3 @@ public abstract class Handler { this.level = newLevel; } } - diff --git a/libcore/logging/src/main/java/java/util/logging/Level.java b/libcore/logging/src/main/java/java/util/logging/Level.java index 32ba01745..f98812739 100644 --- a/libcore/logging/src/main/java/java/util/logging/Level.java +++ b/libcore/logging/src/main/java/java/util/logging/Level.java @@ -41,8 +41,6 @@ import dalvik.system.VMStack; * INFO, WARNING, SEVERE. There are two additional predefined levels, which are * ALL and OFF. ALL indicates logging all messages, and OFF indicates logging no * messages. - *

        - * @since Android 1.0 */ public class Level implements Serializable { @@ -52,70 +50,52 @@ public class Level implements Serializable { /** * The OFF level provides no logging messages. - * - * @since Android 1.0 */ public static final Level OFF = new Level("OFF", Integer.MAX_VALUE); //$NON-NLS-1$ /** * The SEVERE level provides severe failure messages. - * - * @since Android 1.0 */ public static final Level SEVERE = new Level("SEVERE", 1000); //$NON-NLS-1$ /** * The WARNING level provides warnings. - * - * @since Android 1.0 */ public static final Level WARNING = new Level("WARNING", 900); //$NON-NLS-1$ /** * The INFO level provides informative messages. - * - * @since Android 1.0 */ public static final Level INFO = new Level("INFO", 800); //$NON-NLS-1$ /** * The CONFIG level provides static configuration messages. - * - * @since Android 1.0 */ public static final Level CONFIG = new Level("CONFIG", 700); //$NON-NLS-1$ /** * The FINE level provides tracing messages. - * - * @since Android 1.0 */ public static final Level FINE = new Level("FINE", 500); //$NON-NLS-1$ /** * The FINER level provides more detailed tracing messages. - * - * @since Android 1.0 */ public static final Level FINER = new Level("FINER", 400); //$NON-NLS-1$ /** * The FINEST level provides highly detailed tracing messages. - * - * @since Android 1.0 */ public static final Level FINEST = new Level("FINEST", 300); //$NON-NLS-1$ /** * The ALL level provides all logging messages. - * - * @since Android 1.0 */ public static final Level ALL = new Level("ALL", Integer.MIN_VALUE); //$NON-NLS-1$ /** * Parses a level name into a {@code Level} object. - * + * * @param name * the name of the desired {@code level}, which cannot be * {@code null}. @@ -124,13 +104,8 @@ public class Level implements Serializable { * if {@code name} is {@code null}. * @throws IllegalArgumentException * if {@code name} is not valid. - * @since Android 1.0 */ public static Level parse(String name) throws IllegalArgumentException { - // BEGIN android-note - // final modifier removed and IAE added to get closer to the RI - // copied from newer version of harmony - // END android-note if (name == null) { // logging.1C=The 'name' parameter is null. throw new NullPointerException(Messages.getString("logging.1C")); //$NON-NLS-1$ @@ -155,8 +130,8 @@ public class Level implements Serializable { if (isNameAnInt) { /* - * Loop through levels a second time, so that the - * returned instance will be passed on the order of construction. + * Loop through levels a second time, so that the returned + * instance will be passed on the order of construction. */ for (Level level : levels) { if (nameAsInt == level.intValue()) { @@ -168,7 +143,8 @@ public class Level implements Serializable { if (!isNameAnInt) { // logging.1D=Cannot parse this name: {0} - throw new IllegalArgumentException(Messages.getString("logging.1D", name)); //$NON-NLS-1$ + throw new IllegalArgumentException(Messages.getString( + "logging.1D", name)); //$NON-NLS-1$ } return new Level(name, nameAsInt); @@ -176,21 +152,21 @@ public class Level implements Serializable { /** * The name of this Level. - * + * * @serial */ private final String name; /** * The integer value indicating the level. - * + * * @serial */ private final int value; /** * The name of the resource bundle used to localize the level name. - * + * * @serial */ private final String resourceBundleName; @@ -204,14 +180,13 @@ public class Level implements Serializable { /** * Constructs an instance of {@code Level} taking the supplied name and * level value. - * + * * @param name * the name of the level. * @param level * an integer value indicating the level. * @throws NullPointerException * if {@code name} is {@code null}. - * @since Android 1.0 */ protected Level(String name, int level) { this(name, level, null); @@ -220,7 +195,7 @@ public class Level implements Serializable { /** * Constructs an instance of {@code Level} taking the supplied name, level * value and resource bundle name. - * + * * @param name * the name of the level. * @param level @@ -229,7 +204,6 @@ public class Level implements Serializable { * the name of the resource bundle to use. * @throws NullPointerException * if {@code name} is {@code null}. - * @since Android 1.0 */ protected Level(String name, int level, String resourceBundleName) { if (name == null) { @@ -256,9 +230,8 @@ public class Level implements Serializable { /** * Gets the name of this level. - * + * * @return this level's name. - * @since Android 1.0 */ public String getName() { return this.name; @@ -266,9 +239,8 @@ public class Level implements Serializable { /** * Gets the name of the resource bundle associated with this level. - * + * * @return the name of this level's resource bundle. - * @since Android 1.0 */ public String getResourceBundleName() { return this.resourceBundleName; @@ -276,20 +248,17 @@ public class Level implements Serializable { /** * Gets the integer value indicating this level. - * + * * @return this level's integer value. - * @since Android 1.0 */ public final int intValue() { return this.value; } /** - *

        * Serialization helper method to maintain singletons and add any new * levels. - *

        - * + * * @return the resolved instance. */ private Object readResolve() { @@ -298,7 +267,7 @@ public class Level implements Serializable { if (value != level.value) { continue; } - if (!name.equals(name)) { + if (!name.equals(level.name)) { continue; } if (resourceBundleName == level.resourceBundleName) { @@ -316,7 +285,7 @@ public class Level implements Serializable { /** * Serialization helper to setup transient resource bundle instance. - * + * * @param in * the input stream to read the instance data from. * @throws IOException @@ -324,7 +293,8 @@ public class Level implements Serializable { * @throws ClassNotFoundException * if a class is not found. */ - private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { + private void readObject(ObjectInputStream in) throws IOException, + ClassNotFoundException { in.defaultReadObject(); if (resourceBundleName != null) { try { @@ -339,9 +309,8 @@ public class Level implements Serializable { * Gets the localized name of this level. The default locale is used. If no * resource bundle is associated with this level then the original level * name is returned. - * + * * @return the localized name of this level. - * @since Android 1.0 */ public String getLocalizedName() { if (rb == null) { @@ -358,12 +327,11 @@ public class Level implements Serializable { /** * Compares two {@code Level} objects for equality. They are considered to * be equal if they have the same level value. - * + * * @param o * the other object to compare this level to. * @return {@code true} if this object equals to the supplied object, * {@code false} otherwise. - * @since Android 1.0 */ @Override public boolean equals(Object o) { @@ -380,9 +348,8 @@ public class Level implements Serializable { /** * Returns the hash code of this {@code Level} object. - * + * * @return this level's hash code. - * @since Android 1.0 */ @Override public int hashCode() { @@ -392,9 +359,8 @@ public class Level implements Serializable { /** * Returns the string representation of this {@code Level} object. In * this case, it is the level's name. - * + * * @return the string representation of this level. - * @since Android 1.0 */ @Override public final String toString() { diff --git a/libcore/logging/src/main/java/java/util/logging/LogManager.java b/libcore/logging/src/main/java/java/util/logging/LogManager.java index 8409b817d..308daafa5 100644 --- a/libcore/logging/src/main/java/java/util/logging/LogManager.java +++ b/libcore/logging/src/main/java/java/util/logging/LogManager.java @@ -24,6 +24,10 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +// BEGIN android-removed +//import java.lang.management.ManagementFactory; +//import java.lang.reflect.Method; +// END android-removed import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Collection; @@ -33,13 +37,11 @@ import java.util.Iterator; import java.util.Properties; import java.util.Set; import java.util.StringTokenizer; + // BEGIN android-removed -// import java.lang.management.ManagementFactory; -// import java.lang.reflect.Method; -// import javax.management.MBeanServer; -// import javax.management.ObjectName; -// import javax.management.ObjectInstance; -// import javax.management.MalformedObjectNameException; +//import javax.management.MBeanServer; +//import javax.management.ObjectInstance; +//import javax.management.ObjectName; // END android-removed import org.apache.harmony.logging.internal.nls.Messages; @@ -49,23 +51,19 @@ import org.apache.harmony.logging.internal.nls.Messages; * logging framework, and to manage a hierarchical namespace of all named * {@code Logger} objects. *

        - * * There is only one global {@code LogManager} instance in the * application, which can be get by calling static method * {@link #getLogManager()}. This instance is created and * initialized during class initialization and cannot be changed. - *

        *

        * The {@code LogManager} class can be specified by * java.util.logging.manager system property, if the property is unavailable or * invalid, the default class {@link java.util.logging.LogManager} will * be used. - *

        *

        - * When initialization, {@code LogManager} read its configuration from a + * On initialization, {@code LogManager} reads its configuration from a * properties file, which by default is the "lib/logging.properties" in the JRE * directory. - *

        *

        * However, two optional system properties can be used to customize the initial * configuration process of {@code LogManager}. @@ -73,31 +71,26 @@ import org.apache.harmony.logging.internal.nls.Messages; *

      6. "java.util.logging.config.class"
      7. *
      8. "java.util.logging.config.file"
      9. * - *

        *

        * These two properties can be set in three ways, by the Preferences API, by the * "java" command line property definitions, or by system property definitions * passed to JNI_CreateJavaVM. - *

        *

        * The "java.util.logging.config.class" should specifies a class name. If it is * set, this given class will be loaded and instantiated during * {@code LogManager} initialization, so that this object's default * constructor can read the initial configuration and define properties for * {@code LogManager}. - *

        *

        * If "java.util.logging.config.class" property is not set, or it is invalid, or * some exception is thrown during the instantiation, then the * "java.util.logging.config.file" system property can be used to specify a * properties file. The {@code LogManager} will read initial * configuration from this file. - *

        *

        * If neither of these properties is defined, or some exception is thrown * during these two properties using, the {@code LogManager} will read * its initial configuration from default properties file, as described above. - *

        *

        * The global logging properties may include: *

          @@ -113,22 +106,18 @@ import org.apache.harmony.logging.internal.nls.Messages; * some logger, etc. These classes will be loaded and instantiated during * {@code LogManager} configuration *
        - *

        *

        * This class, together with any handler and configuration classes associated * with it, must be loaded from the system classpath when * {@code LogManager} configuration occurs. - *

        *

        * Besides global properties, the properties for loggers and Handlers can be * specified in the property files. The names of these properties will start * with the complete dot separated names for the handlers or loggers. - *

        *

        * In the {@code LogManager}'s hierarchical namespace, * {@code Loggers} are organized based on their dot separated names. For * example, "x.y.z" is child of "x.y". - *

        *

        * Levels for {@code Loggers} can be defined by properties whose name end * with ".level". Thus "alogger.level" defines a level for the logger named as @@ -136,23 +125,15 @@ import org.apache.harmony.logging.internal.nls.Messages; * properties are read and applied in the same order as they are specified in * the property file. The root logger's level can be defined by the property * named as ".level". - *

        *

        * All methods on this type can be taken as being thread safe. - *

        - * + * */ public class LogManager { - /* - * ------------------------------------------------------------------- - * Class variables - * ------------------------------------------------------------------- - */ // The line separator of the underlying OS // Use privileged code to read the line.separator system property - private static final String lineSeparator = - getPrivilegedSystemProperty("line.separator"); //$NON-NLS-1$ + private static final String lineSeparator = getPrivilegedSystemProperty("line.separator"); //$NON-NLS-1$ // The shared logging permission private static final LoggingPermission perm = new LoggingPermission( @@ -160,63 +141,55 @@ public class LogManager { // the singleton instance static LogManager manager; - + /** * The {@code String} value of the {@link LoggingMXBean}'s ObjectName. - * - * @since Android 1.0 */ - public static final String LOGGING_MXBEAN_NAME = - "java.util.logging:type=Logging"; //$NON-NLS-1$ + public static final String LOGGING_MXBEAN_NAME = "java.util.logging:type=Logging"; //$NON-NLS-1$ /** * Get the {@code LoggingMXBean} instance. this implementation always throws * an UnsupportedOperationException. - * + * * @return the {@code LoggingMXBean} instance */ public static LoggingMXBean getLoggingMXBean() { - // BEGIN android-added - throw new UnsupportedOperationException(); - // END android-added - // BEGIN android-removed - // try { - // ObjectName loggingMXBeanName = new ObjectName(LOGGING_MXBEAN_NAME); - // MBeanServer platformBeanServer = - // ManagementFactory.getPlatformMBeanServer(); - // Set loggingMXBeanSet = platformBeanServer.queryMBeans( - // loggingMXBeanName, null); - // - // if (loggingMXBeanSet.size() != 1) { - // // logging.21=There Can Be Only One logging MX bean. - // throw new AssertionError(Messages.getString("logging.21")); - // } - // - // Iterator i = loggingMXBeanSet.iterator(); - // ObjectInstance loggingMXBeanOI = (ObjectInstance) i.next(); - // String lmxbcn = loggingMXBeanOI.getClassName(); - // Class lmxbc = Class.forName(lmxbcn); - // Method giMethod = lmxbc.getDeclaredMethod("getInstance"); - // giMethod.setAccessible(true); - // LoggingMXBean lmxb = (LoggingMXBean) - // giMethod.invoke(null, new Object[] {}); - // - // return lmxb; - // } catch (Exception e) { - // //TODO - // //e.printStackTrace(); - // } - // // logging.22=Exception occurred while getting the logging MX bean. - // throw new AssertionError(Messages.getString("logging.22")); //$NON-NLS-1$ - // END android-removed - } + // BEGIN android-added + throw new UnsupportedOperationException(); + // END android-added + // BEGIN android-removed + // try { + // ObjectName loggingMXBeanName = new ObjectName(LOGGING_MXBEAN_NAME); + // MBeanServer platformBeanServer = ManagementFactory + // .getPlatformMBeanServer(); + // Set loggingMXBeanSet = platformBeanServer.queryMBeans( + // loggingMXBeanName, null); + // + // if (loggingMXBeanSet.size() != 1) { + // // logging.21=There Can Be Only One logging MX bean. + // throw new AssertionError(Messages.getString("logging.21")); //$NON-NLS-1$ + // } + // + // Iterator i = loggingMXBeanSet.iterator(); + // ObjectInstance loggingMXBeanOI = (ObjectInstance) i.next(); + // String lmxbcn = loggingMXBeanOI.getClassName(); + // Class lmxbc = Class.forName(lmxbcn); + // Method giMethod = lmxbc.getDeclaredMethod("getInstance"); //$NON-NLS-1$ + // giMethod.setAccessible(true); + // LoggingMXBean lmxb = (LoggingMXBean) giMethod.invoke(null, + // new Object[] {}); + // + // return lmxb; + // } catch (Exception e) { + // // TODO + // // e.printStackTrace(); + // } + // // logging.22=Exception occurred while getting the logging MX bean. + // throw new AssertionError(Messages.getString("logging.22")); //$NON-NLS-1$ + // END android-removed + } - /* - * ------------------------------------------------------------------- - * Instance variables - * ------------------------------------------------------------------- - */ - //FIXME: use weak reference to avoid heap memory leak + // FIXME: use weak reference to avoid heap memory leak private Hashtable loggers; // the configuration properties @@ -225,19 +198,13 @@ public class LogManager { // the property change listener private PropertyChangeSupport listeners; - /* - * ------------------------------------------------------------------- - * Global initialization - * ------------------------------------------------------------------- - */ - static { // init LogManager singleton instance AccessController.doPrivileged(new PrivilegedAction() { public Object run() { - String className = System.getProperty( - "java.util.logging.manager"); //$NON-NLS-1$ - + String className = System + .getProperty("java.util.logging.manager"); //$NON-NLS-1$ + if (null != className) { manager = (LogManager) getInstanceByClass(className); } @@ -256,7 +223,7 @@ public class LogManager { Logger root = new Logger("", null); //$NON-NLS-1$ root.setLevel(Level.INFO); Logger.global.setParent(root); - + manager.addLogger(root); manager.addLogger(Logger.global); return null; @@ -289,11 +256,6 @@ public class LogManager { }); } - /* - * ------------------------------------------------------------------- - * Methods - * ------------------------------------------------------------------- - */ /* * Package private utilities Returns the line separator of the underlying * OS. @@ -307,7 +269,7 @@ public class LogManager { * that it is trusted to modify the configuration for logging framework. If * the check passes, just return, otherwise {@code SecurityException} * will be thrown. - * + * * @throws SecurityException * if there is a security manager in operation and the invoker * of this method does not have the required security permission @@ -330,7 +292,7 @@ public class LogManager { * unexpectedly garbage collected it is necessary for applications * to maintain references to them. *

        - * + * * @param logger * the logger to be added. * @return true if the given logger is added into the namespace @@ -347,7 +309,6 @@ public class LogManager { return true; } - private void addToFamilyTree(Logger logger, String name) { Logger parent = null; // find parent @@ -359,8 +320,8 @@ public class LogManager { if (parent != null) { logger.internalSetParent(parent); break; - } else if (getProperty(parentName+".level") != null || //$NON-NLS-1$ - getProperty(parentName+".handlers") != null) { //$NON-NLS-1$ + } else if (getProperty(parentName + ".level") != null || //$NON-NLS-1$ + getProperty(parentName + ".handlers") != null) { //$NON-NLS-1$ parent = Logger.getLogger(parentName); logger.internalSetParent(parent); break; @@ -371,16 +332,22 @@ public class LogManager { } // find children - //TODO: performance can be improved here? + // TODO: performance can be improved here? Collection allLoggers = loggers.values(); - for (Logger child : allLoggers) { + for (final Logger child : allLoggers) { Logger oldParent = child.getParent(); if (parent == oldParent && (name.length() == 0 || child.getName().startsWith( name + '.'))) { - child.setParent(logger); + final Logger thisLogger = logger; + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + child.setParent(thisLogger); + return null; + } + }); if (null != oldParent) { - //-- remove from old parent as the parent has been changed + // -- remove from old parent as the parent has been changed oldParent.removeChild(child); } } @@ -389,7 +356,7 @@ public class LogManager { /** * Get the logger with the given name. - * + * * @param name * name of logger * @return logger with given name, or {@code null} if nothing is found. @@ -400,7 +367,7 @@ public class LogManager { /** * Get a {@code Enumeration} of all registered logger names. - * + * * @return enumeration of registered logger names */ public synchronized Enumeration getLoggerNames() { @@ -409,7 +376,7 @@ public class LogManager { /** * Get the global {@code LogManager} instance. - * + * * @return the global {@code LogManager} instance */ public static LogManager getLogManager() { @@ -418,7 +385,7 @@ public class LogManager { /** * Get the value of property with given name. - * + * * @param name * the name of property * @return the value of property @@ -433,7 +400,7 @@ public class LogManager { *

        * Notice : No {@code PropertyChangeEvent} are fired. *

        - * + * * @throws IOException * if any IO related problems happened. * @throws SecurityException @@ -443,19 +410,20 @@ public class LogManager { public void readConfiguration() throws IOException { checkAccess(); // check config class - String configClassName = System.getProperty( - "java.util.logging.config.class"); //$NON-NLS-1$ - if (null == configClassName || null == getInstanceByClass(configClassName)) { - // if config class failed, check config file - String configFile = System.getProperty( - "java.util.logging.config.file"); //$NON-NLS-1$ + String configClassName = System + .getProperty("java.util.logging.config.class"); //$NON-NLS-1$ + if (null == configClassName + || null == getInstanceByClass(configClassName)) { + // if config class failed, check config file + String configFile = System + .getProperty("java.util.logging.config.file"); //$NON-NLS-1$ if (null == configFile) { // if cannot find configFile, use default logging.properties configFile = new StringBuilder().append( System.getProperty("java.home")).append(File.separator) //$NON-NLS-1$ .append("lib").append(File.separator).append( //$NON-NLS-1$ - "logging.properties").toString(); //$NON-NLS-1$ + "logging.properties").toString(); //$NON-NLS-1$ } InputStream input = null; @@ -463,7 +431,7 @@ public class LogManager { // BEGIN android-removed // input = new BufferedInputStream(new FileInputStream(configFile)); // END android-removed - + // BEGIN android-added try { input = new BufferedInputStream( @@ -504,13 +472,12 @@ public class LogManager { return clazz.newInstance(); } catch (Exception e) { try { - Class clazz = Thread.currentThread() - .getContextClassLoader().loadClass(className); + Class clazz = Thread.currentThread().getContextClassLoader() + .loadClass(className); return clazz.newInstance(); } catch (Exception innerE) { - //logging.20=Loading class "{0}" failed - System.err.println(Messages.getString( - "logging.20", className)); //$NON-NLS-1$ + // logging.20=Loading class "{0}" failed + System.err.println(Messages.getString("logging.20", className)); //$NON-NLS-1$ System.err.println(innerE); return null; } @@ -523,7 +490,7 @@ public class LogManager { throws IOException { reset(); props.load(ins); - + // parse property "config" and apply setting String configs = props.getProperty("config"); //$NON-NLS-1$ if (null != configs) { @@ -533,27 +500,25 @@ public class LogManager { getInstanceByClass(configerName); } } - + // set levels for logger Collection allLoggers = loggers.values(); - for(Logger logger : allLoggers){ - String property = props.getProperty( - logger.getName()+".level"); //$NON-NLS-1$ - if(null != property){ + for (Logger logger : allLoggers) { + String property = props.getProperty(logger.getName() + ".level"); //$NON-NLS-1$ + if (null != property) { logger.setLevel(Level.parse(property)); } } listeners.firePropertyChange(null, null, null); } - /** * Re-initialize the properties and configuration from the given * {@code InputStream} *

        * Notice : No {@code PropertyChangeEvent} are fired. *

        - * + * * @param ins * the input stream * @throws IOException @@ -574,7 +539,7 @@ public class LogManager { * level is set to null, except the root logger's level is set to * {@code Level.INFO}. *

        - * + * * @throws SecurityException * if security manager exists and it determines that caller does * not have the required permissions to perform this action. @@ -583,10 +548,10 @@ public class LogManager { checkAccess(); props = new Properties(); Enumeration names = getLoggerNames(); - while(names.hasMoreElements()){ + while (names.hasMoreElements()) { String name = names.nextElement(); Logger logger = getLogger(name); - if(logger != null){ + if (logger != null) { logger.reset(); } } @@ -599,7 +564,7 @@ public class LogManager { /** * Add a {@code PropertyChangeListener}, which will be invoked when * the properties are reread. - * + * * @param l * the {@code PropertyChangeListener} to be added. * @throws SecurityException @@ -607,7 +572,7 @@ public class LogManager { * not have the required permissions to perform this action. */ public void addPropertyChangeListener(PropertyChangeListener l) { - if(l == null){ + if (l == null) { throw new NullPointerException(); } checkAccess(); @@ -617,7 +582,7 @@ public class LogManager { /** * Remove a {@code PropertyChangeListener}, do nothing if the given * listener is not found. - * + * * @param l * the {@code PropertyChangeListener} to be removed. * @throws SecurityException diff --git a/libcore/logging/src/main/java/java/util/logging/LogRecord.java b/libcore/logging/src/main/java/java/util/logging/LogRecord.java index b8a98efa2..0a8e257a4 100644 --- a/libcore/logging/src/main/java/java/util/logging/LogRecord.java +++ b/libcore/logging/src/main/java/java/util/logging/LogRecord.java @@ -40,9 +40,6 @@ import org.apache.harmony.logging.internal.nls.Messages; * {@code getSourceClassName} or {@code getSourceMethodName} if they expect to * use them after passing the {@code LogRecord} object to another thread or * transmitting it over RMI. - *

        - * - * @since Android 1.0 */ public class LogRecord implements Serializable { @@ -65,70 +62,70 @@ public class LogRecord implements Serializable { /** * The logging level. - * + * * @serial */ private Level level; /** * The sequence number. - * + * * @serial */ private long sequenceNumber; /** * The name of the class that issued the logging call. - * + * * @serial */ private String sourceClassName; /** * The name of the method that issued the logging call. - * + * * @serial */ private String sourceMethodName; /** * The original message text. - * + * * @serial */ private String message; /** * The ID of the thread that issued the logging call. - * + * * @serial */ private int threadID; /** * The time that the event occurred, in milliseconds since 1970. - * + * * @serial */ private long millis; /** * The associated {@code Throwable} object if any. - * + * * @serial */ private Throwable thrown; /** * The name of the source logger. - * + * * @serial */ private String loggerName; /** * The name of the resource bundle used to localize the log message. - * + * * @serial */ private String resourceBundleName; @@ -148,14 +145,13 @@ public class LogRecord implements Serializable { * sequence property is set to a new unique value, allocated in increasing * order within the virtual machine. The thread ID is set to a unique value * for the current thread. All other properties are set to {@code null}. - * + * * @param level * the logging level, may not be {@code null}. * @param msg * the raw message. * @throws NullPointerException * if {@code level} is {@code null}. - * @since Android 1.0 */ public LogRecord(Level level, String msg) { if (null == level) { @@ -188,9 +184,8 @@ public class LogRecord implements Serializable { /** * Gets the logging level. - * + * * @return the logging level. - * @since Android 1.0 */ public Level getLevel() { return level; @@ -198,12 +193,11 @@ public class LogRecord implements Serializable { /** * Sets the logging level. - * + * * @param level * the level to set. * @throws NullPointerException * if {@code level} is {@code null}. - * @since Android 1.0 */ public void setLevel(Level level) { if (null == level) { @@ -215,9 +209,8 @@ public class LogRecord implements Serializable { /** * Gets the name of the logger. - * + * * @return the logger name. - * @since Android 1.0 */ public String getLoggerName() { return loggerName; @@ -225,10 +218,9 @@ public class LogRecord implements Serializable { /** * Sets the name of the logger. - * + * * @param loggerName * the logger name to set. - * @since Android 1.0 */ public void setLoggerName(String loggerName) { this.loggerName = loggerName; @@ -236,9 +228,8 @@ public class LogRecord implements Serializable { /** * Gets the raw message. - * + * * @return the raw message, may be {@code null}. - * @since Android 1.0 */ public String getMessage() { return message; @@ -248,10 +239,9 @@ public class LogRecord implements Serializable { * Sets the raw message. When this record is formatted by a logger that has * a localization resource bundle that contains an entry for {@code message}, * then the raw message is replaced with its localized version. - * + * * @param message * the raw message to set, may be {@code null}. - * @since Android 1.0 */ public void setMessage(String message) { this.message = message; @@ -259,9 +249,8 @@ public class LogRecord implements Serializable { /** * Gets the time when this event occurred, in milliseconds since 1970. - * + * * @return the time when this event occurred, in milliseconds since 1970. - * @since Android 1.0 */ public long getMillis() { return millis; @@ -269,10 +258,9 @@ public class LogRecord implements Serializable { /** * Sets the time when this event occurred, in milliseconds since 1970. - * + * * @param millis * the time when this event occurred, in milliseconds since 1970. - * @since Android 1.0 */ public void setMillis(long millis) { this.millis = millis; @@ -280,10 +268,9 @@ public class LogRecord implements Serializable { /** * Gets the parameters. - * + * * @return the array of parameters or {@code null} if there are no * parameters. - * @since Android 1.0 */ public Object[] getParameters() { return parameters; @@ -291,10 +278,9 @@ public class LogRecord implements Serializable { /** * Sets the parameters. - * + * * @param parameters * the array of parameters to set, may be {@code null}. - * @since Android 1.0 */ public void setParameters(Object[] parameters) { this.parameters = parameters; @@ -303,21 +289,20 @@ public class LogRecord implements Serializable { /** * Gets the resource bundle used to localize the raw message during * formatting. - * + * * @return the associated resource bundle, {@code null} if none is * available or the message is not localizable. - * @since Android 1.0 */ public ResourceBundle getResourceBundle() { return resourceBundle; } /** - * Sets the resource bundle. - * + * Sets the resource bundle used to localize the raw message during + * formatting. + * * @param resourceBundle * the resource bundle to set, may be {@code null}. - * @since Android 1.0 */ public void setResourceBundle(ResourceBundle resourceBundle) { this.resourceBundle = resourceBundle; @@ -325,10 +310,9 @@ public class LogRecord implements Serializable { /** * Gets the name of the resource bundle. - * + * * @return the name of the resource bundle, {@code null} if none is * available or the message is not localizable. - * @since Android 1.0 */ public String getResourceBundleName() { return resourceBundleName; @@ -336,10 +320,9 @@ public class LogRecord implements Serializable { /** * Sets the name of the resource bundle. - * + * * @param resourceBundleName * the name of the resource bundle to set. - * @since Android 1.0 */ public void setResourceBundleName(String resourceBundleName) { this.resourceBundleName = resourceBundleName; @@ -347,9 +330,8 @@ public class LogRecord implements Serializable { /** * Gets the sequence number. - * + * * @return the sequence number. - * @since Android 1.0 */ public long getSequenceNumber() { return sequenceNumber; @@ -359,10 +341,9 @@ public class LogRecord implements Serializable { * Sets the sequence number. It is usually not necessary to call this method * to change the sequence number because the number is allocated when this * instance is constructed. - * + * * @param sequenceNumber * the sequence number to set. - * @since Android 1.0 */ public void setSequenceNumber(long sequenceNumber) { this.sequenceNumber = sequenceNumber; @@ -371,9 +352,8 @@ public class LogRecord implements Serializable { /** * Gets the name of the class that is the source of this log record. This * information can be changed, may be {@code null} and is untrusted. - * + * * @return the name of the source class of this log record (possiblity {@code null}) - * @since Android 1.0 */ public String getSourceClassName() { initSource(); @@ -394,7 +374,8 @@ public class LogRecord implements Serializable { break FINDLOG; } } - while(++i * Notice : the ID doesn't necessary map the OS thread ID *

        - * + * * @return the ID of the thread originating this log record. - * @since Android 1.0 */ public int getThreadID() { return threadID; @@ -458,10 +435,9 @@ public class LogRecord implements Serializable { /** * Sets the ID of the thread originating this log record. - * + * * @param threadID * the new ID of the thread originating this log record. - * @since Android 1.0 */ public void setThreadID(int threadID) { this.threadID = threadID; @@ -469,9 +445,8 @@ public class LogRecord implements Serializable { /** * Gets the {@code Throwable} object associated with this log record. - * + * * @return the {@code Throwable} object associated with this log record. - * @since Android 1.0 */ public Throwable getThrown() { return thrown; @@ -479,11 +454,10 @@ public class LogRecord implements Serializable { /** * Sets the {@code Throwable} object associated with this log record. - * + * * @param thrown * the new {@code Throwable} object to associate with this log * record. - * @since Android 1.0 */ public void setThrown(Throwable thrown) { this.thrown = thrown; @@ -514,12 +488,13 @@ public class LogRecord implements Serializable { in.defaultReadObject(); byte major = in.readByte(); byte minor = in.readByte(); - //only check MAJOR version + // only check MAJOR version if (major != MAJOR) { // logging.5=Different version - {0}.{1} - throw new IOException(Messages.getString("logging.5", major, minor)); //$NON-NLS-1$ + throw new IOException(Messages.getString("logging.5", //$NON-NLS-1$ + Byte.valueOf(major), Byte.valueOf(minor))); } - + int length = in.readInt(); if (length >= 0) { parameters = new Object[length]; diff --git a/libcore/logging/src/main/java/java/util/logging/Logger.java b/libcore/logging/src/main/java/java/util/logging/Logger.java index cd88ca006..fe124d392 100644 --- a/libcore/logging/src/main/java/java/util/logging/Logger.java +++ b/libcore/logging/src/main/java/java/util/logging/Logger.java @@ -24,7 +24,6 @@ import java.util.List; import java.util.Locale; import java.util.MissingResourceException; import java.util.ResourceBundle; -import java.util.StringTokenizer; import org.apache.harmony.logging.internal.nls.Messages; @@ -39,7 +38,6 @@ import org.apache.harmony.logging.internal.nls.Messages; * namespace hierarchy managed by a log manager. The naming convention is * usually the same as java package's naming convention, that is using * dot-separated strings. Anonymous loggers do not belong to any namespace. - *

        *

        * Loggers "inherit" log level setting from their parent if their own level is * set to {@code null}. This is also true for the resource bundle. The logger's @@ -49,36 +47,28 @@ import org.apache.harmony.logging.internal.nls.Messages; * context, "inherit" only means that "behavior" is inherited. The internal * field values will not change, for example, {@code getLevel()} still returns * {@code null}. - *

        *

        * When loading a given resource bundle, the logger first tries to use the * context classloader. If that fails, it tries the system classloader. And if * that still fails, it searches up the class stack and uses each class's * classloader to try to locate the resource bundle. - *

        *

        * Some log methods accept log requests that do not specify the source class and * source method. In these cases, the logging framework will automatically infer * the calling class and method, but this is not guaranteed to be accurate. - *

        *

        * Once a {@code LogRecord} object has been passed into the logging framework, * it is owned by the logging framework and the client applications should not * use it any longer. - *

        *

        * All methods of this class are thread-safe. - *

        - * + * * @see LogManager - * @since Android 1.0 */ public class Logger { /** * The global logger is provided as convenience for casual use. - * - * @since Android 1.0 */ public final static Logger global = new Logger("global", null); //$NON-NLS-1$ @@ -116,27 +106,19 @@ public class Logger { private boolean isNamed; private List childs; - + private LogManager manager; // BEGIN android-changed private volatile boolean handlerInited; // END android-changed - - /* - * ------------------------------------------------------------------- - * Constructors - * ------------------------------------------------------------------- - */ - /** * Constructs a {@code Logger} object with the supplied name and resource * bundle name; {@code notifiyParentHandlers} is set to {@code true}. *

        - * Notice : Loggers use a naming hierarchy. Thus "z.x.y" is a child of "z.x". - *

        - * + * Notice : Loggers use a naming hierarchy. Thus "z.x.y" is a child of "z.x". + * * @param name * the name of this logger, may be {@code null} for anonymous * loggers. @@ -145,7 +127,6 @@ public class Logger { * messages, may be {@code null}. * @throws MissingResourceException * if the specified resource bundle can not be loaded. - * @since Android 1.0 */ protected Logger(String name, String resourceBundleName) { // try to load the specified resource bundle first @@ -164,24 +145,24 @@ public class Logger { // any logger is not anonymous by default this.isNamed = true; - //-- 'null' means that level will be inherited from parent (see getLevel) - //-- Level.INFO is default level if we don't set it. It will be - //-- changed to parent level or to configLevel after adding to the - //-- family tree. As of this, actually, setting to Level.INFO is - //-- not needed here. + // -- 'null' means that level will be inherited from parent (see + // getLevel) + // -- Level.INFO is default level if we don't set it. It will be + // -- changed to parent level or to configLevel after adding to the + // -- family tree. As of this, actually, setting to Level.INFO is + // -- not needed here. this.levelObjVal = null; this.levelIntVal = Level.INFO.intValue(); } - //-- should be called under the lm lock + // -- should be called under the lm lock private void setLevelImpl(Level newLevel) { // update levels for the whole hierarchy int oldVal = levelIntVal; levelObjVal = newLevel; if (null == newLevel) { - levelIntVal = null != parent - ? parent.levelIntVal - : Level.INFO.intValue(); + levelIntVal = null != parent ? parent.levelIntVal : Level.INFO + .intValue(); } else { levelIntVal = newLevel.intValue(); } @@ -190,7 +171,7 @@ public class Logger { } } - //-- should be called under the lm lock + // -- should be called under the lm lock private void forceChildsToInherit() { for (Logger child : childs) { if (null == child.levelObjVal) { // should inherit @@ -199,15 +180,9 @@ public class Logger { } } - /* - * ------------------------------------------------------------------- - * Methods - * ------------------------------------------------------------------- - */ - /** * Load the specified resource bundle, use privileged code. - * + * * @param resourceBundleName * the name of the resource bundle to load, cannot be {@code null}. * @return the loaded resource bundle. @@ -216,8 +191,8 @@ public class Logger { */ static ResourceBundle loadResourceBundle(String resourceBundleName) { // try context class loader to load the resource - ClassLoader cl = AccessController.doPrivileged( - new PrivilegedAction() { + ClassLoader cl = AccessController + .doPrivileged(new PrivilegedAction() { public ClassLoader run() { return Thread.currentThread().getContextClassLoader(); } @@ -231,12 +206,11 @@ public class Logger { } } // try system class loader to load the resource - cl = AccessController.doPrivileged( - new PrivilegedAction() { - public ClassLoader run() { - return ClassLoader.getSystemClassLoader(); - } - }); + cl = AccessController.doPrivileged(new PrivilegedAction() { + public ClassLoader run() { + return ClassLoader.getSystemClassLoader(); + } + }); if (null != cl) { try { return ResourceBundle.getBundle(resourceBundleName, Locale @@ -257,8 +231,8 @@ public class Logger { for (int i = 1; i < classes.length; i++) { final int index = i; try { - cl = AccessController.doPrivileged( - new PrivilegedAction() { + cl = AccessController + .doPrivileged(new PrivilegedAction() { public ClassLoader run() { return classes[index].getClassLoader(); } @@ -274,7 +248,7 @@ public class Logger { } // logging.8=Failed to load the specified resource bundle "{0}". throw new MissingResourceException(Messages.getString("logging.8", //$NON-NLS-1$ - resourceBundleName), resourceBundleName, null); + resourceBundleName), resourceBundleName, null); } /** @@ -284,10 +258,8 @@ public class Logger { *

        * The anonymous loggers' parent is set to be the root logger. This way it * inherits the default logging level and handlers from the root logger. - *

        - * + * * @return a new instance of anonymous logger. - * @since Android 1.0 */ public static Logger getAnonymousLogger() { return getAnonymousLogger(null); @@ -300,14 +272,12 @@ public class Logger { *

        * The anonymous loggers' parent is set to be the root logger. This way it * inherits default logging level and handlers from the root logger. - *

        - * + * * @param resourceBundleName * the name of the resource bundle used to localize log messages. * @return a new instance of anonymous logger. * @throws MissingResourceException * if the specified resource bundle can not be loaded. - * @since Android 1.0 */ public static Logger getAnonymousLogger(String resourceBundleName) { final Logger l = new Logger(null, resourceBundleName); @@ -324,26 +294,24 @@ public class Logger { private static void updateResourceBundle(Logger l, String resourceBundleName) { synchronized (l) { if (null == l.getResourceBundleName()) { - if(null == resourceBundleName){ + if (null == resourceBundleName) { return; } /* - * load the resource bundle if none is specified - * before + * load the resource bundle if none is specified before */ l.resBundle = loadResourceBundle(resourceBundleName); l.resBundleName = resourceBundleName; } else if (!l.getResourceBundleName().equals(resourceBundleName)) { /* - * throw exception if the specified resource bundles - * are inconsistent with each other, i.e., different - * names + * throw exception if the specified resource bundles are + * inconsistent with each other, i.e., different names */ - // logging.9=The specified resource bundle name "{0}" is + // logging.9=The specified resource bundle name "{0}" is // inconsistent with the existing one "{1}". throw new IllegalArgumentException(Messages.getString( "logging.9", //$NON-NLS-1$ - resourceBundleName, l.getResourceBundleName())); + resourceBundleName, l.getResourceBundleName())); } } } @@ -378,11 +346,12 @@ public class Logger { * Gets a named logger. The returned logger may already exist or may be * newly created. In the latter case, its level will be set to the * configured level according to the {@code LogManager}'s properties. - * + * * @param name * the name of the logger to get, cannot be {@code null}. * @return a named logger. - * @since Android 1.0 + * @throws MissingResourceException + * If the specified resource bundle can not be loaded. */ public static Logger getLogger(String name) { return getLoggerWithRes(name, null, false); @@ -391,7 +360,7 @@ public class Logger { /** * Gets a named logger associated with the supplied resource bundle. The * resource bundle will be used to localize logging messages. - * + * * @param name * the name of the logger to get, cannot be {@code null}. * @param resourceBundleName @@ -403,7 +372,6 @@ public class Logger { * @throws MissingResourceException * if the name of the resource bundle cannot be found. * @return a named logger. - * @since Android 1.0 */ public static Logger getLogger(String name, String resourceBundleName) { return getLoggerWithRes(name, resourceBundleName, true); @@ -412,13 +380,12 @@ public class Logger { /** * Adds a handler to this logger. The {@code name} will be fed with log * records received by this logger. - * + * * @param handler * the handler object to add, cannot be {@code null}. * @throws SecurityException * if a security manager determines that the caller does not * have the required permission. - * @since Android 1.0 */ public void addHandler(Handler handler) { if (null == handler) { @@ -430,25 +397,25 @@ public class Logger { LogManager.getLogManager().checkAccess(); } initHandler(); - synchronized(this){ + synchronized (this) { this.handlers.add(handler); } } - + /* - * Be cautious to avoid deadlock when using this method, it gets lock on manager - * at first, and then gets lock on this Logger, so any methods should not hold - * lock on this Logger when invoking this method. + * Be cautious to avoid deadlock when using this method, it gets lock on manager + * at first, and then gets lock on this Logger, so any methods should not hold + * lock on this Logger when invoking this method. */ private void initHandler() { - if(!handlerInited){ + if (!handlerInited) { synchronized (this) { if (!handlerInited) { // BEGIN android-added /* * Force LogManager to be initialized, since its * class init code performs necessary one-time setup. - */ + */ LogManager.getLogManager(); // END android-added if (handlers == null) { @@ -463,26 +430,27 @@ public class Logger { if (null == handlerStr) { return; } - StringTokenizer st = new StringTokenizer(handlerStr, " "); //$NON-NLS-1$ - while (st.hasMoreTokens()) { - String handlerName = st.nextToken(); - // BEGIN android-changed - // deal with non-existing handler - try { - Handler handler = (Handler) LogManager - .getInstanceByClass(handlerName); - handlers.add(handler); - String level = manager.getProperty(handlerName - + ".level"); //$NON-NLS-1$ - if (null != level) { - handler.setLevel(Level.parse(level)); - } - } catch (Exception ex) { - ex.printStackTrace(); - } - // END android-changed + String[] strs = handlerStr.split(",|\\s"); //$NON-NLS-1$ + for (int i = 0; i < strs.length; i++) { + String handlerName = strs[i]; + if (handlerName.equals("")) { //$NON-NLS-1$ + continue; + } + // BEGIN android-changed + // deal with non-existing handler + try { + Handler handler = (Handler) LogManager.getInstanceByClass(handlerName); + handlers.add(handler); + String level = manager.getProperty(handlerName + ".level"); //$NON-NLS-1$ + if (null != level) { + handler.setLevel(Level.parse(level)); + } + } catch (Exception ex) { + ex.printStackTrace(); } - handlerInited = true; + // END android-changed + } + handlerInited = true; } } } @@ -490,13 +458,12 @@ public class Logger { /** * Gets all the handlers associated with this logger. - * + * * @return an array of all the handlers associated with this logger. - * @since Android 1.0 */ public Handler[] getHandlers() { initHandler(); - synchronized(this){ + synchronized (this) { return handlers.toArray(new Handler[handlers.size()]); } } @@ -504,13 +471,12 @@ public class Logger { /** * Removes a handler from this logger. If the specified handler does not * exist then this method has no effect. - * + * * @param handler * the handler to be removed. * @throws SecurityException * if a security manager determines that the caller does not * have the required permission. - * @since Android 1.0 */ public void removeHandler(Handler handler) { // Anonymous loggers can always remove handlers @@ -521,16 +487,15 @@ public class Logger { return; } initHandler(); - synchronized(this){ + synchronized (this) { this.handlers.remove(handler); } } /** * Gets the filter used by this logger. - * + * * @return the filter used by this logger, may be {@code null}. - * @since Android 1.0 */ public Filter getFilter() { return this.filter; @@ -538,13 +503,12 @@ public class Logger { /** * Sets the filter used by this logger. - * + * * @param newFilter * the filter to set, may be {@code null}. * @throws SecurityException * if a security manager determines that the caller does not * have the required permission. - * @since Android 1.0 */ public void setFilter(Filter newFilter) { // Anonymous loggers can always set the filter @@ -557,9 +521,8 @@ public class Logger { /** * Gets the logging level of this logger. A {@code null} level indicates * that this logger inherits its parent's level. - * + * * @return the logging level of this logger. - * @since Android 1.0 */ public Level getLevel() { return levelObjVal; @@ -568,13 +531,12 @@ public class Logger { /** * Sets the logging level for this logger. A {@code null} level indicates * that this logger will inherit its parent's level. - * + * * @param newLevel * the logging level to set. * @throws SecurityException * if a security manager determines that the caller does not * have the required permission. - * @since Android 1.0 */ public void setLevel(Level newLevel) { // Anonymous loggers can always set the level @@ -590,10 +552,9 @@ public class Logger { * Gets the flag which indicates whether to use the handlers of this * logger's parent to publish incoming log records, potentially recursively * up the namespace. - * + * * @return {@code true} if set to use parent's handlers, {@code false} * otherwise. - * @since Android 1.0 */ public boolean getUseParentHandlers() { return this.notifyParentHandlers; @@ -602,13 +563,12 @@ public class Logger { /** * Sets the flag which indicates whether to use the handlers of this * logger's parent, potentially recursively up the namespace. - * + * * @param notifyParentHandlers * the new flag indicating whether to use the parent's handlers. * @throws SecurityException * if a security manager determines that the caller does not * have the required permission. - * @since Android 1.0 */ public void setUseParentHandlers(boolean notifyParentHandlers) { // Anonymous loggers can always set the useParentHandlers flag @@ -621,9 +581,8 @@ public class Logger { /** * Gets the nearest parent of this logger in the namespace, a {@code null} * value will be returned if called on the root logger. - * + * * @return the parent of this logger in the namespace. - * @since Android 1.0 */ public Logger getParent() { return parent; @@ -633,14 +592,14 @@ public class Logger { * Sets the parent of this logger in the namespace. This method should * usually be used by the {@code LogManager} object only. This method does * not check security. - * + * * @param newParent * the parent logger to set. - * @since Android 1.0 */ void internalSetParent(Logger newParent) { - //All hierarchy related modifications should get LogManager lock at first - synchronized(LogManager.getLogManager()){ + // All hierarchy related modifications should get LogManager lock at + // first + synchronized (LogManager.getLogManager()) { parent = newParent; // -- update level after setting a parent. // -- if level == null we should inherit the parent's level @@ -654,13 +613,12 @@ public class Logger { /** * Sets the parent of this logger in the namespace. This method should be * used by the {@code LogManager} object only. - * + * * @param parent * the parent logger to set. * @throws SecurityException * if a security manager determines that the caller does not * have the required permission. - * @since Android 1.0 */ public void setParent(Logger parent) { if (null == parent) { @@ -680,12 +638,10 @@ public class Logger { childs.remove(child); } - /** * Gets the name of this logger, {@code null} for anonymous loggers. - * + * * @return the name of this logger. - * @since Android 1.0 */ public String getName() { return this.name; @@ -695,9 +651,8 @@ public class Logger { * Gets the loaded resource bundle used by this logger to localize logging * messages. If the value is {@code null}, the parent's resource bundle will be * inherited. - * + * * @return the loaded resource bundle used by this logger. - * @since Android 1.0 */ public ResourceBundle getResourceBundle() { return this.resBundle; @@ -707,9 +662,8 @@ public class Logger { * Gets the name of the loaded resource bundle used by this logger to * localize logging messages. If the value is {@code null}, the parent's resource * bundle name will be inherited. - * + * * @return the name of the loaded resource bundle used by this logger. - * @since Android 1.0 */ public String getResourceBundleName() { return this.resBundleName; @@ -734,12 +688,11 @@ public class Logger { * Determines whether this logger will actually log messages of the * specified level. The effective level used to do the determination may be * inherited from its parent. The default level is {@code Level.INFO}. - * + * * @param l * the level to check. * @return {@code true} if this logger will actually log this level, * otherwise {@code false}. - * @since Android 1.0 */ public boolean isLoggable(Level l) { return internalIsLoggable(l); @@ -775,12 +728,11 @@ public class Logger { * Logs a message indicating that a method has been entered. A log record * with log level {@code Level.FINER}, log message "ENTRY", the specified * source class name and source method name is submitted for logging. - * + * * @param sourceClass * the calling class name. * @param sourceMethod * the method name. - * @since Android 1.0 */ public void entering(String sourceClass, String sourceMethod) { if (internalIsLoggable(Level.FINER)) { @@ -798,14 +750,13 @@ public class Logger { * with log level {@code Level.FINER}, log message "ENTRY", the specified * source class name, source method name and one parameter is submitted for * logging. - * + * * @param sourceClass * the source class name. * @param sourceMethod * the source method name. * @param param * the parameter for the method call. - * @since Android 1.0 */ public void entering(String sourceClass, String sourceMethod, Object param) { if (internalIsLoggable(Level.FINER)) { @@ -824,14 +775,13 @@ public class Logger { * with log level {@code Level.FINER}, log message "ENTRY", the specified * source class name, source method name and array of parameters is * submitted for logging. - * + * * @param sourceClass * the source class name. * @param sourceMethod * the source method name. * @param params * an array of parameters for the method call. - * @since Android 1.0 */ public void entering(String sourceClass, String sourceMethod, Object[] params) { @@ -858,12 +808,11 @@ public class Logger { * Logs a message indicating that a method is exited. A log record with log * level {@code Level.FINER}, log message "RETURN", the specified source * class name and source method name is submitted for logging. - * + * * @param sourceClass * the calling class name. * @param sourceMethod * the method name. - * @since Android 1.0 */ public void exiting(String sourceClass, String sourceMethod) { if (internalIsLoggable(Level.FINER)) { @@ -880,14 +829,13 @@ public class Logger { * Logs a message indicating that a method is exited. A log record with log * level {@code Level.FINER}, log message "RETURN", the specified source * class name, source method name and return value is submitted for logging. - * + * * @param sourceClass * the source class name. * @param sourceMethod * the source method name. * @param result * the return value of the method call. - * @since Android 1.0 */ public void exiting(String sourceClass, String sourceMethod, Object result) { if (internalIsLoggable(Level.FINER)) { @@ -906,14 +854,13 @@ public class Logger { * log level {@code Level.FINER}, log message "THROW", the specified source * class name, source method name and the {@code Throwable} object is * submitted for logging. - * + * * @param sourceClass * the source class name. * @param sourceMethod * the source method name. * @param thrown * the {@code Throwable} object. - * @since Android 1.0 */ public void throwing(String sourceClass, String sourceMethod, Throwable thrown) { @@ -931,10 +878,9 @@ public class Logger { /** * Logs a message of level {@code Level.SEVERE}; the message is transmitted * to all subscribed handlers. - * + * * @param msg * the message to log. - * @since Android 1.0 */ public void severe(String msg) { if (internalIsLoggable(Level.SEVERE)) { @@ -948,10 +894,9 @@ public class Logger { /** * Logs a message of level {@code Level.WARNING}; the message is * transmitted to all subscribed handlers. - * + * * @param msg * the message to log. - * @since Android 1.0 */ public void warning(String msg) { if (internalIsLoggable(Level.WARNING)) { @@ -965,10 +910,9 @@ public class Logger { /** * Logs a message of level {@code Level.INFO}; the message is transmitted * to all subscribed handlers. - * + * * @param msg * the message to log. - * @since Android 1.0 */ public void info(String msg) { if (internalIsLoggable(Level.INFO)) { @@ -982,10 +926,9 @@ public class Logger { /** * Logs a message of level {@code Level.CONFIG}; the message is transmitted * to all subscribed handlers. - * + * * @param msg * the message to log. - * @since Android 1.0 */ public void config(String msg) { if (internalIsLoggable(Level.CONFIG)) { @@ -999,10 +942,9 @@ public class Logger { /** * Logs a message of level {@code Level.FINE}; the message is transmitted * to all subscribed handlers. - * + * * @param msg * the message to log. - * @since Android 1.0 */ public void fine(String msg) { if (internalIsLoggable(Level.FINE)) { @@ -1016,10 +958,9 @@ public class Logger { /** * Logs a message of level {@code Level.FINER}; the message is transmitted * to all subscribed handlers. - * + * * @param msg * the message to log. - * @since Android 1.0 */ public void finer(String msg) { if (internalIsLoggable(Level.FINER)) { @@ -1033,10 +974,9 @@ public class Logger { /** * Logs a message of level {@code Level.FINEST}; the message is transmitted * to all subscribed handlers. - * + * * @param msg * the message to log. - * @since Android 1.0 */ public void finest(String msg) { if (internalIsLoggable(Level.FINEST)) { @@ -1050,12 +990,11 @@ public class Logger { /** * Logs a message of the specified level. The message is transmitted to all * subscribed handlers. - * + * * @param logLevel * the level of the specified message. * @param msg * the message to log. - * @since Android 1.0 */ public void log(Level logLevel, String msg) { if (internalIsLoggable(logLevel)) { @@ -1069,14 +1008,13 @@ public class Logger { /** * Logs a message of the specified level with the supplied parameter. The * message is then transmitted to all subscribed handlers. - * + * * @param logLevel * the level of the given message. * @param msg * the message to log. * @param param * the parameter associated with the event that is logged. - * @since Android 1.0 */ public void log(Level logLevel, String msg, Object param) { if (internalIsLoggable(logLevel)) { @@ -1091,14 +1029,13 @@ public class Logger { /** * Logs a message of the specified level with the supplied parameter array. * The message is then transmitted to all subscribed handlers. - * + * * @param logLevel * the level of the given message * @param msg * the message to log. * @param params * the parameter array associated with the event that is logged. - * @since Android 1.0 */ public void log(Level logLevel, String msg, Object[] params) { if (internalIsLoggable(logLevel)) { @@ -1113,7 +1050,7 @@ public class Logger { /** * Logs a message of the specified level with the supplied {@code Throwable} * object. The message is then transmitted to all subscribed handlers. - * + * * @param logLevel * the level of the given message. * @param msg @@ -1121,7 +1058,6 @@ public class Logger { * @param thrown * the {@code Throwable} object associated with the event that is * logged. - * @since Android 1.0 */ public void log(Level logLevel, String msg, Throwable thrown) { if (internalIsLoggable(logLevel)) { @@ -1144,10 +1080,9 @@ public class Logger { * logging action, subclasses of this class can override this method to * catch all logging activities. *

        - * + * * @param record * the log record to be logged. - * @since Android 1.0 */ public void log(LogRecord record) { if (internalIsLoggable(record.getLevel())) { @@ -1158,8 +1093,7 @@ public class Logger { } initHandler(); /* - * call the handlers of this logger, throw any exception that - * occurs + * call the handlers of this logger, throw any exception that occurs */ Handler[] allHandlers = getHandlers(); for (Handler element : allHandlers) { @@ -1182,7 +1116,7 @@ public class Logger { /** * Logs a message of the given level with the specified source class name * and source method name. - * + * * @param logLevel * the level of the given message. * @param sourceClass @@ -1191,7 +1125,6 @@ public class Logger { * the source method name. * @param msg * the message to be logged. - * @since Android 1.0 */ public void logp(Level logLevel, String sourceClass, String sourceMethod, String msg) { @@ -1208,7 +1141,7 @@ public class Logger { /** * Logs a message of the given level with the specified source class name, * source method name and parameter. - * + * * @param logLevel * the level of the given message * @param sourceClass @@ -1219,7 +1152,6 @@ public class Logger { * the message to be logged * @param param * the parameter associated with the event that is logged. - * @since Android 1.0 */ public void logp(Level logLevel, String sourceClass, String sourceMethod, String msg, Object param) { @@ -1237,7 +1169,7 @@ public class Logger { /** * Logs a message of the given level with the specified source class name, * source method name and parameter array. - * + * * @param logLevel * the level of the given message. * @param sourceClass @@ -1248,7 +1180,6 @@ public class Logger { * the message to be logged. * @param params * the parameter array associated with the event that is logged. - * @since Android 1.0 */ public void logp(Level logLevel, String sourceClass, String sourceMethod, String msg, Object[] params) { @@ -1266,7 +1197,7 @@ public class Logger { /** * Logs a message of the given level with the specified source class name, * source method name and {@code Throwable} object. - * + * * @param logLevel * the level of the given message. * @param sourceClass @@ -1277,7 +1208,6 @@ public class Logger { * the message to be logged. * @param thrown * the {@code Throwable} object. - * @since Android 1.0 */ public void logp(Level logLevel, String sourceClass, String sourceMethod, String msg, Throwable thrown) { @@ -1297,7 +1227,7 @@ public class Logger { * and source method name, using the given resource bundle to localize the * message. If {@code bundleName} is null, the empty string or not valid then * the message is not localized. - * + * * @param logLevel * the level of the given message. * @param sourceClass @@ -1308,7 +1238,6 @@ public class Logger { * the name of the resource bundle used to localize the message. * @param msg * the message to be logged. - * @since Android 1.0 */ public void logrb(Level logLevel, String sourceClass, String sourceMethod, String bundleName, String msg) { @@ -1334,7 +1263,7 @@ public class Logger { * source method name and parameter, using the given resource bundle to * localize the message. If {@code bundleName} is null, the empty string * or not valid then the message is not localized. - * + * * @param logLevel * the level of the given message. * @param sourceClass @@ -1347,7 +1276,6 @@ public class Logger { * the message to be logged. * @param param * the parameter associated with the event that is logged. - * @since Android 1.0 */ public void logrb(Level logLevel, String sourceClass, String sourceMethod, String bundleName, String msg, Object param) { @@ -1374,7 +1302,7 @@ public class Logger { * source method name and parameter array, using the given resource bundle * to localize the message. If {@code bundleName} is null, the empty string * or not valid then the message is not localized. - * + * * @param logLevel * the level of the given message. * @param sourceClass @@ -1387,7 +1315,6 @@ public class Logger { * the message to be logged. * @param params * the parameter array associated with the event that is logged. - * @since Android 1.0 */ public void logrb(Level logLevel, String sourceClass, String sourceMethod, String bundleName, String msg, Object[] params) { @@ -1414,7 +1341,7 @@ public class Logger { * source method name and {@code Throwable} object, using the given resource * bundle to localize the message. If {@code bundleName} is null, the empty * string or not valid then the message is not localized. - * + * * @param logLevel * the level of the given message * @param sourceClass @@ -1427,7 +1354,6 @@ public class Logger { * the message to be logged. * @param thrown * the {@code Throwable} object. - * @since Android 1.0 */ public void logrb(Level logLevel, String sourceClass, String sourceMethod, String bundleName, String msg, Throwable thrown) { @@ -1459,25 +1385,30 @@ public class Logger { } void setManager(LogManager manager) { - if(this.manager != manager){ + if (this.manager != manager) { this.manager = manager; - handlerInited = false; + handlerInited = false; } - //init level here, but let handlers be for lazy loading - String configedLevel = manager.getProperty(name+ ".level"); //$NON-NLS-1$ + // init level here, but let handlers be for lazy loading + final String configedLevel = manager.getProperty(name + ".level"); //$NON-NLS-1$ if (null != configedLevel) { try { - setLevel(Level.parse(configedLevel)); + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + setLevel(Level.parse(configedLevel)); + return null; + } + }); } catch (IllegalArgumentException e) { - //ignore + // ignore } - } + } } synchronized void reset() { levelObjVal = null; levelIntVal = Level.INFO.intValue(); - if(handlers != null){ + if (handlers != null) { for (Handler element : handlers) { // close all handlers, when unknown exceptions happen, // ignore them and go on @@ -1492,4 +1423,3 @@ public class Logger { handlerInited = false; } } - diff --git a/libcore/logging/src/main/java/java/util/logging/LoggingMXBean.java b/libcore/logging/src/main/java/java/util/logging/LoggingMXBean.java index f6b49a688..18cc4cc58 100644 --- a/libcore/logging/src/main/java/java/util/logging/LoggingMXBean.java +++ b/libcore/logging/src/main/java/java/util/logging/LoggingMXBean.java @@ -25,29 +25,28 @@ import java.util.List; * The ObjectName for identifying the {@code LoggingMXBean} in a bean server is * {@link LogManager#LOGGING_MXBEAN_NAME}. *

        - * - * @since Android 1.0 + * + * @since 1.5 */ public interface LoggingMXBean { + /** * Gets the string value of the logging level of a logger. An empty string * is returned when the logger's level is defined by its parent. A * {@code null} is returned if the specified logger does not exist. - * + * * @param loggerName * the name of the logger lookup. * @return a {@code String} if the logger is found, otherwise {@code null}. * @see Level#getName() - * @since Android 1.0 */ String getLoggerLevel(String loggerName); /** * Gets a list of all currently registered logger names. This is performed * using the {@link LogManager#getLoggerNames()}. - * + * * @return a list of logger names. - * @since Android 1.0 */ List getLoggerNames(); @@ -55,18 +54,17 @@ public interface LoggingMXBean { * Gets the name of the parent logger of a logger. If the logger doesn't * exist then {@code null} is returned. If the logger is the root logger, * then an empty {@code String} is returned. - * + * * @param loggerName * the name of the logger to lookup. * @return a {@code String} if the logger was found, otherwise {@code null}. - * @since Android 1.0 */ String getParentLoggerName(String loggerName); /** * Sets the log level of a logger. LevelName set to {@code null} means the * level is inherited from the nearest non-null ancestor. - * + * * @param loggerName * the name of the logger to set the level on, which must not be * {@code null}. @@ -79,7 +77,6 @@ public interface LoggingMXBean { * if a security manager exists and the caller doesn't have * LoggingPermission("control"). * @see Level#parse(String) - * @since Android 1.0 */ void setLoggerLevel(String loggerName, String levelName); } diff --git a/libcore/logging/src/main/java/java/util/logging/LoggingPermission.java b/libcore/logging/src/main/java/java/util/logging/LoggingPermission.java index fb6d4f820..aa41a2cfb 100644 --- a/libcore/logging/src/main/java/java/util/logging/LoggingPermission.java +++ b/libcore/logging/src/main/java/java/util/logging/LoggingPermission.java @@ -15,7 +15,6 @@ * limitations under the License. */ - package java.util.logging; import java.io.Serializable; @@ -27,20 +26,12 @@ import org.apache.harmony.logging.internal.nls.Messages; /** * The permission required to control the logging when run with a * {@code SecurityManager}. - * */ public final class LoggingPermission extends BasicPermission implements Guard, Serializable { - //for serialization compatibility with J2SE 1.4.2 - private static final long serialVersionUID =63564341580231582L; - - - /* - * ------------------------------------------------------------------- - * Constructors - * ------------------------------------------------------------------- - */ + // for serialization compatibility with J2SE 1.4.2 + private static final long serialVersionUID = 63564341580231582L; /** * Constructs a {@code LoggingPermission} object required to control the @@ -50,7 +41,7 @@ public final class LoggingPermission extends BasicPermission implements Guard, * and depends on the security policy file, therefore programmers shouldn't * normally use them directly. *

        - * + * * @param name * currently must be "control". * @param actions @@ -71,4 +62,3 @@ public final class LoggingPermission extends BasicPermission implements Guard, } } - diff --git a/libcore/logging/src/main/java/java/util/logging/MemoryHandler.java b/libcore/logging/src/main/java/java/util/logging/MemoryHandler.java index c1e8670f2..33120831c 100644 --- a/libcore/logging/src/main/java/java/util/logging/MemoryHandler.java +++ b/libcore/logging/src/main/java/java/util/logging/MemoryHandler.java @@ -22,7 +22,6 @@ import java.security.PrivilegedExceptionAction; import org.apache.harmony.logging.internal.nls.Messages; - /** * A {@code Handler} put the description of log events into a cycled memory * buffer. @@ -30,68 +29,61 @@ import org.apache.harmony.logging.internal.nls.Messages; * Mostly this {@code MemoryHandler} just puts the given {@code LogRecord} into * the internal buffer and doesn't perform any formatting or any other process. * When the buffer is full, the earliest buffered records will be discarded. - *

        *

        * Every {@code MemoryHandler} has a target handler, and push action can be * triggered so that all buffered records will be output to the target handler * and normally the latter will publish the records. After the push action, the * buffer will be cleared. - *

        *

        - * The push action can be triggered in three ways: - *

          - *
        • The push method is called explicitly
        • - *
        • When a new {@code LogRecord} is put into the internal buffer, and it has - * a level which is not less than the specified push level.
        • - *
        • A subclass extends this {@code MemoryHandler} and call push method - * implicitly according to some criteria.
        • - *
        - *

        + * The push method can be called directly, but will also be called automatically + * if a new LogRecord is added that has a level greater than or + * equal to than the value defined for the property + * java.util.logging.MemoryHandler.push. *

        * {@code MemoryHandler} will read following {@code LogManager} properties for * initialization, if given properties are not defined or has invalid values, * default value will be used. *

          - *
        • java.util.logging.MemoryHandler.level specifies the level for this - * {@code Handler}, defaults to {@code Level.ALL}.
        • *
        • java.util.logging.MemoryHandler.filter specifies the {@code Filter} * class name, defaults to no {@code Filter}.
        • - *
        • java.util.logging.MemoryHandler.size specifies the buffer size in number - * of {@code LogRecord}, defaults to 1000.
        • + *
        • java.util.logging.MemoryHandler.level specifies the level for this + * {@code Handler}, defaults to {@code Level.ALL}.
        • *
        • java.util.logging.MemoryHandler.push specifies the push level, defaults * to level.SEVERE.
        • + *
        • java.util.logging.MemoryHandler.size specifies the buffer size in number + * of {@code LogRecord}, defaults to 1000.
        • *
        • java.util.logging.MemoryHandler.target specifies the class of the target * {@code Handler}, no default value, which means this property must be * specified either by property setting or by constructor.
        • *
        - *

        */ public class MemoryHandler extends Handler { - //default maximum buffered number of LogRecord + // default maximum buffered number of LogRecord private static final int DEFAULT_SIZE = 1000; - //target handler + + // target handler private Handler target; - - //buffer size + + // buffer size private int size = DEFAULT_SIZE; - - //push level + + // push level private Level push = Level.SEVERE; - //LogManager instance for convenience + // LogManager instance for convenience private final LogManager manager = LogManager.getLogManager(); - - //buffer + + // buffer private LogRecord[] buffer; - - //current position in buffer + + // current position in buffer private int cursor; - + /** * Default constructor, construct and init a {@code MemoryHandler} using * {@code LogManager} properties or default values. - * + * * @throws RuntimeException * if property value are invalid and no default value could be * used. @@ -99,55 +91,57 @@ public class MemoryHandler extends Handler { public MemoryHandler() { super(); String className = this.getClass().getName(); - //init target - final String targetName = manager.getProperty(className+".target"); //$NON-NLS-1$ + // init target + final String targetName = manager.getProperty(className + ".target"); //$NON-NLS-1$ try { - Class targetClass = AccessController.doPrivileged(new PrivilegedExceptionAction>(){ - public Class run() throws Exception{ - ClassLoader loader = Thread.currentThread().getContextClassLoader(); - if(loader == null){ - loader = ClassLoader.getSystemClassLoader(); - } - return loader.loadClass(targetName); - } - }); + Class targetClass = AccessController + .doPrivileged(new PrivilegedExceptionAction>() { + public Class run() throws Exception { + ClassLoader loader = Thread.currentThread() + .getContextClassLoader(); + if (loader == null) { + loader = ClassLoader.getSystemClassLoader(); + } + return loader.loadClass(targetName); + } + }); target = (Handler) targetClass.newInstance(); } catch (Exception e) { // logging.10=Cannot load target handler:{0} throw new RuntimeException(Messages.getString("logging.10", //$NON-NLS-1$ targetName)); } - //init size - String sizeString = manager.getProperty(className+".size"); //$NON-NLS-1$ + // init size + String sizeString = manager.getProperty(className + ".size"); //$NON-NLS-1$ if (null != sizeString) { try { size = Integer.parseInt(sizeString); - if(size <= 0){ + if (size <= 0) { size = DEFAULT_SIZE; } } catch (Exception e) { - printInvalidPropMessage(className+".size", sizeString, e); //$NON-NLS-1$ + printInvalidPropMessage(className + ".size", sizeString, e); //$NON-NLS-1$ } } - //init push level - String pushName = manager.getProperty(className+".push"); //$NON-NLS-1$ + // init push level + String pushName = manager.getProperty(className + ".push"); //$NON-NLS-1$ if (null != pushName) { try { push = Level.parse(pushName); } catch (Exception e) { - printInvalidPropMessage(className+".push", pushName, e); //$NON-NLS-1$ + printInvalidPropMessage(className + ".push", pushName, e); //$NON-NLS-1$ } } - //init other properties which are common for all Handler - initProperties("ALL", null, "java.util.logging.SimpleFormatter", null); //$NON-NLS-1$//$NON-NLS-2$ + // init other properties which are common for all Handler + initProperties("ALL", null, "java.util.logging.SimpleFormatter", null); //$NON-NLS-1$//$NON-NLS-2$ buffer = new LogRecord[size]; } - + /** * Construct and init a {@code MemoryHandler} using given target, size and * push level, other properties using {@code LogManager} properties or * default values. - * + * * @param target * the given {@code Handler} to output * @param size @@ -156,7 +150,7 @@ public class MemoryHandler extends Handler { * @param pushLevel * the push level * @throws IllegalArgumentException - * if {@code size}<=0 + * if {@code size <= 0} * @throws RuntimeException * if property value are invalid and no default value could be * used. @@ -171,13 +165,13 @@ public class MemoryHandler extends Handler { this.target = target; this.size = size; this.push = pushLevel; - initProperties("ALL", null, "java.util.logging.SimpleFormatter", null); //$NON-NLS-1$//$NON-NLS-2$ + initProperties("ALL", null, "java.util.logging.SimpleFormatter", null); //$NON-NLS-1$//$NON-NLS-2$ buffer = new LogRecord[size]; } - + /** * Close this handler and target handler, free all associated resources. - * + * * @throws SecurityException * if security manager exists and it determines that caller does * not have the required permissions to control this handler. @@ -204,7 +198,7 @@ public class MemoryHandler extends Handler { * Furthermore if the record's level is not less than the push level, the * push action is triggered to output all the buffered records to the target * handler, and the target handler will publish them. - * + * * @param record * the log record */ @@ -225,7 +219,7 @@ public class MemoryHandler extends Handler { /** * Return the push level. - * + * * @return the push level */ public Level getPushLevel() { @@ -233,18 +227,14 @@ public class MemoryHandler extends Handler { } /** - *

        * Check if given {@code LogRecord} would be put into this * {@code MemoryHandler}'s internal buffer. - *

        *

        * The given {@code LogRecord} is loggable if and only if it has appropriate * level and it pass any associated filter's check. - *

        *

        * Note that the push level is not used for this check. - *

        - * + * * @param record * the given {@code LogRecord} * @return the given {@code LogRecord} if it should be logged, {@code false} @@ -261,13 +251,13 @@ public class MemoryHandler extends Handler { */ public void push() { for (int i = cursor; i < size; i++) { - if(null != buffer[i]) { + if (null != buffer[i]) { target.publish(buffer[i]); } buffer[i] = null; } for (int i = 0; i < cursor; i++) { - if(null != buffer[i]) { + if (null != buffer[i]) { target.publish(buffer[i]); } buffer[i] = null; @@ -276,15 +266,15 @@ public class MemoryHandler extends Handler { } /** - * Set the push level. The push level is used to check the push action + * Set the push level. The push level is used to check the push action * triggering. When a new {@code LogRecord} is put into the internal - * buffer and its level is not less than the push level, the push action + * buffer and its level is not less than the push level, the push action * will be triggered. Note that set new push level won't trigger push action. - * + * * @param newLevel * the new level to set. * @throws SecurityException - * if security manager exists and it determines that caller + * if security manager exists and it determines that caller * does not have the required permissions to control this handler. */ public void setPushLevel(Level newLevel) { diff --git a/libcore/logging/src/main/java/java/util/logging/SimpleFormatter.java b/libcore/logging/src/main/java/java/util/logging/SimpleFormatter.java index 159579694..def4ad3cd 100644 --- a/libcore/logging/src/main/java/java/util/logging/SimpleFormatter.java +++ b/libcore/logging/src/main/java/java/util/logging/SimpleFormatter.java @@ -15,7 +15,6 @@ * limitations under the License. */ - package java.util.logging; import java.io.PrintWriter; @@ -26,13 +25,10 @@ import java.util.Date; /** * {@code SimpleFormatter} can be used to print a summary of the information * contained in a {@code LogRecord} object in a human readable format. - * @since Android 1.0 */ public class SimpleFormatter extends Formatter { /** * Constructs a new {@code SimpleFormatter}. - * - * @since Android 1.0 */ public SimpleFormatter() { super(); @@ -41,11 +37,10 @@ public class SimpleFormatter extends Formatter { /** * Converts a {@link LogRecord} object into a human readable string * representation. - * + * * @param r * the log record to be formatted into a string. * @return the formatted string. - * @since Android 1.0 */ @Override public String format(LogRecord r) { @@ -53,7 +48,8 @@ public class SimpleFormatter extends Formatter { sb.append(MessageFormat.format("{0, date} {0, time} ", //$NON-NLS-1$ new Object[] { new Date(r.getMillis()) })); sb.append(r.getSourceClassName()).append(" "); //$NON-NLS-1$ - sb.append(r.getSourceMethodName()).append(LogManager.getSystemLineSeparator()); //$NON-NLS-1$ + sb.append(r.getSourceMethodName()).append( + LogManager.getSystemLineSeparator()); sb.append(r.getLevel().getName()).append(": "); //$NON-NLS-1$ sb.append(formatMessage(r)).append(LogManager.getSystemLineSeparator()); if (null != r.getThrown()) { @@ -66,7 +62,7 @@ public class SimpleFormatter extends Formatter { t.printStackTrace(pw); sb.append(sw.toString()); } finally { - if(pw != null){ + if (pw != null) { try { pw.close(); } catch (Exception e) { @@ -78,4 +74,3 @@ public class SimpleFormatter extends Formatter { return sb.toString(); } } - diff --git a/libcore/logging/src/main/java/java/util/logging/SocketHandler.java b/libcore/logging/src/main/java/java/util/logging/SocketHandler.java index 862600714..38cfd649c 100644 --- a/libcore/logging/src/main/java/java/util/logging/SocketHandler.java +++ b/libcore/logging/src/main/java/java/util/logging/SocketHandler.java @@ -15,12 +15,11 @@ * limitations under the License. */ - package java.util.logging; -import java.net.Socket; import java.io.BufferedOutputStream; import java.io.IOException; +import java.net.Socket; import org.apache.harmony.logging.internal.nls.Messages; @@ -48,16 +47,14 @@ import org.apache.harmony.logging.internal.nls.Messages; *
      10. java.util.logging.SocketHandler.encoding specifies the port number that * this handler should connect to. There's no default value for this property. * - *

        *

        * This handler buffers the outgoing messages, but flushes each time a log * record has been published. - *

        *

        * This class is not thread-safe. - *

        */ public class SocketHandler extends StreamHandler { + // default level private static final String DEFAULT_LEVEL = "ALL"; //$NON-NLS-1$ @@ -71,7 +68,7 @@ public class SocketHandler extends StreamHandler { * Constructs a {@code SocketHandler} object using the properties read by * the log manager, including the host name and port number. Default * formatting uses the XMLFormatter class and level is set to ALL. - * + * * @throws IOException * if failed to connect to the specified host and port. * @throws IllegalArgumentException @@ -92,7 +89,7 @@ public class SocketHandler extends StreamHandler { * Constructs a {@code SocketHandler} object using the specified host name * and port number together with other properties read by the log manager. * Default formatting uses the XMLFormatter class and level is set to ALL. - * + * * @param host * the host name * @param port @@ -146,7 +143,7 @@ public class SocketHandler extends StreamHandler { /** * Closes this handler. The network connection to the host is also closed. - * + * * @throws SecurityException * If a security manager determines that the caller does not * have the required permission to control this handler. @@ -168,7 +165,7 @@ public class SocketHandler extends StreamHandler { /** * Logs a record if necessary. A flush operation will be done afterwards. - * + * * @param record * the log record to be logged. */ @@ -177,5 +174,4 @@ public class SocketHandler extends StreamHandler { super.publish(record); super.flush(); } - } diff --git a/libcore/logging/src/main/java/java/util/logging/StreamHandler.java b/libcore/logging/src/main/java/java/util/logging/StreamHandler.java index ee1219096..7049d45c2 100644 --- a/libcore/logging/src/main/java/java/util/logging/StreamHandler.java +++ b/libcore/logging/src/main/java/java/util/logging/StreamHandler.java @@ -15,7 +15,6 @@ * limitations under the License. */ - package java.util.logging; import java.io.OutputStream; @@ -30,30 +29,26 @@ import org.apache.harmony.logging.internal.nls.Messages; * is, objects of the class {@link java.io.OutputStream}. *

        * A {@code StreamHandler} object reads the following properties from the log - * manager to initialize itself: + * manager to initialize itself. A default value will be used if a property is + * not found or has an invalid value. *

          - *
        • java.util.logging.StreamHandler.level specifies the logging level, - * defaults to {@code Level.INFO} if this property is not found or has an - * invalid value. + *
        • java.util.logging.StreamHandler.encoding specifies the encoding this + * handler will use to encode log messages. Default is the encoding used by the + * current platform. *
        • java.util.logging.StreamHandler.filter specifies the name of the filter - * class to be associated with this handler, defaults to {@code null} if this - * property is not found or has an invalid value. + * class to be associated with this handler. No Filter is used by + * default. *
        • java.util.logging.StreamHandler.formatter specifies the name of the - * formatter class to be associated with this handler, defaults to - * {@code java.util.logging.SimpleFormatter} if this property is not found or - * has an invalid value. - *
        • java.util.logging.StreamHandler.encoding specifies the encoding this - * handler will use to encode log messages, defaults to {@code null} if this - * property is not found or has an invalid value. + * formatter class to be associated with this handler. Default is + * {@code java.util.logging.SimpleFormatter}. + *
        • java.util.logging.StreamHandler.level specifies the logging level. + * Defaults is {@code Level.INFO}. *
        - *

        *

        * This class is not thread-safe. - *

        - * - * @since Android 1.0 */ public class StreamHandler extends Handler { + // the output stream this handler writes to private OutputStream os; @@ -66,11 +61,9 @@ public class StreamHandler extends Handler { /** * Constructs a {@code StreamHandler} object. The new stream handler * does not have an associated output stream. - * - * @since Android 1.0 */ public StreamHandler() { - initProperties("INFO", null, "java.util.logging.SimpleFormatter", //$NON-NLS-1$//$NON-NLS-2$ + initProperties("INFO", null, "java.util.logging.SimpleFormatter", //$NON-NLS-1$//$NON-NLS-2$ null); this.os = null; this.writer = null; @@ -80,7 +73,7 @@ public class StreamHandler extends Handler { /** * Constructs a {@code StreamHandler} object with the supplied output * stream. Default properties are read. - * + * * @param os * the output stream this handler writes to. */ @@ -106,14 +99,13 @@ public class StreamHandler extends Handler { /** * Constructs a {@code StreamHandler} object with the supplied output stream * and formatter. - * + * * @param os * the output stream this handler writes to. * @param formatter * the formatter this handler uses to format the output. * @throws NullPointerException * if {@code os} or {@code formatter} is {@code null}. - * @since Android 1.0 */ public StreamHandler(OutputStream os, Formatter formatter) { this(); @@ -160,7 +152,7 @@ public class StreamHandler extends Handler { /** * Sets the output stream this handler writes to. Note it does nothing else. - * + * * @param newOs * the new output stream */ @@ -168,13 +160,12 @@ public class StreamHandler extends Handler { this.os = newOs; } - /** * Sets the output stream this handler writes to. If there's an existing * output stream, the tail string of the associated formatter will be - * written to it. Then it will be flushed, closed and replaced with + * written to it. Then it will be flushed, closed and replaced with * {@code os}. - * + * * @param os * the new output stream. * @throws SecurityException @@ -197,7 +188,7 @@ public class StreamHandler extends Handler { /** * Sets the character encoding used by this handler. A {@code null} value * indicates that the default encoding should be used. - * + * * @param encoding * the character encoding to set. * @throws SecurityException @@ -205,12 +196,11 @@ public class StreamHandler extends Handler { * have the required permission. * @throws UnsupportedEncodingException * if the specified encoding is not supported by the runtime. - * @since Android 1.0 */ @Override public void setEncoding(String encoding) throws SecurityException, UnsupportedEncodingException { - //flush first before set new encoding + // flush first before set new encoding this.flush(); super.setEncoding(encoding); // renew writer only if the writer exists @@ -234,7 +224,7 @@ public class StreamHandler extends Handler { /** * Closes this handler, but the underlying output stream is only closed if * {@code closeStream} is {@code true}. Security is not checked. - * + * * @param closeStream * whether to close the underlying output stream. */ @@ -264,11 +254,10 @@ public class StreamHandler extends Handler { * this handler is written out. A flush operation and a subsequent close * operation is then performed upon the output stream. Client applications * should not use a handler after closing it. - * + * * @throws SecurityException * if a security manager determines that the caller does not * have the required permission. - * @since Android 1.0 */ @Override public void close() { @@ -278,8 +267,6 @@ public class StreamHandler extends Handler { /** * Flushes any buffered output. - * - * @since Android 1.0 */ @Override public void flush() { @@ -291,7 +278,8 @@ public class StreamHandler extends Handler { this.os.flush(); } } catch (Exception e) { - // logging.16=Exception occurred while flushing the output stream. + // logging.16=Exception occurred while flushing the output + // stream. getErrorManager().error(Messages.getString("logging.16"), //$NON-NLS-1$ e, ErrorManager.FLUSH_FAILURE); } @@ -309,10 +297,9 @@ public class StreamHandler extends Handler { * * If it is the first time a log record is written out, the head string of * the formatter associated with this handler is written out first. - * + * * @param record * the log record to be logged. - * @since Android 1.0 */ @Override public synchronized void publish(LogRecord record) { @@ -325,7 +312,8 @@ public class StreamHandler extends Handler { try { msg = getFormatter().format(record); } catch (Exception e) { - // logging.17=Exception occurred while formatting the log record. + // logging.17=Exception occurred while formatting the log + // record. getErrorManager().error(Messages.getString("logging.17"), //$NON-NLS-1$ e, ErrorManager.FORMAT_FAILURE); } @@ -345,13 +333,11 @@ public class StreamHandler extends Handler { * {@code false}. *

        * Notice : Case of no output stream will return {@code false}. - *

        - * + * * @param record * the log record to be checked. * @return {@code true} if {@code record} needs to be logged, {@code false} * otherwise. - * @since Android 1.0 */ @Override public boolean isLoggable(LogRecord record) { @@ -363,5 +349,4 @@ public class StreamHandler extends Handler { } return false; } - } diff --git a/libcore/logging/src/main/java/java/util/logging/XMLFormatter.java b/libcore/logging/src/main/java/java/util/logging/XMLFormatter.java index 6279d8cf8..ff9681381 100644 --- a/libcore/logging/src/main/java/java/util/logging/XMLFormatter.java +++ b/libcore/logging/src/main/java/java/util/logging/XMLFormatter.java @@ -15,7 +15,6 @@ * limitations under the License. */ - package java.util.logging; import java.security.AccessController; @@ -30,8 +29,6 @@ import java.util.ResourceBundle; * {@code XMLFormatter} uses the output handler's encoding if it is specified, * otherwise the default platform encoding is used instead. UTF-8 is the * recommended encoding. - * - * @since Android 1.0 */ public class XMLFormatter extends Formatter { @@ -42,8 +39,6 @@ public class XMLFormatter extends Formatter { /** * Constructs a new {@code XMLFormatter}. - * - * @since Android 1.0 */ public XMLFormatter() { super(); @@ -51,61 +46,63 @@ public class XMLFormatter extends Formatter { /** * Converts a {@code LogRecord} into an XML string. - * + * * @param r * the log record to be formatted. * @return the log record formatted as an XML string. - * @since Android 1.0 */ + @SuppressWarnings("nls") @Override public String format(LogRecord r) { - //call a method of LogRecord to ensure not null + // call a method of LogRecord to ensure not null long time = r.getMillis(); - //format to date - String date = MessageFormat.format("{0, date} {0, time}", //$NON-NLS-1$ + // format to date + String date = MessageFormat.format("{0, date} {0, time}", new Object[] { new Date(time) }); StringBuilder sb = new StringBuilder(); - sb.append(("")).append(lineSeperator); //$NON-NLS-1$ - sb.append(indent).append(("")).append(date).append(("")) //$NON-NLS-1$ //$NON-NLS-2$ + sb.append(("")).append(lineSeperator); + sb.append(indent).append(("")).append(date).append(("")) .append(lineSeperator); - sb.append(indent).append(("")).append(time).append( //$NON-NLS-1$ - ("")).append(lineSeperator); //$NON-NLS-1$ - sb.append(indent).append(("")).append(r.getSequenceNumber()) //$NON-NLS-1$ - .append(("")).append(lineSeperator); //$NON-NLS-1$ + sb.append(indent).append(("")).append(time).append( + ("")).append(lineSeperator); + sb.append(indent).append(("")).append(r.getSequenceNumber()) + .append(("")).append(lineSeperator); if (null != r.getLoggerName()) { - sb.append(indent).append(("")).append(r.getLoggerName()) //$NON-NLS-1$ - .append(("")).append(lineSeperator); //$NON-NLS-1$ + sb.append(indent).append(("")).append(r.getLoggerName()) + .append(("")).append(lineSeperator); } - sb.append(indent).append(("")).append(r.getLevel().getName()) //$NON-NLS-1$ - .append(("")).append(lineSeperator); //$NON-NLS-1$ + sb.append(indent).append(("")).append(r.getLevel().getName()) + .append(("")).append(lineSeperator); if (null != r.getSourceClassName()) { - sb.append(indent).append(("")) //$NON-NLS-1$ - .append(r.getSourceClassName()).append(("")) //$NON-NLS-1$ + sb.append(indent).append(("")) + .append(r.getSourceClassName()).append(("")) .append(lineSeperator); } if (null != r.getSourceMethodName()) { - sb.append(indent).append(("")).append( //$NON-NLS-1$ - r.getSourceMethodName()).append(("")).append( //$NON-NLS-1$ + sb.append(indent).append(("")).append( + r.getSourceMethodName()).append(("")).append( lineSeperator); } - sb.append(indent).append(("")).append(r.getThreadID()).append( //$NON-NLS-1$ - ("")).append(lineSeperator); //$NON-NLS-1$ + sb.append(indent).append(("")).append(r.getThreadID()).append( + ("")).append(lineSeperator); formatMessages(r, sb); Object[] params; if ((params = r.getParameters()) != null) { for (Object element : params) { - sb.append(indent).append(("")).append(element).append( //$NON-NLS-1$ - ("")).append(lineSeperator); //$NON-NLS-1$ + sb.append(indent).append(("")).append(element).append( + ("")).append(lineSeperator); } } formatThrowable(r, sb); - sb.append(("")).append(lineSeperator); //$NON-NLS-1$ + sb.append(("")).append(lineSeperator); return sb.toString(); } + @SuppressWarnings("nls") private void formatMessages(LogRecord r, StringBuilder sb) { - //get localized message if has, but don't call Formatter.formatMessage to parse pattern string + // get localized message if has, but don't call Formatter.formatMessage + // to parse pattern string ResourceBundle rb = r.getResourceBundle(); String pattern = r.getMessage(); if (null != rb && null != pattern) { @@ -118,49 +115,50 @@ public class XMLFormatter extends Formatter { if (message == null) { message = pattern; - sb.append(indent).append(("")).append(message).append( //$NON-NLS-1$ - ("")).append(lineSeperator); //$NON-NLS-1$ + sb.append(indent).append(("")).append(message).append( + ("")).append(lineSeperator); } else { - sb.append(indent).append(("")).append(message).append( //$NON-NLS-1$ - ("")).append(lineSeperator); //$NON-NLS-1$ - sb.append(indent).append(("")).append(pattern).append( //$NON-NLS-1$ - ("")).append(lineSeperator); //$NON-NLS-1$ - sb.append(indent).append(("")).append( //$NON-NLS-1$ - r.getResourceBundleName()).append(("")) //$NON-NLS-1$ + sb.append(indent).append(("")).append(message).append( + ("")).append(lineSeperator); + sb.append(indent).append(("")).append(pattern).append( + ("")).append(lineSeperator); + sb.append(indent).append(("")).append( + r.getResourceBundleName()).append(("")) .append(lineSeperator); } - } else if(null != pattern){ - sb.append(indent).append(("")).append(pattern).append( //$NON-NLS-1$ - ("")).append(lineSeperator); //$NON-NLS-1$ - } else{ - sb.append(indent).append(("")); //$NON-NLS-1$ + } else if (null != pattern) { + sb.append(indent).append(("")).append(pattern).append( + ("")).append(lineSeperator); + } else { + sb.append(indent).append(("")); } } + @SuppressWarnings("nls") private void formatThrowable(LogRecord r, StringBuilder sb) { Throwable t; if ((t = r.getThrown()) != null) { - sb.append(indent).append("").append(lineSeperator); //$NON-NLS-1$ - sb.append(indent).append(indent).append("").append( //$NON-NLS-1$ - t.toString()).append("").append(lineSeperator); //$NON-NLS-1$ - //format throwable's stack trace + sb.append(indent).append("").append(lineSeperator); + sb.append(indent).append(indent).append("").append( + t.toString()).append("").append(lineSeperator); + // format throwable's stack trace StackTraceElement[] elements = t.getStackTrace(); for (StackTraceElement e : elements) { - sb.append(indent).append(indent).append("").append( //$NON-NLS-1$ + sb.append(indent).append(indent).append("").append( lineSeperator); sb.append(indent).append(indent).append(indent).append( - "").append(e.getClassName()).append("") //$NON-NLS-1$//$NON-NLS-2$ + "").append(e.getClassName()).append("") .append(lineSeperator); sb.append(indent).append(indent).append(indent).append( - "").append(e.getMethodName()).append( //$NON-NLS-1$ - "").append(lineSeperator); //$NON-NLS-1$ + "").append(e.getMethodName()).append( + "").append(lineSeperator); sb.append(indent).append(indent).append(indent) - .append("").append(e.getLineNumber()).append( //$NON-NLS-1$ - "").append(lineSeperator); //$NON-NLS-1$ - sb.append(indent).append(indent).append("").append( //$NON-NLS-1$ + .append("").append(e.getLineNumber()).append( + "").append(lineSeperator); + sb.append(indent).append(indent).append("").append( lineSeperator); } - sb.append(indent).append("").append(lineSeperator); //$NON-NLS-1$ + sb.append(indent).append("").append(lineSeperator); } } @@ -168,54 +166,48 @@ public class XMLFormatter extends Formatter { * Returns the header string for a set of log records formatted as XML * strings, using the output handler's encoding if it is defined, otherwise * using the default platform encoding. - * + * * @param h * the output handler, may be {@code null}. * @return the header string for log records formatted as XML strings. - * @since Android 1.0 */ + @SuppressWarnings("nls") @Override public String getHead(Handler h) { String encoding = null; - if(null != h) { + if (null != h) { encoding = h.getEncoding(); } if (null == encoding) { - encoding = getSystemProperty("file.encoding"); //$NON-NLS-1$ + encoding = getSystemProperty("file.encoding"); } StringBuilder sb = new StringBuilder(); - sb.append("").append(lineSeperator); //$NON-NLS-1$ - sb.append("").append(lineSeperator); //$NON-NLS-1$ - sb.append(("")); //$NON-NLS-1$ + sb.append("").append(lineSeperator); + sb.append("").append(lineSeperator); + sb.append(("")); return sb.toString(); } /** * Returns the tail string for a set of log records formatted as XML * strings. - * + * * @param h * the output handler, may be {@code null}. * @return the tail string for log records formatted as XML strings. - * @since Android 1.0 */ @Override - @SuppressWarnings("unused") public String getTail(Handler h) { return ""; //$NON-NLS-1$ } - //use privilege code to get system property + // use privilege code to get system property private static String getSystemProperty(final String key) { - return AccessController.doPrivileged( - new PrivilegedAction() { + return AccessController.doPrivileged(new PrivilegedAction() { public String run() { return System.getProperty(key); } }); } - } - - diff --git a/libcore/logging/src/test/java/org/apache/harmony/logging/tests/java/util/logging/LoggerTest.java b/libcore/logging/src/test/java/org/apache/harmony/logging/tests/java/util/logging/LoggerTest.java index 3a06078b4..8a981bbf8 100644 --- a/libcore/logging/src/test/java/org/apache/harmony/logging/tests/java/util/logging/LoggerTest.java +++ b/libcore/logging/src/test/java/org/apache/harmony/logging/tests/java/util/logging/LoggerTest.java @@ -29,12 +29,15 @@ import java.util.logging.LogManager; import java.util.logging.LogRecord; import java.util.logging.Logger; import java.util.logging.LoggingPermission; +import java.io.File; +import java.io.FileInputStream; import junit.framework.TestCase; import org.apache.harmony.logging.tests.java.util.logging.util.EnvironmentHelper; import tests.util.CallVerificationStack; +import dalvik.annotation.KnownFailure; import dalvik.annotation.SideEffect; import dalvik.annotation.TestLevel; import dalvik.annotation.TestTargetClass; @@ -4628,6 +4631,27 @@ public class LoggerTest extends TestCase { } } + /* + * test initHandler + */ + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "", + method = "initHandler", + args = {} + ) + @KnownFailure(value="bug 2002061O") + public void test_initHandler() throws Exception { + File logProps = new File(LOGGING_CONFIG_FILE); + LogManager lm = LogManager.getLogManager(); + lm.readConfiguration(new FileInputStream(logProps)); + + Logger log = Logger.getLogger(""); + // can log properly + Handler[] handlers = log.getHandlers(); + assertEquals(2, handlers.length); + } + /* * A mock logger, used to test the protected constructors and fields. diff --git a/libcore/logging/src/test/resources/config/java/util/logging/logging.config b/libcore/logging/src/test/resources/config/java/util/logging/logging.config index f4c5146b5..6e7394b3e 100644 --- a/libcore/logging/src/test/resources/config/java/util/logging/logging.config +++ b/libcore/logging/src/test/resources/config/java/util/logging/logging.config @@ -1,3 +1,3 @@ -handlers=org.apache.harmony.logging.tests.java.util.logging.LogManagerTest$MockHandler java.util.logging.ConsoleHandler +handlers=org.apache.harmony.logging.tests.java.util.logging.LogManagerTest$MockHandler , java.util.logging.ConsoleHandler .level=ALL org.apache.harmony.logging.tests.java.util.logging.LogManagerTest$MockHandler.level=OFF \ No newline at end of file diff --git a/libcore/luni-kernel/src/main/java/java/lang/Class.java b/libcore/luni-kernel/src/main/java/java/lang/Class.java index 70ae3c142..b8e39032a 100644 --- a/libcore/luni-kernel/src/main/java/java/lang/Class.java +++ b/libcore/luni-kernel/src/main/java/java/lang/Class.java @@ -440,7 +440,8 @@ public final class Class implements Serializable, AnnotatedElement, GenericDe * @see ClassLoader#isSystemClassLoader() */ ClassLoader getClassLoaderImpl() { - return getClassLoader(this); + ClassLoader loader = getClassLoader(this); + return loader == null ? BootClassLoader.getInstance() : loader; } /* @@ -468,6 +469,7 @@ public final class Class implements Serializable, AnnotatedElement, GenericDe * * @param parameterTypes * the parameter types of the requested constructor. + * {@code (Class[]) null} is equivalent to the empty array. * @return the constructor described by {@code parameterTypes}. * @throws NoSuchMethodException * if the constructor can not be found. @@ -586,6 +588,7 @@ public final class Class implements Serializable, AnnotatedElement, GenericDe * * @param parameterTypes * the parameter types of the requested constructor. + * {@code (Class[]) null} is equivalent to the empty array. * @return the constructor described by {@code parameterTypes}. * @throws NoSuchMethodException * if the requested constructor can not be found. @@ -658,12 +661,14 @@ public final class Class implements Serializable, AnnotatedElement, GenericDe sb.append(getSimpleName()); sb.append('('); boolean first = true; - for (Class p : parameterTypes) { - if (!first) { - sb.append(','); + if (parameterTypes != null) { + for (Class p : parameterTypes) { + if (!first) { + sb.append(','); + } + first = false; + sb.append(p.getSimpleName()); } - first = false; - sb.append(p.getSimpleName()); } sb.append(')'); throw new NoSuchMethodException(sb.toString()); @@ -740,6 +745,7 @@ public final class Class implements Serializable, AnnotatedElement, GenericDe * the requested method's name. * @param parameterTypes * the parameter types of the requested method. + * {@code (Class[]) null} is equivalent to the empty array. * @return the method described by {@code name} and {@code parameterTypes}. * @throws NoSuchMethodException * if the requested constructor can not be found. @@ -990,6 +996,7 @@ public final class Class implements Serializable, AnnotatedElement, GenericDe * the requested method's name. * @param parameterTypes * the parameter types of the requested method. + * {@code (Class[]) null} is equivalent to the empty array. * @return the public field specified by {@code name}. * @throws NoSuchMethodException * if the method can not be found. diff --git a/libcore/luni-kernel/src/main/java/java/lang/Thread.java b/libcore/luni-kernel/src/main/java/java/lang/Thread.java index 3cde7e154..4f9f98857 100644 --- a/libcore/luni-kernel/src/main/java/java/lang/Thread.java +++ b/libcore/luni-kernel/src/main/java/java/lang/Thread.java @@ -73,14 +73,16 @@ import org.apache.harmony.security.fortress.SecurityUtils; */ public class Thread implements Runnable { + private static final int NANOS_PER_MILLI = 1000000; + /** Park states */ private static class ParkState { /** park state indicating unparked */ private static final int UNPARKED = 1; - + /** park state indicating preemptively unparked */ private static final int PREEMPTIVELY_UNPARKED = 2; - + /** park state indicating parked */ private static final int PARKED = 3; } @@ -120,21 +122,21 @@ public class Thread implements Runnable { /** * The maximum priority value allowed for a thread. - * + * * @since Android 1.0 */ public final static int MAX_PRIORITY = 10; /** * The minimum priority value allowed for a thread. - * + * * @since Android 1.0 */ public final static int MIN_PRIORITY = 1; /** * The normal (default) priority value assigned to threads. - * + * * @since Android 1.0 */ public final static int NORM_PRIORITY = 5; @@ -198,7 +200,7 @@ public class Thread implements Runnable { /** the park state of the thread */ private int parkState = ParkState.UNPARKED; - + /** * Constructs a new {@code Thread} with no {@code Runnable} object and a * newly generated name. The new {@code Thread} will belong to the same @@ -206,7 +208,7 @@ public class Thread implements Runnable { * * @see java.lang.ThreadGroup * @see java.lang.Runnable - * + * * @since Android 1.0 */ public Thread() { @@ -217,14 +219,14 @@ public class Thread implements Runnable { * Constructs a new {@code Thread} with a {@code Runnable} object and a * newly generated name. The new {@code Thread} will belong to the same * {@code ThreadGroup} as the {@code Thread} calling this constructor. - * + * * @param runnable * a {@code Runnable} whose method run will be * executed by the new {@code Thread} * * @see java.lang.ThreadGroup * @see java.lang.Runnable - * + * * @since Android 1.0 */ public Thread(Runnable runnable) { @@ -235,13 +237,13 @@ public class Thread implements Runnable { * Constructs a new {@code Thread} with a {@code Runnable} object and name * provided. The new {@code Thread} will belong to the same {@code * ThreadGroup} as the {@code Thread} calling this constructor. - * + * * @param runnable * a {@code Runnable} whose method run will be * executed by the new {@code Thread} * @param threadName * the name for the {@code Thread} being created - * + * * @see java.lang.ThreadGroup * @see java.lang.Runnable * @@ -259,10 +261,10 @@ public class Thread implements Runnable { * Constructs a new {@code Thread} with no {@code Runnable} object and the * name provided. The new {@code Thread} will belong to the same {@code * ThreadGroup} as the {@code Thread} calling this constructor. - * + * * @param threadName * the name for the {@code Thread} being created - * + * * @see java.lang.ThreadGroup * @see java.lang.Runnable * @@ -280,7 +282,7 @@ public class Thread implements Runnable { * Constructs a new {@code Thread} with a {@code Runnable} object and a * newly generated name. The new {@code Thread} will belong to the {@code * ThreadGroup} passed as parameter. - * + * * @param group * {@code ThreadGroup} to which the new {@code Thread} will * belong @@ -296,7 +298,7 @@ public class Thread implements Runnable { * @see java.lang.Runnable * @see java.lang.SecurityException * @see java.lang.SecurityManager - * + * * @since Android 1.0 */ public Thread(ThreadGroup group, Runnable runnable) { @@ -306,7 +308,7 @@ public class Thread implements Runnable { /** * Constructs a new {@code Thread} with a {@code Runnable} object, the given * name and belonging to the {@code ThreadGroup} passed as parameter. - * + * * @param group * ThreadGroup to which the new {@code Thread} will belong * @param runnable @@ -323,7 +325,7 @@ public class Thread implements Runnable { * @see java.lang.Runnable * @see java.lang.SecurityException * @see java.lang.SecurityManager - * + * * @since Android 1.0 */ public Thread(ThreadGroup group, Runnable runnable, String threadName) { @@ -337,7 +339,7 @@ public class Thread implements Runnable { /** * Constructs a new {@code Thread} with no {@code Runnable} object, the * given name and belonging to the {@code ThreadGroup} passed as parameter. - * + * * @param group * {@code ThreadGroup} to which the new {@code Thread} will belong * @param threadName @@ -351,7 +353,7 @@ public class Thread implements Runnable { * @see java.lang.Runnable * @see java.lang.SecurityException * @see java.lang.SecurityManager - * + * * @since Android 1.0 */ public Thread(ThreadGroup group, String threadName) { @@ -365,7 +367,7 @@ public class Thread implements Runnable { /** * Constructs a new {@code Thread} with a {@code Runnable} object, the given * name and belonging to the {@code ThreadGroup} passed as parameter. - * + * * @param group * {@code ThreadGroup} to which the new {@code Thread} will * belong @@ -387,7 +389,7 @@ public class Thread implements Runnable { * @see java.lang.Runnable * @see java.lang.SecurityException * @see java.lang.SecurityManager - * + * * @since Android 1.0 */ public Thread(ThreadGroup group, Runnable runnable, String threadName, long stackSize) { @@ -526,9 +528,9 @@ public class Thread implements Runnable { /** * Returns the number of active {@code Thread}s in the running {@code * Thread}'s group and its subgroups. - * + * * @return the number of {@code Thread}s - * + * * @since Android 1.0 */ public static int activeCount() { @@ -540,14 +542,14 @@ public class Thread implements Runnable { * there's none installed, this method is a no-op. If there's a * SecurityManager installed, {@link SecurityManager#checkAccess(Thread)} is * called for that SecurityManager. - * + * * @throws SecurityException * if a SecurityManager is installed and it does not allow * access to the Thread. - * + * * @see java.lang.SecurityException * @see java.lang.SecurityManager - * + * * @since Android 1.0 */ public final void checkAccess() { @@ -562,12 +564,12 @@ public class Thread implements Runnable { /** * Returns the number of stack frames in this thread. - * + * * @return Number of stack frames * @deprecated The results of this call were never well defined. To make * things worse, it would depend on whether the Thread was * suspended or not, and suspend was deprecated too. - * + * * @since Android 1.0 */ @Deprecated @@ -579,7 +581,7 @@ public class Thread implements Runnable { * Returns the Thread of the caller, that is, the current Thread. * * @return the current Thread. - * + * * @since Android 1.0 */ public static Thread currentThread() { @@ -590,7 +592,7 @@ public class Thread implements Runnable { * Destroys the receiver without any monitor cleanup. * * @deprecated Not implemented. - * + * * @since Android 1.0 */ @Deprecated @@ -601,9 +603,9 @@ public class Thread implements Runnable { /** * Prints to the standard error stream a text representation of the current * stack for this Thread. - * + * * @see Throwable#printStackTrace() - * + * * @since Android 1.0 */ public static void dumpStack() { @@ -615,7 +617,7 @@ public class Thread implements Runnable { * receiver - and subgroups - into the array threads passed as * parameter. If the array passed as parameter is too small no exception is * thrown - the extra elements are simply not copied. - * + * * @param threads * array into which the Threads will be copied * @return How many Threads were copied over @@ -624,7 +626,7 @@ public class Thread implements Runnable { * {@link SecurityManager#checkAccess(Thread)} * @see java.lang.SecurityException * @see java.lang.SecurityManager - * + * * @since Android 1.0 */ public static int enumerate(Thread[] threads) { @@ -638,13 +640,13 @@ public class Thread implements Runnable { * Returns the stack traces of all the currently live threads and puts them * into the given map. *

        - * + * * @return A Map of current Threads to StackTraceElement arrays. * @throws SecurityException * if the current SecurityManager fails the * {@link SecurityManager#checkPermission(java.security.Permission)} * call. - * + * * @since Android 1.0 */ public static Map getAllStackTraces() { @@ -682,14 +684,14 @@ public class Thread implements Runnable { * * are satisfied, a security check for * RuntimePermission("getClassLoader") is performed first. - * + * * @return ClassLoader The context ClassLoader * @see java.lang.ClassLoader * @see #getContextClassLoader() - * + * * @throws SecurityException * if the aforementioned security check fails. - * + * * @since Android 1.0 */ public ClassLoader getContextClassLoader() { @@ -717,7 +719,7 @@ public class Thread implements Runnable { * * @return an {@link UncaughtExceptionHandler} or null if * none exists. - * + * * @since Android 1.0 */ public static UncaughtExceptionHandler getDefaultUncaughtExceptionHandler() { @@ -729,9 +731,9 @@ public class Thread implements Runnable { * generated on thread creation, is unique to the thread, and doesn't change * during the lifetime of the thread; the ID may be reused after the thread * has been terminated. - * + * * @return the thread's ID. - * + * * @since Android 1.0 */ public long getId() { @@ -742,7 +744,7 @@ public class Thread implements Runnable { * Returns the name of the Thread. * * @return the Thread's name - * + * * @since Android 1.0 */ public final String getName() { @@ -754,7 +756,7 @@ public class Thread implements Runnable { * * @return the Thread's priority * @see Thread#setPriority - * + * * @since Android 1.0 */ public final int getPriority() { @@ -768,13 +770,13 @@ public class Thread implements Runnable { * The RuntimePermission("getStackTrace") is checked before * returning a result. *

        - * + * * @return an array of StackTraceElements. * @throws SecurityException * if the current SecurityManager fails the * {@link SecurityManager#checkPermission(java.security.Permission)} * call. - * + * * @since Android 1.0 */ public StackTraceElement[] getStackTrace() { @@ -790,9 +792,9 @@ public class Thread implements Runnable { /** * Returns the current state of the Thread. This method is useful for * monitoring purposes. - * + * * @return a {@link State} value. - * + * * @since Android 1.0 */ public State getState() { @@ -803,7 +805,7 @@ public class Thread implements Runnable { // deletes the reference we won't run into a null reference later. VMThread thread = vmThread; if (thread != null) { - // If the Thread Object became invalid or was not yet started, + // If the Thread Object became invalid or was not yet started, // getStatus() will return -1. int state = thread.getStatus(); if(state != -1) { @@ -812,12 +814,12 @@ public class Thread implements Runnable { } return hasBeenStarted ? Thread.State.TERMINATED : Thread.State.NEW; } - + /** * Returns the ThreadGroup to which this Thread belongs. - * + * * @return the Thread's ThreadGroup - * + * * @since Android 1.0 */ public final ThreadGroup getThreadGroup() { @@ -833,9 +835,9 @@ public class Thread implements Runnable { * Returns the thread's uncaught exception handler. If not explicitly set, * then the ThreadGroup's handler is returned. If the thread is terminated, * then null is returned. - * + * * @return an {@link UncaughtExceptionHandler} instance or {@code null}. - * + * * @since Android 1.0 */ public UncaughtExceptionHandler getUncaughtExceptionHandler() { @@ -868,14 +870,14 @@ public class Thread implements Runnable { * their interrupt status set and return immediately. They don't receive an * exception in this case. *
          - * + * * @throws SecurityException * if checkAccess() fails with a SecurityException * @see java.lang.SecurityException * @see java.lang.SecurityManager * @see Thread#interrupted * @see Thread#isInterrupted - * + * * @since Android 1.0 */ public void interrupt() { @@ -896,12 +898,12 @@ public class Thread implements Runnable { * currentThread()) has a pending interrupt request ( * true) or not (false). It also has the side-effect of * clearing the flag. - * + * * @return a boolean indicating the interrupt status * @see Thread#currentThread * @see Thread#interrupt * @see Thread#isInterrupted - * + * * @since Android 1.0 */ public static boolean interrupted() { @@ -913,16 +915,14 @@ public class Thread implements Runnable { * still runs code (hasn't died yet). Returns false either if * the receiver hasn't been started yet or if it has already started and run * to completion and died. - * + * * @return a boolean indicating the lifeness of the Thread * @see Thread#start - * + * * @since Android 1.0 */ public final boolean isAlive() { - Thread.State state = getState(); - - return (state != Thread.State.TERMINATED && state != Thread.State.NEW); + return (vmThread != null); } /** @@ -934,7 +934,7 @@ public class Thread implements Runnable { * * @return a boolean indicating whether the Thread is a daemon * @see Thread#setDaemon - * + * * @since Android 1.0 */ public final boolean isDaemon() { @@ -949,7 +949,7 @@ public class Thread implements Runnable { * @return a boolean indicating the interrupt status * @see Thread#interrupt * @see Thread#interrupted - * + * * @since Android 1.0 */ public boolean isInterrupted() { @@ -969,11 +969,20 @@ public class Thread implements Runnable { * the receiver while it was in the join() call * @see Object#notifyAll * @see java.lang.ThreadDeath - * + * * @since Android 1.0 */ public final void join() throws InterruptedException { - join(0, 0); + VMThread t = vmThread; + if (t == null) { + return; + } + + synchronized (t) { + while (isAlive()) { + t.wait(); + } + } } /** @@ -986,7 +995,7 @@ public class Thread implements Runnable { * the receiver while it was in the join() call * @see Object#notifyAll * @see java.lang.ThreadDeath - * + * * @since Android 1.0 */ public final void join(long millis) throws InterruptedException { @@ -1004,22 +1013,49 @@ public class Thread implements Runnable { * the receiver while it was in the join() call * @see Object#notifyAll * @see java.lang.ThreadDeath - * + * * @since Android 1.0 */ public final void join(long millis, int nanos) throws InterruptedException { - if (millis < 0 || nanos < 0 || nanos > 999999) { + if (millis < 0 || nanos < 0 || nanos >= NANOS_PER_MILLI) { throw new IllegalArgumentException(); } - VMThread t; + // avoid overflow: if total > 292,277 years, just wait forever + boolean overflow = millis >= (Long.MAX_VALUE - nanos) / NANOS_PER_MILLI; + boolean forever = (millis | nanos) == 0; + if (forever | overflow) { + join(); + return; + } + + VMThread t = vmThread; + if (t == null) { + return; + } + + synchronized (t) { + if (!isAlive()) { + return; + } - t = this.vmThread; + // guaranteed not to overflow + long nanosToWait = millis * NANOS_PER_MILLI + nanos; - if (t != null) { - synchronized (t) { - if (isAlive()) - t.wait(millis, nanos); + // wait until this thread completes or the timeout has elapsed + long start = System.nanoTime(); + while (true) { + t.wait(millis, nanos); + if (!isAlive()) { + break; + } + long nanosElapsed = System.nanoTime() - start; + long nanosRemaining = nanosToWait - nanosElapsed; + if (nanosRemaining <= 0) { + break; + } + millis = nanosRemaining / NANOS_PER_MILLI; + nanos = (int) (nanosRemaining - millis * NANOS_PER_MILLI); } } } @@ -1029,12 +1065,12 @@ public class Thread implements Runnable { * suspended, or suspended and already resumed. If the receiver is * suspended, however, makes it resume to the point where it was when it was * suspended. - * + * * @throws SecurityException * if checkAccess() fails with a SecurityException * @see Thread#suspend() * @deprecated Used with deprecated method {@link Thread#suspend} - * + * * @since Android 1.0 */ @Deprecated @@ -1052,7 +1088,7 @@ public class Thread implements Runnable { * holds. If no Runnable is set, does nothing. * * @see Thread#start - * + * * @since Android 1.0 */ public void run() { @@ -1073,7 +1109,7 @@ public class Thread implements Runnable { * checkPermission call. * @see java.lang.ClassLoader * @see #getContextClassLoader() - * + * * @since Android 1.0 */ public void setContextClassLoader(ClassLoader cl) { @@ -1088,13 +1124,13 @@ public class Thread implements Runnable { /** * Set if the receiver is a daemon Thread or not. This can only be done * before the Thread starts running. - * + * * @param isDaemon * indicates whether the Thread should be daemon or not * @throws SecurityException * if checkAccess() fails with a SecurityException * @see Thread#isDaemon - * + * * @since Android 1.0 */ public final void setDaemon(boolean isDaemon) { @@ -1118,13 +1154,13 @@ public class Thread implements Runnable { * The RuntimePermission("setDefaultUncaughtExceptionHandler") * is checked prior to setting the handler. *

          - * + * * @param handler * The handler to set or null. * @throws SecurityException * if the current SecurityManager fails the checkPermission * call. - * + * * @since Android 1.0 */ public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler handler) { @@ -1159,7 +1195,7 @@ public class Thread implements Runnable { * @throws SecurityException if checkAccess() fails with a * SecurityException * @see Thread#getName - * + * * @since Android 1.0 */ public final void setName(String threadName) { @@ -1182,7 +1218,7 @@ public class Thread implements Runnable { * be the parameter that was passed - it will depend on the receiver's * ThreadGroup. The priority cannot be set to be higher than the receiver's * ThreadGroup's maxPriority(). - * + * * @param priority * new priority for the Thread * @throws SecurityException @@ -1191,7 +1227,7 @@ public class Thread implements Runnable { * if the new priority is greater than Thread.MAX_PRIORITY or * less than Thread.MIN_PRIORITY * @see Thread#getPriority - * + * * @since Android 1.0 */ public final void setPriority(int priority) { @@ -1218,12 +1254,12 @@ public class Thread implements Runnable { * Sets the uncaught exception handler. This handler is invoked in case this * Thread dies due to an unhandled exception. *

          - * + * * @param handler * The handler to set or null. * @throws SecurityException * if the current SecurityManager fails the checkAccess call. - * + * * @since Android 1.0 */ public void setUncaughtExceptionHandler(UncaughtExceptionHandler handler) { @@ -1236,14 +1272,14 @@ public class Thread implements Runnable { * Causes the thread which sent this message to sleep for the given interval * of time (given in milliseconds). The precision is not guaranteed - the * Thread may sleep more or less than requested. - * + * * @param time * The time to sleep in milliseconds. * @throws InterruptedException * if interrupt() was called for this Thread while * it was sleeping * @see Thread#interrupt() - * + * * @since Android 1.0 */ public static void sleep(long time) throws InterruptedException { @@ -1254,7 +1290,7 @@ public class Thread implements Runnable { * Causes the thread which sent this message to sleep for the given interval * of time (given in milliseconds and nanoseconds). The precision is not * guaranteed - the Thread may sleep more or less than requested. - * + * * @param millis * The time to sleep in milliseconds. * @param nanos @@ -1263,7 +1299,7 @@ public class Thread implements Runnable { * if interrupt() was called for this Thread while * it was sleeping * @see Thread#interrupt() - * + * * @since Android 1.0 */ public static void sleep(long millis, int nanos) throws InterruptedException { @@ -1276,9 +1312,9 @@ public class Thread implements Runnable { * Thread calling start()). * * @throws IllegalThreadStateException if the Thread has been started before - * + * * @see Thread#run - * + * * @since Android 1.0 */ public synchronized void start() { @@ -1300,7 +1336,7 @@ public class Thread implements Runnable { * SecurityException * @deprecated because stopping a thread in this manner is unsafe and can * leave your application and the VM in an unpredictable state. - * + * * @since Android 1.0 */ @Deprecated @@ -1321,7 +1357,7 @@ public class Thread implements Runnable { * null * @deprecated because stopping a thread in this manner is unsafe and can * leave your application and the VM in an unpredictable state. - * + * * @since Android 1.0 */ @Deprecated @@ -1350,12 +1386,12 @@ public class Thread implements Runnable { * resume() is sent to it. Suspend requests are not queued, which * means that N requests are equivalent to just one - only one resume * request is needed in this case. - * + * * @throws SecurityException * if checkAccess() fails with a SecurityException * @see Thread#resume() * @deprecated May cause deadlocks. - * + * * @since Android 1.0 */ @Deprecated @@ -1371,9 +1407,9 @@ public class Thread implements Runnable { /** * Returns a string containing a concise, human-readable description of the * Thread. It includes the Thread's name, priority, and group name. - * + * * @return a printable representation for the receiver. - * + * * @since Android 1.0 */ @Override @@ -1384,7 +1420,7 @@ public class Thread implements Runnable { /** * Causes the calling Thread to yield execution time to another Thread that * is ready to run. The actual scheduling is implementation-dependent. - * + * * @since Android 1.0 */ public static void yield() { @@ -1398,7 +1434,7 @@ public class Thread implements Runnable { * @param object the object to test for the monitor lock * @return true if the current thread has a monitor lock on the specified * object; false otherwise - * + * * @since Android 1.0 */ public static boolean holdsLock(Object object) { @@ -1410,7 +1446,7 @@ public class Thread implements Runnable { * terminated by an uncaught exception. Upon such termination, the handler * is notified of the terminating thread and causal exception. If there is * no explicit handler set then the thread's group is the default handler. - * + * * @since Android 1.0 */ public static interface UncaughtExceptionHandler { @@ -1421,7 +1457,7 @@ public class Thread implements Runnable { * * @param thread the thread that has an uncaught exception * @param ex the exception that was thrown - * + * * @since Android 1.0 */ void uncaughtException(Thread thread, Throwable ex); @@ -1444,7 +1480,7 @@ public class Thread implements Runnable { parkState = ParkState.PREEMPTIVELY_UNPARKED; return; } - + synchronized (vmt) { switch (parkState) { case ParkState.PREEMPTIVELY_UNPARKED: { @@ -1468,12 +1504,12 @@ public class Thread implements Runnable { } } } - + /** * Implementation of parkFor(). See {@link LangAccessImpl}. * This method must only be called when this is the current * thread. - * + * * @param nanos number of nanoseconds to park for */ /*package*/ void parkFor(long nanos) { @@ -1483,7 +1519,7 @@ public class Thread implements Runnable { // Running threads should always have an associated vmThread. throw new AssertionError(); } - + synchronized (vmt) { switch (parkState) { case ParkState.PREEMPTIVELY_UNPARKED: { @@ -1491,8 +1527,8 @@ public class Thread implements Runnable { break; } case ParkState.UNPARKED: { - long millis = nanos / 1000000; - nanos %= 1000000; + long millis = nanos / NANOS_PER_MILLI; + nanos %= NANOS_PER_MILLI; parkState = ParkState.PARKED; try { @@ -1507,7 +1543,7 @@ public class Thread implements Runnable { */ if (parkState == ParkState.PARKED) { parkState = ParkState.UNPARKED; - } + } } break; } @@ -1515,7 +1551,7 @@ public class Thread implements Runnable { throw new AssertionError( "shouldn't happen: attempt to repark"); } - } + } } } @@ -1523,7 +1559,7 @@ public class Thread implements Runnable { * Implementation of parkUntil(). See {@link LangAccessImpl}. * This method must only be called when this is the current * thread. - * + * * @param time absolute milliseconds since the epoch to park until */ /*package*/ void parkUntil(long time) { @@ -1533,7 +1569,7 @@ public class Thread implements Runnable { // Running threads should always have an associated vmThread. throw new AssertionError(); } - + synchronized (vmt) { /* * Note: This conflates the two time bases of "wall clock" @@ -1554,7 +1590,7 @@ public class Thread implements Runnable { if (delayMillis <= 0) { parkState = ParkState.UNPARKED; } else { - parkFor(delayMillis * 1000000); + parkFor(delayMillis * NANOS_PER_MILLI); } } } diff --git a/libcore/luni-kernel/src/main/native/java_lang_ProcessManager.c b/libcore/luni-kernel/src/main/native/java_lang_ProcessManager.c index fcff1df46..eaefc9fb6 100644 --- a/libcore/luni-kernel/src/main/native/java_lang_ProcessManager.c +++ b/libcore/luni-kernel/src/main/native/java_lang_ProcessManager.c @@ -91,7 +91,8 @@ static void java_lang_ProcessManager_watchChildren(JNIEnv* env, jobject o) { while (1) { int status; - pid_t pid = wait(&status); + /* wait for children in our process group */ + pid_t pid = waitpid(0, &status, 0); if (pid >= 0) { // Extract real status. diff --git a/libcore/luni/src/main/java/java/io/BufferedInputStream.java b/libcore/luni/src/main/java/java/io/BufferedInputStream.java index 0b9afc329..22379ddfc 100644 --- a/libcore/luni/src/main/java/java/io/BufferedInputStream.java +++ b/libcore/luni/src/main/java/java/io/BufferedInputStream.java @@ -30,68 +30,49 @@ import java.util.logging.Logger; * drawback is that some extra space is required to hold the buffer and that * copying takes place when filling that buffer, but this is usually outweighed * by the performance benefits. - * + * *

          A typical application pattern for the class looks like this:

          - * + * *

            * BufferedInputStream buf = new BufferedInputStream(new FileInputStream("file.java"));
            * 
          - * + * * @see BufferedOutputStream - * - * @since Android 1.0 */ public class BufferedInputStream extends FilterInputStream { - // BEGIN android-changed - // The address of the buffer should not be cached in a register. - // This was changed to be more close to the RI. /** * The buffer containing the current bytes read from the target InputStream. - * - * @since Android 1.0 */ protected volatile byte[] buf; - // END android-changed /** * The total number of bytes inside the byte array {@code buf}. - * - * @since Android 1.0 */ protected int count; /** * The current limit, which when passed, invalidates the current mark. - * - * @since Android 1.0 */ protected int marklimit; /** * The currently marked position. -1 indicates no mark has been set or the * mark has been invalidated. - * - * @since Android 1.0 */ protected int markpos = -1; /** * The current position within the byte array {@code buf}. - * - * @since Android 1.0 */ protected int pos; - private boolean closed = false; - /** * Constructs a new {@code BufferedInputStream} on the {@link InputStream} * {@code in}. The default buffer size (8 KB) is allocated and all reads * can now be filtered through this stream. - * + * * @param in * the InputStream the buffer reads from. - * @since Android 1.0 */ public BufferedInputStream(InputStream in) { super(in); @@ -116,14 +97,13 @@ public class BufferedInputStream extends FilterInputStream { * Constructs a new {@code BufferedInputStream} on the {@link InputStream} * {@code in}. The buffer size is specified by the parameter {@code size} * and all reads are now filtered through this stream. - * + * * @param in * the input stream the buffer reads from. * @param size * the size of buffer to allocate. * @throws IllegalArgumentException * if {@code size < 0}. - * @since Android 1.0 */ public BufferedInputStream(InputStream in, int size) { super(in); @@ -138,43 +118,43 @@ public class BufferedInputStream extends FilterInputStream { * Returns the number of bytes that are available before this stream will * block. This method returns the number of bytes available in the buffer * plus those available in the source stream. - * + * * @return the number of bytes available before blocking. * @throws IOException * if this stream is closed. - * @since Android 1.0 */ @Override public synchronized int available() throws IOException { - if (buf == null) { + InputStream localIn = in; // 'in' could be invalidated by close() + if (buf == null || localIn == null) { // K0059=Stream is closed throw new IOException(Msg.getString("K0059")); //$NON-NLS-1$ } - return count - pos + in.available(); + return count - pos + localIn.available(); } /** * Closes this stream. The source stream is closed and any resources * associated with it are released. - * + * * @throws IOException * if an error occurs while closing this stream. - * @since Android 1.0 */ @Override - public synchronized void close() throws IOException { - if (null != in) { - super.close(); - in = null; - } + public void close() throws IOException { buf = null; - closed = true; + InputStream localIn = in; + in = null; + if (localIn != null) { + localIn.close(); + } } - private int fillbuf() throws IOException { + private int fillbuf(InputStream localIn, byte[] localBuf) + throws IOException { if (markpos == -1 || (pos - markpos >= marklimit)) { /* Mark position not set or exceeded readlimit */ - int result = in.read(buf); + int result = localIn.read(localBuf); if (result > 0) { markpos = -1; pos = 0; @@ -182,22 +162,25 @@ public class BufferedInputStream extends FilterInputStream { } return result; } - if (markpos == 0 && marklimit > buf.length) { - /* Increase buffer size to accomodate the readlimit */ - int newLength = buf.length * 2; + if (markpos == 0 && marklimit > localBuf.length) { + /* Increase buffer size to accommodate the readlimit */ + int newLength = localBuf.length * 2; if (newLength > marklimit) { newLength = marklimit; } byte[] newbuf = new byte[newLength]; - System.arraycopy(buf, 0, newbuf, 0, buf.length); - buf = newbuf; + System.arraycopy(localBuf, 0, newbuf, 0, localBuf.length); + // Reassign buf, which will invalidate any local references + // FIXME: what if buf was null? + localBuf = buf = newbuf; } else if (markpos > 0) { - System.arraycopy(buf, markpos, buf, 0, buf.length - markpos); + System.arraycopy(localBuf, markpos, localBuf, 0, localBuf.length + - markpos); } /* Set the new position and mark position */ pos -= markpos; count = markpos = 0; - int bytesread = in.read(buf, pos, buf.length - pos); + int bytesread = localIn.read(localBuf, pos, localBuf.length - pos); count = bytesread <= 0 ? pos : pos + bytesread; return bytesread; } @@ -209,12 +192,11 @@ public class BufferedInputStream extends FilterInputStream { * position if {@code readlimit} has not been surpassed. The underlying * buffer may be increased in size to allow {@code readlimit} number of * bytes to be supported. - * + * * @param readlimit * the number of bytes that can be read before the mark is * invalidated. * @see #reset() - * @since Android 1.0 */ @Override public synchronized void mark(int readlimit) { @@ -225,11 +207,10 @@ public class BufferedInputStream extends FilterInputStream { /** * Indicates whether {@code BufferedInputStream} supports the {@code mark()} * and {@code reset()} methods. - * + * * @return {@code true} for BufferedInputStreams. * @see #mark(int) * @see #reset() - * @since Android 1.0 */ @Override public boolean markSupported() { @@ -241,28 +222,39 @@ public class BufferedInputStream extends FilterInputStream { * range from 0 to 255. Returns -1 if the end of the source string has been * reached. If the internal buffer does not contain any available bytes then * it is filled from the source stream and the first byte is returned. - * + * * @return the byte read or -1 if the end of the source stream has been * reached. * @throws IOException * if this stream is closed or another IOException occurs. - * @since Android 1.0 */ @Override public synchronized int read() throws IOException { - if (in == null) { + // Use local refs since buf and in may be invalidated by an + // unsynchronized close() + byte[] localBuf = buf; + InputStream localIn = in; + if (localBuf == null || localIn == null) { // K0059=Stream is closed throw new IOException(Msg.getString("K0059")); //$NON-NLS-1$ } /* Are there buffered bytes available? */ - if (pos >= count && fillbuf() == -1) { + if (pos >= count && fillbuf(localIn, localBuf) == -1) { return -1; /* no, fill buffer */ } + // localBuf may have been invalidated by fillbuf + if (localBuf != buf) { + localBuf = buf; + if (localBuf == null) { + // K0059=Stream is closed + throw new IOException(Msg.getString("K0059")); //$NON-NLS-1$ + } + } /* Did filling the buffer fail with -1 (EOF)? */ if (count - pos > 0) { - return buf[pos++] & 0xFF; + return localBuf[pos++] & 0xFF; } return -1; } @@ -275,7 +267,7 @@ public class BufferedInputStream extends FilterInputStream { * mark has not been set and the requested number of bytes is larger than * the receiver's buffer size, this implementation bypasses the buffer and * simply places the results directly into {@code buffer}. - * + * * @param buffer * the byte array in which to store the bytes read. * @param offset @@ -291,12 +283,14 @@ public class BufferedInputStream extends FilterInputStream { * @throws IOException * if the stream is already closed or another IOException * occurs. - * @since Android 1.0 */ @Override public synchronized int read(byte[] buffer, int offset, int length) throws IOException { - if (closed) { + // Use local ref since buf may be invalidated by an unsynchronized + // close() + byte[] localBuf = buf; + if (localBuf == null) { // K0059=Stream is closed throw new IOException(Msg.getString("K0059")); //$NON-NLS-1$ } @@ -316,7 +310,9 @@ public class BufferedInputStream extends FilterInputStream { if (length == 0) { return 0; } - if (null == buf) { + InputStream localIn = in; + if (localIn == null) { + // K0059=Stream is closed throw new IOException(Msg.getString("K0059")); //$NON-NLS-1$ } @@ -324,9 +320,9 @@ public class BufferedInputStream extends FilterInputStream { if (pos < count) { /* There are bytes available in the buffer. */ int copylength = count - pos >= length ? length : count - pos; - System.arraycopy(buf, pos, buffer, offset, copylength); + System.arraycopy(localBuf, pos, buffer, offset, copylength); pos += copylength; - if (copylength == length || in.available() == 0) { + if (copylength == length || localIn.available() == 0) { return copylength; } offset += copylength; @@ -341,24 +337,33 @@ public class BufferedInputStream extends FilterInputStream { * If we're not marked and the required size is greater than the * buffer, simply read the bytes directly bypassing the buffer. */ - if (markpos == -1 && required >= buf.length) { - read = in.read(buffer, offset, required); + if (markpos == -1 && required >= localBuf.length) { + read = localIn.read(buffer, offset, required); if (read == -1) { return required == length ? -1 : length - required; } } else { - if (fillbuf() == -1) { + if (fillbuf(localIn, localBuf) == -1) { return required == length ? -1 : length - required; } + // localBuf may have been invalidated by fillbuf + if (localBuf != buf) { + localBuf = buf; + if (localBuf == null) { + // K0059=Stream is closed + throw new IOException(Msg.getString("K0059")); //$NON-NLS-1$ + } + } + read = count - pos >= required ? required : count - pos; - System.arraycopy(buf, pos, buffer, offset, read); + System.arraycopy(localBuf, pos, buffer, offset, read); pos += read; } required -= read; if (required == 0) { return length; } - if (in.available() == 0) { + if (localIn.available() == 0) { return length - required; } offset += read; @@ -367,13 +372,12 @@ public class BufferedInputStream extends FilterInputStream { /** * Resets this stream to the last marked location. - * + * * @throws IOException * if this stream is closed, no mark has been set or the mark is * no longer valid because more than {@code readlimit} bytes * have been read since setting the mark. * @see #mark(int) - * @since Android 1.0 */ @Override public synchronized void reset() throws IOException { @@ -383,7 +387,7 @@ public class BufferedInputStream extends FilterInputStream { * so it is preferable to avoid loading up the whole big set of * messages just for these cases. */ - if (closed) { + if (buf == null) { throw new IOException("Stream is closed"); } if (-1 == markpos) { @@ -397,24 +401,31 @@ public class BufferedInputStream extends FilterInputStream { * Skips {@code amount} number of bytes in this stream. Subsequent * {@code read()}'s will not return these bytes unless {@code reset()} is * used. - * + * * @param amount * the number of bytes to skip. {@code skip} does nothing and * returns 0 if {@code amount} is less than zero. * @return the number of bytes actually skipped. * @throws IOException * if this stream is closed or another IOException occurs. - * @since Android 1.0 */ @Override public synchronized long skip(long amount) throws IOException { - if (null == in) { + // Use local refs since buf and in may be invalidated by an + // unsynchronized close() + byte[] localBuf = buf; + InputStream localIn = in; + if (localBuf == null) { // K0059=Stream is closed throw new IOException(Msg.getString("K0059")); //$NON-NLS-1$ } if (amount < 1) { return 0; } + if (localIn == null) { + // K0059=Stream is closed + throw new IOException(Msg.getString("K0059")); //$NON-NLS-1$ + } if (count - pos >= amount) { pos += amount; @@ -425,7 +436,7 @@ public class BufferedInputStream extends FilterInputStream { if (markpos != -1) { if (amount <= marklimit) { - if (fillbuf() == -1) { + if (fillbuf(localIn, localBuf) == -1) { return read; } if (count - pos >= amount - read) { @@ -437,9 +448,7 @@ public class BufferedInputStream extends FilterInputStream { pos = count; return read; } - markpos = -1; } - return read + in.skip(amount - read); + return read + localIn.skip(amount - read); } } - diff --git a/libcore/luni/src/main/java/java/io/BufferedOutputStream.java b/libcore/luni/src/main/java/java/io/BufferedOutputStream.java index 835d13f15..6d52deed8 100644 --- a/libcore/luni/src/main/java/java/io/BufferedOutputStream.java +++ b/libcore/luni/src/main/java/java/io/BufferedOutputStream.java @@ -30,40 +30,33 @@ import java.util.logging.Logger; * drawback is that some extra space is required to hold the buffer and that * copying takes place when flushing that buffer, but this is usually outweighed * by the performance benefits. - * + * *

          A typical application pattern for the class looks like this:

          - * + * *

            * BufferedOutputStream buf = new BufferedOutputStream(new FileOutputStream("file.java"));
            * 
          - * + * * @see BufferedInputStream - * - * @since Android 1.0 */ public class BufferedOutputStream extends FilterOutputStream { /** * The buffer containing the bytes to be written to the target stream. - * - * @since Android 1.0 */ protected byte[] buf; /** * The total number of bytes inside the byte array {@code buf}. - * - * @since Android 1.0 */ protected int count; /** * Constructs a new {@code BufferedOutputStream} on the {@link OutputStream} * {@code out}. The buffer size is set to the default value of 8 KB. - * + * * @param out * the {@code OutputStream} for which write operations are * buffered. - * @since Android 1.0 */ public BufferedOutputStream(OutputStream out) { super(out); @@ -87,14 +80,13 @@ public class BufferedOutputStream extends FilterOutputStream { /** * Constructs a new {@code BufferedOutputStream} on the {@link OutputStream} * {@code out}. The buffer size is set to {@code size}. - * + * * @param out * the output stream for which write operations are buffered. * @param size * the size of the buffer in bytes. * @throws IllegalArgumentException * if {@code size <= 0}. - * @since Android 1.0 */ public BufferedOutputStream(OutputStream out, int size) { super(out); @@ -108,17 +100,13 @@ public class BufferedOutputStream extends FilterOutputStream { /** * Flushes this stream to ensure all pending data is written out to the * target stream. In addition, the target stream is flushed. - * + * * @throws IOException * if an error occurs attempting to flush this stream. - * @since Android 1.0 */ @Override public synchronized void flush() throws IOException { - if (count > 0) { - out.write(buf, 0, count); - } - count = 0; + flushInternal(); out.flush(); } @@ -128,7 +116,7 @@ public class BufferedOutputStream extends FilterOutputStream { * bytes, they are copied in. If not, the buffered bytes plus the bytes in * {@code buffer} are written to the target stream, the target is flushed, * and the buffer is cleared. - * + * * @param buffer * the buffer to be written. * @param offset @@ -144,7 +132,8 @@ public class BufferedOutputStream extends FilterOutputStream { * if an error occurs attempting to write to this stream. * @throws NullPointerException * if {@code buffer} is {@code null}. - * @since Android 1.0 + * @throws ArrayIndexOutOfBoundsException + * If offset or count is outside of bounds. */ @Override public synchronized void write(byte[] buffer, int offset, int length) @@ -153,6 +142,13 @@ public class BufferedOutputStream extends FilterOutputStream { // K0047=buffer is null throw new NullPointerException(Msg.getString("K0047")); //$NON-NLS-1$ } + + if (length >= buf.length) { + flushInternal(); + out.write(buffer, offset, length); + return; + } + // BEGIN android-changed // Exception priorities (in case of multiple errors) differ from // RI, but are spec-compliant. @@ -163,32 +159,15 @@ public class BufferedOutputStream extends FilterOutputStream { throw new ArrayIndexOutOfBoundsException(Msg.getString("K002f")); //$NON-NLS-1$ } // END android-changed - if (count == 0 && length >= buf.length) { - out.write(buffer, offset, length); - return; - } - int available = buf.length - count; - if (length < available) { - available = length; - } - if (available > 0) { - System.arraycopy(buffer, offset, buf, count, available); - count += available; - } - if (count == buf.length) { - out.write(buf, 0, buf.length); - count = 0; - if (length > available) { - offset += available; - available = length - available; - if (available >= buf.length) { - out.write(buffer, offset, available); - } else { - System.arraycopy(buffer, offset, buf, count, available); - count += available; - } - } + + // flush the internal buffer first if we have not enough space left + if (length >= (buf.length - count)) { + flushInternal(); } + + // the length is always less than (buf.length - count) here so arraycopy is safe + System.arraycopy(buffer, offset, buf, count, length); + count += length; } /** @@ -197,12 +176,11 @@ public class BufferedOutputStream extends FilterOutputStream { * copied into the buffer and the count incremented. Otherwise, the buffer * plus {@code oneByte} are written to the target stream, the target is * flushed, and the buffer is reset. - * + * * @param oneByte * the byte to be written. * @throws IOException * if an error occurs attempting to write to this stream. - * @since Android 1.0 */ @Override public synchronized void write(int oneByte) throws IOException { @@ -212,4 +190,14 @@ public class BufferedOutputStream extends FilterOutputStream { } buf[count++] = (byte) oneByte; } + + /** + * Flushes only internal buffer. + */ + private void flushInternal() throws IOException { + if (count > 0) { + out.write(buf, 0, count); + } + count = 0; + } } diff --git a/libcore/luni/src/main/java/java/io/BufferedReader.java b/libcore/luni/src/main/java/java/io/BufferedReader.java index e82e53855..77f19757e 100644 --- a/libcore/luni/src/main/java/java/io/BufferedReader.java +++ b/libcore/luni/src/main/java/java/io/BufferedReader.java @@ -32,14 +32,13 @@ import java.util.logging.Logger; * benefits. * *

          A typical application pattern for the class looks like this:

          - * + * *

            * BufferedReader buf = new BufferedReader(new FileReader("file.java"));
            * 
          * * @see BufferedWriter - * - * @since Android 1.0 + * @since 1.1 */ public class BufferedReader extends Reader { @@ -61,7 +60,6 @@ public class BufferedReader extends Reader { * * @param in * the Reader that is buffered. - * @since Android 1.0 */ public BufferedReader(Reader in) { super(in); @@ -94,7 +92,6 @@ public class BufferedReader extends Reader { * the size of the buffer to allocate. * @throws IllegalArgumentException * if {@code size <= 0}. - * @since Android 1.0 */ public BufferedReader(Reader in, int size) { super(in); @@ -112,7 +109,6 @@ public class BufferedReader extends Reader { * * @throws IOException * if an error occurs while closing this reader. - * @since Android 1.0 */ @Override public void close() throws IOException { @@ -181,7 +177,6 @@ public class BufferedReader extends Reader { * if an error occurs while setting a mark in this reader. * @see #markSupported() * @see #reset() - * @since Android 1.0 */ @Override public void mark(int readlimit) throws IOException { @@ -204,7 +199,6 @@ public class BufferedReader extends Reader { * @return {@code true} for {@code BufferedReader}. * @see #mark(int) * @see #reset() - * @since Android 1.0 */ @Override public boolean markSupported() { @@ -217,12 +211,11 @@ public class BufferedReader extends Reader { * character from the buffer. If there are no characters available in the * buffer, it fills the buffer and then returns a character. It returns -1 * if there are no more characters in the source reader. - * + * * @return the character read or -1 if the end of the source reader has been * reached. * @throws IOException * if this reader is closed or some other I/O error occurs. - * @since Android 1.0 */ @Override public int read() throws IOException { @@ -234,6 +227,7 @@ public class BufferedReader extends Reader { if (pos < count || fillbuf() != -1) { return buf[pos++]; } + markpos = -1; return -1; } } @@ -263,7 +257,6 @@ public class BufferedReader extends Reader { * {@code buffer}. * @throws IOException * if this reader is closed or some other I/O error occurs. - * @since Android 1.0 */ @Override public int read(char[] buffer, int offset, int length) throws IOException { @@ -342,7 +335,6 @@ public class BufferedReader extends Reader { * read before the end of the reader has been reached. * @throws IOException * if this reader is closed or some other I/O error occurs. - * @since Android 1.0 */ public String readLine() throws IOException { synchronized (lock) { @@ -355,6 +347,9 @@ public class BufferedReader extends Reader { } for (int charPos = pos; charPos < count; charPos++) { char ch = buf[charPos]; + // BEGIN android-note + // a switch statement may be more efficient + // END android-note if (ch > '\r') { continue; } @@ -393,6 +388,9 @@ public class BufferedReader extends Reader { } } for (int charPos = pos; charPos < count; charPos++) { + // BEGIN android-note + // use a local variable for buf[charPos] and a switch statement + // END android-note if (eol == '\0') { if ((buf[charPos] == '\n' || buf[charPos] == '\r')) { eol = buf[charPos]; @@ -403,7 +401,7 @@ public class BufferedReader extends Reader { } pos = charPos + 1; return result.toString(); - } else if (eol != '\0') { + } else { if (charPos > pos) { result.append(buf, pos, charPos - pos - 1); } @@ -424,7 +422,7 @@ public class BufferedReader extends Reader { /** * Indicates whether this reader is ready to be read without blocking. - * + * * @return {@code true} if this reader will not block when {@code read} is * called, {@code false} if unknown or blocking will occur. * @throws IOException @@ -432,7 +430,6 @@ public class BufferedReader extends Reader { * @see #read() * @see #read(char[], int, int) * @see #readLine() - * @since Android 1.0 */ @Override public boolean ready() throws IOException { @@ -453,7 +450,6 @@ public class BufferedReader extends Reader { * if this reader is closed or no mark has been set. * @see #mark(int) * @see #markSupported() - * @since Android 1.0 */ @Override public void reset() throws IOException { @@ -484,7 +480,6 @@ public class BufferedReader extends Reader { * @see #mark(int) * @see #markSupported() * @see #reset() - * @since Android 1.0 */ @Override public long skip(long amount) throws IOException { diff --git a/libcore/luni/src/main/java/java/io/BufferedWriter.java b/libcore/luni/src/main/java/java/io/BufferedWriter.java index 761b9c5a4..86949fa7b 100644 --- a/libcore/luni/src/main/java/java/io/BufferedWriter.java +++ b/libcore/luni/src/main/java/java/io/BufferedWriter.java @@ -33,16 +33,14 @@ import java.util.logging.Logger; * some extra space is required to hold the buffer and that copying takes place * when filling that buffer, but this is usually outweighed by the performance * benefits. - * + * *

          A typical application pattern for the class looks like this:

          - * + * *

            * BufferedWriter buf = new BufferedWriter(new FileWriter("file.java"));
            * 
          - * + * * @see BufferedReader - * - * @since Android 1.0 */ public class BufferedWriter extends Writer { @@ -59,10 +57,9 @@ public class BufferedWriter extends Writer { * Constructs a new {@code BufferedWriter} with {@code out} as the writer * for which to buffer write operations. The buffer size is set to the * default value of 8 KB. - * + * * @param out * the writer for which character writing is buffered. - * @since Android 1.0 */ public BufferedWriter(Writer out) { super(out); @@ -88,14 +85,13 @@ public class BufferedWriter extends Writer { * Constructs a new {@code BufferedWriter} with {@code out} as the writer * for which to buffer write operations. The buffer size is set to {@code * size}. - * + * * @param out * the writer for which character writing is buffered. * @param size * the size of the buffer in bytes. * @throws IllegalArgumentException * if {@code size <= 0}. - * @since Android 1.0 */ public BufferedWriter(Writer out, int size) { super(out); @@ -110,16 +106,15 @@ public class BufferedWriter extends Writer { * Closes this writer. The contents of the buffer are flushed, the target * writer is closed, and the buffer is released. Only the first invocation * of close has any effect. - * + * * @throws IOException * if an error occurs while closing this writer. - * @since Android 1.0 */ @Override public void close() throws IOException { synchronized (lock) { if (!isClosed()) { - flush(); + flushInternal(); out.close(); buf = null; out = null; @@ -130,10 +125,9 @@ public class BufferedWriter extends Writer { /** * Flushes this writer. The contents of the buffer are committed to the * target writer and it is then flushed. - * + * * @throws IOException * if an error occurs while flushing this writer. - * @since Android 1.0 */ @Override public void flush() throws IOException { @@ -141,17 +135,24 @@ public class BufferedWriter extends Writer { if (isClosed()) { throw new IOException(Msg.getString("K005d")); //$NON-NLS-1$ } - if (pos > 0) { - out.write(buf, 0, pos); - } - pos = 0; + flushInternal(); out.flush(); } } + /** + * Flushes the internal buffer. + */ + private void flushInternal() throws IOException { + if (pos > 0) { + out.write(buf, 0, pos); + } + pos = 0; + } + /** * Indicates whether this writer is closed. - * + * * @return {@code true} if this writer is closed, {@code false} otherwise. */ private boolean isClosed() { @@ -162,10 +163,9 @@ public class BufferedWriter extends Writer { * Writes a newline to this writer. A newline is determined by the System * property "line.separator". The target writer may or may not be flushed * when a newline is written. - * + * * @throws IOException * if an error occurs attempting to write to this writer. - * @since Android 1.0 */ public void newLine() throws IOException { write(lineSeparator, 0, lineSeparator.length()); @@ -176,7 +176,7 @@ public class BufferedWriter extends Writer { * {@code cbuf} to this writer. If {@code count} is greater than this * writer's buffer, then the buffer is flushed and the characters are * written directly to the target writer. - * + * * @param cbuf * the array containing characters to write. * @param offset @@ -189,7 +189,6 @@ public class BufferedWriter extends Writer { * {@code cbuf}. * @throws IOException * if this writer is closed or another I/O error occurs. - * @since Android 1.0 */ @Override public void write(char[] cbuf, int offset, int count) throws IOException { @@ -243,12 +242,11 @@ public class BufferedWriter extends Writer { * Writes the character {@code oneChar} to this writer. If the buffer * gets full by writing this character, this writer is flushed. Only the * lower two bytes of the integer {@code oneChar} are written. - * + * * @param oneChar * the character to write. * @throws IOException * if this writer is closed or another I/O error occurs. - * @since Android 1.0 */ @Override public void write(int oneChar) throws IOException { @@ -270,7 +268,7 @@ public class BufferedWriter extends Writer { * then this writer is flushed and the remaining characters are written * directly to the target writer. If count is negative no characters are * written to the buffer. This differs from the behavior of the superclass. - * + * * @param str * the non-null String containing characters to write. * @param offset @@ -283,7 +281,6 @@ public class BufferedWriter extends Writer { * @throws IndexOutOfBoundsException * if {@code offset < 0} or {@code offset + count} is greater * than the length of {@code str}. - * @since Android 1.0 */ @Override public void write(String str, int offset, int count) throws IOException { diff --git a/libcore/luni/src/main/java/java/io/ByteArrayInputStream.java b/libcore/luni/src/main/java/java/io/ByteArrayInputStream.java index 1564ca58c..f0b721985 100644 --- a/libcore/luni/src/main/java/java/io/ByteArrayInputStream.java +++ b/libcore/luni/src/main/java/java/io/ByteArrayInputStream.java @@ -23,48 +23,38 @@ import org.apache.harmony.luni.util.Msg; /** * A specialized {@link InputStream } for reading the contents of a byte array. - * + * * @see ByteArrayOutputStream - * - * @since Android 1.0 */ public class ByteArrayInputStream extends InputStream { /** * The {@code byte} array containing the bytes to stream over. - * - * @since Android 1.0 */ protected byte[] buf; /** * The current position within the byte array. - * - * @since Android 1.0 */ protected int pos; /** - * The current mark position. - * - * @since Android 1.0 + * The current mark position. Initially set to 0 or the offset + * parameter within the constructor. */ protected int mark; /** * The total number of bytes initially available in the byte array * {@code buf}. - * - * @since Android 1.0 */ protected int count; /** * Constructs a new {@code ByteArrayInputStream} on the byte array * {@code buf}. - * + * * @param buf * the byte array to stream over. - * @since Android 1.0 */ public ByteArrayInputStream(byte buf[]) { this.mark = 0; @@ -76,14 +66,13 @@ public class ByteArrayInputStream extends InputStream { * Constructs a new {@code ByteArrayInputStream} on the byte array * {@code buf} with the initial position set to {@code offset} and the * number of bytes available set to {@code offset} + {@code length}. - * + * * @param buf * the byte array to stream over. * @param offset * the initial position in {@code buf} to start streaming from. * @param length * the number of bytes available for streaming. - * @since Android 1.0 */ public ByteArrayInputStream(byte[] buf, int offset, int length) { // BEGIN android-note @@ -99,9 +88,8 @@ public class ByteArrayInputStream extends InputStream { * Returns the number of bytes that are available before this stream will * block. This method returns the number of bytes yet to be read from the * source byte array. - * + * * @return the number of bytes available before blocking. - * @since Android 1.0 */ @Override public synchronized int available() { @@ -110,10 +98,9 @@ public class ByteArrayInputStream extends InputStream { /** * Closes this stream and frees resources associated with this stream. - * + * * @throws IOException * if an I/O error occurs while closing this stream. - * @since Android 1.0 */ @Override public void close() throws IOException { @@ -124,12 +111,11 @@ public class ByteArrayInputStream extends InputStream { * Sets a mark position in this ByteArrayInputStream. The parameter * {@code readlimit} is ignored. Sending {@code reset()} will reposition the * stream back to the marked position. - * + * * @param readlimit * ignored. * @see #markSupported() * @see #reset() - * @since Android 1.0 */ @Override public synchronized void mark(int readlimit) { @@ -140,11 +126,10 @@ public class ByteArrayInputStream extends InputStream { * Indicates whether this stream supports the {@code mark()} and * {@code reset()} methods. Returns {@code true} since this class supports * these methods. - * + * * @return always {@code true}. * @see #mark(int) * @see #reset() - * @since Android 1.0 */ @Override public boolean markSupported() { @@ -155,9 +140,8 @@ public class ByteArrayInputStream extends InputStream { * Reads a single byte from the source byte array and returns it as an * integer in the range from 0 to 255. Returns -1 if the end of the source * array has been reached. - * + * * @return the byte read or -1 if the end of this stream has been reached. - * @since Android 1.0 */ @Override public synchronized int read() { @@ -168,7 +152,7 @@ public class ByteArrayInputStream extends InputStream { * Reads at most {@code len} bytes from this stream and stores * them in byte array {@code b} starting at {@code offset}. This * implementation reads bytes from the source byte array. - * + * * @param b * the byte array in which to store the bytes read. * @param offset @@ -183,8 +167,7 @@ public class ByteArrayInputStream extends InputStream { * {@code offset + length} is greater than the size of * {@code b}. * @throws NullPointerException - * if {@code b} is null. - * @since Android 1.0 + * if {@code b} is {@code null}. */ @Override public synchronized int read(byte[] b, int offset, int length) { @@ -222,9 +205,8 @@ public class ByteArrayInputStream extends InputStream { * Resets this stream to the last marked location. This implementation * resets the position to either the marked position, the start position * supplied in the constructor or 0 if neither has been provided. - * + * * @see #mark(int) - * @since Android 1.0 */ @Override public synchronized void reset() { @@ -236,11 +218,10 @@ public class ByteArrayInputStream extends InputStream { * {@code read()}s will not return these bytes unless {@code reset()} is * used. This implementation skips {@code count} number of bytes in the * target stream. It does nothing and returns 0 if {@code n} is negative. - * + * * @param n * the number of bytes to skip. * @return the number of bytes actually skipped. - * @since Android 1.0 */ @Override public synchronized long skip(long n) { diff --git a/libcore/luni/src/main/java/java/io/ByteArrayOutputStream.java b/libcore/luni/src/main/java/java/io/ByteArrayOutputStream.java index 8c6fa0557..4d5a738b9 100644 --- a/libcore/luni/src/main/java/java/io/ByteArrayOutputStream.java +++ b/libcore/luni/src/main/java/java/io/ByteArrayOutputStream.java @@ -24,23 +24,17 @@ import org.apache.harmony.luni.util.Msg; * (internal) byte array. As bytes are written to this stream, the byte array * may be expanded to hold more bytes. When the writing is considered to be * finished, a copy of the byte array can be requested from the class. - * + * * @see ByteArrayInputStream - * - * @since Android 1.0 */ public class ByteArrayOutputStream extends OutputStream { /** * The byte array containing the bytes written. - * - * @since Android 1.0 */ protected byte[] buf; /** * The number of bytes written. - * - * @since Android 1.0 */ protected int count; @@ -48,8 +42,6 @@ public class ByteArrayOutputStream extends OutputStream { * Constructs a new ByteArrayOutputStream with a default size of 32 bytes. * If more than 32 bytes are written to this instance, the underlying byte * array will expand. - * - * @since Android 1.0 */ public ByteArrayOutputStream() { super(); @@ -60,13 +52,12 @@ public class ByteArrayOutputStream extends OutputStream { * Constructs a new {@code ByteArrayOutputStream} with a default size of * {@code size} bytes. If more than {@code size} bytes are written to this * instance, the underlying byte array will expand. - * + * * @param size * initial size for the underlying byte array, must be * non-negative. * @throws IllegalArgumentException * if {@code size} < 0. - * @since Android 1.0 */ public ByteArrayOutputStream(int size) { super(); @@ -79,10 +70,9 @@ public class ByteArrayOutputStream extends OutputStream { /** * Closes this stream. This releases system resources used for this stream. - * + * * @throws IOException * if an error occurs while attempting to close this stream. - * @since Android 1.0 */ @Override public void close() throws IOException { @@ -109,8 +99,6 @@ public class ByteArrayOutputStream extends OutputStream { * Resets this stream to the beginning of the underlying byte array. All * subsequent writes will overwrite any bytes previously stored in this * stream. - * - * @since Android 1.0 */ public synchronized void reset() { count = 0; @@ -118,9 +106,8 @@ public class ByteArrayOutputStream extends OutputStream { /** * Returns the total number of bytes written to this stream so far. - * + * * @return the number of bytes written to this stream. - * @since Android 1.0 */ public int size() { return count; @@ -130,9 +117,8 @@ public class ByteArrayOutputStream extends OutputStream { * Returns the contents of this ByteArrayOutputStream as a byte array. Any * changes made to the receiver after returning will not be reflected in the * byte array returned to the caller. - * + * * @return this stream's current contents as a byte array. - * @since Android 1.0 */ public synchronized byte[] toByteArray() { byte[] newArray = new byte[count]; @@ -144,9 +130,8 @@ public class ByteArrayOutputStream extends OutputStream { * Returns the contents of this ByteArrayOutputStream as a string. Any * changes made to the receiver after returning will not be reflected in the * string returned to the caller. - * + * * @return this stream's current contents as a string. - * @since Android 1.0 */ @Override @@ -161,13 +146,12 @@ public class ByteArrayOutputStream extends OutputStream { * {@code c == (char)(((hibyte & 0xff) << 8) | (b & 0xff))}. This method is * deprecated and either {@link #toString()} or {@link #toString(String)} * should be used. - * + * * @param hibyte * the high byte of each resulting Unicode character. * @return this stream's current contents as a string with the high byte set * to {@code hibyte}. * @deprecated Use {@link #toString()}. - * @since Android 1.0 */ @Deprecated public String toString(int hibyte) { @@ -181,14 +165,13 @@ public class ByteArrayOutputStream extends OutputStream { /** * Returns the contents of this ByteArrayOutputStream as a string converted * according to the encoding declared in {@code enc}. - * + * * @param enc * a string representing the encoding to use when translating * this stream to a string. * @return this stream's current contents as an encoded string. * @throws UnsupportedEncodingException * if the provided encoding is not supported. - * @since Android 1.0 */ public String toString(String enc) throws UnsupportedEncodingException { return new String(buf, 0, count, enc); @@ -197,18 +180,19 @@ public class ByteArrayOutputStream extends OutputStream { /** * Writes {@code count} bytes from the byte array {@code buffer} starting at * offset {@code index} to this stream. - * + * * @param buffer * the buffer to be written. * @param offset * the initial position in {@code buffer} to retrieve bytes. * @param len * the number of bytes of {@code buffer} to write. + * @throws NullPointerException + * if {@code buffer} is {@code null}. * @throws IndexOutOfBoundsException * if {@code offset < 0} or {@code len < 0}, or if * {@code offset + len} is greater than the length of * {@code buffer}. - * @since Android 1.0 */ @Override public synchronized void write(byte[] buffer, int offset, int len) { @@ -239,28 +223,26 @@ public class ByteArrayOutputStream extends OutputStream { /** * Writes the specified byte {@code oneByte} to the OutputStream. Only the * low order byte of {@code oneByte} is written. - * + * * @param oneByte * the byte to be written. - * @since Android 1.0 */ @Override public synchronized void write(int oneByte) { if (count == buf.length) { expand(1); } - buf[count++] = (byte)oneByte; + buf[count++] = (byte) oneByte; } /** * Takes the contents of this stream and writes it to the output stream * {@code out}. - * + * * @param out * an OutputStream on which to write the contents of this stream. * @throws IOException * if an error occurs while writing to {@code out}. - * @since Android 1.0 */ public synchronized void writeTo(OutputStream out) throws IOException { out.write(buf, 0, count); diff --git a/libcore/luni/src/main/java/java/io/CharArrayReader.java b/libcore/luni/src/main/java/java/io/CharArrayReader.java index 95a8826a5..d2757f6f9 100644 --- a/libcore/luni/src/main/java/java/io/CharArrayReader.java +++ b/libcore/luni/src/main/java/java/io/CharArrayReader.java @@ -21,37 +21,27 @@ import org.apache.harmony.luni.util.Msg; /** * A specialized {@link Reader} for reading the contents of a char array. - * + * * @see CharArrayWriter - * - * @since Android 1.0 */ public class CharArrayReader extends Reader { /** * The buffer for characters. - * - * @since Android 1.0 */ protected char buf[]; /** * The current buffer position. - * - * @since Android 1.0 */ protected int pos; /** * The current mark position. - * - * @since Android 1.0 */ protected int markedPos = -1; /** * The ending index of the buffer. - * - * @since Android 1.0 */ protected int count; @@ -59,10 +49,9 @@ public class CharArrayReader extends Reader { * Constructs a CharArrayReader on the char array {@code buf}. The size of * the reader is set to the length of the buffer and the object to to read * from is set to {@code buf}. - * + * * @param buf * the char array from which to read. - * @since Android 1.0 */ public CharArrayReader(char[] buf) { super(buf); @@ -74,7 +63,7 @@ public class CharArrayReader extends Reader { * Constructs a CharArrayReader on the char array {@code buf}. The size of * the reader is set to {@code length} and the start position from which to * read the buffer is set to {@code offset}. - * + * * @param buf * the char array from which to read. * @param offset @@ -84,7 +73,6 @@ public class CharArrayReader extends Reader { * @throws IllegalArgumentException * if {@code offset < 0} or {@code length < 0}, or if * {@code offset} is greater than the size of {@code buf} . - * @since Android 1.0 */ public CharArrayReader(char[] buf, int offset, int length) { super(buf); @@ -92,7 +80,7 @@ public class CharArrayReader extends Reader { // Exception priorities (in case of multiple errors) differ from // RI, but are spec-compliant. // made implicit null check explicit, - // removed redundant check, used (offset | length) < 0 instead of + // removed redundant check, used (offset | length) < 0 instead of // (offset < 0) || (length < 0) to safe one operation if (buf == null) { throw new NullPointerException(Msg.getString("K0047")); //$NON-NLS-1$ @@ -103,6 +91,7 @@ public class CharArrayReader extends Reader { // END android-changed this.buf = buf; this.pos = offset; + this.markedPos = offset; /* This is according to spec */ this.count = this.pos + length < buf.length ? length : buf.length; @@ -112,8 +101,6 @@ public class CharArrayReader extends Reader { * This method closes this CharArrayReader. Once it is closed, you can no * longer read from it. Only the first invocation of this method has any * effect. - * - * @since Android 1.0 */ @Override public void close() { @@ -126,7 +113,7 @@ public class CharArrayReader extends Reader { /** * Indicates whether this reader is open. - * + * * @return {@code true} if the reader is open, {@code false} otherwise. */ private boolean isOpen() { @@ -135,7 +122,7 @@ public class CharArrayReader extends Reader { /** * Indicates whether this reader is closed. - * + * * @return {@code true} if the reader is closed, {@code false} otherwise. */ private boolean isClosed() { @@ -147,12 +134,11 @@ public class CharArrayReader extends Reader { * ignored for CharArrayReaders. Calling {@code reset()} will reposition the * reader back to the marked position provided the mark has not been * invalidated. - * + * * @param readLimit * ignored for CharArrayReaders. * @throws IOException * if this reader is closed. - * @since Android 1.0 */ @Override public void mark(int readLimit) throws IOException { @@ -167,11 +153,10 @@ public class CharArrayReader extends Reader { /** * Indicates whether this reader supports the {@code mark()} and * {@code reset()} methods. - * + * * @return {@code true} for CharArrayReader. * @see #mark(int) * @see #reset() - * @since Android 1.0 */ @Override public boolean markSupported() { @@ -182,12 +167,11 @@ public class CharArrayReader extends Reader { * Reads a single character from this reader and returns it as an integer * with the two higher-order bytes set to 0. Returns -1 if no more * characters are available from this reader. - * + * * @return the character read as an int or -1 if the end of the reader has * been reached. * @throws IOException * if this reader is closed. - * @since Android 1.0 */ @Override public int read() throws IOException { @@ -207,7 +191,7 @@ public class CharArrayReader extends Reader { * stores them at {@code offset} in the character array {@code buf}. * Returns the number of characters actually read or -1 if the end of reader * was encountered. - * + * * @param buffer * the character array to store the characters read. * @param offset @@ -223,7 +207,6 @@ public class CharArrayReader extends Reader { * {@code buffer}. * @throws IOException * if this reader is closed. - * @since Android 1.0 */ @Override public int read(char[] buffer, int offset, int len) throws IOException { @@ -264,12 +247,11 @@ public class CharArrayReader extends Reader { * {@code false} if this reader may or may not block when {@code read} is * called. The implementation in CharArrayReader always returns {@code true} * even when it has been closed. - * + * * @return {@code true} if this reader will not block when {@code read} is * called, {@code false} if unknown or blocking will occur. * @throws IOException * if this reader is closed. - * @since Android 1.0 */ @Override public boolean ready() throws IOException { @@ -286,10 +268,9 @@ public class CharArrayReader extends Reader { * Invocations of {@code read()} and {@code skip()} will occur from this new * location. If this reader has not been marked, it is reset to the * beginning of the string. - * + * * @throws IOException * if this reader is closed. - * @since Android 1.0 */ @Override public void reset() throws IOException { @@ -305,13 +286,12 @@ public class CharArrayReader extends Reader { * Skips {@code count} number of characters in this reader. Subsequent * {@code read()}s will not return these characters unless {@code reset()} * is used. This method does nothing and returns 0 if {@code n} is negative. - * + * * @param n * the number of characters to skip. * @return the number of characters actually skipped. * @throws IOException * if this reader is closed. - * @since Android 1.0 */ @Override public long skip(long n) throws IOException { diff --git a/libcore/luni/src/main/java/java/io/CharArrayWriter.java b/libcore/luni/src/main/java/java/io/CharArrayWriter.java index 7e58b2b93..6b3fa393e 100644 --- a/libcore/luni/src/main/java/java/io/CharArrayWriter.java +++ b/libcore/luni/src/main/java/java/io/CharArrayWriter.java @@ -24,33 +24,25 @@ import org.apache.harmony.luni.util.Msg; * char array. As bytes are written to this writer, the char array may be * expanded to hold more characters. When the writing is considered to be * finished, a copy of the char array can be requested from the class. - * + * * @see CharArrayReader - * - * @since Android 1.0 */ public class CharArrayWriter extends Writer { /** * The buffer for characters. - * - * @since Android 1.0 */ protected char[] buf; /** * The ending index of the buffer. - * - * @since Android 1.0 */ protected int count; /** - * Constructs a new CharArrayWriter which has a buffer allocated with the - * default size of 32 characters. This buffer is also used as the + * Constructs a new {@code CharArrayWriter} which has a buffer allocated + * with the default size of 32 characters. This buffer is also used as the * {@code lock} to synchronize access to this writer. - * - * @since Android 1.0 */ public CharArrayWriter() { super(); @@ -59,15 +51,14 @@ public class CharArrayWriter extends Writer { } /** - * Constructs a new CharArrayWriter which has a buffer allocated with the - * size of {@code initialSize} characters. The buffer is also used as the - * {@code lock} to synchronize access to this writer. - * + * Constructs a new {@code CharArrayWriter} which has a buffer allocated + * with the size of {@code initialSize} characters. The buffer is also used + * as the {@code lock} to synchronize access to this writer. + * * @param initialSize * the initial size of this CharArrayWriters buffer. * @throws IllegalArgumentException * if {@code initialSize < 0}. - * @since Android 1.0 */ public CharArrayWriter(int initialSize) { super(); @@ -79,9 +70,7 @@ public class CharArrayWriter extends Writer { } /** - * Closes this writer. The implementation in CharArrayWriter does nothing. - * - * @since Android 1.0 + * Closes this writer. The implementation in {@code CharArrayWriter} does nothing. */ @Override public void close() { @@ -94,15 +83,14 @@ public class CharArrayWriter extends Writer { return; } - char[] newbuf = new char[buf.length + (2 * i)]; + int newLen = Math.max(2 * buf.length, count + i); + char[] newbuf = new char[newLen]; System.arraycopy(buf, 0, newbuf, 0, count); buf = newbuf; } /** - * Flushes this writer. The implementation in CharArrayWriter does nothing. - * - * @since Android 1.0 + * Flushes this writer. The implementation in {@code CharArrayWriter} does nothing. */ @Override public void flush() { @@ -113,8 +101,6 @@ public class CharArrayWriter extends Writer { * Resets this writer. The current write position is reset to the beginning * of the buffer. All written characters are lost and the size of this * writer is set to 0. - * - * @since Android 1.0 */ public void reset() { synchronized (lock) { @@ -126,9 +112,8 @@ public class CharArrayWriter extends Writer { * Returns the size of this writer, that is the number of characters it * stores. This number changes if this writer is reset or when more * characters are written to it. - * + * * @return this CharArrayWriter's current size in characters. - * @since Android 1.0 */ public int size() { synchronized (lock) { @@ -140,9 +125,8 @@ public class CharArrayWriter extends Writer { * Returns the contents of the receiver as a char array. The array returned * is a copy and any modifications made to this writer after calling this * method are not reflected in the result. - * + * * @return this CharArrayWriter's contents as a new char array. - * @since Android 1.0 */ public char[] toCharArray() { synchronized (lock) { @@ -153,12 +137,11 @@ public class CharArrayWriter extends Writer { } /** - * Returns the contents of this CharArrayWriter as a string. The string - * returned is a copy and any modifications made to this writer after + * Returns the contents of this {@code CharArrayWriter} as a string. The + * string returned is a copy and any modifications made to this writer after * calling this method are not reflected in the result. - * + * * @return this CharArrayWriters contents as a new string. - * @since Android 1.0 */ @Override public String toString() { @@ -170,7 +153,7 @@ public class CharArrayWriter extends Writer { /** * Writes {@code count} characters starting at {@code offset} in {@code c} * to this writer. - * + * * @param c * the non-null array containing characters to write. * @param offset @@ -180,7 +163,6 @@ public class CharArrayWriter extends Writer { * @throws IndexOutOfBoundsException * if {@code offset < 0} or {@code len < 0}, or if * {@code offset + len} is bigger than the size of {@code c}. - * @since Android 1.0 */ @Override public void write(char[] c, int offset, int len) { @@ -210,10 +192,9 @@ public class CharArrayWriter extends Writer { * Writes the specified character {@code oneChar} to this writer. * This implementation writes the two low order bytes of the integer * {@code oneChar} to the buffer. - * + * * @param oneChar * the character to write. - * @since Android 1.0 */ @Override public void write(int oneChar) { @@ -226,7 +207,7 @@ public class CharArrayWriter extends Writer { /** * Writes {@code count} number of characters starting at {@code offset} from * the string {@code str} to this CharArrayWriter. - * + * * @param str * the non-null string containing the characters to write. * @param offset @@ -234,12 +215,11 @@ public class CharArrayWriter extends Writer { * @param len * the number of characters to retrieve and write. * @throws NullPointerException - * if {@code str} is null. + * if {@code str} is {@code null}. * @throws StringIndexOutOfBoundsException * if {@code offset < 0} or {@code len < 0}, or if * {@code offset + len} is bigger than the length of * {@code str}. - * @since Android 1.0 */ @Override public void write(String str, int offset, int len) { @@ -264,17 +244,16 @@ public class CharArrayWriter extends Writer { } /** - * Writes the contents of this CharArrayWriter to another Writer. The output - * is all the characters that have been written to the receiver since the - * last reset or since it was created. - * + * Writes the contents of this {@code CharArrayWriter} to another {@code + * Writer}. The output is all the characters that have been written to the + * receiver since the last reset or since it was created. + * * @param out - * the non-null Writer on which to write the contents. + * the non-null {@code Writer} on which to write the contents. * @throws NullPointerException - * if {@code out} is null. + * if {@code out} is {@code null}. * @throws IOException * if an error occurs attempting to write out the contents. - * @since Android 1.0 */ public void writeTo(Writer out) throws IOException { synchronized (lock) { @@ -283,13 +262,12 @@ public class CharArrayWriter extends Writer { } /** - * Appends a char {@code c} to the CharArrayWriter. The method works the - * same way as {@code write(c)}. - * + * Appends a char {@code c} to the {@code CharArrayWriter}. The method works + * the same way as {@code write(c)}. + * * @param c * the character appended to the CharArrayWriter. * @return this CharArrayWriter. - * @since Android 1.0 */ @Override public CharArrayWriter append(char c) { @@ -298,14 +276,14 @@ public class CharArrayWriter extends Writer { } /** - * Appends a CharSequence {@code csq} to the CharArrayWriter. The method + * Appends a {@code CharSequence} to the {@code CharArrayWriter}. The method * works the same way as {@code write(csq.toString())}. If {@code csq} is - * null, then it will be substituted with the string "null". - * + * {@code null}, then it will be substituted with the string {@code "null"}. + * * @param csq - * the CharSequence appended to the CharArrayWriter, may be null. + * the {@code CharSequence} appended to the {@code + * CharArrayWriter}, may be {@code null}. * @return this CharArrayWriter. - * @since Android 1.0 */ @Override public CharArrayWriter append(CharSequence csq) { @@ -318,26 +296,27 @@ public class CharArrayWriter extends Writer { } /** - * Append a subsequence of a CharSequence {@code csq} to the - * CharArrayWriter. The first and last characters of the subsequence are - * specified by the parameters {@code start} and {@code end}. The - * CharArrayWriter.append({@code csq}) works the same way as - * {@code CharArrayWriter.write(csq.subSequence(start, end).toString)}. If - * {@code csq} is null, then it will be substituted with the string "null". - * + * Append a subsequence of a {@code CharSequence} to the {@code + * CharArrayWriter}. The first and last characters of the subsequence are + * specified by the parameters {@code start} and {@code end}. A call to + * {@code CharArrayWriter.append(csq)} works the same way as {@code + * CharArrayWriter.write(csq.subSequence(start, end).toString)}. If {@code + * csq} is {@code null}, then it will be substituted with the string {@code + * "null"}. + * * @param csq - * the CharSequence appended to the CharArrayWriter, may be null. + * the {@code CharSequence} appended to the {@code + * CharArrayWriter}, may be {@code null}. * @param start - * the index of the first character in the CharSequence appended - * to the CharArrayWriter. + * the index of the first character in the {@code CharSequence} + * appended to the {@code CharArrayWriter}. * @param end - * the index of the character after the last one in the - * CharSequence appended to the CharArrayWriter. + * the index of the character after the last one in the {@code + * CharSequence} appended to the {@code CharArrayWriter}. * @return this CharArrayWriter. * @throws IndexOutOfBoundsException * if {@code start < 0}, {@code end < 0}, {@code start > end}, * or if {@code end} is greater than the length of {@code csq}. - * @since Android 1.0 */ @Override public CharArrayWriter append(CharSequence csq, int start, int end) { diff --git a/libcore/luni/src/main/java/java/io/CharConversionException.java b/libcore/luni/src/main/java/java/io/CharConversionException.java index 2247527d6..97dc18632 100644 --- a/libcore/luni/src/main/java/java/io/CharConversionException.java +++ b/libcore/luni/src/main/java/java/io/CharConversionException.java @@ -19,8 +19,6 @@ package java.io; /** * The top level class for character conversion exceptions. - * - * @since Android 1.0 */ public class CharConversionException extends IOException { @@ -29,8 +27,6 @@ public class CharConversionException extends IOException { /** * Constructs a new {@code CharConversionException} with its stack trace * filled in. - * - * @since Android 1.0 */ public CharConversionException() { super(); @@ -39,10 +35,9 @@ public class CharConversionException extends IOException { /** * Constructs a new {@code CharConversionException} with its stack trace and * detail message filled in. - * + * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public CharConversionException(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/io/Closeable.java b/libcore/luni/src/main/java/java/io/Closeable.java index 015b39ad3..94b6bd8b2 100644 --- a/libcore/luni/src/main/java/java/io/Closeable.java +++ b/libcore/luni/src/main/java/java/io/Closeable.java @@ -21,18 +21,15 @@ package java.io; * are not used any longer. This usually includes all sorts of * {@link InputStream}s and {@link OutputStream}s. Calling the {@code close} * method releases resources that the object holds. - * - * @since Android 1.0 */ public interface Closeable { /** * Closes the object and release any system resources it holds. If the * object has already been closed, then invoking this method has no effect. - * + * * @throws IOException * if any error occurs when closing the object. - * @since Android 1.0 */ public void close() throws IOException; } \ No newline at end of file diff --git a/libcore/luni/src/main/java/java/io/DataInput.java b/libcore/luni/src/main/java/java/io/DataInput.java index a199d2f42..02a28d942 100644 --- a/libcore/luni/src/main/java/java/io/DataInput.java +++ b/libcore/luni/src/main/java/java/io/DataInput.java @@ -23,13 +23,12 @@ package java.io; * {@link DataOutput}. Types that can be read include byte, 16-bit short, 32-bit * int, 32-bit float, 64-bit long, 64-bit double, byte strings, and MUTF-8 * strings. - * + * *

          MUTF-8 (Modified UTF-8) Encoding

          *

          * When encoding strings as UTF, implementations of {@code DataInput} and * {@code DataOutput} use a slightly modified form of UTF-8, hereafter referred * to as MUTF-8. This form is identical to standard UTF-8, except: - *

          *
            *
          • Only the one-, two-, and three-byte encodings are used.
          • *
          • Code points in the range U+10000 … @@ -42,12 +41,9 @@ package java.io; * further information about character encoding. MUTF-8 is actually closer to * the (relatively less well-known) encoding CESU-8 than to UTF-8 per se. - *

            - * + * * @see DataInputStream * @see RandomAccessFile - * - * @since Android 1.0 */ public interface DataInput { /** @@ -59,7 +55,6 @@ public interface DataInput { * @throws IOException * if an I/O error occurs while reading. * @see DataOutput#writeBoolean(boolean) - * @since Android 1.0 */ public abstract boolean readBoolean() throws IOException; @@ -72,7 +67,6 @@ public interface DataInput { * @throws IOException * if an I/O error occurs while reading. * @see DataOutput#writeByte(int) - * @since Android 1.0 */ public abstract byte readByte() throws IOException; @@ -85,7 +79,6 @@ public interface DataInput { * @throws IOException * if an I/O error occurs while reading. * @see DataOutput#writeChar(int) - * @since Android 1.0 */ public abstract char readChar() throws IOException; @@ -98,7 +91,6 @@ public interface DataInput { * @throws IOException * if an I/O error occurs while reading. * @see DataOutput#writeDouble(double) - * @since Android 1.0 */ public abstract double readDouble() throws IOException; @@ -111,7 +103,6 @@ public interface DataInput { * @throws IOException * if an I/O error occurs while reading. * @see DataOutput#writeFloat(float) - * @since Android 1.0 */ public abstract float readFloat() throws IOException; @@ -127,7 +118,6 @@ public interface DataInput { * if an I/O error occurs while reading. * @see DataOutput#write(byte[]) * @see DataOutput#write(byte[], int, int) - * @since Android 1.0 */ public abstract void readFully(byte[] buffer) throws IOException; @@ -149,7 +139,6 @@ public interface DataInput { * if an I/O error occurs while reading. * @see DataOutput#write(byte[]) * @see DataOutput#write(byte[], int, int) - * @since Android 1.0 */ public abstract void readFully(byte[] buffer, int offset, int count) throws IOException; @@ -163,7 +152,6 @@ public interface DataInput { * @throws IOException * if an I/O error occurs while reading. * @see DataOutput#writeInt(int) - * @since Android 1.0 */ public abstract int readInt() throws IOException; @@ -179,7 +167,6 @@ public interface DataInput { * request can be satisfied. * @throws IOException * if an I/O error occurs while reading. - * @since Android 1.0 */ public abstract String readLine() throws IOException; @@ -192,7 +179,6 @@ public interface DataInput { * @throws IOException * if an I/O error occurs while reading. * @see DataOutput#writeLong(long) - * @since Android 1.0 */ public abstract long readLong() throws IOException; @@ -205,7 +191,6 @@ public interface DataInput { * @throws IOException * if an I/O error occurs while reading. * @see DataOutput#writeShort(int) - * @since Android 1.0 */ public abstract short readShort() throws IOException; @@ -218,7 +203,6 @@ public interface DataInput { * @throws IOException * if an I/O error occurs while reading. * @see DataOutput#writeByte(int) - * @since Android 1.0 */ public abstract int readUnsignedByte() throws IOException; @@ -231,7 +215,6 @@ public interface DataInput { * @throws IOException * if an I/O error occurs while reading. * @see DataOutput#writeShort(int) - * @since Android 1.0 */ public abstract int readUnsignedShort() throws IOException; @@ -244,7 +227,6 @@ public interface DataInput { * @throws IOException * if an I/O error occurs while reading. * @see DataOutput#writeUTF(java.lang.String) - * @since Android 1.0 */ public abstract String readUTF() throws IOException; @@ -258,7 +240,6 @@ public interface DataInput { * @return the number of bytes actually skipped. * @throws IOException * if a problem occurs during skipping. - * @since Android 1.0 */ public abstract int skipBytes(int count) throws IOException; } diff --git a/libcore/luni/src/main/java/java/io/DataInputStream.java b/libcore/luni/src/main/java/java/io/DataInputStream.java index 88b10a5df..cb219863e 100644 --- a/libcore/luni/src/main/java/java/io/DataInputStream.java +++ b/libcore/luni/src/main/java/java/io/DataInputStream.java @@ -21,38 +21,38 @@ import org.apache.harmony.luni.util.Msg; import org.apache.harmony.luni.util.Util; /** - * Wraps an existing {@link InputStream} and reads typed data from it. + * Wraps an existing {@link InputStream} and reads typed data from it. * Typically, this stream has been written by a DataOutputStream. Types that can * be read include byte, 16-bit short, 32-bit int, 32-bit float, 64-bit long, * 64-bit double, byte strings, and strings encoded in * {@link DataInput modified UTF-8}. - * + * * @see DataOutputStream - * - * @since Android 1.0 */ public class DataInputStream extends FilterInputStream implements DataInput { + byte[] buff; + /** * Constructs a new DataInputStream on the InputStream {@code in}. All * reads are then filtered through this stream. Note that data read by this * stream is not in a human readable format and was most likely created by a * DataOutputStream. - * + * * @param in * the source InputStream the filter reads from. * @see DataOutputStream * @see RandomAccessFile - * @since Android 1.0 */ public DataInputStream(InputStream in) { super(in); + buff = new byte[8]; } /** * Reads bytes from this stream into the byte array {@code buffer}. Returns * the number of bytes that have been read. - * + * * @param buffer * the buffer to read bytes into. * @return the number of bytes that have been read or -1 if the end of the @@ -61,7 +61,6 @@ public class DataInputStream extends FilterInputStream implements DataInput { * if a problem occurs while reading from this stream. * @see DataOutput#write(byte[]) * @see DataOutput#write(byte[], int, int) - * @since Android 1.0 */ @Override public final int read(byte[] buffer) throws IOException { @@ -73,7 +72,7 @@ public class DataInputStream extends FilterInputStream implements DataInput { * the byte array {@code buffer} starting at {@code offset}. Returns the * number of bytes that have been read or -1 if no bytes have been read and * the end of the stream has been reached. - * + * * @param buffer * the byte array in which to store the bytes read. * @param offset @@ -87,7 +86,6 @@ public class DataInputStream extends FilterInputStream implements DataInput { * if a problem occurs while reading from this stream. * @see DataOutput#write(byte[]) * @see DataOutput#write(byte[], int, int) - * @since Android 1.0 */ @Override public final int read(byte[] buffer, int offset, int length) @@ -97,7 +95,7 @@ public class DataInputStream extends FilterInputStream implements DataInput { /** * Reads a boolean from this stream. - * + * * @return the next boolean value from the source stream. * @throws EOFException * if the end of the filtered stream is reached before one byte @@ -105,7 +103,6 @@ public class DataInputStream extends FilterInputStream implements DataInput { * @throws IOException * if a problem occurs while reading from this stream. * @see DataOutput#writeBoolean(boolean) - * @since Android 1.0 */ public final boolean readBoolean() throws IOException { int temp = in.read(); @@ -117,7 +114,7 @@ public class DataInputStream extends FilterInputStream implements DataInput { /** * Reads an 8-bit byte value from this stream. - * + * * @return the next byte value from the source stream. * @throws EOFException * if the end of the filtered stream is reached before one byte @@ -125,7 +122,6 @@ public class DataInputStream extends FilterInputStream implements DataInput { * @throws IOException * if a problem occurs while reading from this stream. * @see DataOutput#writeByte(int) - * @since Android 1.0 */ public final byte readByte() throws IOException { int temp = in.read(); @@ -137,7 +133,7 @@ public class DataInputStream extends FilterInputStream implements DataInput { /** * Reads a 16-bit character value from this stream. - * + * * @return the next char value from the source stream. * @throws EOFException * if the end of the filtered stream is reached before two bytes @@ -145,20 +141,29 @@ public class DataInputStream extends FilterInputStream implements DataInput { * @throws IOException * if a problem occurs while reading from this stream. * @see DataOutput#writeChar(int) - * @since Android 1.0 */ public final char readChar() throws IOException { - int b1 = in.read(); - int b2 = in.read(); - if ((b1 | b2) < 0) { + if (readToBuff(2) < 0){ throw new EOFException(); } - return (char) ((b1 << 8) + b2); + return (char) (((buff[0] & 0xff) << 8) | (buff[1] & 0xff)); + + } + + private int readToBuff(int count) throws IOException { + int offset = 0; + + while(offset < count) { + int bytesRead = in.read(buff, offset, count - offset); + if(bytesRead == -1) return bytesRead; + offset += bytesRead; + } + return offset; } /** * Reads a 64-bit double value from this stream. - * + * * @return the next double value from the source stream. * @throws EOFException * if the end of the filtered stream is reached before eight @@ -166,7 +171,6 @@ public class DataInputStream extends FilterInputStream implements DataInput { * @throws IOException * if a problem occurs while reading from this stream. * @see DataOutput#writeDouble(double) - * @since Android 1.0 */ public final double readDouble() throws IOException { return Double.longBitsToDouble(readLong()); @@ -174,7 +178,7 @@ public class DataInputStream extends FilterInputStream implements DataInput { /** * Reads a 32-bit float value from this stream. - * + * * @return the next float value from the source stream. * @throws EOFException * if the end of the filtered stream is reached before four @@ -182,7 +186,6 @@ public class DataInputStream extends FilterInputStream implements DataInput { * @throws IOException * if a problem occurs while reading from this stream. * @see DataOutput#writeFloat(float) - * @since Android 1.0 */ public final float readFloat() throws IOException { return Float.intBitsToFloat(readInt()); @@ -192,7 +195,7 @@ public class DataInputStream extends FilterInputStream implements DataInput { * Reads bytes from this stream into the byte array {@code buffer}. This * method will block until {@code buffer.length} number of bytes have been * read. - * + * * @param buffer * to read bytes into. * @throws EOFException @@ -202,7 +205,6 @@ public class DataInputStream extends FilterInputStream implements DataInput { * if a problem occurs while reading from this stream. * @see DataOutput#write(byte[]) * @see DataOutput#write(byte[], int, int) - * @since Android 1.0 */ public final void readFully(byte[] buffer) throws IOException { readFully(buffer, 0, buffer.length); @@ -213,7 +215,7 @@ public class DataInputStream extends FilterInputStream implements DataInput { * buffer} starting at the position {@code offset}. This method blocks until * {@code length} bytes have been read. If {@code length} is zero, then this * method returns without reading any bytes. - * + * * @param buffer * the byte array into which the data is read. * @param offset @@ -232,7 +234,6 @@ public class DataInputStream extends FilterInputStream implements DataInput { * @throws NullPointerException * if {@code buffer} or the source stream are null. * @see java.io.DataInput#readFully(byte[], int, int) - * @since Android 1.0 */ public final void readFully(byte[] buffer, int offset, int length) throws IOException { @@ -271,7 +272,7 @@ public class DataInputStream extends FilterInputStream implements DataInput { /** * Reads a 32-bit integer value from this stream. - * + * * @return the next int value from the source stream. * @throws EOFException * if the end of the filtered stream is reached before four @@ -279,25 +280,13 @@ public class DataInputStream extends FilterInputStream implements DataInput { * @throws IOException * if a problem occurs while reading from this stream. * @see DataOutput#writeInt(int) - * @since Android 1.0 */ public final int readInt() throws IOException { - // BEGIN android-changed - byte[] buf = new byte[4]; - int nread = 0; - while (nread < 4) { - int nbytes = in.read(buf, nread, 4 - nread); - if (nbytes == -1) { - throw new EOFException(); - } - nread += nbytes; + if (readToBuff(4) < 0){ + throw new EOFException(); } - int b1 = buf[0] & 0xff; - int b2 = buf[1] & 0xff; - int b3 = buf[2] & 0xff; - int b4 = buf[3] & 0xff; - return ((b1 << 24) + (b2 << 16) + (b3 << 8) + b4); - // END android-changed + return ((buff[0] & 0xff) << 24) | ((buff[1] & 0xff) << 16) | + ((buff[2] & 0xff) << 8) | (buff[3] & 0xff); } /** @@ -305,17 +294,16 @@ public class DataInputStream extends FilterInputStream implements DataInput { * source stream. A line is represented by zero or more characters followed * by {@code '\n'}, {@code '\r'}, {@code "\r\n"} or the end of the stream. * The string does not include the newline sequence. - * + * * @return the contents of the line or {@code null} if no characters were * read before the end of the source stream has been reached. * @throws IOException * if a problem occurs while reading from this stream. * @deprecated Use {@link BufferedReader} - * @since Android 1.0 */ @Deprecated public final String readLine() throws IOException { - StringBuffer line = new StringBuffer(80); // Typical line length + StringBuilder line = new StringBuilder(80); // Typical line length boolean foundTerminator = false; while (true) { int nextByte = in.read(); @@ -350,7 +338,7 @@ public class DataInputStream extends FilterInputStream implements DataInput { /** * Reads a 64-bit long value from this stream. - * + * * @return the next long value from the source stream. * @throws EOFException * if the end of the filtered stream is reached before eight @@ -358,24 +346,22 @@ public class DataInputStream extends FilterInputStream implements DataInput { * @throws IOException * if a problem occurs while reading from this stream. * @see DataOutput#writeLong(long) - * @since Android 1.0 */ public final long readLong() throws IOException { - int i1 = readInt(); - int b1 = in.read(); - int b2 = in.read(); - int b3 = in.read(); - int b4 = in.read(); - if ((b1 | b2 | b3 | b4) < 0) { + if (readToBuff(8) < 0){ throw new EOFException(); } - return (((long) i1) << 32) + ((long) b1 << 24) + (b2 << 16) + (b3 << 8) - + b4; + int i1 = ((buff[0] & 0xff) << 24) | ((buff[1] & 0xff) << 16) | + ((buff[2] & 0xff) << 8) | (buff[3] & 0xff); + int i2 = ((buff[4] & 0xff) << 24) | ((buff[5] & 0xff) << 16) | + ((buff[6] & 0xff) << 8) | (buff[7] & 0xff); + + return ((i1 & 0xffffffffL) << 32) | (i2 & 0xffffffffL); } /** * Reads a 16-bit short value from this stream. - * + * * @return the next short value from the source stream. * @throws EOFException * if the end of the filtered stream is reached before two bytes @@ -383,29 +369,18 @@ public class DataInputStream extends FilterInputStream implements DataInput { * @throws IOException * if a problem occurs while reading from this stream. * @see DataOutput#writeShort(int) - * @since Android 1.0 */ public final short readShort() throws IOException { - // BEGIN android-changed - byte[] buf = new byte[2]; - int nread = 0; - while (nread < 2) { - int nbytes = in.read(buf, nread, 2 - nread); - if (nbytes == -1) { - throw new EOFException(); - } - nread += nbytes; + if (readToBuff(2) < 0){ + throw new EOFException(); } - int b1 = buf[0] & 0xff; - int b2 = buf[1] & 0xff; - return (short) ((b1 << 8) + b2); - // END android-changed + return (short) (((buff[0] & 0xff) << 8) | (buff[1] & 0xff)); } /** * Reads an unsigned 8-bit byte value from this stream and returns it as an * int. - * + * * @return the next unsigned byte value from the source stream. * @throws EOFException * if the end of the filtered stream has been reached before one @@ -413,7 +388,6 @@ public class DataInputStream extends FilterInputStream implements DataInput { * @throws IOException * if a problem occurs while reading from this stream. * @see DataOutput#writeByte(int) - * @since Android 1.0 */ public final int readUnsignedByte() throws IOException { int temp = in.read(); @@ -426,7 +400,7 @@ public class DataInputStream extends FilterInputStream implements DataInput { /** * Reads a 16-bit unsigned short value from this stream and returns it as an * int. - * + * * @return the next unsigned short value from the source stream. * @throws EOFException * if the end of the filtered stream is reached before two bytes @@ -434,21 +408,18 @@ public class DataInputStream extends FilterInputStream implements DataInput { * @throws IOException * if a problem occurs while reading from this stream. * @see DataOutput#writeShort(int) - * @since Android 1.0 */ public final int readUnsignedShort() throws IOException { - int b1 = in.read(); - int b2 = in.read(); - if ((b1 | b2) < 0) { + if (readToBuff(2) < 0){ throw new EOFException(); } - return ((b1 << 8) + b2); + return (char) (((buff[0] & 0xff) << 8) | (buff[1] & 0xff)); } /** * Reads an string encoded in {@link DataInput modified UTF-8} from this * stream. - * + * * @return the next {@link DataInput MUTF-8} encoded string read from the * source stream. * @throws EOFException if the end of the input is reached before the read @@ -456,90 +427,28 @@ public class DataInputStream extends FilterInputStream implements DataInput { * @throws IOException * if a problem occurs while reading from this stream. * @see DataOutput#writeUTF(java.lang.String) - * @since Android 1.0 */ public final String readUTF() throws IOException { - int utfSize = readUnsignedShort(); - return decodeUTF(utfSize); + return decodeUTF(readUnsignedShort()); } - // BEGIN android-removed - // static final int MAX_BUF_SIZE = 8192; - // - // private static class CacheLock { - // } - // - // static final Object cacheLock = new CacheLock(); - // - // static boolean useShared = true; - // - // static byte[] byteBuf = new byte[0]; - // - // static char[] charBuf = new char[0]; - // END android-removed String decodeUTF(int utfSize) throws IOException { - // BEGIN android-removed - // byte[] buf; - // char[] out = null; - // boolean makeBuf = true; - // - // /* - // * Try to avoid the synchronization -- if we get a stale value for - // * useShared then there is no foul below, but those that sync on the - // * lock must see the right value. - // */ - // if (utfSize <= MAX_BUF_SIZE && useShared) { - // synchronized (cacheLock) { - // if (useShared) { - // useShared = false; - // makeBuf = false; - // } - // } - // } - // if (makeBuf) { - // buf = new byte[utfSize]; - // out = new char[utfSize]; - // } else { - // /* - // * Need to 'sample' byteBuf and charBuf before using them because - // * they are not protected by the cacheLock. They may get out of sync - // * with the static and one another, but that is ok because we - // * explicitly check and fix their length after sampling. - // */ - // buf = byteBuf; - // if (buf.length < utfSize) { - // buf = byteBuf = new byte[utfSize]; - // } - // out = charBuf; - // if (out.length < utfSize) { - // out = charBuf = new char[utfSize]; - // } - // } - // END android-removed - // BEGIN android-added + return decodeUTF(utfSize, this); + } + + private static String decodeUTF(int utfSize, DataInput in) throws IOException { byte[] buf = new byte[utfSize]; char[] out = new char[utfSize]; - // END android-added - readFully(buf, 0, utfSize); - String result; - result = Util.convertUTF8WithBuf(buf, out, 0, utfSize); - // BEGIN android-removed - // if (!makeBuf) { - // /* - // * Do not synchronize useShared on cacheLock, it will make it back - // * to main storage at some point, and no harm until it does. - // */ - // useShared = true; - // } - //END android-removed - return result; + in.readFully(buf, 0, utfSize); + + return Util.convertUTF8WithBuf(buf, out, 0, utfSize); } /** * Reads a string encoded in {@link DataInput modified UTF-8} from the * {@code DataInput} stream {@code in}. - * + * * @param in * the input stream to read from. * @return the next {@link DataInput MUTF-8} encoded string from the source @@ -547,19 +456,18 @@ public class DataInputStream extends FilterInputStream implements DataInput { * @throws IOException * if a problem occurs while reading from this stream. * @see DataOutputStream#writeUTF(java.lang.String) - * @since Android 1.0 */ public static final String readUTF(DataInput in) throws IOException { - return in.readUTF(); + return decodeUTF(in.readUnsignedShort(), in); } /** * Skips {@code count} number of bytes in this stream. Subsequent {@code * read()}s will not return these bytes unless {@code reset()} is used. - * + * * This method will not throw an {@link EOFException} if the end of the * input is reached before {@code count} bytes where skipped. - * + * * @param count * the number of bytes to skip. * @return the number of bytes actually skipped. @@ -567,7 +475,6 @@ public class DataInputStream extends FilterInputStream implements DataInput { * if a problem occurs during skipping. * @see #mark(int) * @see #reset() - * @since Android 1.0 */ public final int skipBytes(int count) throws IOException { int skipped = 0; diff --git a/libcore/luni/src/main/java/java/io/DataOutput.java b/libcore/luni/src/main/java/java/io/DataOutput.java index d028a18ca..8275958d4 100644 --- a/libcore/luni/src/main/java/java/io/DataOutput.java +++ b/libcore/luni/src/main/java/java/io/DataOutput.java @@ -23,35 +23,29 @@ package java.io; * DataInput. Types that can be written include byte, 16-bit short, 32-bit int, * 32-bit float, 64-bit long, 64-bit double, byte strings, and {@link DataInput * MUTF-8} encoded strings. - * + * * @see DataOutputStream * @see RandomAccessFile - * - * @since Android 1.0 */ public interface DataOutput { /** * Writes the entire contents of the byte array {@code buffer} to this * stream. - * + * * @param buffer * the buffer to write. * @throws IOException * if an I/O error occurs while writing. * @see DataInput#readFully(byte[]) * @see DataInput#readFully(byte[], int, int) - * @since Android 1.0 */ public abstract void write(byte[] buffer) throws IOException; - // BEGIN android-note - // changed array notation to be consistent with the rest of harmony - // END android-note /** * Writes {@code count} bytes from the byte array {@code buffer} starting at * offset {@code index}. - * + * * @param buffer * the buffer to write. * @param offset @@ -62,7 +56,6 @@ public interface DataOutput { * if an I/O error occurs while writing. * @see DataInput#readFully(byte[]) * @see DataInput#readFully(byte[], int, int) - * @since Android 1.0 */ public abstract void write(byte[] buffer, int offset, int count) throws IOException; // BEGIN android-note @@ -71,51 +64,47 @@ public interface DataOutput { /** * Writes the specified 8-bit byte. - * + * * @param oneByte * the byte to write. * @throws IOException * if an I/O error occurs while writing. * @see DataInput#readByte() - * @since Android 1.0 */ public abstract void write(int oneByte) throws IOException; /** * Writes the specified boolean. - * + * * @param val * the boolean value to write. * @throws IOException * if an I/O error occurs while writing. * @see DataInput#readBoolean() - * @since Android 1.0 */ public abstract void writeBoolean(boolean val) throws IOException; /** * Writes the specified 8-bit byte. - * + * * @param val * the byte value to write. * @throws IOException * if an I/O error occurs while writing. * @see DataInput#readByte() * @see DataInput#readUnsignedByte() - * @since Android 1.0 */ public abstract void writeByte(int val) throws IOException; /** * Writes the low order 8-bit bytes from the specified string. - * + * * @param str * the string containing the bytes to write. * @throws IOException * if an I/O error occurs while writing. * @see DataInput#readFully(byte[]) * @see DataInput#readFully(byte[],int,int) - * @since Android 1.0 */ public abstract void writeBytes(String str) throws IOException; @@ -123,106 +112,95 @@ public interface DataOutput { * Writes the specified 16-bit character. Only the two least significant * bytes of the integer {@code oneByte} are written, with the higher one * written first. This represents the Unicode value of the char. - * + * * @param val * the character to write. * @throws IOException * if an I/O error occurs while writing. * @see DataInput#readChar() - * @since Android 1.0 */ public abstract void writeChar(int val) throws IOException; - // BEGIN android-note - // renamed parameter because it was misleading - // END android-note /** * Writes the 16-bit characters contained in {@code str}. - * + * * @param str * the string that contains the characters to write. * @throws IOException * if an I/O error occurs while writing. * @see DataInput#readChar() - * @since Android 1.0 */ public abstract void writeChars(String str) throws IOException; /** * Writes the specified 64-bit double. The resulting output is the eight * bytes returned by {@link Double#doubleToLongBits(double)}. - * + * * @param val * the double to write. * @throws IOException * if an I/O error occurs while writing. * @see DataInput#readDouble() - * @since Android 1.0 */ public abstract void writeDouble(double val) throws IOException; /** * Writes the specified 32-bit float. The resulting output is the four bytes * returned by {@link Float#floatToIntBits(float)}. - * + * * @param val * the float to write. * @throws IOException * if an I/O error occurs while writing. * @see DataInput#readFloat() - * @since Android 1.0 */ public abstract void writeFloat(float val) throws IOException; /** * Writes the specified 32-bit int. The resulting output is the four bytes, * highest order first, of {@code val}. - * + * * @param val * the int to write. * @throws IOException * if an I/O error occurs while writing. * @see DataInput#readInt() - * @since Android 1.0 */ public abstract void writeInt(int val) throws IOException; /** * Writes the specified 64-bit long. The resulting output is the eight * bytes, highest order first, of {@code val}. - * + * * @param val * the long to write. * @throws IOException * if an I/O error occurs while writing. * @see DataInput#readLong() - * @since Android 1.0 */ public abstract void writeLong(long val) throws IOException; /** * Writes the specified 16-bit short. Only the lower two bytes of {@code * val} are written with the higher one written first. - * + * * @param val * the short to write. * @throws IOException * if an I/O error occurs while writing. * @see DataInput#readShort() * @see DataInput#readUnsignedShort() - * @since Android 1.0 */ public abstract void writeShort(int val) throws IOException; /** * Writes the specified string encoded in {@link DataInput modified UTF-8}. - * + * * @param str * the string to write encoded in {@link DataInput modified UTF-8}. * @throws IOException * if an I/O error occurs while writing. * @see DataInput#readUTF() - * @since Android 1.0 */ public abstract void writeUTF(String str) throws IOException; } diff --git a/libcore/luni/src/main/java/java/io/DataOutputStream.java b/libcore/luni/src/main/java/java/io/DataOutputStream.java index 7f45c5802..18e04a58e 100644 --- a/libcore/luni/src/main/java/java/io/DataOutputStream.java +++ b/libcore/luni/src/main/java/java/io/DataOutputStream.java @@ -24,41 +24,37 @@ import org.apache.harmony.luni.util.Msg; * Typically, this stream can be read in by DataInputStream. Types that can be * written include byte, 16-bit short, 32-bit int, 32-bit float, 64-bit long, * 64-bit double, byte strings, and {@link DataInput MUTF-8} encoded strings. - * + * * @see DataInputStream - * - * @since Android 1.0 */ public class DataOutputStream extends FilterOutputStream implements DataOutput { /** * The number of bytes written out so far. - * - * @since Android 1.0 */ protected int written; + byte buff[]; /** * Constructs a new {@code DataOutputStream} on the {@code OutputStream} * {@code out}. Note that data written by this stream is not in a human * readable form but can be reconstructed by using a {@link DataInputStream} * on the resulting output. - * + * * @param out * the target stream for writing. - * @since Android 1.0 */ public DataOutputStream(OutputStream out) { super(out); + buff = new byte[8]; } /** * Flushes this stream to ensure all pending data is sent out to the target * stream. This implementation then also flushes the target stream. - * + * * @throws IOException * if an error occurs attempting to flush this stream. - * @since Android 1.0 */ @Override public void flush() throws IOException { @@ -67,9 +63,8 @@ public class DataOutputStream extends FilterOutputStream implements DataOutput { /** * Returns the total number of bytes written to the target stream so far. - * + * * @return the number of bytes written to the target stream. - * @since Android 1.0 */ public final int size() { if (written < 0) { @@ -81,7 +76,7 @@ public class DataOutputStream extends FilterOutputStream implements DataOutput { /** * Writes {@code count} bytes from the byte array {@code buffer} starting at * {@code offset} to the target stream. - * + * * @param buffer * the buffer to write to the target stream. * @param offset @@ -94,7 +89,6 @@ public class DataOutputStream extends FilterOutputStream implements DataOutput { * if {@code buffer} is {@code null}. * @see DataInputStream#readFully(byte[]) * @see DataInputStream#readFully(byte[], int, int) - * @since Android 1.0 */ @Override public void write(byte[] buffer, int offset, int count) throws IOException { @@ -111,13 +105,12 @@ public class DataOutputStream extends FilterOutputStream implements DataOutput { /** * Writes a byte to the target stream. Only the least significant byte of * the integer {@code oneByte} is written. - * + * * @param oneByte * the byte to write to the target stream. * @throws IOException * if an error occurs while writing to the target stream. * @see DataInputStream#readByte() - * @since Android 1.0 */ @Override public void write(int oneByte) throws IOException { @@ -127,13 +120,12 @@ public class DataOutputStream extends FilterOutputStream implements DataOutput { /** * Writes a boolean to the target stream. - * + * * @param val * the boolean value to write to the target stream. * @throws IOException * if an error occurs while writing to the target stream. * @see DataInputStream#readBoolean() - * @since Android 1.0 */ public final void writeBoolean(boolean val) throws IOException { out.write(val ? 1 : 0); @@ -143,14 +135,13 @@ public class DataOutputStream extends FilterOutputStream implements DataOutput { /** * Writes an 8-bit byte to the target stream. Only the least significant * byte of the integer {@code val} is written. - * + * * @param val * the byte value to write to the target stream. * @throws IOException * if an error occurs while writing to the target stream. * @see DataInputStream#readByte() * @see DataInputStream#readUnsignedByte() - * @since Android 1.0 */ public final void writeByte(int val) throws IOException { out.write(val); @@ -159,14 +150,13 @@ public class DataOutputStream extends FilterOutputStream implements DataOutput { /** * Writes the low order bytes from a string to the target stream. - * + * * @param str * the string containing the bytes to write to the target stream. * @throws IOException * if an error occurs while writing to the target stream. * @see DataInputStream#readFully(byte[]) * @see DataInputStream#readFully(byte[],int,int) - * @since Android 1.0 */ public final void writeBytes(String str) throws IOException { if (str.length() == 0) { @@ -184,31 +174,30 @@ public class DataOutputStream extends FilterOutputStream implements DataOutput { * Writes a 16-bit character to the target stream. Only the two lower bytes * of the integer {@code val} are written, with the higher one written * first. This corresponds to the Unicode value of {@code val}. - * + * * @param val * the character to write to the target stream * @throws IOException * if an error occurs while writing to the target stream. * @see DataInputStream#readChar() - * @since Android 1.0 */ public final void writeChar(int val) throws IOException { - out.write(val >> 8); - out.write(val); + buff[0] = (byte) (val >> 8); + buff[1] = (byte) val; + out.write(buff, 0, 2); written += 2; } /** * Writes the 16-bit characters contained in {@code str} to the target * stream. - * + * * @param str * the string that contains the characters to write to this * stream. * @throws IOException * if an error occurs while writing to the target stream. * @see DataInputStream#readChar() - * @since Android 1.0 */ public final void writeChars(String str) throws IOException { byte newBytes[] = new byte[str.length() * 2]; @@ -224,13 +213,12 @@ public class DataOutputStream extends FilterOutputStream implements DataOutput { /** * Writes a 64-bit double to the target stream. The resulting output is the * eight bytes resulting from calling Double.doubleToLongBits(). - * + * * @param val * the double to write to the target stream. * @throws IOException * if an error occurs while writing to the target stream. * @see DataInputStream#readDouble() - * @since Android 1.0 */ public final void writeDouble(double val) throws IOException { writeLong(Double.doubleToLongBits(val)); @@ -239,13 +227,12 @@ public class DataOutputStream extends FilterOutputStream implements DataOutput { /** * Writes a 32-bit float to the target stream. The resulting output is the * four bytes resulting from calling Float.floatToIntBits(). - * + * * @param val * the float to write to the target stream. * @throws IOException * if an error occurs while writing to the target stream. * @see DataInputStream#readFloat() - * @since Android 1.0 */ public final void writeFloat(float val) throws IOException { writeInt(Float.floatToIntBits(val)); @@ -254,63 +241,68 @@ public class DataOutputStream extends FilterOutputStream implements DataOutput { /** * Writes a 32-bit int to the target stream. The resulting output is the * four bytes, highest order first, of {@code val}. - * + * * @param val * the int to write to the target stream. * @throws IOException * if an error occurs while writing to the target stream. * @see DataInputStream#readInt() - * @since Android 1.0 */ public final void writeInt(int val) throws IOException { - out.write(val >> 24); - out.write(val >> 16); - out.write(val >> 8); - out.write(val); + buff[0] = (byte) (val >> 24); + buff[1] = (byte) (val >> 16); + buff[2] = (byte) (val >> 8); + buff[3] = (byte) val; + out.write(buff, 0, 4); written += 4; } /** * Writes a 64-bit long to the target stream. The resulting output is the * eight bytes, highest order first, of {@code val}. - * + * * @param val * the long to write to the target stream. * @throws IOException * if an error occurs while writing to the target stream. * @see DataInputStream#readLong() - * @since Android 1.0 */ public final void writeLong(long val) throws IOException { - writeInt((int) (val >> 32)); - writeInt((int) val); + buff[0] = (byte) (val >> 56); + buff[1] = (byte) (val >> 48); + buff[2] = (byte) (val >> 40); + buff[3] = (byte) (val >> 32); + buff[4] = (byte) (val >> 24); + buff[5] = (byte) (val >> 16); + buff[6] = (byte) (val >> 8); + buff[7] = (byte) val; + out.write(buff, 0, 8); + written += 8; } /** * Writes the specified 16-bit short to the target stream. Only the lower * two bytes of the integer {@code val} are written, with the higher one * written first. - * + * * @param val * the short to write to the target stream. * @throws IOException * if an error occurs while writing to the target stream. * @see DataInputStream#readShort() * @see DataInputStream#readUnsignedShort() - * @since Android 1.0 */ public final void writeShort(int val) throws IOException { - writeChar(val); + buff[0] = (byte) (val >> 8); + buff[1] = (byte) val; + out.write(buff, 0, 2); + written += 2; } - - // BEGIN android-added - static final int MAX_BUF_SIZE = 8192; - // END android-added - + /** * Writes the specified encoded in {@link DataInput modified UTF-8} to this * stream. - * + * * @param str * the string to write to the target stream encoded in * {@link DataInput modified UTF-8}. @@ -319,58 +311,14 @@ public class DataOutputStream extends FilterOutputStream implements DataOutput { * @throws UTFDataFormatException * if the encoded string is longer than 65535 bytes. * @see DataInputStream#readUTF() - * @since Android 1.0 */ public final void writeUTF(String str) throws IOException { - int length = str.length(); - // BEGIN android-changed - if (length <= MAX_BUF_SIZE / 3) { - int size = length * 3; - byte[] utfBytes = new byte[size]; - // boolean makeBuf = true; - // synchronized (DataInputStream.byteBuf) { - // if (DataInputStream.useShared) { - // DataInputStream.useShared = false; - // makeBuf = false; - // } - // } - // if (makeBuf) { - // utfBytes = new byte[size]; - // } else { - // if (DataInputStream.byteBuf.length < size) { - // DataInputStream.byteBuf = new byte[size]; - // } - // utfBytes = DataInputStream.byteBuf; - // } - int utfIndex = 0; - for (int i = 0; i < length; i++) { - int charValue = str.charAt(i); - if (charValue > 0 && charValue <= 127) { - utfBytes[utfIndex++] = (byte) charValue; - } else if (charValue <= 2047) { - utfBytes[utfIndex++] = (byte) (0xc0 | (0x1f & (charValue >> 6))); - utfBytes[utfIndex++] = (byte) (0x80 | (0x3f & charValue)); - } else { - utfBytes[utfIndex++] = (byte) (0xe0 | (0x0f & (charValue >> 12))); - utfBytes[utfIndex++] = (byte) (0x80 | (0x3f & (charValue >> 6))); - utfBytes[utfIndex++] = (byte) (0x80 | (0x3f & charValue)); - } - } - writeShort(utfIndex); - write(utfBytes, 0, utfIndex); - // if (!makeBuf) { - // DataInputStream.useShared = true; - // } - } else { - long utfCount; - if (length <= 65535 && (utfCount = countUTFBytes(str)) <= 65535) { - writeShort((int) utfCount); - writeUTFBytes(str, utfCount); - } else { - throw new UTFDataFormatException(Msg.getString("K0068")); //$NON-NLS-1$ - } + long utfCount = countUTFBytes(str); + if (utfCount > 65535) { + throw new UTFDataFormatException(Msg.getString("K0068")); //$NON-NLS-1$ } - // END android-changed + writeShort((int) utfCount); + writeUTFBytes(str, utfCount); } long countUTFBytes(String str) { @@ -389,76 +337,23 @@ public class DataOutputStream extends FilterOutputStream implements DataOutput { } void writeUTFBytes(String str, long count) throws IOException { - boolean single = true; int size = (int) count; - // BEGIN android-changed - if (count > MAX_BUF_SIZE) { - single = false; - size = MAX_BUF_SIZE; - } + int length = str.length(); byte[] utfBytes = new byte[size]; - // END android-changed - // BEGIN android-removed - // boolean makeBuf = true; - // if (DataInputStream.useShared) { - // synchronized (DataInputStream.cacheLock) { - // if (DataInputStream.useShared) { - // DataInputStream.useShared = false; - // makeBuf = false; - // } - // } - // } - // if (makeBuf) { - // utfBytes = new byte[size]; - // } else { - // // byteBuf is not protected by the cacheLock, so sample it first - // utfBytes = DataInputStream.byteBuf; - // if (utfBytes.length < size) { - // utfBytes = DataInputStream.byteBuf = new byte[size]; - // } - // } - // END android-removed - - int utfIndex = 0, i = 0, length = str.length(); - int end = length; - while (i < length) { - if (!single) { - end = i + ((utfBytes.length - utfIndex) / 3); - if (end > length) { - end = length; - } - } - for (int j = i; j < end; j++) { - int charValue = str.charAt(j); - if (charValue > 0 && charValue <= 127) { - utfBytes[utfIndex++] = (byte) charValue; - } else if (charValue <= 2047) { - utfBytes[utfIndex++] = (byte) (0xc0 | (0x1f & (charValue >> 6))); - utfBytes[utfIndex++] = (byte) (0x80 | (0x3f & charValue)); - } else { - utfBytes[utfIndex++] = (byte) (0xe0 | (0x0f & (charValue >> 12))); - utfBytes[utfIndex++] = (byte) (0x80 | (0x3f & (charValue >> 6))); - utfBytes[utfIndex++] = (byte) (0x80 | (0x3f & charValue)); - } - } - if (single || utfIndex > utfBytes.length - 300) { - write(utfBytes, 0, utfIndex); - if (single) { - return; - } - utfIndex = 0; - } - i = end; - } - if (utfIndex > 0) { - write(utfBytes, 0, utfIndex); + int utfIndex = 0; + for (int i = 0; i < length; i++) { + int charValue = str.charAt(i); + if (charValue > 0 && charValue <= 127) { + utfBytes[utfIndex++] = (byte) charValue; + } else if (charValue <= 2047) { + utfBytes[utfIndex++] = (byte) (0xc0 | (0x1f & (charValue >> 6))); + utfBytes[utfIndex++] = (byte) (0x80 | (0x3f & charValue)); + } else { + utfBytes[utfIndex++] = (byte) (0xe0 | (0x0f & (charValue >> 12))); + utfBytes[utfIndex++] = (byte) (0x80 | (0x3f & (charValue >> 6))); + utfBytes[utfIndex++] = (byte) (0x80 | (0x3f & charValue)); + } } - // BEGIN android-removed - // if (!makeBuf) { - // // Update the useShared flag optimistically (see DataInputStream - // // equivalent) - // DataInputStream.useShared = true; - // } - // END android-removed + write(utfBytes, 0, utfIndex); } } diff --git a/libcore/luni/src/main/java/java/io/EOFException.java b/libcore/luni/src/main/java/java/io/EOFException.java index 40beadd5e..7999f4344 100644 --- a/libcore/luni/src/main/java/java/io/EOFException.java +++ b/libcore/luni/src/main/java/java/io/EOFException.java @@ -20,8 +20,6 @@ package java.io; /** * Thrown when a program encounters the end of a file or stream during an input * operation. - * - * @since Android 1.0 */ public class EOFException extends IOException { @@ -29,8 +27,6 @@ public class EOFException extends IOException { /** * Constructs a new {@code EOFException} with its stack trace filled in. - * - * @since Android 1.0 */ public EOFException() { super(); @@ -42,7 +38,6 @@ public class EOFException extends IOException { * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public EOFException(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/io/EmulatedFields.java b/libcore/luni/src/main/java/java/io/EmulatedFields.java index 4ef241dbc..a947a52d6 100644 --- a/libcore/luni/src/main/java/java/io/EmulatedFields.java +++ b/libcore/luni/src/main/java/java/io/EmulatedFields.java @@ -103,17 +103,18 @@ class EmulatedFields { } /** - * Return a boolean indicating if the field named name has - * been assigned a value explicitly (false) or if it still holds a default - * value for the type (true) because it hasn't been assigned to yet. + * Returns {@code true} indicating the field called {@code name} has not had + * a value explicitly assigned and that it still holds a default value for + * its type, or {@code false} indicating that the field named has been + * assigned a value explicitly. * * @param name - * a String, the name of the field to test - * @return true if name still holds its - * default value, false otherwise + * the name of the field to test. + * @return {@code true} if {@code name} still holds its default value, + * {@code false} otherwise * * @throws IllegalArgumentException - * If name is null + * if {@code name} is {@code null} */ public boolean defaulted(String name) throws IllegalArgumentException { ObjectSlot slot = findSlot(name, null); @@ -124,23 +125,23 @@ class EmulatedFields { } /** - * Find and return an ObjectSlot that corresponds to a field named - * fieldName and type fieldType. If the field - * type fieldType corresponds to a primitive type, the field - * type has to match exactly or null is returned. If the - * field type fieldType corresponds to an object type, the - * field type has to be compatible in terms of assignment, or null is - * returned. If fieldType is null, no such - * compatibility checking is performed and the slot is returned. + * Finds and returns an ObjectSlot that corresponds to a field named {@code + * fieldName} and type {@code fieldType}. If the field type {@code + * fieldType} corresponds to a primitive type, the field type has to match + * exactly or {@code null} is returned. If the field type {@code fieldType} + * corresponds to an object type, the field type has to be compatible in + * terms of assignment, or null is returned. If {@code fieldType} is {@code + * null}, no such compatibility checking is performed and the slot is + * returned. * * @param fieldName - * A String, the name of the field to find + * the name of the field to find * @param fieldType - * A Class, the type of the field. This will be used to test - * compatibility. If null, no testing is done, the corresponding - * slot is returned. - * @return If there is no field with that name, or no compatible field - * (relative to fieldType) + * the type of the field. This will be used to test + * compatibility. If {@code null}, no testing is done, the + * corresponding slot is returned. + * @return the object slot, or {@code null} if there is no field with that + * name, or no compatible field (relative to {@code fieldType}) */ private ObjectSlot findSlot(String fieldName, Class fieldType) { boolean isPrimitive = fieldType != null && fieldType.isPrimitive(); @@ -187,19 +188,19 @@ class EmulatedFields { } /** - * Find and return the byte value of a given field named name + * Finds and returns the byte value of a given field named {@code name} * in the receiver. If the field has not been assigned any value yet, the - * default value defaultValue is returned instead. + * default value {@code defaultValue} is returned instead. * * @param name - * A String, the name of the field to find + * the name of the field to find. * @param defaultValue - * Return value in case the field has not been assigned to yet. + * return value in case the field has not been assigned to yet. * @return the value of the given field if it has been assigned, the default - * value otherwise + * value otherwise. * * @throws IllegalArgumentException - * If the corresponding field can not be found. + * if the corresponding field can not be found. */ public byte get(String name, byte defaultValue) throws IllegalArgumentException { @@ -213,19 +214,19 @@ class EmulatedFields { } /** - * Find and return the char value of a given field named name - * in the receiver. If the field has not been assigned any value yet, the - * default value defaultValue is returned instead. + * Finds and returns the char value of a given field named {@code name} in the + * receiver. If the field has not been assigned any value yet, the default + * value {@code defaultValue} is returned instead. * * @param name - * A String, the name of the field to find + * the name of the field to find. * @param defaultValue - * Return value in case the field has not been assigned to yet. + * return value in case the field has not been assigned to yet. * @return the value of the given field if it has been assigned, the default - * value otherwise + * value otherwise. * * @throws IllegalArgumentException - * If the corresponding field can not be found. + * if the corresponding field can not be found. */ public char get(String name, char defaultValue) throws IllegalArgumentException { @@ -239,19 +240,19 @@ class EmulatedFields { } /** - * Find and return the double value of a given field named name + * Finds and returns the double value of a given field named {@code name} * in the receiver. If the field has not been assigned any value yet, the - * default value defaultValue is returned instead. + * default value {@code defaultValue} is returned instead. * * @param name - * A String, the name of the field to find + * the name of the field to find. * @param defaultValue - * Return value in case the field has not been assigned to yet. + * return value in case the field has not been assigned to yet. * @return the value of the given field if it has been assigned, the default - * value otherwise + * value otherwise. * * @throws IllegalArgumentException - * If the corresponding field can not be found. + * if the corresponding field can not be found. */ public double get(String name, double defaultValue) throws IllegalArgumentException { @@ -265,19 +266,19 @@ class EmulatedFields { } /** - * Find and return the float value of a given field named name - * in the receiver. If the field has not been assigned any value yet, the - * default value defaultValue is returned instead. + * Finds and returns the float value of a given field named {@code name} in + * the receiver. If the field has not been assigned any value yet, the + * default value {@code defaultValue} is returned instead. * * @param name - * A String, the name of the field to find + * the name of the field to find. * @param defaultValue - * Return value in case the field has not been assigned to yet. + * return value in case the field has not been assigned to yet. * @return the value of the given field if it has been assigned, the default - * value otherwise + * value otherwise. * * @throws IllegalArgumentException - * If the corresponding field can not be found. + * if the corresponding field can not be found. */ public float get(String name, float defaultValue) throws IllegalArgumentException { @@ -291,19 +292,19 @@ class EmulatedFields { } /** - * Find and return the int value of a given field named name - * in the receiver. If the field has not been assigned any value yet, the - * default value defaultValue is returned instead. + * Finds and returns the int value of a given field named {@code name} in the + * receiver. If the field has not been assigned any value yet, the default + * value {@code defaultValue} is returned instead. * * @param name - * A String, the name of the field to find + * the name of the field to find. * @param defaultValue - * Return value in case the field has not been assigned to yet. + * return value in case the field has not been assigned to yet. * @return the value of the given field if it has been assigned, the default - * value otherwise + * value otherwise. * * @throws IllegalArgumentException - * If the corresponding field can not be found. + * if the corresponding field can not be found. */ public int get(String name, int defaultValue) throws IllegalArgumentException { @@ -317,19 +318,19 @@ class EmulatedFields { } /** - * Find and return the long value of a given field named name - * in the receiver. If the field has not been assigned any value yet, the - * default value defaultValue is returned instead. + * Finds and returns the long value of a given field named {@code name} in the + * receiver. If the field has not been assigned any value yet, the default + * value {@code defaultValue} is returned instead. * * @param name - * A String, the name of the field to find + * the name of the field to find. * @param defaultValue - * Return value in case the field has not been assigned to yet. + * return value in case the field has not been assigned to yet. * @return the value of the given field if it has been assigned, the default - * value otherwise + * value otherwise. * * @throws IllegalArgumentException - * If the corresponding field can not be found. + * if the corresponding field can not be found. */ public long get(String name, long defaultValue) throws IllegalArgumentException { @@ -343,19 +344,19 @@ class EmulatedFields { } /** - * Find and return the Object value of a given field named name - * in the receiver. If the field has not been assigned any value yet, the - * default value defaultValue is returned instead. + * Finds and returns the Object value of a given field named {@code name} in + * the receiver. If the field has not been assigned any value yet, the + * default value {@code defaultValue} is returned instead. * * @param name - * A String, the name of the field to find + * the name of the field to find. * @param defaultValue - * Return value in case the field has not been assigned to yet. + * return value in case the field has not been assigned to yet. * @return the value of the given field if it has been assigned, the default - * value otherwise + * value otherwise. * * @throws IllegalArgumentException - * If the corresponding field can not be found. + * if the corresponding field can not be found. */ public Object get(String name, Object defaultValue) throws IllegalArgumentException { @@ -368,19 +369,19 @@ class EmulatedFields { } /** - * Find and return the short value of a given field named name - * in the receiver. If the field has not been assigned any value yet, the - * default value defaultValue is returned instead. + * Finds and returns the short value of a given field named {@code name} in + * the receiver. If the field has not been assigned any value yet, the + * default value {@code defaultValue} is returned instead. * * @param name - * A String, the name of the field to find + * the name of the field to find. * @param defaultValue - * Return value in case the field has not been assigned to yet. + * return value in case the field has not been assigned to yet. * @return the value of the given field if it has been assigned, the default - * value otherwise + * value otherwise. * * @throws IllegalArgumentException - * If the corresponding field can not be found. + * if the corresponding field can not be found. */ public short get(String name, short defaultValue) throws IllegalArgumentException { @@ -394,20 +395,19 @@ class EmulatedFields { } /** - * Find and return the boolean value of a given field named - * name in the receiver. If the field has not been assigned - * any value yet, the default value defaultValue is returned - * instead. + * Finds and returns the boolean value of a given field named {@code name} in + * the receiver. If the field has not been assigned any value yet, the + * default value {@code defaultValue} is returned instead. * * @param name - * A String, the name of the field to find + * the name of the field to find. * @param defaultValue - * Return value in case the field has not been assigned to yet. + * return value in case the field has not been assigned to yet. * @return the value of the given field if it has been assigned, the default - * value otherwise + * value otherwise. * * @throws IllegalArgumentException - * If the corresponding field can not be found. + * if the corresponding field can not be found. */ public boolean get(String name, boolean defaultValue) throws IllegalArgumentException { @@ -421,16 +421,16 @@ class EmulatedFields { } /** - * Find and set the byte value of a given field named name in - * the receiver. + * Find and set the byte value of a given field named {@code name} in the + * receiver. * * @param name - * A String, the name of the field to set + * the name of the field to set. * @param value - * New value for the field. + * new value for the field. * * @throws IllegalArgumentException - * If the corresponding field can not be found. + * if the corresponding field can not be found. */ public void put(String name, byte value) throws IllegalArgumentException { ObjectSlot slot = findSlot(name, Byte.TYPE); @@ -442,16 +442,16 @@ class EmulatedFields { } /** - * Find and set the char value of a given field named name in - * the receiver. + * Find and set the char value of a given field named {@code name} in the + * receiver. * * @param name - * A String, the name of the field to set + * the name of the field to set. * @param value - * New value for the field. + * new value for the field. * * @throws IllegalArgumentException - * If the corresponding field can not be found. + * if the corresponding field can not be found. */ public void put(String name, char value) throws IllegalArgumentException { ObjectSlot slot = findSlot(name, Character.TYPE); @@ -463,16 +463,16 @@ class EmulatedFields { } /** - * Find and set the double value of a given field named name - * in the receiver. + * Find and set the double value of a given field named {@code name} in the + * receiver. * * @param name - * A String, the name of the field to set + * the name of the field to set. * @param value - * New value for the field. + * new value for the field. * * @throws IllegalArgumentException - * If the corresponding field can not be found. + * if the corresponding field can not be found. */ public void put(String name, double value) throws IllegalArgumentException { ObjectSlot slot = findSlot(name, Double.TYPE); @@ -484,16 +484,16 @@ class EmulatedFields { } /** - * Find and set the float value of a given field named name - * in the receiver. + * Find and set the float value of a given field named {@code name} in the + * receiver. * * @param name - * A String, the name of the field to set + * the name of the field to set. * @param value - * New value for the field. + * new value for the field. * * @throws IllegalArgumentException - * If the corresponding field can not be found. + * if the corresponding field can not be found. */ public void put(String name, float value) throws IllegalArgumentException { ObjectSlot slot = findSlot(name, Float.TYPE); @@ -505,16 +505,16 @@ class EmulatedFields { } /** - * Find and set the int value of a given field named name in - * the receiver. + * Find and set the int value of a given field named {@code name} in the + * receiver. * * @param name - * A String, the name of the field to set + * the name of the field to set. * @param value - * New value for the field. + * new value for the field. * * @throws IllegalArgumentException - * If the corresponding field can not be found. + * if the corresponding field can not be found. */ public void put(String name, int value) throws IllegalArgumentException { ObjectSlot slot = findSlot(name, Integer.TYPE); @@ -526,16 +526,16 @@ class EmulatedFields { } /** - * Find and set the long value of a given field named name in - * the receiver. + * Find and set the long value of a given field named {@code name} in the + * receiver. * * @param name - * A String, the name of the field to set + * the name of the field to set. * @param value - * New value for the field. + * new value for the field. * * @throws IllegalArgumentException - * If the corresponding field can not be found. + * if the corresponding field can not be found. */ public void put(String name, long value) throws IllegalArgumentException { ObjectSlot slot = findSlot(name, Long.TYPE); @@ -547,16 +547,16 @@ class EmulatedFields { } /** - * Find and set the Object value of a given field named name - * in the receiver. + * Find and set the Object value of a given field named {@code name} in the + * receiver. * * @param name - * A String, the name of the field to set + * the name of the field to set. * @param value - * New value for the field. + * new value for the field. * * @throws IllegalArgumentException - * If the corresponding field can not be found. + * if the corresponding field can not be found. */ public void put(String name, Object value) throws IllegalArgumentException { Class valueClass = null; @@ -572,16 +572,16 @@ class EmulatedFields { } /** - * Find and set the short value of a given field named name - * in the receiver. + * Find and set the short value of a given field named {@code name} in the + * receiver. * * @param name - * A String, the name of the field to set + * the name of the field to set. * @param value - * New value for the field. + * new value for the field. * * @throws IllegalArgumentException - * If the corresponding field can not be found. + * if the corresponding field can not be found. */ public void put(String name, short value) throws IllegalArgumentException { ObjectSlot slot = findSlot(name, Short.TYPE); @@ -593,16 +593,16 @@ class EmulatedFields { } /** - * Find and set the boolean value of a given field named name - * in the receiver. + * Find and set the boolean value of a given field named {@code name} in the + * receiver. * * @param name - * A String, the name of the field to set + * the name of the field to set. * @param value - * New value for the field. + * new value for the field. * * @throws IllegalArgumentException - * If the corresponding field can not be found. + * if the corresponding field can not be found. */ public void put(String name, boolean value) throws IllegalArgumentException { ObjectSlot slot = findSlot(name, Boolean.TYPE); diff --git a/libcore/luni/src/main/java/java/io/Externalizable.java b/libcore/luni/src/main/java/java/io/Externalizable.java index 03049eb4c..d07f0b7dd 100644 --- a/libcore/luni/src/main/java/java/io/Externalizable.java +++ b/libcore/luni/src/main/java/java/io/Externalizable.java @@ -20,8 +20,6 @@ package java.io; /** * Defines an interface for classes that want to be serializable, but have their * own binary representation. - * - * @since Android 1.0 */ public interface Externalizable extends Serializable { /** @@ -33,7 +31,6 @@ public interface Externalizable extends Serializable { * if an error occurs attempting to read from {@code input}. * @throws ClassNotFoundException * if the class of the instance being loaded cannot be found. - * @since Android 1.0 */ public void readExternal(ObjectInput input) throws IOException, ClassNotFoundException; @@ -45,7 +42,6 @@ public interface Externalizable extends Serializable { * the ObjectOutput to write the object to. * @throws IOException * if an error occurs attempting to write to {@code output}. - * @since Android 1.0 */ public void writeExternal(ObjectOutput output) throws IOException; } diff --git a/libcore/luni/src/main/java/java/io/File.java b/libcore/luni/src/main/java/java/io/File.java index f4e862371..c802995f7 100644 --- a/libcore/luni/src/main/java/java/io/File.java +++ b/libcore/luni/src/main/java/java/io/File.java @@ -21,15 +21,12 @@ import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.security.AccessController; +import java.security.SecureRandom; import java.util.ArrayList; -// BEGIN android-added -import java.util.Collections; -// END android-added import java.util.List; -// BEGIN android-removed -// import org.apache.harmony.luni.util.DeleteOnExit; -// END android-removed +import org.apache.harmony.luni.internal.io.FileCanonPathCache; +import org.apache.harmony.luni.util.DeleteOnExit; import org.apache.harmony.luni.util.Msg; import org.apache.harmony.luni.util.PriviAction; import org.apache.harmony.luni.util.Util; @@ -45,50 +42,39 @@ import org.apache.harmony.luni.util.Util; *

            * When manipulating file paths, the static fields of this class may be used to * determine the platform specific separators. - * + * * @see java.io.Serializable * @see java.lang.Comparable - * - * @since Android 1.0 */ public class File implements Serializable, Comparable { + private static final long serialVersionUID = 301077366599181567L; + private static final String EMPTY_STRING = ""; //$NON-NLS-1$ + private String path; transient byte[] properPath; /** - * The system dependent file separator character. Since Android is a Unix- - * based system, this defaults to '/'. - * - * @since Android 1.0 + * The system dependent file separator character. */ public static final char separatorChar; /** * The system dependent file separator string. The initial value of this - * field is the system property "file.separator". Since Android is a Unix- - * based system, this defaults to "/". - * - * @since Android 1.0 + * field is the system property "file.separator". */ public static final String separator; /** - * The system dependent path separator character. Since Android is a Unix- - * based system, this defaults to ':'. - * - * @since Android 1.0 + * The system dependent path separator character. */ public static final char pathSeparatorChar; /** * The system dependent path separator string. The initial value of this - * field is the system property "path.separator". Since Android is a Unix- - * based system, this defaults to ':'. - * - * @since Android 1.0 + * field is the system property "path.separator". */ public static final String pathSeparator; @@ -115,14 +101,13 @@ public class File implements Serializable, Comparable { /** * Constructs a new file using the specified directory and name. - * + * * @param dir * the directory where the file is stored. * @param name * the file's name. * @throws NullPointerException - * if {@code name} is null. - * @since Android 1.0 + * if {@code name} is {@code null}. */ public File(File dir, String name) { if (name == null) { @@ -137,10 +122,9 @@ public class File implements Serializable, Comparable { /** * Constructs a new file using the specified path. - * + * * @param path * the path to be used for the file. - * @since Android 1.0 */ public File(String path) { // path == null check & NullPointerException thrown by fixSlashes @@ -150,14 +134,13 @@ public class File implements Serializable, Comparable { /** * Constructs a new File using the specified directory path and file name, * placing a path separator between the two. - * + * * @param dirPath * the path to the directory where the file is stored. * @param name * the file's name. * @throws NullPointerException - * if {@code name} is null. - * @since Android 1.0 + * if {@code name} is {@code null}. */ public File(String dirPath, String name) { if (name == null) { @@ -175,7 +158,7 @@ public class File implements Serializable, Comparable { * needs to be an absolute and hierarchical Unified Resource Identifier with * file scheme and non-empty path component, but with undefined authority, * query or fragment components. - * + * * @param uri * the Unified Resource Identifier that is used to construct this * file. @@ -183,7 +166,6 @@ public class File implements Serializable, Comparable { * if {@code uri} does not comply with the conditions above. * @see #toURI * @see java.net.URI - * @since Android 1.0 */ public File(URI uri) { // check pre-conditions @@ -193,7 +175,7 @@ public class File implements Serializable, Comparable { private String calculatePath(String dirPath, String name) { dirPath = fixSlashes(dirPath); - if (!name.equals("")) { //$NON-NLS-1$ + if (!name.equals(EMPTY_STRING) || dirPath.equals(EMPTY_STRING)) { // Remove all the proceeding separator chars from name name = fixSlashes(name); @@ -217,36 +199,37 @@ public class File implements Serializable, Comparable { return dirPath; } + @SuppressWarnings("nls") private void checkURI(URI uri) { if (!uri.isAbsolute()) { - throw new IllegalArgumentException(Msg.getString("K031a", uri)); //$NON-NLS-1$ - } else if (!uri.getRawSchemeSpecificPart().startsWith("/")) { //$NON-NLS-1$ - throw new IllegalArgumentException(Msg.getString("K031b", uri)); //$NON-NLS-1$ + throw new IllegalArgumentException(Msg.getString("K031a", uri)); + } else if (!uri.getRawSchemeSpecificPart().startsWith("/")) { + throw new IllegalArgumentException(Msg.getString("K031b", uri)); } String temp = uri.getScheme(); - if (temp == null || !temp.equals("file")) { //$NON-NLS-1$ - throw new IllegalArgumentException(Msg.getString("K031c", uri)); //$NON-NLS-1$ + if (temp == null || !temp.equals("file")) { + throw new IllegalArgumentException(Msg.getString("K031c", uri)); } temp = uri.getRawPath(); if (temp == null || temp.length() == 0) { - throw new IllegalArgumentException(Msg.getString("K031d", uri)); //$NON-NLS-1$ + throw new IllegalArgumentException(Msg.getString("K031d", uri)); } if (uri.getRawAuthority() != null) { - throw new IllegalArgumentException(Msg.getString( - "K031e", new String[] { "authority", uri.toString() })); //$NON-NLS-1$ //$NON-NLS-2$ + throw new IllegalArgumentException(Msg.getString("K031e", + new String[] { "authority", uri.toString() })); } if (uri.getRawQuery() != null) { - throw new IllegalArgumentException(Msg.getString( - "K031e", new String[] { "query", uri.toString() })); //$NON-NLS-1$//$NON-NLS-2$ + throw new IllegalArgumentException(Msg.getString("K031e", + new String[] { "query", uri.toString() })); } if (uri.getRawFragment() != null) { - throw new IllegalArgumentException(Msg.getString( - "K031e", new String[] { "fragment", uri.toString() })); //$NON-NLS-1$ //$NON-NLS-2$ + throw new IllegalArgumentException(Msg.getString("K031e", + new String[] { "fragment", uri.toString() })); } } @@ -259,9 +242,8 @@ public class File implements Serializable, Comparable { * file systems, each with its own platform-dependent root. Further, the * canonical pathname of any file on the system will always begin with one * of the returned file system roots. - * + * * @return the array of file system roots. - * @since Android 1.0 */ public static File[] listRoots() { byte[][] rootsList = rootsImpl(); @@ -293,7 +275,8 @@ public class File implements Serializable, Comparable { char newPath[] = origPath.toCharArray(); for (int i = 0; i < length; i++) { char pathChar = newPath[i]; - if (pathChar == '\\' || pathChar == '/') { + if ((separatorChar == '\\' && pathChar == '\\') + || pathChar == '/') { /* UNC Name requires 2 leading slashes */ if ((foundSlash && i == uncIndex) || !foundSlash) { newPath[newLength++] = separatorChar; @@ -319,42 +302,40 @@ public class File implements Serializable, Comparable { && (newLength > (uncIndex + 1) || (newLength == 2 && newPath[0] != separatorChar))) { newLength--; } - String tempPath = new String(newPath, 0, newLength); - // If it's the same keep it identical for SecurityManager purposes - if (!tempPath.equals(origPath)) { - return tempPath; - } - return origPath; + + return new String(newPath, 0, newLength); } /** * Indicates whether the current context is allowed to read from this file. - * + * * @return {@code true} if this file can be read, {@code false} otherwise. * @throws SecurityException * if a {@code SecurityManager} is installed and it denies the * read request. - * @since Android 1.0 */ public boolean canRead() { + if (path.length() == 0) { + return false; + } SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkRead(path); } + byte[] pp = properPath(true); // BEGIN android-changed - return exists() && isReadableImpl(properPath(true)); + return exists() && isReadableImpl(pp); // END android-changed } /** * Indicates whether the current context is allowed to write to this file. - * + * * @return {@code true} if this file can be written, {@code false} * otherwise. * @throws SecurityException * if a {@code SecurityManager} is installed and it denies the * write request. - * @since Android 1.0 */ public boolean canWrite() { SecurityManager security = System.getSecurityManager(); @@ -375,13 +356,12 @@ public class File implements Serializable, Comparable { /** * Returns the relative sort ordering of the paths for this file and the * file {@code another}. The ordering is platform dependent. - * + * * @param another * a file to compare this file to * @return an int determined by comparing the two paths. Possible values are * described in the Comparable interface. * @see Comparable - * @since Android 1.0 */ public int compareTo(File another) { if (caseSensitive) { @@ -392,13 +372,12 @@ public class File implements Serializable, Comparable { /** * Deletes this file. Directories must be empty before they will be deleted. - * + * * @return {@code true} if this file was deleted, {@code false} otherwise. * @throws SecurityException * if a {@code SecurityManager} is installed and it denies the * request. * @see java.lang.SecurityManager#checkDelete - * @since Android 1.0 */ public boolean delete() { SecurityManager security = System.getSecurityManager(); @@ -418,13 +397,12 @@ public class File implements Serializable, Comparable { /** * Schedules this file to be automatically deleted once the virtual machine - * terminates. This will only happen when the virtual machine terminates + * terminates. This will only happen when the virtual machine terminates * normally as described by the Java Language Specification section 12.9. - * + * * @throws SecurityException * if a {@code SecurityManager} is installed and it denies the * request. - * @since Android 1.0 */ public void deleteOnExit() { SecurityManager security = System.getSecurityManager(); @@ -439,12 +417,11 @@ public class File implements Serializable, Comparable { /** * Compares {@code obj} to this file and returns {@code true} if they * represent the same object using a path specific comparison. - * + * * @param obj * the object to compare this file with. * @return {@code true} if {@code obj} is the same as this object, * {@code false} otherwise. - * @since Android 1.0 */ @Override public boolean equals(Object obj) { @@ -460,13 +437,13 @@ public class File implements Serializable, Comparable { /** * Returns a boolean indicating whether this file can be found on the * underlying file system. - * + * * @return {@code true} if this file exists, {@code false} otherwise. * @throws SecurityException * if a {@code SecurityManager} is installed and it denies read * access to this file. * @see #getPath - * @since Android 1.0 + * @see java.lang.SecurityManager#checkRead(FileDescriptor) */ public boolean exists() { if (path.length() == 0) { @@ -483,22 +460,20 @@ public class File implements Serializable, Comparable { /** * Returns the absolute path of this file. - * + * * @return the absolute file path. * @see java.lang.SecurityManager#checkPropertyAccess - * @since Android 1.0 */ public String getAbsolutePath() { byte[] absolute = properPath(false); - return Util.toString(absolute); + return Util.toUTF8String(absolute); } /** * Returns a new file constructed using the absolute path of this file. - * + * * @return a new file from this file's absolute path. * @see java.lang.SecurityManager#checkPropertyAccess - * @since Android 1.0 */ public File getAbsoluteFile() { return new File(this.getAbsolutePath()); @@ -513,42 +488,26 @@ public class File implements Serializable, Comparable { * with a direct directory reference. If the file does not exist, * getCanonicalPath() may not resolve any references and simply returns an * absolute path name or throws an IOException. - * + * * @return the canonical path of this file. * @throws IOException * if an I/O error occurs. * @see java.lang.SecurityManager#checkPropertyAccess - * @since Android 1.0 */ public String getCanonicalPath() throws IOException { byte[] result = properPath(false); + String absPath = Util.toUTF8String(result); + String canonPath = FileCanonPathCache.get(absPath); - boolean exists = false; - byte[] pathBytes = result; - do { - byte[] linkBytes = getLinkImpl(pathBytes); - if (linkBytes == pathBytes) { - break; - } - if (linkBytes[0] == separatorChar) { - pathBytes = linkBytes; - } else { - int index = pathBytes.length - 1; - while (pathBytes[index] != separatorChar) { - index--; - } - byte[] temp = new byte[index + 1 + linkBytes.length]; - System.arraycopy(pathBytes, 0, temp, 0, index + 1); - System.arraycopy(linkBytes, 0, temp, index + 1, - linkBytes.length); - pathBytes = temp; - } - exists = existsImpl(pathBytes); - } while (exists); - if (exists) { - result = pathBytes; + if (canonPath != null) { + return canonPath; + } + if(separatorChar == '/') { + // resolve the full path first + result = resolveLink(result, result.length, false); + // resolve the parent directories + result = resolve(result); } - int numSeparators = 1; for (int i = 0; i < result.length; i++) { if (result[i] == separatorChar) { @@ -614,18 +573,106 @@ public class File implements Serializable, Comparable { newResult[newLength] = 0; newResult = getCanonImpl(newResult); newLength = newResult.length; - return Util.toString(newResult, 0, newLength); + canonPath = Util.toUTF8String(newResult, 0, newLength); + FileCanonPathCache.put(absPath, canonPath); + return canonPath; + } + + /* + * Resolve symbolic links in the parent directories. + */ + private byte[] resolve(byte[] newResult) throws IOException { + int last = 1, nextSize, linkSize; + byte[] linkPath = newResult, bytes; + boolean done, inPlace; + for (int i = 1; i <= newResult.length; i++) { + if (i == newResult.length || newResult[i] == separatorChar) { + done = i >= newResult.length - 1; + // if there is only one segment, do nothing + if (done && linkPath.length == 1) { + return newResult; + } + inPlace = false; + if (linkPath == newResult) { + bytes = newResult; + // if there are no symbolic links, terminate the C string + // instead of copying + if (!done) { + inPlace = true; + newResult[i] = '\0'; + } + } else { + nextSize = i - last + 1; + linkSize = linkPath.length; + if (linkPath[linkSize - 1] == separatorChar) { + linkSize--; + } + bytes = new byte[linkSize + nextSize]; + System.arraycopy(linkPath, 0, bytes, 0, linkSize); + System.arraycopy(newResult, last - 1, bytes, linkSize, + nextSize); + // the full path has already been resolved + } + if (done) { + return bytes; + } + linkPath = resolveLink(bytes, inPlace ? i : bytes.length, true); + if (inPlace) { + newResult[i] = '/'; + } + last = i + 1; + } + } + throw new InternalError(); + } + + /* + * Resolve a symbolic link. While the path resolves to an existing path, + * keep resolving. If an absolute link is found, resolve the parent + * directories if resolveAbsolute is true. + */ + private byte[] resolveLink(byte[] pathBytes, int length, + boolean resolveAbsolute) throws IOException { + boolean restart = false; + byte[] linkBytes, temp; + do { + linkBytes = getLinkImpl(pathBytes); + if (linkBytes == pathBytes) { + break; + } + if (linkBytes[0] == separatorChar) { + // link to an absolute path, if resolving absolute paths, + // resolve the parent dirs again + restart = resolveAbsolute; + pathBytes = linkBytes; + } else { + int last = length - 1; + while (pathBytes[last] != separatorChar) { + last--; + } + last++; + temp = new byte[last + linkBytes.length]; + System.arraycopy(pathBytes, 0, temp, 0, last); + System.arraycopy(linkBytes, 0, temp, last, linkBytes.length); + pathBytes = temp; + } + length = pathBytes.length; + } while (existsImpl(pathBytes)); + // resolve the parent directories + if (restart) { + return resolve(pathBytes); + } + return pathBytes; } /** * Returns a new file created using the canonical path of this file. * Equivalent to {@code new File(this.getCanonicalPath())}. - * + * * @return the new file constructed from this file's canonical path. * @throws IOException * if an I/O error occurs. * @see java.lang.SecurityManager#checkPropertyAccess - * @since Android 1.0 */ public File getCanonicalFile() throws IOException { return new File(getCanonicalPath()); @@ -635,10 +682,9 @@ public class File implements Serializable, Comparable { /** * Returns the name of the file or directory represented by this file. - * + * * @return this file's name or an empty string if there is no name part in * the file's path. - * @since Android 1.0 */ public String getName() { int separatorIndex = path.lastIndexOf(separator); @@ -650,9 +696,8 @@ public class File implements Serializable, Comparable { * Returns the pathname of the parent of this file. This is the path up to * but not including the last name. {@code null} is returned if there is no * parent. - * + * * @return this file's parent pathname or {@code null}. - * @since Android 1.0 */ public String getParent() { int length = path.length(), firstInPath = 0; @@ -677,9 +722,8 @@ public class File implements Serializable, Comparable { * Returns a new file made from the pathname of the parent of this file. * This is the path up to but not including the last name. {@code null} is * returned when there is no parent. - * + * * @return a new file representing this file's parent or {@code null}. - * @since Android 1.0 */ public File getParentFile() { String tempParent = getParent(); @@ -691,9 +735,8 @@ public class File implements Serializable, Comparable { /** * Returns the path of this file. - * + * * @return this file's path. - * @since Android 1.0 */ public String getPath() { return path; @@ -702,10 +745,9 @@ public class File implements Serializable, Comparable { /** * Returns an integer hash code for the receiver. Any two objects for which * {@code equals} returns {@code true} must return the same hash code. - * + * * @return this files's hash value. * @see #equals - * @since Android 1.0 */ @Override public int hashCode() { @@ -721,11 +763,10 @@ public class File implements Serializable, Comparable { * the character '/'; on Windows it is absolute if either it starts with * '\', '/', '\\' (to represent a file server), or a letter followed by a * colon. - * + * * @return {@code true} if this file's pathname is absolute, {@code false} * otherwise. * @see #getPath - * @since Android 1.0 */ public boolean isAbsolute() { // BEGIN android-changed @@ -741,13 +782,12 @@ public class File implements Serializable, Comparable { /** * Indicates if this file represents a directory on the * underlying file system. - * + * * @return {@code true} if this file is a directory, {@code false} * otherwise. * @throws SecurityException * if a {@code SecurityManager} is installed and it denies read * access to this file. - * @since Android 1.0 */ public boolean isDirectory() { if (path.length() == 0) { @@ -765,12 +805,11 @@ public class File implements Serializable, Comparable { /** * Indicates if this file represents a file on the underlying * file system. - * + * * @return {@code true} if this file is a file, {@code false} otherwise. * @throws SecurityException * if a {@code SecurityManager} is installed and it denies read * access to this file. - * @since Android 1.0 */ public boolean isFile() { if (path.length() == 0) { @@ -787,16 +826,15 @@ public class File implements Serializable, Comparable { /** * Returns whether or not this file is a hidden file as defined by the - * operating system. The notion of "hidden" is system-dependent. For - * Unix systems (like Android) a file is considered hidden if its name - * starts with a ".". For Windows systems there is an explicit flag in the - * file system for this purpose. - * + * operating system. The notion of "hidden" is system-dependent. For Unix + * systems a file is considered hidden if its name starts with a ".". For + * Windows systems there is an explicit flag in the file system for this + * purpose. + * * @return {@code true} if the file is hidden, {@code false} otherwise. * @throws SecurityException * if a {@code SecurityManager} is installed and it denies read * access to this file. - * @since Android 1.0 */ public boolean isHidden() { if (path.length() == 0) { @@ -822,12 +860,11 @@ public class File implements Serializable, Comparable { /** * Returns the time when this file was last modified, measured in * milliseconds since January 1st, 1970, midnight. - * + * * @return the time when this file was last modified. * @throws SecurityException * if a {@code SecurityManager} is installed and it denies read * access to this file. - * @since Android 1.0 */ public long lastModified() { SecurityManager security = System.getSecurityManager(); @@ -847,7 +884,7 @@ public class File implements Serializable, Comparable { /** * Sets the time this file was last modified, measured in milliseconds since * January 1st, 1970, midnight. - * + * * @param time * the last modification time for this file. * @return {@code true} if the operation is successful, {@code false} @@ -857,7 +894,6 @@ public class File implements Serializable, Comparable { * @throws SecurityException * if a {@code SecurityManager} is installed and it denies write * access to this file. - * @since Android 1.0 */ public boolean setLastModified(long time) { if (time < 0) { @@ -875,13 +911,12 @@ public class File implements Serializable, Comparable { /** * Marks this file or directory to be read-only as defined by the operating * system. - * + * * @return {@code true} if the operation is successful, {@code false} * otherwise. * @throws SecurityException * if a {@code SecurityManager} is installed and it denies write * access to this file. - * @since Android 1.0 */ public boolean setReadOnly() { SecurityManager security = System.getSecurityManager(); @@ -895,12 +930,11 @@ public class File implements Serializable, Comparable { /** * Returns the length of this file in bytes. - * + * * @return the number of bytes in this file. * @throws SecurityException * if a {@code SecurityManager} is installed and it denies read * access to this file. - * @since Android 1.0 */ public long length() { SecurityManager security = System.getSecurityManager(); @@ -914,35 +948,42 @@ public class File implements Serializable, Comparable { /** * Returns an array of strings with the file names in the directory - * represented by this file. The result is {@ null} if this file is not a - * directory. + * represented by this file. The result is {@code null} if this file is not + * a directory. *

            * The entries {@code .} and {@code ..} representing the current and parent * directory are not returned as part of the list. - *

            - * + * * @return an array of strings with file names or {@code null}. * @throws SecurityException * if a {@code SecurityManager} is installed and it denies read * access to this file. * @see #isDirectory - * @since Android 1.0 + * @see java.lang.SecurityManager#checkRead(FileDescriptor) */ public java.lang.String[] list() { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkRead(path); } - if (!isDirectory()) { + + if (path.length() == 0) { return null; } - byte[][] implList = listImpl(properPath(true)); + + byte[] bs = properPath(true); + if (!isDirectoryImpl(bs) || !existsImpl(bs) || !isReadableImpl(bs)) { + return null; + } + + byte[][] implList = listImpl(bs); if (implList == null) { + // empty list return new String[0]; } String result[] = new String[implList.length]; for (int index = 0; index < implList.length; index++) { - result[index] = Util.toString(implList[index]); + result[index] = Util.toUTF8String(implList[index]); } return result; } @@ -952,13 +993,13 @@ public class File implements Serializable, Comparable { * file. The result is {@code null} if this file is not a directory. The * paths of the files in the array are absolute if the path of this file is * absolute, they are relative otherwise. - * + * * @return an array of files or {@code null}. * @throws SecurityException * if a {@code SecurityManager} is installed and it denies read * access to this file. * @see #list - * @since Android 1.0 + * @see #isDirectory */ public File[] listFiles() { String[] tempNames = list(); @@ -982,8 +1023,7 @@ public class File implements Serializable, Comparable { *

            * The entries {@code .} and {@code ..} representing the current and parent * directories are not returned as part of the list. - *

            - * + * * @param filter * the filter to match names against, may be {@code null}. * @return an array of files or {@code null}. @@ -991,7 +1031,9 @@ public class File implements Serializable, Comparable { * if a {@code SecurityManager} is installed and it denies read * access to this file. * @see #list(FilenameFilter filter) - * @since Android 1.0 + * @see #getPath + * @see #isDirectory + * @see java.lang.SecurityManager#checkRead(FileDescriptor) */ public File[] listFiles(FilenameFilter filter) { String[] tempNames = list(filter); @@ -1014,25 +1056,33 @@ public class File implements Serializable, Comparable { *

            * The entries {@code .} and {@code ..} representing the current and parent * directories are not returned as part of the list. - *

            - * + * * @param filter * the filter to match names against, may be {@code null}. * @return an array of files or {@code null}. * @throws SecurityException * if a {@code SecurityManager} is installed and it denies read * access to this file. - * @since Android 1.0 + * @see #getPath + * @see #isDirectory + * @see java.lang.SecurityManager#checkRead(FileDescriptor) */ public File[] listFiles(FileFilter filter) { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkRead(path); } - if (!isDirectory()) { + + if (path.length() == 0) { + return null; + } + + byte[] bs = properPath(true); + if (!isDirectoryImpl(bs) || !existsImpl(bs) || !isReadableImpl(bs)) { return null; } - byte[][] implList = listImpl(properPath(true)); + + byte[][] implList = listImpl(bs); if (implList == null) { return new File[0]; } @@ -1056,38 +1106,46 @@ public class File implements Serializable, Comparable { *

            * The entries {@code .} and {@code ..} representing the current and parent * directories are not returned as part of the list. - *

            - * + * * @param filter * the filter to match names against, may be {@code null}. * @return an array of files or {@code null}. * @throws SecurityException * if a {@code SecurityManager} is installed and it denies read * access to this file. - * @since Android 1.0 + * @see #getPath + * @see #isDirectory + * @see java.lang.SecurityManager#checkRead(FileDescriptor) */ public java.lang.String[] list(FilenameFilter filter) { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkRead(path); } - if (!isDirectory()) { + + if (path.length() == 0) { + return null; + } + + byte[] bs = properPath(true); + if (!isDirectoryImpl(bs) || !existsImpl(bs) || !isReadableImpl(bs)) { return null; } - byte[][] implList = listImpl(properPath(true)); + + byte[][] implList = listImpl(bs); if (implList == null) { + // empty list return new String[0]; } - java.util.Vector tempResult = new java.util.Vector(); + List tempResult = new ArrayList(); for (int index = 0; index < implList.length; index++) { String aName = Util.toString(implList[index]); if (filter == null || filter.accept(this, aName)) { - tempResult.addElement(aName); + tempResult.add(aName); } } - String[] result = new String[tempResult.size()]; - tempResult.copyInto(result); - return result; + + return tempResult.toArray(new String[tempResult.size()]); } private synchronized static native byte[][] listImpl(byte[] path); @@ -1095,14 +1153,13 @@ public class File implements Serializable, Comparable { /** * Creates the directory named by the trailing filename of this file. Does * not create the complete path required to create this directory. - * + * * @return {@code true} if the directory has been created, {@code false} * otherwise. * @throws SecurityException * if a {@code SecurityManager} is installed and it denies write * access for this file. * @see #mkdirs - * @since Android 1.0 */ public boolean mkdir() { SecurityManager security = System.getSecurityManager(); @@ -1117,7 +1174,7 @@ public class File implements Serializable, Comparable { /** * Creates the directory named by the trailing filename of this file, * including the complete directory path required to create this directory. - * + * * @return {@code true} if the necessary directories have been created, * {@code false} if the target directory already exists or one of * the directories can not be created. @@ -1125,7 +1182,6 @@ public class File implements Serializable, Comparable { * if a {@code SecurityManager} is installed and it denies write * access for this file. * @see #mkdir - * @since Android 1.0 */ public boolean mkdirs() { /* If the terminal directory already exists, answer false */ @@ -1151,7 +1207,7 @@ public class File implements Serializable, Comparable { /** * Creates a new, empty file on the file system according to the path * information stored in this file. - * + * * @return {@code true} if the file has been created, {@code false} if it * already exists. * @throws IOException @@ -1160,7 +1216,6 @@ public class File implements Serializable, Comparable { * @throws SecurityException * if a {@code SecurityManager} is installed and it denies write * access for this file. - * @since Android 1.0 */ public boolean createNewFile() throws IOException { SecurityManager security = System.getSecurityManager(); @@ -1215,10 +1270,11 @@ public class File implements Serializable, Comparable { /** * Creates an empty temporary file using the given prefix and suffix as part - * of the file name. If suffix is null, {@code .tmp} is used. This method - * is a convenience method that calls {@link #createTempFile(String, String, - * File)} with the third argument being {@code null}. - * + * of the file name. If suffix is {@code null}, {@code .tmp} is used. This + * method is a convenience method that calls + * {@link #createTempFile(String, String, File)} with the third argument + * being {@code null}. + * * @param prefix * the prefix to the temp file name. * @param suffix @@ -1226,7 +1282,6 @@ public class File implements Serializable, Comparable { * @return the temporary file. * @throws IOException * if an error occurs when writing the file. - * @since Android 1.0 */ public static File createTempFile(String prefix, String suffix) throws IOException { @@ -1236,7 +1291,7 @@ public class File implements Serializable, Comparable { /** * Creates an empty temporary file in the given directory using the given * prefix and suffix as part of the file name. - * + * * @param prefix * the prefix to the temp file name. * @param suffix @@ -1246,26 +1301,30 @@ public class File implements Serializable, Comparable { * {@code null} for the default location for temporary files, * which is taken from the "java.io.tmpdir" system property. It * may be necessary to set this property to an existing, writable - * directory for this method to work properly. + * directory for this method to work properly. * @return the temporary file. * @throws IllegalArgumentException * if the length of {@code prefix} is less than 3. * @throws IOException * if an error occurs when writing the file. - * @since Android 1.0 */ + @SuppressWarnings("nls") public static File createTempFile(String prefix, String suffix, File directory) throws IOException { // Force a prefix null check first if (prefix.length() < 3) { - throw new IllegalArgumentException(Msg.getString("K006b")); //$NON-NLS-1$ - } - String newSuffix = suffix == null ? ".tmp" : suffix; //$NON-NLS-1$ - String tmpDir = "."; //$NON-NLS-1$ - tmpDir = AccessController.doPrivileged(new PriviAction( - "java.io.tmpdir", ".")); //$NON-NLS-1$//$NON-NLS-2$ - File result, tmpDirFile = directory == null ? new File(tmpDir) - : directory; + throw new IllegalArgumentException(Msg.getString("K006b")); + } + String newSuffix = suffix == null ? ".tmp" : suffix; + File tmpDirFile; + if (directory == null) { + String tmpDir = AccessController.doPrivileged( + new PriviAction("java.io.tmpdir", ".")); + tmpDirFile = new File(tmpDir); + } else { + tmpDirFile = directory; + } + File result; do { result = genTempFile(prefix, newSuffix, tmpDirFile); } while (!result.createNewFile()); @@ -1274,7 +1333,7 @@ public class File implements Serializable, Comparable { private static File genTempFile(String prefix, String suffix, File directory) { if (counter == 0) { - int newInt = new java.util.Random().nextInt(); + int newInt = new SecureRandom().nextInt(); counter = ((newInt / 65535) & 0xFFFF) + 0x2710; } StringBuilder newName = new StringBuilder(); @@ -1290,7 +1349,7 @@ public class File implements Serializable, Comparable { * Returns a string representing the proper path for this file. If this file * path is absolute, the user.dir property is not prepended, otherwise it * is. - * + * * @param internal * is user.dir internal. * @return the proper path. @@ -1311,7 +1370,7 @@ public class File implements Serializable, Comparable { userdir = System.getProperty("user.dir"); //$NON-NLS-1$ } if (path.length() == 0) { - return properPath = Util.getBytes(userdir); + return properPath = Util.getUTF8Bytes(userdir); } int length = userdir.length(); if (length > 0 && userdir.charAt(length - 1) == separatorChar) { @@ -1328,14 +1387,13 @@ public class File implements Serializable, Comparable { /** * Renames this file to the name represented by the {@code dest} file. This * works for both normal files and directories. - * + * * @param dest * the file containing the new name. * @return {@code true} if the File was renamed, {@code false} otherwise. * @throws SecurityException * if a {@code SecurityManager} is installed and it denies write * access for this file or the {@code dest} file. - * @since Android 1.0 */ public boolean renameTo(java.io.File dest) { SecurityManager security = System.getSecurityManager(); @@ -1351,9 +1409,8 @@ public class File implements Serializable, Comparable { /** * Returns a string containing a concise, human-readable description of this * file. - * + * * @return a printable representation of this file. - * @since Android 1.0 */ @Override public String toString() { @@ -1364,22 +1421,22 @@ public class File implements Serializable, Comparable { * Returns a Uniform Resource Identifier for this file. The URI is system * dependent and may not be transferable between different operating / file * systems. - * + * * @return an URI for this file. - * @since Android 1.0 */ + @SuppressWarnings("nls") public URI toURI() { String name = getAbsoluteName(); try { - if (!name.startsWith("/")) { //$NON-NLS-1$ + if (!name.startsWith("/")) { // start with sep. - return new URI("file", null, //$NON-NLS-1$ - new StringBuilder(name.length() + 1).append('/') - .append(name).toString(), null, null); - } else if (name.startsWith("//")) { //$NON-NLS-1$ - return new URI("file", name, null); // UNC path //$NON-NLS-1$ + return new URI("file", null, new StringBuilder( + name.length() + 1).append('/').append(name).toString(), + null, null); + } else if (name.startsWith("//")) { + return new URI("file", "", name, null); // UNC path } - return new URI("file", null, name, null, null); //$NON-NLS-1$ + return new URI("file", null, name, null, null); } catch (URISyntaxException e) { // this should never happen return null; @@ -1390,22 +1447,23 @@ public class File implements Serializable, Comparable { * Returns a Uniform Resource Locator for this file. The URL is system * dependent and may not be transferable between different operating / file * systems. - * - * @return an URL for this file. + * + * @return a URL for this file. * @throws java.net.MalformedURLException - * if the path cannot be transformed into an URL. - * @since Android 1.0 + * if the path cannot be transformed into a URL. */ + @SuppressWarnings("nls") public URL toURL() throws java.net.MalformedURLException { String name = getAbsoluteName(); - if (!name.startsWith("/")) { //$NON-NLS-1$ + if (!name.startsWith("/")) { // start with sep. - return new URL("file", "", -1, new StringBuilder(name.length() + 1) //$NON-NLS-1$ //$NON-NLS-2$ - .append('/').append(name).toString(), null); - } else if (name.startsWith("//")) { //$NON-NLS-1$ - return new URL("file:" + name); // UNC path //$NON-NLS-1$ + return new URL( + "file", EMPTY_STRING, -1, new StringBuilder(name.length() + 1) //$NON-NLS-1$ + .append('/').append(name).toString(), null); + } else if (name.startsWith("//")) { + return new URL("file:" + name); // UNC path } - return new URL("file", "", -1, name, null); //$NON-NLS-1$ //$NON-NLS-2$ + return new URL("file", EMPTY_STRING, -1, name, null); } private String getAbsoluteName() { @@ -1436,60 +1494,3 @@ public class File implements Serializable, Comparable { path = path.replace(inSeparator, separatorChar); } } - -// BEGIN android-added -/** - * Implements the actual DeleteOnExit mechanism. Is registered as a shutdown - * hook in the Runtime, once it is actually being used. - */ -class DeleteOnExit extends Thread { - - /** - * Our singleton instance. - */ - private static DeleteOnExit instance; - - /** - * Our list of files scheduled for deletion. - */ - private ArrayList files = new ArrayList(); - - /** - * Returns our singleton instance, creating it if necessary. - */ - public static synchronized DeleteOnExit getInstance() { - if (instance == null) { - instance = new DeleteOnExit(); - Runtime.getRuntime().addShutdownHook(instance); - } - - return instance; - } - - /** - * Schedules a file for deletion. - * - * @param filename The file to delete. - */ - public void addFile(String filename) { - synchronized(files) { - if (!files.contains(filename)) { - files.add(filename); - } - } - } - - /** - * Does the actual work. Note we (a) first sort the files lexicographically - * and then (b) delete them in reverse order. This is to make sure files - * get deleted before their parent directories. - */ - @Override - public void run() { - Collections.sort(files); - for (int i = files.size() - 1; i >= 0; i--) { - new File(files.get(i)).delete(); - } - } -} -// END android-added diff --git a/libcore/luni/src/main/java/java/io/FileDescriptor.java b/libcore/luni/src/main/java/java/io/FileDescriptor.java index e131c8823..15eb98315 100644 --- a/libcore/luni/src/main/java/java/io/FileDescriptor.java +++ b/libcore/luni/src/main/java/java/io/FileDescriptor.java @@ -30,33 +30,25 @@ package java.io; * System.err streams respectively. *

            * Applications should not create new FileDescriptors. - * + * * @see FileInputStream#getFD() * @see FileOutputStream#getFD() * @see RandomAccessFile#getFD() - * - * @since Android 1.0 */ public final class FileDescriptor { /** * The FileDescriptor representing standard input. - * - * @since Android 1.0 */ public static final FileDescriptor in = new FileDescriptor(); /** * FileDescriptor representing standard out. - * - * @since Android 1.0 */ public static final FileDescriptor out = new FileDescriptor(); /** * FileDescriptor representing standard error. - * - * @since Android 1.0 */ public static final FileDescriptor err = new FileDescriptor(); @@ -68,8 +60,8 @@ public final class FileDescriptor { */ int descriptor = -1; // END android-changed - - boolean readOnly = false; + + boolean readOnly = false; private static native void oneTimeInitialization(); @@ -84,8 +76,6 @@ public final class FileDescriptor { /** * Constructs a new FileDescriptor containing an invalid handle. The * contained handle is usually modified by native code at a later point. - * - * @since Android 1.0 */ public FileDescriptor() { super(); @@ -94,10 +84,9 @@ public final class FileDescriptor { /** * Ensures that data which is buffered within the underlying implementation * is written out to the appropriate device before returning. - * + * * @throws SyncFailedException * when the operation fails. - * @since Android 1.0 */ public void sync() throws SyncFailedException { // if the descriptor is a read-only one, do nothing @@ -105,15 +94,16 @@ public final class FileDescriptor { syncImpl(); } } - + private native void syncImpl() throws SyncFailedException; /** * Indicates whether this FileDescriptor is valid. - * + * * @return {@code true} if this FileDescriptor is valid, {@code false} * otherwise. - * @since Android 1.0 */ - public native boolean valid(); + public boolean valid() { + return descriptor != -1; + } } diff --git a/libcore/luni/src/main/java/java/io/FileFilter.java b/libcore/luni/src/main/java/java/io/FileFilter.java index 674ce1370..5638dd9d6 100644 --- a/libcore/luni/src/main/java/java/io/FileFilter.java +++ b/libcore/luni/src/main/java/java/io/FileFilter.java @@ -24,21 +24,18 @@ package java.io; /** * An interface for filtering {@link File} objects based on their names * or other information. - * + * * @see File#listFiles(FileFilter) - * - * @since Android 1.0 */ public interface FileFilter { /** * Indicating whether a specific file should be included in a pathname list. - * + * * @param pathname * the abstract file to check. * @return {@code true} if the file should be included, {@code false} * otherwise. - * @since Android 1.0 */ public abstract boolean accept(File pathname); } diff --git a/libcore/luni/src/main/java/java/io/FileInputStream.java b/libcore/luni/src/main/java/java/io/FileInputStream.java index c39d1e73f..12432627f 100644 --- a/libcore/luni/src/main/java/java/io/FileInputStream.java +++ b/libcore/luni/src/main/java/java/io/FileInputStream.java @@ -31,11 +31,9 @@ import org.apache.harmony.nio.FileChannelFactory; * Since this may induce some performance penalty, in particular if many small * read requests are made, a FileInputStream is often wrapped by a * BufferedInputStream. - * + * * @see BufferedInputStream * @see FileOutputStream - * - * @since Android 1.0 */ public class FileInputStream extends InputStream implements Closeable { /** @@ -58,7 +56,7 @@ public class FileInputStream extends InputStream implements Closeable { /** * Constructs a new {@code FileInputStream} based on {@code file}. - * + * * @param file * the file from which this stream reads. * @throws FileNotFoundException @@ -66,7 +64,6 @@ public class FileInputStream extends InputStream implements Closeable { * @throws SecurityException * if a {@code SecurityManager} is installed and it denies the * read request. - * @since Android 1.0 */ public FileInputStream(File file) throws FileNotFoundException { super(); @@ -90,7 +87,7 @@ public class FileInputStream extends InputStream implements Closeable { * Constructs a new {@code FileInputStream} on the {@link FileDescriptor} * {@code fd}. The file must already be open, therefore no * {@code FileNotFoundException} will be thrown. - * + * * @param fd * the FileDescriptor from which this stream reads. * @throws NullPointerException @@ -98,7 +95,6 @@ public class FileInputStream extends InputStream implements Closeable { * @throws SecurityException * if a {@code SecurityManager} is installed and it denies the * read request. - * @since Android 1.0 */ public FileInputStream(FileDescriptor fd) { super(); @@ -121,7 +117,7 @@ public class FileInputStream extends InputStream implements Closeable { * Constructs a new {@code FileInputStream} on the file named * {@code fileName}. The path of {@code fileName} may be absolute or * relative to the system property {@code "user.dir"}. - * + * * @param fileName * the path and name of the file from which this stream reads. * @throws FileNotFoundException @@ -129,7 +125,6 @@ public class FileInputStream extends InputStream implements Closeable { * @throws SecurityException * if a {@code SecurityManager} is installed and it denies the * read request. - * @since Android 1.0 */ public FileInputStream(String fileName) throws FileNotFoundException { this(null == fileName ? (File) null : new File(fileName)); @@ -139,11 +134,10 @@ public class FileInputStream extends InputStream implements Closeable { * Returns the number of bytes that are available before this stream will * block. This method always returns the size of the file minus the current * position. - * + * * @return the number of bytes available before blocking. * @throws IOException * if an error occurs in this stream. - * @since Android 1.0 */ @Override public int available() throws IOException { @@ -157,7 +151,7 @@ public class FileInputStream extends InputStream implements Closeable { // use. return fileSystem.ioctlAvailable(fd.descriptor); - // END android-added + // END android-added // BEGIN android-deleted // synchronized (repositioningLock) { @@ -179,10 +173,9 @@ public class FileInputStream extends InputStream implements Closeable { /** * Closes this stream. - * + * * @throws IOException * if an error occurs attempting to close this stream. - * @since Android 1.0 */ @Override public void close() throws IOException { @@ -203,10 +196,9 @@ public class FileInputStream extends InputStream implements Closeable { /** * Ensures that all resources for this stream are released when it is about * to be garbage collected. - * + * * @throws IOException * if an error occurs attempting to finalize this stream. - * @since Android 1.0 */ @Override protected void finalize() throws IOException { @@ -220,10 +212,8 @@ public class FileInputStream extends InputStream implements Closeable { * that is the same as the current position of this stream within the file. * All changes made to the underlying file descriptor state via the channel * are visible by the input stream and vice versa. - *

            - * + * * @return the file channel for this stream. - * @since Android 1.0 */ public FileChannel getChannel() { // BEGIN android-changed @@ -240,12 +230,11 @@ public class FileInputStream extends InputStream implements Closeable { /** * Returns the {@link FileDescriptor} representing the operating system * resource for this stream. - * + * * @return the {@code FileDescriptor} for this stream. * @throws IOException * if an error occurs while getting this stream's * {@code FileDescriptor}. - * @since Android 1.0 */ public final FileDescriptor getFD() throws IOException { return fd; @@ -255,11 +244,10 @@ public class FileInputStream extends InputStream implements Closeable { * Reads a single byte from this stream and returns it as an integer in the * range from 0 to 255. Returns -1 if the end of this stream has been * reached. - * + * * @return the byte read or -1 if the end of this stream has been reached. * @throws IOException * if this stream is closed or another I/O error occurs. - * @since Android 1.0 */ @Override public int read() throws IOException { @@ -271,14 +259,13 @@ public class FileInputStream extends InputStream implements Closeable { /** * Reads bytes from this stream and stores them in the byte array * {@code buffer}. - * + * * @param buffer * the byte array in which to store the bytes read. * @return the number of bytes actually read or -1 if the end of the stream * has been reached. * @throws IOException * if this stream is closed or another I/O error occurs. - * @since Android 1.0 */ @Override public int read(byte[] buffer) throws IOException { @@ -288,7 +275,7 @@ public class FileInputStream extends InputStream implements Closeable { /** * Reads at most {@code count} bytes from this stream and stores them in the * byte array {@code buffer} starting at {@code offset}. - * + * * @param buffer * the byte array in which to store the bytes read. * @param offset @@ -304,7 +291,6 @@ public class FileInputStream extends InputStream implements Closeable { * {@code buffer}. * @throws IOException * if the stream is closed or another IOException occurs. - * @since Android 1.0 */ @Override public int read(byte[] buffer, int offset, int count) throws IOException { @@ -338,14 +324,13 @@ public class FileInputStream extends InputStream implements Closeable { * Skips {@code count} number of bytes in this stream. Subsequent * {@code read()}'s will not return these bytes unless {@code reset()} is * used. This method may perform multiple reads to read {@code count} bytes. - * + * * @param count * the number of bytes to skip. * @return the number of bytes actually skipped. * @throws IOException * if {@code count < 0}, this stream is closed or another * IOException occurs. - * @since Android 1.0 */ @Override public long skip(long count) throws IOException { diff --git a/libcore/luni/src/main/java/java/io/FileNotFoundException.java b/libcore/luni/src/main/java/java/io/FileNotFoundException.java index a0f5bc61c..2ab9e895e 100644 --- a/libcore/luni/src/main/java/java/io/FileNotFoundException.java +++ b/libcore/luni/src/main/java/java/io/FileNotFoundException.java @@ -19,8 +19,6 @@ package java.io; /** * Thrown when a file specified by a program cannot be found. - * - * @since Android 1.0 */ public class FileNotFoundException extends IOException { @@ -29,8 +27,6 @@ public class FileNotFoundException extends IOException { /** * Constructs a new {@code FileNotFoundException} with its stack trace * filled in. - * - * @since Android 1.0 */ public FileNotFoundException() { super(); @@ -42,7 +38,6 @@ public class FileNotFoundException extends IOException { * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public FileNotFoundException(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/io/FileOutputStream.java b/libcore/luni/src/main/java/java/io/FileOutputStream.java index 00ae5d8f0..f0b25c93f 100644 --- a/libcore/luni/src/main/java/java/io/FileOutputStream.java +++ b/libcore/luni/src/main/java/java/io/FileOutputStream.java @@ -31,10 +31,10 @@ import org.apache.harmony.nio.FileChannelFactory; * Since this may induce some performance penalty, in particular if many small * write requests are made, a FileOutputStream is often wrapped by a * BufferedOutputStream. - * + * * @see BufferedOutputStream * @see FileInputStream - * + * * @since Android 1.0 */ public class FileOutputStream extends OutputStream implements Closeable { @@ -55,7 +55,7 @@ public class FileOutputStream extends OutputStream implements Closeable { /** * Constructs a new FileOutputStream on the File {@code file}. If the file * exists, it is overwritten. - * + * * @param file * the file to which this stream writes. * @throws FileNotFoundException @@ -63,7 +63,7 @@ public class FileOutputStream extends OutputStream implements Closeable { * @throws SecurityException * if a {@code SecurityManager} is installed and it denies the * write request. - * @since Android 1.0 + * @see java.lang.SecurityManager#checkWrite(FileDescriptor) */ public FileOutputStream(File file) throws FileNotFoundException { this(file, false); @@ -73,7 +73,7 @@ public class FileOutputStream extends OutputStream implements Closeable { * Constructs a new FileOutputStream on the File {@code file}. The * parameter {@code append} determines whether or not the file is opened and * appended to or just opened and overwritten. - * + * * @param file * the file to which this stream writes. * @param append @@ -83,7 +83,8 @@ public class FileOutputStream extends OutputStream implements Closeable { * @throws SecurityException * if a {@code SecurityManager} is installed and it denies the * write request. - * @since Android 1.0 + * @see java.lang.SecurityManager#checkWrite(FileDescriptor) + * @see java.lang.SecurityManager#checkWrite(String) */ public FileOutputStream(File file, boolean append) throws FileNotFoundException { @@ -104,7 +105,7 @@ public class FileOutputStream extends OutputStream implements Closeable { * Constructs a new FileOutputStream on the FileDescriptor {@code fd}. The * file must already be open, therefore no {@code FileNotFoundException} * will be thrown. - * + * * @param fd * the FileDescriptor to which this stream writes. * @throws NullPointerException @@ -112,7 +113,7 @@ public class FileOutputStream extends OutputStream implements Closeable { * @throws SecurityException * if a {@code SecurityManager} is installed and it denies the * write request. - * @since Android 1.0 + * @see java.lang.SecurityManager#checkWrite(FileDescriptor) */ public FileOutputStream(FileDescriptor fd) { super(); @@ -133,7 +134,7 @@ public class FileOutputStream extends OutputStream implements Closeable { * Constructs a new FileOutputStream on the file named {@code filename}. If * the file exists, it is overwritten. The {@code filename} may be absolute * or relative to the system property {@code "user.dir"}. - * + * * @param filename * the name of the file to which this stream writes. * @throws FileNotFoundException @@ -141,7 +142,6 @@ public class FileOutputStream extends OutputStream implements Closeable { * @throws SecurityException * if a {@code SecurityManager} is installed and it denies the * write request. - * @since Android 1.0 */ public FileOutputStream(String filename) throws FileNotFoundException { this(filename, false); @@ -152,17 +152,16 @@ public class FileOutputStream extends OutputStream implements Closeable { * The parameter {@code append} determines whether or not the file is opened * and appended to or just opened and overwritten. The {@code filename} may * be absolute or relative to the system property {@code "user.dir"}. - * - * @param append - * indicates whether or not to append to an existing file. + * * @param filename * the name of the file to which this stream writes. + * @param append + * indicates whether or not to append to an existing file. * @throws FileNotFoundException * if the file cannot be opened for writing. * @throws SecurityException * if a {@code SecurityManager} is installed and it denies the * write request. - * @since Android 1.0 */ public FileOutputStream(String filename, boolean append) throws FileNotFoundException { @@ -172,10 +171,9 @@ public class FileOutputStream extends OutputStream implements Closeable { /** * Closes this stream. This implementation closes the underlying operating * system resources allocated to represent this stream. - * + * * @throws IOException * if an error occurs attempting to close this stream. - * @since Android 1.0 */ @Override public void close() throws IOException { @@ -204,10 +202,9 @@ public class FileOutputStream extends OutputStream implements Closeable { /** * Frees any resources allocated for this stream before it is garbage * collected. This method is called from the Java Virtual Machine. - * + * * @throws IOException * if an error occurs attempting to finalize this stream. - * @since Android 1.0 */ @Override protected void finalize() throws IOException { @@ -221,10 +218,8 @@ public class FileOutputStream extends OutputStream implements Closeable { * file that is the same as the current position of this stream within the * file. All changes made to the underlying file descriptor state via the * channel are visible by the output stream and vice versa. - *

            - * + * * @return the file channel representation for this stream. - * @since Android 1.0 */ public FileChannel getChannel() { return channel; @@ -233,12 +228,11 @@ public class FileOutputStream extends OutputStream implements Closeable { /** * Returns a FileDescriptor which represents the lowest level representation * of an operating system stream resource. - * + * * @return a FileDescriptor representing this stream. * @throws IOException * if an error occurs attempting to get the FileDescriptor of * this stream. - * @since Android 1.0 */ public final FileDescriptor getFD() throws IOException { return fd; @@ -247,13 +241,12 @@ public class FileOutputStream extends OutputStream implements Closeable { /** * Writes the entire contents of the byte array {@code buffer} to this * stream. - * + * * @param buffer * the buffer to be written to the file. * @throws IOException * if this stream is closed or an error occurs attempting to * write to this stream. - * @since Android 1.0 */ @Override public void write(byte[] buffer) throws IOException { @@ -263,7 +256,7 @@ public class FileOutputStream extends OutputStream implements Closeable { /** * Writes {@code count} bytes from the byte array {@code buffer} starting at * {@code offset} to this stream. - * + * * @param buffer * the buffer to write to this stream. * @param offset @@ -279,7 +272,6 @@ public class FileOutputStream extends OutputStream implements Closeable { * write to this stream. * @throws NullPointerException * if {@code buffer} is {@code null}. - * @since Android 1.0 */ @Override public void write(byte[] buffer, int offset, int count) throws IOException { @@ -308,13 +300,12 @@ public class FileOutputStream extends OutputStream implements Closeable { /** * Writes the specified byte {@code oneByte} to this stream. Only the low * order byte of the integer {@code oneByte} is written. - * + * * @param oneByte * the byte to be written. * @throws IOException * if this stream is closed an error occurs attempting to write * to this stream. - * @since Android 1.0 */ @Override public void write(int oneByte) throws IOException { diff --git a/libcore/luni/src/main/java/java/io/FilePermission.java b/libcore/luni/src/main/java/java/io/FilePermission.java index 3702ac768..610adeebb 100644 --- a/libcore/luni/src/main/java/java/io/FilePermission.java +++ b/libcore/luni/src/main/java/java/io/FilePermission.java @@ -34,7 +34,6 @@ import org.apache.harmony.luni.util.Msg; * and directories contained in that directory. If the pathname * ends in {@code /-}, it includes all the files and directories in that * directory recursively. The following pathnames have a special meaning: - *

            *
              *
            • * "*": all files in the current directory; @@ -46,8 +45,6 @@ import org.apache.harmony.luni.util.Msg; * "<<ALL FILES>>": any file and directory in the file system. *
            • *
            - * - * @since Android 1.0 */ public final class FilePermission extends Permission implements Serializable { @@ -87,8 +84,7 @@ public final class FilePermission extends Permission implements Serializable { * it contains a string other than "read", "write", "execute" * and "delete". * @throws NullPointerException - * if {@code path} is null. - * @since Android 1.0 + * if {@code path} is {@code null}. */ public FilePermission(String path, String actions) { super(path); @@ -197,7 +193,6 @@ public final class FilePermission extends Permission implements Serializable { * Returns the actions associated with this file permission. * * @return the actions associated with this file permission. - * @since Android 1.0 */ @Override public String getActions() { @@ -213,7 +208,6 @@ public final class FilePermission extends Permission implements Serializable { * the object to check equality with. * @return {@code true} if this file permission is equal to {@code obj}, * {@code false} otherwise. - * @since Android 1.0 */ @Override public boolean equals(Object obj) { @@ -245,7 +239,6 @@ public final class FilePermission extends Permission implements Serializable { * the permission to check. * @return {@code true} if the argument permission is implied by the * receiver, and {@code false} if it is not. - * @since Android 1.0 */ @Override public boolean implies(Permission p) { @@ -361,7 +354,6 @@ public final class FilePermission extends Permission implements Serializable { * * @return A new PermissionCollection object suitable for storing * FilePermission objects. - * @since Android 1.0 */ @Override public PermissionCollection newPermissionCollection() { @@ -372,7 +364,6 @@ public final class FilePermission extends Permission implements Serializable { * Calculates the hash code value for this file permission. * * @return the hash code value for this file permission. - * @since Android 1.0 */ @Override public int hashCode() { diff --git a/libcore/luni/src/main/java/java/io/FilePermissionCollection.java b/libcore/luni/src/main/java/java/io/FilePermissionCollection.java index 9d49b7990..304944257 100644 --- a/libcore/luni/src/main/java/java/io/FilePermissionCollection.java +++ b/libcore/luni/src/main/java/java/io/FilePermissionCollection.java @@ -25,8 +25,6 @@ import java.util.Vector; /** * Collects {@link FilePermission} objects and allows to query whether a * particular permission is implied by it. - * - * @since Android 1.0 */ final class FilePermissionCollection extends PermissionCollection implements Serializable { diff --git a/libcore/luni/src/main/java/java/io/FileReader.java b/libcore/luni/src/main/java/java/io/FileReader.java index 90cd4547b..c62fdeb4b 100644 --- a/libcore/luni/src/main/java/java/io/FileReader.java +++ b/libcore/luni/src/main/java/java/io/FileReader.java @@ -27,8 +27,6 @@ package java.io; * * @see BufferedReader * @see FileWriter - * - * @since Android 1.0 */ public class FileReader extends InputStreamReader { @@ -39,7 +37,6 @@ public class FileReader extends InputStreamReader { * a File to be opened for reading characters from. * @throws FileNotFoundException * if {@code file} does not exist. - * @since Android 1.0 */ public FileReader(File file) throws FileNotFoundException { super(new FileInputStream(file)); @@ -52,7 +49,6 @@ public class FileReader extends InputStreamReader { * * @param fd * the previously opened file descriptor. - * @since Android 1.0 */ public FileReader(FileDescriptor fd) { super(new FileInputStream(fd)); @@ -65,7 +61,6 @@ public class FileReader extends InputStreamReader { * an absolute or relative path specifying the file to open. * @throws FileNotFoundException * if there is no file named {@code filename}. - * @since Android 1.0 */ public FileReader(String filename) throws FileNotFoundException { super(new FileInputStream(filename)); diff --git a/libcore/luni/src/main/java/java/io/FileWriter.java b/libcore/luni/src/main/java/java/io/FileWriter.java index 63aee884b..86a9af88b 100644 --- a/libcore/luni/src/main/java/java/io/FileWriter.java +++ b/libcore/luni/src/main/java/java/io/FileWriter.java @@ -27,8 +27,6 @@ package java.io; * * @see BufferedWriter * @see FileReader - * - * @since Android 1.0 */ public class FileWriter extends OutputStreamWriter { @@ -39,7 +37,6 @@ public class FileWriter extends OutputStreamWriter { * the non-null File to write bytes to. * @throws IOException * if {@code file} cannot be opened for writing. - * @since Android 1.0 */ public FileWriter(File file) throws IOException { super(new FileOutputStream(file)); @@ -56,7 +53,6 @@ public class FileWriter extends OutputStreamWriter { * indicates whether or not to append to an existing file. * @throws IOException * if the {@code file} cannot be opened for writing. - * @since Android 1.0 */ public FileWriter(File file, boolean append) throws IOException { super(new FileOutputStream(file, append)); @@ -67,7 +63,6 @@ public class FileWriter extends OutputStreamWriter { * * @param fd * the non-null FileDescriptor to write bytes to. - * @since Android 1.0 */ public FileWriter(FileDescriptor fd) { super(new FileOutputStream(fd)); @@ -80,7 +75,6 @@ public class FileWriter extends OutputStreamWriter { * the non-null name of the file to write bytes to. * @throws IOException * if the file cannot be opened for writing. - * @since Android 1.0 */ public FileWriter(String filename) throws IOException { super(new FileOutputStream(new File(filename))); @@ -97,7 +91,6 @@ public class FileWriter extends OutputStreamWriter { * indicates whether or not to append to an existing file. * @throws IOException * if the {@code file} cannot be opened for writing. - * @since Android 1.0 */ public FileWriter(String filename, boolean append) throws IOException { super(new FileOutputStream(filename, append)); diff --git a/libcore/luni/src/main/java/java/io/FilenameFilter.java b/libcore/luni/src/main/java/java/io/FilenameFilter.java index 40a6cca3b..a51bcc6ab 100644 --- a/libcore/luni/src/main/java/java/io/FilenameFilter.java +++ b/libcore/luni/src/main/java/java/io/FilenameFilter.java @@ -21,9 +21,8 @@ package java.io; * An interface for filtering {@link File} objects based on their names * or the directory they reside in. * + * @see File * @see File#list(FilenameFilter) - * - * @since Android 1.0 */ public interface FilenameFilter { @@ -34,10 +33,9 @@ public interface FilenameFilter { * the directory in which the {@code filename} was found. * @param filename * the name of the file in {@code dir} to test. - * @return {@code true} if the filename matches the filter - * and can be included in the list, {@code false} + * @return {@code true} if the filename matches the filter + * and can be included in the list, {@code false} * otherwise. - * @since Android 1.0 */ public abstract boolean accept(File dir, String filename); } diff --git a/libcore/luni/src/main/java/java/io/FilterInputStream.java b/libcore/luni/src/main/java/java/io/FilterInputStream.java index 511006018..0baa40944 100644 --- a/libcore/luni/src/main/java/java/io/FilterInputStream.java +++ b/libcore/luni/src/main/java/java/io/FilterInputStream.java @@ -26,21 +26,13 @@ package java.io; * from this class. * * @see FilterOutputStream - * - * @since Android 1.0 */ public class FilterInputStream extends InputStream { - // BEGIN android-changed - // The underlying input stream address should not be cached in a register. - // This was changed to be more close to the RI. /** * The source input stream that is filtered. - * - * @since Android 1.0 */ protected volatile InputStream in; - // END android-changed /** * Constructs a new {@code FilterInputStream} with the specified input @@ -48,7 +40,6 @@ public class FilterInputStream extends InputStream { * * @param in * the non-null InputStream to filter reads on. - * @since Android 1.0 */ protected FilterInputStream(InputStream in) { super(); @@ -57,12 +48,11 @@ public class FilterInputStream extends InputStream { /** * Returns the number of bytes that are available before this stream will - * block. - * + * block. + * * @return the number of bytes available before blocking. * @throws IOException * if an error occurs in this stream. - * @since Android 1.0 */ @Override public int available() throws IOException { @@ -74,7 +64,6 @@ public class FilterInputStream extends InputStream { * * @throws IOException * if an error occurs while closing this stream. - * @since Android 1.0 */ @Override public void close() throws IOException { @@ -88,14 +77,12 @@ public class FilterInputStream extends InputStream { * position, provided that {@code readlimit} has not been surpassed. *

            * This implementation sets a mark in the filtered stream. - *

            - * + * * @param readlimit * the number of bytes that can be read from this stream before * the mark is invalidated. * @see #markSupported() * @see #reset() - * @since Android 1.0 */ @Override public synchronized void mark(int readlimit) { @@ -112,7 +99,6 @@ public class FilterInputStream extends InputStream { * @see #mark(int) * @see #reset() * @see #skip(long) - * @since Android 1.0 */ @Override public boolean markSupported() { @@ -128,7 +114,6 @@ public class FilterInputStream extends InputStream { * reached. * @throws IOException * if the stream is closed or another IOException occurs. - * @since Android 1.0 */ @Override public int read() throws IOException { @@ -147,7 +132,6 @@ public class FilterInputStream extends InputStream { * filtered stream has been reached while reading. * @throws IOException * if this stream is closed or another IOException occurs. - * @since Android 1.0 */ @Override public int read(byte[] buffer) throws IOException { @@ -172,7 +156,6 @@ public class FilterInputStream extends InputStream { * filtered stream has been reached while reading. * @throws IOException * if this stream is closed or another I/O error occurs. - * @since Android 1.0 */ @Override public int read(byte[] buffer, int offset, int count) throws IOException { @@ -189,7 +172,6 @@ public class FilterInputStream extends InputStream { * bytes have been read since setting the mark. * @see #mark(int) * @see #markSupported() - * @since Android 1.0 */ @Override public synchronized void reset() throws IOException { @@ -208,12 +190,10 @@ public class FilterInputStream extends InputStream { * @throws IOException * if this stream is closed or another IOException occurs. * @see #mark(int) - * @see #reset() - * @since Android 1.0 + * @see #reset() */ @Override public long skip(long count) throws IOException { return in.skip(count); } } - diff --git a/libcore/luni/src/main/java/java/io/FilterOutputStream.java b/libcore/luni/src/main/java/java/io/FilterOutputStream.java index 212b22e19..66765f9e5 100644 --- a/libcore/luni/src/main/java/java/io/FilterOutputStream.java +++ b/libcore/luni/src/main/java/java/io/FilterOutputStream.java @@ -26,27 +26,22 @@ import org.apache.harmony.luni.util.Msg; * decompression of the underlying stream. Output streams that wrap another * output stream and provide some additional functionality on top of it usually * inherit from this class. - * + * * @see FilterOutputStream - * - * @since Android 1.0 */ public class FilterOutputStream extends OutputStream { /** * The target output stream for this filter stream. - * - * @since Android 1.0 */ protected OutputStream out; /** * Constructs a new {@code FilterOutputStream} with {@code out} as its * target stream. - * + * * @param out * the target stream that this stream writes to. - * @since Android 1.0 */ public FilterOutputStream(OutputStream out) { this.out = out; @@ -54,10 +49,9 @@ public class FilterOutputStream extends OutputStream { /** * Closes this stream. This implementation closes the target stream. - * + * * @throws IOException * if an error occurs attempting to close this stream. - * @since Android 1.0 */ @Override public void close() throws IOException { @@ -73,10 +67,9 @@ public class FilterOutputStream extends OutputStream { /** * Ensures that all pending data is sent out to the target stream. This * implementation flushes the target stream. - * + * * @throws IOException * if an error occurs attempting to flush this stream. - * @since Android 1.0 */ @Override public void flush() throws IOException { @@ -87,12 +80,11 @@ public class FilterOutputStream extends OutputStream { * Writes the entire contents of the byte array {@code buffer} to this * stream. This implementation writes the {@code buffer} to the target * stream. - * + * * @param buffer * the buffer to be written. * @throws IOException * if an I/O error occurs while writing to this stream. - * @since Android 1.0 */ @Override public void write(byte[] buffer) throws IOException { @@ -105,7 +97,7 @@ public class FilterOutputStream extends OutputStream { /** * Writes {@code count} bytes from the byte array {@code buffer} starting at * {@code offset} to the target stream. - * + * * @param buffer * the buffer to write. * @param offset @@ -118,7 +110,6 @@ public class FilterOutputStream extends OutputStream { * {@code buffer}. * @throws IOException * if an I/O error occurs while writing to this stream. - * @since Android 1.0 */ @Override public void write(byte[] buffer, int offset, int count) throws IOException { @@ -149,12 +140,11 @@ public class FilterOutputStream extends OutputStream { /** * Writes one byte to the target stream. Only the low order byte of the * integer {@code oneByte} is written. - * + * * @param oneByte * the byte to be written. * @throws IOException * if an I/O error occurs while writing to this stream. - * @since Android 1.0 */ @Override public void write(int oneByte) throws IOException { diff --git a/libcore/luni/src/main/java/java/io/FilterReader.java b/libcore/luni/src/main/java/java/io/FilterReader.java index 7fa96b5d6..e8e06dd09 100644 --- a/libcore/luni/src/main/java/java/io/FilterReader.java +++ b/libcore/luni/src/main/java/java/io/FilterReader.java @@ -26,15 +26,11 @@ package java.io; * class. * * @see FilterWriter - * - * @since Android 1.0 */ public abstract class FilterReader extends Reader { /** * The target Reader which is being filtered. - * - * @since Android 1.0 */ protected Reader in; @@ -43,7 +39,6 @@ public abstract class FilterReader extends Reader { * * @param in * The non-null Reader to filter reads on. - * @since Android 1.0 */ protected FilterReader(Reader in) { super(in); @@ -55,7 +50,6 @@ public abstract class FilterReader extends Reader { * * @throws IOException * if an error occurs while closing this reader. - * @since Android 1.0 */ @Override public void close() throws IOException { @@ -71,7 +65,6 @@ public abstract class FilterReader extends Reader { * position, provided that {@code readlimit} has not been surpassed. *

            * This implementation sets a mark in the filtered reader. - *

            * * @param readlimit * the number of bytes that can be read from this reader before @@ -80,7 +73,6 @@ public abstract class FilterReader extends Reader { * if an error occurs while marking this reader. * @see #markSupported() * @see #reset() - * @since Android 1.0 */ @Override public synchronized void mark(int readlimit) throws IOException { @@ -98,7 +90,6 @@ public abstract class FilterReader extends Reader { * @see #mark(int) * @see #reset() * @see #skip(long) - * @since Android 1.0 */ @Override public boolean markSupported() { @@ -116,7 +107,6 @@ public abstract class FilterReader extends Reader { * been reached. * @throws IOException * if an error occurs while reading from this reader. - * @since Android 1.0 */ @Override public int read() throws IOException { @@ -142,7 +132,6 @@ public abstract class FilterReader extends Reader { * filtered reader has been reached while reading. * @throws IOException * if an error occurs while reading from this reader. - * @since Android 1.0 */ @Override public int read(char[] buffer, int offset, int count) throws IOException { @@ -161,7 +150,6 @@ public abstract class FilterReader extends Reader { * is called, {@code false} if unknown or blocking will occur. * @throws IOException * if the reader is closed or some other I/O error occurs. - * @since Android 1.0 */ @Override public boolean ready() throws IOException { @@ -182,7 +170,6 @@ public abstract class FilterReader extends Reader { * {@code mark()} and {@code reset()}. * @see #mark(int) * @see #markSupported() - * @since Android 1.0 */ @Override public void reset() throws IOException { @@ -205,7 +192,6 @@ public abstract class FilterReader extends Reader { * @see #mark(int) * @see #markSupported() * @see #reset() - * @since Android 1.0 */ @Override public long skip(long count) throws IOException { diff --git a/libcore/luni/src/main/java/java/io/FilterWriter.java b/libcore/luni/src/main/java/java/io/FilterWriter.java index 66c1d34da..cd48f64e0 100644 --- a/libcore/luni/src/main/java/java/io/FilterWriter.java +++ b/libcore/luni/src/main/java/java/io/FilterWriter.java @@ -24,26 +24,22 @@ package java.io; * decompression of the underlying writer. Writers that wrap another writer and * provide some additional functionality on top of it usually inherit from this * class. - * + * * @see FilterReader - * - * @since Android 1.0 */ public abstract class FilterWriter extends Writer { /** * The Writer being filtered. - * @since Android 1.0 */ protected Writer out; /** * Constructs a new FilterWriter on the Writer {@code out}. All writes are * now filtered through this writer. - * + * * @param out * the target Writer to filter writes on. - * @since Android 1.0 */ protected FilterWriter(Writer out) { super(out); @@ -52,10 +48,9 @@ public abstract class FilterWriter extends Writer { /** * Closes this writer. This implementation closes the target writer. - * + * * @throws IOException * if an error occurs attempting to close this writer. - * @since Android 1.0 */ @Override public void close() throws IOException { @@ -67,10 +62,9 @@ public abstract class FilterWriter extends Writer { /** * Flushes this writer to ensure all pending data is sent out to the target * writer. This implementation flushes the target writer. - * + * * @throws IOException * if an error occurs attempting to flush this writer. - * @since Android 1.0 */ @Override public void flush() throws IOException { @@ -82,7 +76,7 @@ public abstract class FilterWriter extends Writer { /** * Writes {@code count} characters from the char array {@code buffer} * starting at position {@code offset} to the target writer. - * + * * @param buffer * the buffer to write. * @param offset @@ -91,7 +85,6 @@ public abstract class FilterWriter extends Writer { * the number of characters in {@code buffer} to write. * @throws IOException * if an error occurs while writing to this writer. - * @since Android 1.0 */ @Override public void write(char[] buffer, int offset, int count) throws IOException { @@ -106,12 +99,11 @@ public abstract class FilterWriter extends Writer { /** * Writes the specified character {@code oneChar} to the target writer. Only the * two least significant bytes of the integer {@code oneChar} are written. - * + * * @param oneChar * the char to write to the target writer. * @throws IOException * if an error occurs while writing to this writer. - * @since Android 1.0 */ @Override public void write(int oneChar) throws IOException { @@ -124,7 +116,7 @@ public abstract class FilterWriter extends Writer { * Writes {@code count} characters from the string {@code str} starting at * position {@code index} to this writer. This implementation writes * {@code str} to the target writer. - * + * * @param str * the string to be written. * @param offset @@ -133,7 +125,6 @@ public abstract class FilterWriter extends Writer { * the number of chars in {@code str} to write. * @throws IOException * if an error occurs while writing to this writer. - * @since Android 1.0 */ @Override public void write(String str, int offset, int count) throws IOException { diff --git a/libcore/luni/src/main/java/java/io/Flushable.java b/libcore/luni/src/main/java/java/io/Flushable.java index 55fcc92f5..c36de30b0 100644 --- a/libcore/luni/src/main/java/java/io/Flushable.java +++ b/libcore/luni/src/main/java/java/io/Flushable.java @@ -20,17 +20,14 @@ package java.io; * Defines an interface for classes that can (or need to) be flushed, typically * before some output processing is considered to be finished and the object * gets closed. - * - * @since Android 1.0 */ public interface Flushable { /** * Flushes the object by writing out any buffered data to the underlying * output. - * + * * @throws IOException * if there are any issues writing the data. - * @since Android 1.0 */ void flush() throws IOException; } diff --git a/libcore/luni/src/main/java/java/io/IOException.java b/libcore/luni/src/main/java/java/io/IOException.java index 869a6d07b..24cea9fbc 100644 --- a/libcore/luni/src/main/java/java/io/IOException.java +++ b/libcore/luni/src/main/java/java/io/IOException.java @@ -22,8 +22,6 @@ package java.io; * calling the constructor, as usual. Note there are also several subclasses of * this class for more specific error situations, such as * {@link FileNotFoundException} or {@link EOFException}. - * - * @since Android 1.0 */ public class IOException extends Exception { @@ -31,8 +29,6 @@ public class IOException extends Exception { /** * Constructs a new {@code IOException} with its stack trace filled in. - * - * @since Android 1.0 */ public IOException() { super(); @@ -44,7 +40,6 @@ public class IOException extends Exception { * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public IOException(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/io/InputStream.java b/libcore/luni/src/main/java/java/io/InputStream.java index 23fd89c12..f20ce7d36 100644 --- a/libcore/luni/src/main/java/java/io/InputStream.java +++ b/libcore/luni/src/main/java/java/io/InputStream.java @@ -34,10 +34,8 @@ import org.apache.harmony.luni.util.Msg; *

            * Many specialized input streams for purposes like reading from a file already * exist in this package. - * + * * @see OutputStream - * - * @since Android 1.0 */ public abstract class InputStream extends Object implements Closeable { @@ -46,8 +44,6 @@ public abstract class InputStream extends Object implements Closeable { /** * This constructor does nothing. It is provided for signature * compatibility. - * - * @since Android 1.0 */ public InputStream() { /* empty */ @@ -57,11 +53,10 @@ public abstract class InputStream extends Object implements Closeable { * Returns the number of bytes that are available before this stream will * block. This implementation always returns 0. Subclasses should override * and indicate the correct number of bytes available. - * + * * @return the number of bytes available before blocking. * @throws IOException * if an error occurs in this stream. - * @since Android 1.0 */ public int available() throws IOException { return 0; @@ -70,10 +65,9 @@ public abstract class InputStream extends Object implements Closeable { /** * Closes this stream. Concrete implementations of this class should free * any resources during close. This implementation does nothing. - * + * * @throws IOException * if an error occurs while closing this stream. - * @since Android 1.0 */ public void close() throws IOException { /* empty */ @@ -87,14 +81,12 @@ public abstract class InputStream extends Object implements Closeable { *

            * This default implementation does nothing and concrete subclasses must * provide their own implementation. - *

            - * + * * @param readlimit * the number of bytes that can be read from this stream before * the mark is invalidated. * @see #markSupported() * @see #reset() - * @since Android 1.0 */ public void mark(int readlimit) { /* empty */ @@ -103,11 +95,10 @@ public abstract class InputStream extends Object implements Closeable { /** * Indicates whether this stream supports the {@code mark()} and * {@code reset()} methods. The default implementation returns {@code false}. - * + * * @return always {@code false}. * @see #mark(int) * @see #reset() - * @since Android 1.0 */ public boolean markSupported() { return false; @@ -118,24 +109,22 @@ public abstract class InputStream extends Object implements Closeable { * range from 0 to 255. Returns -1 if the end of the stream has been * reached. Blocks until one byte has been read, the end of the source * stream is detected or an exception is thrown. - * + * * @return the byte read or -1 if the end of stream has been reached. * @throws IOException * if the stream is closed or another IOException occurs. - * @since Android 1.0 */ public abstract int read() throws IOException; /** * Reads bytes from this stream and stores them in the byte array {@code b}. - * + * * @param b * the byte array in which to store the bytes read. * @return the number of bytes actually read or -1 if the end of the stream * has been reached. * @throws IOException * if this stream is closed or another IOException occurs. - * @since Android 1.0 */ public int read(byte[] b) throws IOException { // BEGIN android-note @@ -147,7 +136,7 @@ public abstract class InputStream extends Object implements Closeable { /** * Reads at most {@code length} bytes from this stream and stores them in * the byte array {@code b} starting at {@code offset}. - * + * * @param b * the byte array in which to store the bytes read. * @param offset @@ -163,7 +152,6 @@ public abstract class InputStream extends Object implements Closeable { * {@code b}. * @throws IOException * if the stream is closed or another IOException occurs. - * @since Android 1.0 */ public int read(byte[] b, int offset, int length) throws IOException { // BEGIN android-note @@ -195,7 +183,7 @@ public abstract class InputStream extends Object implements Closeable { } throw e; } - b[offset + i] = (byte)c; + b[offset + i] = (byte) c; } return length; } @@ -208,11 +196,9 @@ public abstract class InputStream extends Object implements Closeable { *

            * This implementation always throws an {@code IOException} and concrete * subclasses should provide the proper implementation. - *

            - * + * * @throws IOException * if this stream is closed or another IOException occurs. - * @since Android 1.0 */ public synchronized void reset() throws IOException { throw new IOException(); @@ -225,14 +211,12 @@ public abstract class InputStream extends Object implements Closeable { *

            * This default implementation reads {@code n} bytes into a temporary * buffer. Concrete subclasses should provide their own implementation. - *

            - * + * * @param n * the number of bytes to skip. * @return the number of bytes actually skipped. * @throws IOException * if this stream is closed or another IOException occurs. - * @since Android 1.0 */ public long skip(long n) throws IOException { if (n <= 0) { diff --git a/libcore/luni/src/main/java/java/io/InputStreamReader.java b/libcore/luni/src/main/java/java/io/InputStreamReader.java index 0f74b140d..0fdcb9aaa 100644 --- a/libcore/luni/src/main/java/java/io/InputStreamReader.java +++ b/libcore/luni/src/main/java/java/io/InputStreamReader.java @@ -26,15 +26,11 @@ import java.nio.charset.CodingErrorAction; import java.nio.charset.MalformedInputException; import java.nio.charset.UnmappableCharacterException; import java.security.AccessController; -import java.util.HashMap; +import org.apache.harmony.luni.util.HistoricalNamesUtil; import org.apache.harmony.luni.util.Msg; import org.apache.harmony.luni.util.PriviAction; -// BEGIN android-note -// Later changes from Harmony have been integrated into this version. -// END android-note - /** * A class for turning a byte stream into a character stream. Data read from the * source input stream is converted into characters by either a default or a @@ -42,10 +38,8 @@ import org.apache.harmony.luni.util.PriviAction; * "file.encoding" system property. {@code InputStreamReader} contains a buffer * of bytes read from the source stream and converts these into characters as * needed. The buffer size is 8K. - * + * * @see OutputStreamWriter - * - * @since Android 1.0 */ public class InputStreamReader extends Reader { private InputStream in; @@ -63,10 +57,9 @@ public class InputStreamReader extends Reader { * {@code in}. This constructor sets the character converter to the encoding * specified in the "file.encoding" property and falls back to ISO 8859_1 * (ISO-Latin-1) if the property doesn't exist. - * + * * @param in * the input stream from which to read characters. - * @since Android 1.0 */ public InputStreamReader(InputStream in) { super(in); @@ -85,7 +78,7 @@ public class InputStreamReader extends Reader { * character converter that is used to decode bytes into characters is * identified by name by {@code enc}. If the encoding cannot be found, an * UnsupportedEncodingException error is thrown. - * + * * @param in * the InputStream from which to read characters. * @param enc @@ -94,7 +87,6 @@ public class InputStreamReader extends Reader { * if {@code enc} is {@code null}. * @throws UnsupportedEncodingException * if the encoding specified by {@code enc} cannot be found. - * @since Android 1.0 */ public InputStreamReader(InputStream in, final String enc) throws UnsupportedEncodingException { @@ -117,12 +109,11 @@ public class InputStreamReader extends Reader { /** * Constructs a new InputStreamReader on the InputStream {@code in} and * CharsetDecoder {@code dec}. - * + * * @param in * the source InputStream from which to read characters. * @param dec * the CharsetDecoder used by the character conversion. - * @since Android 1.0 */ public InputStreamReader(InputStream in, CharsetDecoder dec) { super(in); @@ -135,12 +126,11 @@ public class InputStreamReader extends Reader { /** * Constructs a new InputStreamReader on the InputStream {@code in} and * Charset {@code charset}. - * + * * @param in * the source InputStream from which to read characters. * @param charset * the Charset that defines the character converter - * @since Android 1.0 */ public InputStreamReader(InputStream in, Charset charset) { super(in); @@ -154,10 +144,9 @@ public class InputStreamReader extends Reader { /** * Closes this reader. This implementation closes the source InputStream and * releases all local storage. - * + * * @throws IOException * if an error occurs attempting to close this reader. - * @since Android 1.0 */ @Override public void close() throws IOException { @@ -178,10 +167,9 @@ public class InputStreamReader extends Reader { /** * Returns the name of the encoding used to convert bytes into characters. * The value {@code null} is returned if this reader has been closed. - * + * * @return the name of the character converter or {@code null} if this * reader is closed. - * @since Android 1.0 */ public String getEncoding() { if (!isOpen()) { @@ -190,172 +178,17 @@ public class InputStreamReader extends Reader { return HistoricalNamesUtil.getHistoricalName(decoder.charset().name()); } - /* - * helper for getEncoding() - */ - @SuppressWarnings("nls") - static class HistoricalNamesUtil { - private static HashMap historicalNames = new HashMap(); - static { - historicalNames.put("Big5-HKSCS", "Big5_HKSCS"); - historicalNames.put("EUC-JP", "EUC_JP"); - historicalNames.put("EUC-KR", "EUC_KR"); - historicalNames.put("GB2312", "EUC_CN"); - historicalNames.put("IBM-Thai", "Cp838"); - historicalNames.put("IBM00858", "Cp858"); - historicalNames.put("IBM01140", "Cp1140"); - historicalNames.put("IBM01141", "Cp1141"); - historicalNames.put("IBM01142", "Cp1142"); - historicalNames.put("IBM01143", "Cp1143"); - historicalNames.put("IBM01144", "Cp1144"); - historicalNames.put("IBM01145", "Cp1145"); - historicalNames.put("IBM01146", "Cp1146"); - historicalNames.put("IBM01147", "Cp1147"); - historicalNames.put("IBM01148", "Cp1148"); - historicalNames.put("IBM01149", "Cp1149"); - historicalNames.put("IBM037", "Cp037"); - historicalNames.put("IBM1026", "Cp1026"); - historicalNames.put("IBM1047", "Cp1047"); - historicalNames.put("IBM273", "Cp273"); - historicalNames.put("IBM277", "Cp277"); - historicalNames.put("IBM278", "Cp278"); - historicalNames.put("IBM280", "Cp280"); - historicalNames.put("IBM284", "Cp284"); - historicalNames.put("IBM285", "Cp285"); - historicalNames.put("IBM297", "Cp297"); - historicalNames.put("IBM420", "Cp420"); - historicalNames.put("IBM424", "Cp424"); - historicalNames.put("IBM437", "Cp437"); - historicalNames.put("IBM500", "Cp500"); - historicalNames.put("IBM775", "Cp775"); - historicalNames.put("IBM850", "Cp850"); - historicalNames.put("IBM852", "Cp852"); - historicalNames.put("IBM855", "Cp855"); - historicalNames.put("IBM857", "Cp857"); - historicalNames.put("IBM860", "Cp860"); - historicalNames.put("IBM861", "Cp861"); - historicalNames.put("IBM862", "Cp862"); - historicalNames.put("IBM863", "Cp863"); - historicalNames.put("IBM864", "Cp864"); - historicalNames.put("IBM865", "Cp865"); - historicalNames.put("IBM866", "Cp866"); - historicalNames.put("IBM868", "Cp868"); - historicalNames.put("IBM869", "Cp869"); - historicalNames.put("IBM870", "Cp870"); - historicalNames.put("IBM871", "Cp871"); - historicalNames.put("IBM918", "Cp918"); - historicalNames.put("ISO-2022-CN", "ISO2022CN"); - historicalNames.put("ISO-2022-JP", "ISO2022JP"); - historicalNames.put("ISO-2022-KR", "ISO2022KR"); - historicalNames.put("ISO-8859-1", "ISO8859_1"); - historicalNames.put("ISO-8859-13", "ISO8859_13"); - historicalNames.put("ISO-8859-15", "ISO8859_15"); - historicalNames.put("ISO-8859-2", "ISO8859_2"); - historicalNames.put("ISO-8859-3", "ISO8859_3"); - historicalNames.put("ISO-8859-4", "ISO8859_4"); - historicalNames.put("ISO-8859-5", "ISO8859_5"); - historicalNames.put("ISO-8859-6", "ISO8859_6"); - historicalNames.put("ISO-8859-7", "ISO8859_7"); - historicalNames.put("ISO-8859-8", "ISO8859_8"); - historicalNames.put("ISO-8859-9", "ISO8859_9"); - historicalNames.put("KOI8-R", "KOI8_R"); - historicalNames.put("Shift_JIS", "SJIS"); - historicalNames.put("TIS-620", "TIS620"); - historicalNames.put("US-ASCII", "ASCII"); - historicalNames.put("UTF-16BE", "UnicodeBigUnmarked"); - historicalNames.put("UTF-16LE", "UnicodeLittleUnmarked"); - historicalNames.put("UTF-8", "UTF8"); - historicalNames.put("windows-1250", "Cp1250"); - historicalNames.put("windows-1251", "Cp1251"); - historicalNames.put("windows-1252", "Cp1252"); - historicalNames.put("windows-1253", "Cp1253"); - historicalNames.put("windows-1254", "Cp1254"); - historicalNames.put("windows-1255", "Cp1255"); - historicalNames.put("windows-1256", "Cp1256"); - historicalNames.put("windows-1257", "Cp1257"); - historicalNames.put("windows-1258", "Cp1258"); - historicalNames.put("windows-31j", "MS932"); - historicalNames.put("x-Big5-Solaris", "Big5_Solaris"); - historicalNames.put("x-euc-jp-linux", "EUC_JP_LINUX"); - historicalNames.put("x-EUC-TW", "EUC_TW"); - historicalNames.put("x-eucJP-Open", "EUC_JP_Solaris"); - historicalNames.put("x-IBM1006", "Cp1006"); - historicalNames.put("x-IBM1025", "Cp1025"); - historicalNames.put("x-IBM1046", "Cp1046"); - historicalNames.put("x-IBM1097", "Cp1097"); - historicalNames.put("x-IBM1098", "Cp1098"); - historicalNames.put("x-IBM1112", "Cp1112"); - historicalNames.put("x-IBM1122", "Cp1122"); - historicalNames.put("x-IBM1123", "Cp1123"); - historicalNames.put("x-IBM1124", "Cp1124"); - historicalNames.put("x-IBM1381", "Cp1381"); - historicalNames.put("x-IBM1383", "Cp1383"); - historicalNames.put("x-IBM33722", "Cp33722"); - historicalNames.put("x-IBM737", "Cp737"); - historicalNames.put("x-IBM856", "Cp856"); - historicalNames.put("x-IBM874", "Cp874"); - historicalNames.put("x-IBM875", "Cp875"); - historicalNames.put("x-IBM921", "Cp921"); - historicalNames.put("x-IBM922", "Cp922"); - historicalNames.put("x-IBM930", "Cp930"); - historicalNames.put("x-IBM933", "Cp933"); - historicalNames.put("x-IBM935", "Cp935"); - historicalNames.put("x-IBM937", "Cp937"); - historicalNames.put("x-IBM939", "Cp939"); - historicalNames.put("x-IBM942", "Cp942"); - historicalNames.put("x-IBM942C", "Cp942C"); - historicalNames.put("x-IBM943", "Cp943"); - historicalNames.put("x-IBM943C", "Cp943C"); - historicalNames.put("x-IBM948", "Cp948"); - historicalNames.put("x-IBM949", "Cp949"); - historicalNames.put("x-IBM949C", "Cp949C"); - historicalNames.put("x-IBM950", "Cp950"); - historicalNames.put("x-IBM964", "Cp964"); - historicalNames.put("x-IBM970", "Cp970"); - historicalNames.put("x-ISCII91", "ISCII91"); - historicalNames.put("x-ISO-2022-CN-CNS", "ISO2022CN"); - historicalNames.put("x-ISO-2022-CN-GB", "ISO2022CN"); - historicalNames.put("x-JISAutoDetect", "JISAutoDetect"); - historicalNames.put("x-MacArabic", "MacArabic"); - historicalNames.put("x-MacCentralEurope", "MacCentralEurope"); - historicalNames.put("x-MacCroatian", "MacCroatian"); - historicalNames.put("x-MacCyrillic", "MacCyrillic"); - historicalNames.put("x-MacDingbat", "MacDingbat"); - historicalNames.put("x-MacGreek", "MacGreek"); - historicalNames.put("x-MacHebrew", "MacHebrew"); - historicalNames.put("x-MacIceland", "MacIceland"); - historicalNames.put("x-MacRoman", "MacRoman"); - historicalNames.put("x-MacRomania", "MacRomania"); - historicalNames.put("x-MacSymbol", "MacSymbol"); - historicalNames.put("x-MacThai", "MacThai"); - historicalNames.put("x-MacTurkish", "MacTurkish"); - historicalNames.put("x-MacUkraine", "MacUkraine"); - historicalNames.put("x-MS950-HKSCS", "MS950_HKSCS"); - historicalNames.put("x-mswin-936", "MS936"); - historicalNames.put("x-PCK", "PCK"); - historicalNames.put("x-windows-874", "MS874"); - historicalNames.put("x-windows-949", "MS949"); - historicalNames.put("x-windows-950", "MS950"); - } - - public static String getHistoricalName(String name) { - return (!historicalNames.containsKey(name) ? name : historicalNames - .get(name)); - } - } - /** * Reads a single character from this reader and returns it as an integer * with the two higher-order bytes set to 0. Returns -1 if the end of the * reader has been reached. The byte value is either obtained from * converting bytes in this reader's buffer or by first filling the buffer * from the source InputStream and then reading from the buffer. - * + * * @return the character read or -1 if the end of the reader has been * reached. * @throws IOException * if this reader is closed or some other I/O error occurs. - * @since Android 1.0 */ @Override public int read() throws IOException { @@ -377,7 +210,7 @@ public class InputStreamReader extends Reader { * been reached. The bytes are either obtained from converting bytes in this * reader's buffer or by first filling the buffer from the source * InputStream and then reading from the buffer. - * + * * @param buf * the array to store the characters read. * @param offset @@ -393,7 +226,6 @@ public class InputStreamReader extends Reader { * {@code buf}. * @throws IOException * if this reader is closed or some other I/O error occurs. - * @since Android 1.0 */ @Override public int read(char[] buf, int offset, int length) throws IOException { @@ -465,10 +297,7 @@ public class InputStreamReader extends Reader { if (result == CoderResult.UNDERFLOW && endOfInput) { result = decoder.decode(bytes, out, true); - // FIXME: should flush at first, but seems ICU has a bug that it - // will throw IAE if some malform/unmappable bytes found during - // decoding - // result = decoder.flush(out); + decoder.flush(out); decoder.reset(); } if (result.isMalformed()) { @@ -496,12 +325,11 @@ public class InputStreamReader extends Reader { * {@code read()} is called. This implementation returns {@code true} if * there are bytes available in the buffer or the source stream has bytes * available. - * + * * @return {@code true} if the receiver will not block when {@code read()} * is called, {@code false} if unknown or blocking will occur. * @throws IOException * if this reader is closed or some other I/O error occurs. - * @since Android 1.0 */ @Override public boolean ready() throws IOException { diff --git a/libcore/luni/src/main/java/java/io/InterruptedIOException.java b/libcore/luni/src/main/java/java/io/InterruptedIOException.java index de11d2805..4d537fc42 100644 --- a/libcore/luni/src/main/java/java/io/InterruptedIOException.java +++ b/libcore/luni/src/main/java/java/io/InterruptedIOException.java @@ -21,8 +21,6 @@ package java.io; * Signals that a blocking I/O operation has been interrupted. The number of * bytes that were transferred successfully before the interruption took place * is stored in a field of the exception. - * - * @since Android 1.0 */ public class InterruptedIOException extends IOException { @@ -30,16 +28,12 @@ public class InterruptedIOException extends IOException { /** * The number of bytes transferred before the I/O interrupt occurred. - * - * @since Android 1.0 */ public int bytesTransferred; /** * Constructs a new {@code InterruptedIOException} with its stack trace * filled in. - * - * @since Android 1.0 */ public InterruptedIOException() { super(); @@ -51,7 +45,6 @@ public class InterruptedIOException extends IOException { * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public InterruptedIOException(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/io/InvalidClassException.java b/libcore/luni/src/main/java/java/io/InvalidClassException.java index 10764b5c7..4712bb6c4 100644 --- a/libcore/luni/src/main/java/java/io/InvalidClassException.java +++ b/libcore/luni/src/main/java/java/io/InvalidClassException.java @@ -30,8 +30,6 @@ package java.io; * * @see ObjectInputStream #readObject() * @see ObjectInputValidation#validateObject() - * - * @since Android 1.0 */ public class InvalidClassException extends ObjectStreamException { @@ -39,8 +37,6 @@ public class InvalidClassException extends ObjectStreamException { /** * The fully qualified name of the class that caused the problem. - * - * @since Android 1.0 */ public String classname; @@ -50,7 +46,6 @@ public class InvalidClassException extends ObjectStreamException { * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public InvalidClassException(String detailMessage) { super(detailMessage); @@ -65,7 +60,6 @@ public class InvalidClassException extends ObjectStreamException { * the name of the class that caused the exception. * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public InvalidClassException(String className, String detailMessage) { super(detailMessage); @@ -80,7 +74,6 @@ public class InvalidClassException extends ObjectStreamException { * * @return the detail message, possibly concatenated with the name of the * class that caused the problem. - * @since Android 1.0 */ @Override public String getMessage() { diff --git a/libcore/luni/src/main/java/java/io/InvalidObjectException.java b/libcore/luni/src/main/java/java/io/InvalidObjectException.java index a2bb5dfb1..365209d45 100644 --- a/libcore/luni/src/main/java/java/io/InvalidObjectException.java +++ b/libcore/luni/src/main/java/java/io/InvalidObjectException.java @@ -22,8 +22,6 @@ package java.io; * * @see ObjectInputStream#registerValidation(ObjectInputValidation, int) * @see ObjectInputValidation#validateObject() - * - * @since Android 1.0 */ public class InvalidObjectException extends ObjectStreamException { @@ -35,7 +33,6 @@ public class InvalidObjectException extends ObjectStreamException { * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public InvalidObjectException(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/io/LineNumberInputStream.java b/libcore/luni/src/main/java/java/io/LineNumberInputStream.java index 3bb399740..1a40177da 100644 --- a/libcore/luni/src/main/java/java/io/LineNumberInputStream.java +++ b/libcore/luni/src/main/java/java/io/LineNumberInputStream.java @@ -27,10 +27,8 @@ import org.apache.harmony.luni.util.Msg; * line terminator sequences are {@code '\r'}, {@code '\n'} and {@code "\r\n"}. * When using {@code read}, line terminator sequences are always translated into * {@code '\n'}. - * + * * @deprecated Use {@link LineNumberReader} - * - * @since Android 1.0 */ @Deprecated public class LineNumberInputStream extends FilterInputStream { @@ -46,10 +44,9 @@ public class LineNumberInputStream extends FilterInputStream { /** * Constructs a new {@code LineNumberInputStream} on the {@link InputStream} * {@code in}. Line numbers are counted for all data read from this stream. - * + * * @param in * The non-null input stream to count line numbers. - * @since Android 1.0 */ public LineNumberInputStream(InputStream in) { super(in); @@ -63,12 +60,10 @@ public class LineNumberInputStream extends FilterInputStream { * which are converted into {@code '\n'} by this stream. Therefore, * {@code available} returns only {@code in.available() / 2} bytes as * result. - *

            - * + * * @return the guaranteed number of bytes available before blocking. * @throws IOException * if an error occurs in this stream. - * @since Android 1.0 */ @Override public int available() throws IOException { @@ -77,9 +72,8 @@ public class LineNumberInputStream extends FilterInputStream { /** * Returns the current line number for this stream. Numbering starts at 0. - * + * * @return the current line number. - * @since Android 1.0 */ public int getLineNumber() { return lineNumber; @@ -94,14 +88,12 @@ public class LineNumberInputStream extends FilterInputStream { * line number count. *

            * This implementation sets a mark in the filtered stream. - *

            - * + * * @param readlimit * the number of bytes that can be read from this stream before * the mark is invalidated. * @see #markSupported() * @see #reset() - * @since Android 1.0 */ @Override public void mark(int readlimit) { @@ -119,14 +111,13 @@ public class LineNumberInputStream extends FilterInputStream { * Recognized line terminator sequences are {@code '\r'}, {@code '\n'} and * {@code "\r\n"}. Line terminator sequences are always translated into * {@code '\n'}. - *

            - * + * * @return the byte read or -1 if the end of the filtered stream has been * reached. * @throws IOException * if the stream is closed or another IOException occurs. - * @since Android 1.0 */ + @SuppressWarnings("fallthrough") @Override public int read() throws IOException { int currentChar = lastChar; @@ -159,8 +150,7 @@ public class LineNumberInputStream extends FilterInputStream { * Recognized line terminator sequences are {@code '\r'}, {@code '\n'} and * {@code "\r\n"}. Line terminator sequences are always translated into * {@code '\n'}. - *

            - * + * * @param buffer * the array in which to store the bytes read. * @param offset @@ -178,7 +168,6 @@ public class LineNumberInputStream extends FilterInputStream { * if this stream is closed or another IOException occurs. * @throws NullPointerException * if {@code buffer} is {@code null}. - * @since Android 1.0 */ @Override public int read(byte[] buffer, int offset, int length) throws IOException { @@ -217,14 +206,13 @@ public class LineNumberInputStream extends FilterInputStream { /** * Resets this stream to the last marked location. It also resets the line * count to what is was when this stream was marked. - * + * * @throws IOException * if this stream is already closed, no mark has been set or the * mark is no longer valid because more than {@code readlimit} * bytes have been read since setting the mark. * @see #mark(int) * @see #markSupported() - * @since Android 1.0 */ @Override public void reset() throws IOException { @@ -237,12 +225,11 @@ public class LineNumberInputStream extends FilterInputStream { * Sets the line number of this stream to the specified * {@code lineNumber}. Note that this may have side effects on the * line number associated with the last marked position. - * + * * @param lineNumber * the new lineNumber value. * @see #mark(int) * @see #reset() - * @since Android 1.0 */ public void setLineNumber(int lineNumber) { this.lineNumber = lineNumber; @@ -254,7 +241,7 @@ public class LineNumberInputStream extends FilterInputStream { * used. This implementation skips {@code count} number of bytes in the * filtered stream and increments the line number count whenever line * terminator sequences are skipped. - * + * * @param count * the number of bytes to skip. * @return the number of bytes actually skipped. @@ -263,7 +250,6 @@ public class LineNumberInputStream extends FilterInputStream { * @see #mark(int) * @see #read() * @see #reset() - * @since Android 1.0 */ @Override public long skip(long count) throws IOException { diff --git a/libcore/luni/src/main/java/java/io/LineNumberReader.java b/libcore/luni/src/main/java/java/io/LineNumberReader.java index 8110426e8..8ecdd95a2 100644 --- a/libcore/luni/src/main/java/java/io/LineNumberReader.java +++ b/libcore/luni/src/main/java/java/io/LineNumberReader.java @@ -22,8 +22,6 @@ package java.io; * while reading the data. The line number starts at 0 and is incremented any * time {@code '\r'}, {@code '\n'} or {@code "\r\n"} is read. The class has an * internal buffer for its data. The size of the buffer defaults to 8 KB. - * - * @since Android 1.0 */ public class LineNumberReader extends BufferedReader { @@ -38,10 +36,9 @@ public class LineNumberReader extends BufferedReader { /** * Constructs a new LineNumberReader on the Reader {@code in}. The internal * buffer gets the default size (8 KB). - * + * * @param in * the Reader that is buffered. - * @since Android 1.0 */ public LineNumberReader(Reader in) { super(in); @@ -50,14 +47,13 @@ public class LineNumberReader extends BufferedReader { /** * Constructs a new LineNumberReader on the Reader {@code in}. The size of * the internal buffer is specified by the parameter {@code size}. - * + * * @param in * the Reader that is buffered. * @param size * the size of the buffer to allocate. * @throws IllegalArgumentException * if {@code size <= 0}. - * @since Android 1.0 */ public LineNumberReader(Reader in, int size) { super(in, size); @@ -65,9 +61,8 @@ public class LineNumberReader extends BufferedReader { /** * Returns the current line number for this reader. Numbering starts at 0. - * + * * @return the current line number. - * @since Android 1.0 */ public int getLineNumber() { synchronized (lock) { @@ -82,7 +77,7 @@ public class LineNumberReader extends BufferedReader { * position, provided that {@code readlimit} has not been surpassed. The * line number associated with this marked position is also stored so that * it can be restored when {@code reset()} is called. - * + * * @param readlimit * the number of characters that can be read from this stream * before the mark is invalidated. @@ -90,7 +85,6 @@ public class LineNumberReader extends BufferedReader { * if an error occurs while setting the mark in this reader. * @see #markSupported() * @see #reset() - * @since Android 1.0 */ @Override public void mark(int readlimit) throws IOException { @@ -110,14 +104,13 @@ public class LineNumberReader extends BufferedReader { * Recognized line terminator sequences are {@code '\r'}, {@code '\n'} and * {@code "\r\n"}. Line terminator sequences are always translated into * {@code '\n'}. - *

            - * + * * @return the character read or -1 if the end of the source reader has been * reached. * @throws IOException * if the reader is closed or another IOException occurs. - * @since Android 1.0 */ + @SuppressWarnings("fallthrough") @Override public int read() throws IOException { synchronized (lock) { @@ -148,8 +141,7 @@ public class LineNumberReader extends BufferedReader { * Recognized line terminator sequences are {@code '\r'}, {@code '\n'} and * {@code "\r\n"}. Line terminator sequences are always translated into * {@code '\n'}. - *

            - * + * * @param buffer * the array in which to store the characters read. * @param offset @@ -161,7 +153,6 @@ public class LineNumberReader extends BufferedReader { * source reader has been reached while reading. * @throws IOException * if this reader is closed or another IOException occurs. - * @since Android 1.0 */ @Override public int read(char[] buffer, int offset, int count) throws IOException { @@ -193,28 +184,17 @@ public class LineNumberReader extends BufferedReader { * represented by 0 or more characters followed by {@code '\r'}, * {@code '\n'}, {@code "\r\n"} or the end of the stream. The returned * string does not include the newline sequence. - * + * * @return the contents of the line or {@code null} if no characters have * been read before the end of the stream has been reached. * @throws IOException * if this reader is closed or another IOException occurs. - * @since Android 1.0 */ @Override public String readLine() throws IOException { synchronized (lock) { - /* Typical Line Length */ - StringBuilder result = new StringBuilder(80); - while (true) { - int character = read(); - if (character == -1) { - return result.length() != 0 ? result.toString() : null; - } - if (character == '\n') { - return result.toString(); - } - result.append((char) character); - } + lineNumber++; + return super.readLine(); } } @@ -222,14 +202,13 @@ public class LineNumberReader extends BufferedReader { * Resets this reader to the last marked location. It also resets the line * count to what is was when this reader was marked. This implementation * resets the source reader. - * + * * @throws IOException * if this reader is already closed, no mark has been set or the * mark is no longer valid because more than {@code readlimit} * bytes have been read since setting the mark. * @see #mark(int) * @see #markSupported() - * @since Android 1.0 */ @Override public void reset() throws IOException { @@ -244,12 +223,11 @@ public class LineNumberReader extends BufferedReader { * Sets the line number of this reader to the specified {@code lineNumber}. * Note that this may have side effects on the line number associated with * the last marked position. - * + * * @param lineNumber * the new line number value. * @see #mark(int) * @see #reset() - * @since Android 1.0 */ public void setLineNumber(int lineNumber) { synchronized (lock) { @@ -263,7 +241,7 @@ public class LineNumberReader extends BufferedReader { * is used. This implementation skips {@code count} number of characters in * the source reader and increments the line number count whenever line * terminator sequences are skipped. - * + * * @param count * the number of characters to skip. * @return the number of characters actually skipped. @@ -274,7 +252,6 @@ public class LineNumberReader extends BufferedReader { * @see #mark(int) * @see #read() * @see #reset() - * @since Android 1.0 */ @Override public long skip(long count) throws IOException { diff --git a/libcore/luni/src/main/java/java/io/NotActiveException.java b/libcore/luni/src/main/java/java/io/NotActiveException.java index 402eabe4f..51f4d3f84 100644 --- a/libcore/luni/src/main/java/java/io/NotActiveException.java +++ b/libcore/luni/src/main/java/java/io/NotActiveException.java @@ -29,8 +29,6 @@ package java.io; *
          • {@link ObjectInputStream#registerValidation(ObjectInputValidation, int)}
          • *
          • {@link ObjectOutputStream#defaultWriteObject()}
          • *
          - * - * @since Android 1.0 */ public class NotActiveException extends ObjectStreamException { @@ -39,8 +37,6 @@ public class NotActiveException extends ObjectStreamException { /** * Constructs a new {@code NotActiveException} with its stack trace filled * in. - * - * @since Android 1.0 */ public NotActiveException() { super(); @@ -52,7 +48,6 @@ public class NotActiveException extends ObjectStreamException { * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public NotActiveException(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/io/NotSerializableException.java b/libcore/luni/src/main/java/java/io/NotSerializableException.java index ac57a76ee..f6e93a734 100644 --- a/libcore/luni/src/main/java/java/io/NotSerializableException.java +++ b/libcore/luni/src/main/java/java/io/NotSerializableException.java @@ -26,8 +26,6 @@ package java.io; * * @see ObjectOutput#writeObject(Object) * @see ObjectOutputStream#writeObject(Object) - * - * @since Android 1.0 */ public class NotSerializableException extends ObjectStreamException { @@ -36,8 +34,6 @@ public class NotSerializableException extends ObjectStreamException { /** * Constructs a new {@code NotSerializableException} with its stack trace * filled in. - * - * @since Android 1.0 */ public NotSerializableException() { super(); @@ -49,7 +45,6 @@ public class NotSerializableException extends ObjectStreamException { * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public NotSerializableException(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/io/ObjectInput.java b/libcore/luni/src/main/java/java/io/ObjectInput.java index 7ca3cbed6..3b325534d 100644 --- a/libcore/luni/src/main/java/java/io/ObjectInput.java +++ b/libcore/luni/src/main/java/java/io/ObjectInput.java @@ -22,8 +22,6 @@ package java.io; * * @see ObjectInputStream * @see ObjectOutput - * - * @since Android 1.0 */ public interface ObjectInput extends DataInput { /** @@ -33,7 +31,6 @@ public interface ObjectInput extends DataInput { * @return the number of bytes available. * @throws IOException * if an I/O error occurs. - * @since Android 1.0 */ public int available() throws IOException; @@ -43,7 +40,6 @@ public interface ObjectInput extends DataInput { * * @throws IOException * if an I/O error occurs while closing the input stream. - * @since Android 1.0 */ public void close() throws IOException; @@ -55,7 +51,6 @@ public interface ObjectInput extends DataInput { * @return the byte read or -1 if the end of this stream has been reached. * @throws IOException * if this stream is closed or another I/O error occurs. - * @since Android 1.0 */ public int read() throws IOException; @@ -69,7 +64,6 @@ public interface ObjectInput extends DataInput { * reached. * @throws IOException * if this stream is closed or another I/O error occurs. - * @since Android 1.0 */ public int read(byte[] buffer) throws IOException; @@ -89,7 +83,6 @@ public interface ObjectInput extends DataInput { * reached. * @throws IOException * if this stream is closed or another I/O error occurs. - * @since Android 1.0 */ public int read(byte[] buffer, int offset, int count) throws IOException; @@ -102,7 +95,6 @@ public interface ObjectInput extends DataInput { * if the object's class cannot be found. * @throws IOException * if this stream is closed or another I/O error occurs. - * @since Android 1.0 */ public Object readObject() throws ClassNotFoundException, IOException; @@ -117,7 +109,6 @@ public interface ObjectInput extends DataInput { * * @throws IOException * if this stream is closed or another I/O error occurs. - * @since Android 1.0 */ public long skip(long toSkip) throws IOException; } diff --git a/libcore/luni/src/main/java/java/io/ObjectInputStream.java b/libcore/luni/src/main/java/java/io/ObjectInputStream.java index 9483ab016..6d24eb282 100644 --- a/libcore/luni/src/main/java/java/io/ObjectInputStream.java +++ b/libcore/luni/src/main/java/java/io/ObjectInputStream.java @@ -17,6 +17,11 @@ package java.io; +// BEGIN android-note +// Harmony uses ObjectAccessors to access fields through JNI. Android has not +// yet migrated that API. As a consequence, there's a lot of changes here... +// END android-note + import java.io.EmulatedFields.ObjectSlot; import java.lang.reflect.Array; import java.lang.reflect.Constructor; @@ -27,14 +32,20 @@ import java.lang.reflect.Proxy; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.ArrayList; -import java.util.Hashtable; -import java.util.IdentityHashMap; +import java.util.HashMap; import java.util.Iterator; // BEGIN android-added import dalvik.system.VMStack; // END android-added +// BEGIN android-removed +// import org.apache.harmony.misc.accessors.ObjectAccessor; +// import org.apache.harmony.misc.accessors.AccessorFactory; +// END android-removed + +import org.apache.harmony.kernel.vm.VM; +import org.apache.harmony.luni.internal.nls.Messages; import org.apache.harmony.luni.util.Msg; import org.apache.harmony.luni.util.PriviAction; @@ -42,18 +53,19 @@ import org.apache.harmony.luni.util.PriviAction; * A specialized {@link InputStream} that is able to read (deserialize) Java * objects as well as primitive data types (int, byte, char etc.). The data has * typically been saved using an ObjectOutputStream. - * + * * @see ObjectOutputStream * @see ObjectInput * @see Serializable * @see Externalizable - * - * @since Android 1.0 */ public class ObjectInputStream extends InputStream implements ObjectInput, ObjectStreamConstants { - private static InputStream emptyStream = new ByteArrayInputStream( + // BEGIN android-note + // this is non-static to avoid sync contention. Would static be faster? + // END android-note + private InputStream emptyStream = new ByteArrayInputStream( new byte[0]); // To put into objectsRead when reading unsharedObject @@ -85,7 +97,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput, private boolean enableResolve; // Table mapping Integer (handle) -> Object - private Hashtable objectsRead; + private HashMap objectsRead; // Used by defaultReadObject private Object currentObject; @@ -109,10 +121,8 @@ public class ObjectInputStream extends InputStream implements ObjectInput, // Handle for the current class descriptor private Integer descriptorHandle; - // cache for readResolve methods - private IdentityHashMap, Object> readResolveCache; - - private static final Hashtable> PRIMITIVE_CLASSES = new Hashtable>(); + private static final HashMap> PRIMITIVE_CLASSES = + new HashMap>(); static { PRIMITIVE_CLASSES.put("byte", byte.class); //$NON-NLS-1$ @@ -125,6 +135,10 @@ public class ObjectInputStream extends InputStream implements ObjectInput, PRIMITIVE_CLASSES.put("double", double.class); //$NON-NLS-1$ } + // BEGIN android-removed + // private ObjectAccessor accessor = AccessorFactory.getObjectAccessor(); + // END android-removed + // Internal type used to keep track of validators & corresponding priority static class InputValidationDesc { ObjectInputValidation validator; @@ -135,22 +149,19 @@ public class ObjectInputStream extends InputStream implements ObjectInput, /** * GetField is an inner class that provides access to the persistent fields * read from the source stream. - * - * @since Android 1.0 */ public abstract static class GetField { /** * Gets the ObjectStreamClass that describes a field. - * + * * @return the descriptor class for a serialized field. - * @since Android 1.0 */ public abstract ObjectStreamClass getObjectStreamClass(); /** * Indicates if the field identified by {@code name} is defaulted. This * means that it has no value in this stream. - * + * * @param name * the name of the field to check. * @return {@code true} if the field is defaulted, {@code false} @@ -160,7 +171,6 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * @throws IOException * if an error occurs while reading from the source input * stream. - * @since Android 1.0 */ public abstract boolean defaulted(String name) throws IOException, IllegalArgumentException; @@ -168,7 +178,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput, /** * Gets the value of the boolean field identified by {@code name} from * the persistent field. - * + * * @param name * the name of the field to get. * @param defaultValue @@ -181,7 +191,6 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * @throws IllegalArgumentException * if the type of the field identified by {@code name} is * not {@code boolean}. - * @since Android 1.0 */ public abstract boolean get(String name, boolean defaultValue) throws IOException, IllegalArgumentException; @@ -189,7 +198,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput, /** * Gets the value of the character field identified by {@code name} from * the persistent field. - * + * * @param name * the name of the field to get. * @param defaultValue @@ -202,7 +211,6 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * @throws IllegalArgumentException * if the type of the field identified by {@code name} is * not {@code char}. - * @since Android 1.0 */ public abstract char get(String name, char defaultValue) throws IOException, IllegalArgumentException; @@ -210,7 +218,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput, /** * Gets the value of the byte field identified by {@code name} from the * persistent field. - * + * * @param name * the name of the field to get. * @param defaultValue @@ -223,7 +231,6 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * @throws IllegalArgumentException * if the type of the field identified by {@code name} is * not {@code byte}. - * @since Android 1.0 */ public abstract byte get(String name, byte defaultValue) throws IOException, IllegalArgumentException; @@ -231,7 +238,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput, /** * Gets the value of the short field identified by {@code name} from the * persistent field. - * + * * @param name * the name of the field to get. * @param defaultValue @@ -244,7 +251,6 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * @throws IllegalArgumentException * if the type of the field identified by {@code name} is * not {@code short}. - * @since Android 1.0 */ public abstract short get(String name, short defaultValue) throws IOException, IllegalArgumentException; @@ -252,7 +258,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput, /** * Gets the value of the integer field identified by {@code name} from * the persistent field. - * + * * @param name * the name of the field to get. * @param defaultValue @@ -265,15 +271,14 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * @throws IllegalArgumentException * if the type of the field identified by {@code name} is * not {@code int}. - * @since Android 1.0 */ public abstract int get(String name, int defaultValue) throws IOException, IllegalArgumentException; - /** + /** * Gets the value of the long field identified by {@code name} from the * persistent field. - * + * * @param name * the name of the field to get. * @param defaultValue @@ -286,7 +291,6 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * @throws IllegalArgumentException * if the type of the field identified by {@code name} is * not {@code long}. - * @since Android 1.0 */ public abstract long get(String name, long defaultValue) throws IOException, IllegalArgumentException; @@ -294,7 +298,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput, /** * Gets the value of the float field identified by {@code name} from the * persistent field. - * + * * @param name * the name of the field to get. * @param defaultValue @@ -307,7 +311,6 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * @throws IllegalArgumentException * if the type of the field identified by {@code float} is * not {@code char}. - * @since Android 1.0 */ public abstract float get(String name, float defaultValue) throws IOException, IllegalArgumentException; @@ -315,7 +318,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput, /** * Gets the value of the double field identified by {@code name} from * the persistent field. - * + * * @param name * the name of the field to get. * @param defaultValue @@ -328,7 +331,6 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * @throws IllegalArgumentException * if the type of the field identified by {@code name} is * not {@code double}. - * @since Android 1.0 */ public abstract double get(String name, double defaultValue) throws IOException, IllegalArgumentException; @@ -336,7 +338,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput, /** * Gets the value of the object field identified by {@code name} from * the persistent field. - * + * * @param name * the name of the field to get. * @param defaultValue @@ -349,7 +351,6 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * @throws IllegalArgumentException * if the type of the field identified by {@code name} is * not {@code Object}. - * @since Android 1.0 */ public abstract Object get(String name, Object defaultValue) throws IOException, IllegalArgumentException; @@ -359,14 +360,13 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * Constructs a new ObjectInputStream. This default constructor can be used * by subclasses that do not want to use the public constructor if it * allocates unneeded data. - * + * * @throws IOException * if an error occurs when creating this stream. * @throws SecurityException * if a security manager is installed and it denies subclassing * this class. * @see SecurityManager#checkPermission(java.security.Permission) - * @since Android 1.0 */ protected ObjectInputStream() throws IOException, SecurityException { super(); @@ -382,7 +382,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput, /** * Constructs a new ObjectInputStream that reads from the InputStream * {@code input}. - * + * * @param input * the non-null source InputStream to filter reads on. * @throws IOException @@ -393,7 +393,6 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * @throws SecurityException * if a security manager is installed and it denies subclassing * this class. - * @since Android 1.0 */ public ObjectInputStream(InputStream input) throws StreamCorruptedException, IOException { @@ -437,7 +436,6 @@ public class ObjectInputStream extends InputStream implements ObjectInput, primitiveTypes = new DataInputStream(this); enableResolve = false; this.subclassOverridingImplementation = false; - this.readResolveCache = new IdentityHashMap, Object>(); resetState(); nestedLevels = 0; // So read...() methods can be used by @@ -452,12 +450,11 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * Returns the number of bytes of primitive data that can be read from this * stream without blocking. This method should not be used at any arbitrary * position; just when reading primitive data types (int, char etc). - * + * * @return the number of available primitive data bytes. * @throws IOException * if any I/O problem occurs while computing the available * bytes. - * @since Android 1.0 */ @Override public int available() throws IOException { @@ -470,7 +467,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * Checks to if it is ok to read primitive types from this stream at * this point. One is not supposed to read primitive types when about to * read an object, for example, so an exception has to be thrown. - * + * * @throws IOException * If any IO problem occurred when trying to read primitive type * or if it is illegal to read primitive types @@ -514,10 +511,9 @@ public class ObjectInputStream extends InputStream implements ObjectInput, /** * Closes this stream. This implementation closes the source stream. - * + * * @throws IOException * if an error occurs while closing this stream. - * @since Android 1.0 */ @Override public void close() throws IOException { @@ -528,7 +524,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * Default method to read objects from this stream. Serializable fields * defined in the object's class and superclasses are read from the source * stream. - * + * * @throws ClassNotFoundException * if the object's class cannot be found. * @throws IOException @@ -536,7 +532,6 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * @throws NotActiveException * if this method is not called from {@code readObject()}. * @see ObjectOutputStream#defaultWriteObject - * @since Android 1.0 */ public void defaultReadObject() throws IOException, ClassNotFoundException, NotActiveException { @@ -552,7 +547,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * Enables object replacement for this stream. By default this is not * enabled. Only trusted subclasses (loaded with system class loader) are * allowed to change this status. - * + * * @param enable * {@code true} to enable object replacement; {@code false} to * disable it. @@ -562,7 +557,6 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * object replacement for this stream. * @see #resolveObject * @see ObjectOutputStream#enableReplaceObject - * @since Android 1.0 */ protected boolean enableResolveObject(boolean enable) throws SecurityException { @@ -581,7 +575,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput, /** * Checks if two classes belong to the same package. - * + * * @param c1 * one of the classes to test. * @param c2 @@ -594,12 +588,9 @@ public class ObjectInputStream extends InputStream implements ObjectInput, String nameC2 = c2.getName(); int indexDotC1 = nameC1.lastIndexOf('.'); int indexDotC2 = nameC2.lastIndexOf('.'); - // BEGIN android-changed - // copied from newer version of harmony if (indexDotC1 != indexDotC2) { return false; // cannot be in the same package if indices are not } - // END android-changed // the same if (indexDotC1 < 0) { return true; // both of them are in default package @@ -608,16 +599,17 @@ public class ObjectInputStream extends InputStream implements ObjectInput, nameC2.substring(0, indexDotC2)); } + // BEGIN android-added /** * Create and return a new instance of class {@code instantiationClass} * but running the constructor defined in class * {@code constructorClass} (same as {@code instantiationClass} * or a superclass). - * + * * Has to be native to avoid visibility rules and to be able to have * {@code instantiationClass} not the same as * {@code constructorClass} (no such API in java.lang.reflect). - * + * * @param instantiationClass * The new object will be an instance of this class * @param constructorClass @@ -626,26 +618,27 @@ public class ObjectInputStream extends InputStream implements ObjectInput, */ private static native Object newInstance(Class instantiationClass, Class constructorClass); + // END android-added /** * Return the next {@code int} handle to be used to indicate cyclic * references being loaded from the stream. - * + * * @return the next handle to represent the next cyclic reference */ - private int nextHandle() { - return this.currentHandle++; + private Integer nextHandle() { + return Integer.valueOf(this.currentHandle++); } /** * Return the next token code (TC) from the receiver, which indicates what * kind of object follows - * + * * @return the next TC from the receiver - * + * * @throws IOException * If an IO error occurs - * + * * @see ObjectStreamConstants */ private byte nextTC() throws IOException { @@ -670,12 +663,11 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * Reads a single byte from the source stream and returns it as an integer * in the range from 0 to 255. Returns -1 if the end of the source stream * has been reached. Blocks if no input is available. - * + * * @return the byte read or -1 if the end of the source stream has been * reached. * @throws IOException * if an error occurs while reading from this stream. - * @since Android 1.0 */ @Override public int read() throws IOException { @@ -688,7 +680,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * in byte array {@code buffer} starting at offset {@code count}. Blocks * until {@code count} bytes have been read, the end of the source stream is * detected or an exception is thrown. - * + * * @param buffer * the array in which to store the bytes read. * @param offset @@ -706,7 +698,6 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * if an error occurs while reading from this stream. * @throws NullPointerException * if {@code buffer} is {@code null}. - * @since Android 1.0 */ @Override public int read(byte[] buffer, int offset, int length) throws IOException { @@ -734,9 +725,9 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * Reads and returns an array of raw bytes with primitive data. The array * will have up to 255 bytes. The primitive data will be in the format * described by {@code DataOutputStream}. - * + * * @return The primitive data read, as raw bytes - * + * * @throws IOException * If an IO exception happened when reading the primitive data. */ @@ -750,9 +741,9 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * Reads and returns an array of raw bytes with primitive data. The array * will have more than 255 bytes. The primitive data will be in the format * described by {@code DataOutputStream}. - * + * * @return The primitive data read, as raw bytes - * + * * @throws IOException * If an IO exception happened when reading the primitive data. */ @@ -764,14 +755,13 @@ public class ObjectInputStream extends InputStream implements ObjectInput, /** * Reads a boolean from the source stream. - * + * * @return the boolean value read from the source stream. * @throws EOFException * if the end of the input is reached before the read * request can be satisfied. * @throws IOException * if an error occurs while reading from the source stream. - * @since Android 1.0 */ public boolean readBoolean() throws IOException { return primitiveTypes.readBoolean(); @@ -779,14 +769,13 @@ public class ObjectInputStream extends InputStream implements ObjectInput, /** * Reads a byte (8 bit) from the source stream. - * + * * @return the byte value read from the source stream. * @throws EOFException * if the end of the input is reached before the read * request can be satisfied. * @throws IOException * if an error occurs while reading from the source stream. - * @since Android 1.0 */ public byte readByte() throws IOException { return primitiveTypes.readByte(); @@ -794,14 +783,13 @@ public class ObjectInputStream extends InputStream implements ObjectInput, /** * Reads a character (16 bit) from the source stream. - * + * * @return the char value read from the source stream. * @throws EOFException * if the end of the input is reached before the read * request can be satisfied. * @throws IOException * if an error occurs while reading from the source stream. - * @since Android 1.0 */ public char readChar() throws IOException { return primitiveTypes.readChar(); @@ -809,7 +797,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput, /** * Reads and discards block data and objects until TC_ENDBLOCKDATA is found. - * + * * @throws IOException * If an IO exception happened when reading the optional class * annotation. @@ -834,9 +822,9 @@ public class ObjectInputStream extends InputStream implements ObjectInput, /** * Reads a class descriptor (an {@code ObjectStreamClass}) from the * stream. - * + * * @return the class descriptor read from the stream - * + * * @throws IOException * If an IO exception happened when reading the class * descriptor. @@ -855,8 +843,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput, ObjectStreamClass streamClass = ObjectStreamClass .lookup(proxyClass); streamClass.setLoadFields(new ObjectStreamField[0]); - registerObjectRead(streamClass, Integer.valueOf(nextHandle()), - false); + registerObjectRead(streamClass, nextHandle(), false); checkedSetSuperClassDesc(streamClass, readClassDesc()); return streamClass; case TC_REFERENCE: @@ -872,11 +859,11 @@ public class ObjectInputStream extends InputStream implements ObjectInput, /** * Reads the content of the receiver based on the previously read token * {@code tc}. - * + * * @param tc * The token code for the next item in the stream * @return the object read from the stream - * + * * @throws IOException * If an IO exception happened when reading the class * descriptor. @@ -922,11 +909,11 @@ public class ObjectInputStream extends InputStream implements ObjectInput, /** * Reads the content of the receiver based on the previously read token * {@code tc}. Primitive data content is considered an error. - * + * * @param unshared * read the object unshared * @return the object read from the stream - * + * * @throws IOException * If an IO exception happened when reading the class * descriptor. @@ -990,9 +977,9 @@ public class ObjectInputStream extends InputStream implements ObjectInput, /** * Reads the next item from the stream assuming it is a cyclic reference to * an object previously read. Return the actual object previously read. - * + * * @return the object previously read from the stream - * + * * @throws IOException * If an IO exception happened when reading the class * descriptor. @@ -1006,14 +993,13 @@ public class ObjectInputStream extends InputStream implements ObjectInput, /** * Reads a double (64 bit) from the source stream. - * + * * @return the double value read from the source stream. * @throws EOFException * if the end of the input is reached before the read * request can be satisfied. * @throws IOException * if an error occurs while reading from the source stream. - * @since Android 1.0 */ public double readDouble() throws IOException { return primitiveTypes.readDouble(); @@ -1029,9 +1015,9 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * When exceptions are found normally in the object graph, they are loaded * as a regular object, and not by this method. In that case, the set of * "known objects" is not reset. - * + * * @return the exception read - * + * * @throws IOException * If an IO exception happened when reading the exception * object. @@ -1064,17 +1050,17 @@ public class ObjectInputStream extends InputStream implements ObjectInput, /** * Reads a collection of field descriptors (name, type name, etc) for the * class descriptor {@code cDesc} (an {@code ObjectStreamClass}) - * + * * @param cDesc * The class descriptor (an {@code ObjectStreamClass}) * for which to write field information - * + * * @throws IOException * If an IO exception happened when reading the field * descriptors. * @throws ClassNotFoundException * If a class for one of the field types could not be found - * + * * @see #readObject() */ private void readFieldDescriptors(ObjectStreamClass cDesc) @@ -1109,17 +1095,45 @@ public class ObjectInputStream extends InputStream implements ObjectInput, enableResolve = old; } } + + classSig = formatClassSig(classSig); ObjectStreamField f = new ObjectStreamField(classSig, fieldName); fields[i] = f; } } + /* + * Format the class signature for ObjectStreamField, for example, + * "[L[Ljava.lang.String;;" is converted to "[Ljava.lang.String;" + */ + private static String formatClassSig(String classSig) { + int start = 0; + int end = classSig.length(); + + if (end <= 0) { + return classSig; + } + + while (classSig.startsWith("[L", start) //$NON-NLS-1$ + && classSig.charAt(end - 1) == ';') { + start += 2; + end--; + } + + if (start > 0) { + start -= 2; + end++; + return classSig.substring(start, end); + } + return classSig; + } + /** * Reads the persistent fields of the object that is currently being read * from the source stream. The values read are stored in a GetField object * that provides access to the persistent fields. This GetField object is * then returned. - * + * * @return the GetField object from which persistent fields can be accessed * by name. * @throws ClassNotFoundException @@ -1129,7 +1143,6 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * if an error occurs while reading from this stream. * @throws NotActiveException * if this stream is currently not reading an object. - * @since Android 1.0 */ public GetField readFields() throws IOException, ClassNotFoundException, NotActiveException { @@ -1146,11 +1159,11 @@ public class ObjectInputStream extends InputStream implements ObjectInput, /** * Reads a collection of field values for the emulated fields * {@code emulatedFields} - * + * * @param emulatedFields * an {@code EmulatedFieldsForLoading}, concrete subclass * of {@code GetField} - * + * * @throws IOException * If an IO exception happened when reading the field values. * @throws InvalidClassException @@ -1159,7 +1172,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * @throws OptionalDataException * If optional data could not be found when reading the * exception graph - * + * * @see #readFields * @see #readObject() */ @@ -1206,13 +1219,13 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * This is the default mechanism, when emulated fields (an * {@code GetField}) are not used. Actual values to load are stored * directly into the object {@code obj}. - * + * * @param obj * Instance in which the fields will be set. * @param classDesc * A class descriptor (an {@code ObjectStreamClass}) * defining which fields should be loaded. - * + * * @throws IOException * If an IO exception happened when reading the field values. * @throws InvalidClassException @@ -1223,7 +1236,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * exception graph * @throws ClassNotFoundException * If a class of an object being de-serialized can not be found - * + * * @see #readFields * @see #readObject() */ @@ -1238,9 +1251,16 @@ public class ObjectInputStream extends InputStream implements ObjectInput, } for (ObjectStreamField fieldDesc : fields) { + + // BEGIN android-removed + // // get associated Field + // long fieldID = fieldDesc.getFieldID(accessor, declaringClass); + // END android-removed + // Code duplication starts, just because Java is typed if (fieldDesc.isPrimitive()) { try { + // BEGIN android-changed switch (fieldDesc.getTypeCode()) { case 'B': setField(obj, declaringClass, fieldDesc.getName(), @@ -1278,19 +1298,22 @@ public class ObjectInputStream extends InputStream implements ObjectInput, throw new StreamCorruptedException(Msg.getString( "K00d5", fieldDesc.getTypeCode())); //$NON-NLS-1$ } + // END android-changed } catch (NoSuchFieldError err) { } } else { // Object type (array included). String fieldName = fieldDesc.getName(); boolean setBack = false; + // BEGIN android-added ObjectStreamField field = classDesc.getField(fieldName); - if (mustResolve && field == null) { + // END android-added + if (mustResolve && fieldDesc == null) { setBack = true; mustResolve = false; } Object toSet; - if (field != null && field.isUnshared()) { + if (fieldDesc != null && fieldDesc.isUnshared()) { toSet = readUnshared(); } else { toSet = readObject(); @@ -1298,18 +1321,15 @@ public class ObjectInputStream extends InputStream implements ObjectInput, if (setBack) { mustResolve = true; } - if (field != null) { + if (fieldDesc != null) { if (toSet != null) { - // BEGIN android-removed - // Class fieldType = field.getType(); - // END android-removed - // BEGIN android-added - // Originally getTypeInternal() was called getType(). - // After the semantics of getType() changed inside - // Harmony, the check below wasn't adjusted and didn't - // work anymore. + // BEGIN android-changed + // Get the field type from the local field rather than + // from the stream's supplied data. That's the field + // we'll be setting, so that's the one that needs to be + // validated. Class fieldType = field.getTypeInternal(); - // END android-added + // END android-added Class valueType = toSet.getClass(); if (!fieldType.isAssignableFrom(valueType)) { throw new ClassCastException(Msg.getString( @@ -1319,8 +1339,10 @@ public class ObjectInputStream extends InputStream implements ObjectInput, + fieldName })); } try { + // BEGIN android-changed objSetField(obj, declaringClass, fieldName, field .getTypeString(), toSet); + // END android-changed } catch (NoSuchFieldError e) { // Ignored } @@ -1332,14 +1354,13 @@ public class ObjectInputStream extends InputStream implements ObjectInput, /** * Reads a float (32 bit) from the source stream. - * + * * @return the float value read from the source stream. * @throws EOFException * if the end of the input is reached before the read * request can be satisfied. * @throws IOException * if an error occurs while reading from the source stream. - * @since Android 1.0 */ public float readFloat() throws IOException { return primitiveTypes.readFloat(); @@ -1348,7 +1369,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput, /** * Reads bytes from the source stream into the byte array {@code buffer}. * This method will block until {@code buffer.length} bytes have been read. - * + * * @param buffer * the array in which to store the bytes read. * @throws EOFException @@ -1356,7 +1377,6 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * request can be satisfied. * @throws IOException * if an error occurs while reading from the source stream. - * @since Android 1.0 */ public void readFully(byte[] buffer) throws IOException { primitiveTypes.readFully(buffer); @@ -1366,7 +1386,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * Reads bytes from the source stream into the byte array {@code buffer}. * This method will block until {@code length} number of bytes have been * read. - * + * * @param buffer * the byte array in which to store the bytes read. * @param offset @@ -1379,7 +1399,6 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * request can be satisfied. * @throws IOException * if an error occurs while reading from the source stream. - * @since Android 1.0 */ public void readFully(byte[] buffer, int offset, int length) throws IOException { @@ -1394,13 +1413,13 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * (corresponding to class descriptor {@code classDesc}) defines * private instance method {@code readObject} it will be used to load * field values. - * + * * @param object * Instance into which stored field values loaded. * @param classDesc * A class descriptor (an {@code ObjectStreamClass}) * defining which fields should be loaded. - * + * * @throws IOException * If an IO exception happened when reading the field values in * the hierarchy. @@ -1409,7 +1428,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * @throws NotActiveException * If {@code defaultReadObject} is called from the wrong * context. - * + * * @see #defaultReadObject * @see #readObject() */ @@ -1449,7 +1468,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput, int index = findStreamSuperclass(superclass, streamClassList, lastIndex); if (index == -1) { - readObjectNoData(object, superclass); + readObjectNoData(object, superclass, ObjectStreamClass.lookupStreamClass(superclass)); } else { for (int j = lastIndex; j <= index; j++) { readObjectForClass(object, streamClassList.get(j)); @@ -1483,16 +1502,13 @@ public class ObjectInputStream extends InputStream implements ObjectInput, return -1; } - private void readObjectNoData(Object object, Class cl) + private void readObjectNoData(Object object, Class cl, ObjectStreamClass classDesc) throws ObjectStreamException { - if (!ObjectStreamClass.isSerializable(cl)) { + if (!classDesc.isSerializable()) { return; } - - final Method readMethod = ObjectStreamClass - .getPrivateReadObjectNoDataMethod(cl); - if (readMethod != null) { - AccessController.doPrivileged(new PriviAction(readMethod)); + if (classDesc.hasMethodReadObjectNoData()){ + final Method readMethod = classDesc.getMethodReadObjectNoData(); try { readMethod.invoke(object, new Object[0]); } catch (InvocationTargetException e) { @@ -1507,6 +1523,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput, throw new RuntimeException(e.toString()); } } + } private void readObjectForClass(Object object, ObjectStreamClass classDesc) @@ -1518,12 +1535,12 @@ public class ObjectInputStream extends InputStream implements ObjectInput, boolean hadWriteMethod = (classDesc.getFlags() & SC_WRITE_METHOD) > 0; Class targetClass = classDesc.forClass(); + final Method readMethod; if (targetClass == null || !mustResolve) { readMethod = null; } else { - readMethod = ObjectStreamClass - .getPrivateReadObjectMethod(targetClass); + readMethod = classDesc.getMethodReadObject(); } try { if (readMethod != null) { @@ -1562,30 +1579,27 @@ public class ObjectInputStream extends InputStream implements ObjectInput, /** * Reads an integer (32 bit) from the source stream. - * + * * @return the integer value read from the source stream. * @throws EOFException * if the end of the input is reached before the read * request can be satisfied. * @throws IOException * if an error occurs while reading from the source stream. - * @since Android 1.0 */ public int readInt() throws IOException { return primitiveTypes.readInt(); } /** - * Reads the next line from the source stream. Lines are terminated by + * Reads the next line from the source stream. Lines are terminated by * {@code '\r'}, {@code '\n'}, {@code "\r\n"} or an {@code EOF}. - * + * * @return the string read from the source stream. * @throws IOException * if an error occurs while reading from the source stream. * @deprecated Use {@link BufferedReader} - * @since Android 1.0 */ - @SuppressWarnings("deprecation") @Deprecated public String readLine() throws IOException { return primitiveTypes.readLine(); @@ -1593,14 +1607,13 @@ public class ObjectInputStream extends InputStream implements ObjectInput, /** * Reads a long (64 bit) from the source stream. - * + * * @return the long value read from the source stream. * @throws EOFException * if the end of the input is reached before the read * request can be satisfied. * @throws IOException * if an error occurs while reading from the source stream. - * @since Android 1.0 */ public long readLong() throws IOException { return primitiveTypes.readLong(); @@ -1609,11 +1622,11 @@ public class ObjectInputStream extends InputStream implements ObjectInput, /** * Read a new array from the receiver. It is assumed the array has not been * read yet (not a cyclic reference). Return the array read. - * + * * @param unshared * read the object unshared * @return the array read - * + * * @throws IOException * If an IO exception happened when reading the array. * @throws ClassNotFoundException @@ -1629,7 +1642,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput, throw new InvalidClassException(Msg.getString("K00d1")); //$NON-NLS-1$ } - Integer newHandle = Integer.valueOf(nextHandle()); + Integer newHandle = nextHandle(); // Array size int size = input.readInt(); @@ -1690,6 +1703,10 @@ public class ObjectInputStream extends InputStream implements ObjectInput, // Array of Objects Object[] objectArray = (Object[]) result; for (int i = 0; i < size; i++) { + // TODO: This place is the opportunity for enhancement + // We can implement writing elements through fast-path, + // without setting up the context (see readObject()) for + // each element with public API objectArray[i] = readObject(); } } @@ -1703,11 +1720,11 @@ public class ObjectInputStream extends InputStream implements ObjectInput, /** * Reads a new class from the receiver. It is assumed the class has not been * read yet (not a cyclic reference). Return the class read. - * + * * @param unshared * read the object unshared * @return The {@code java.lang.Class} read from the stream. - * + * * @throws IOException * If an IO exception happened when reading the class. * @throws ClassNotFoundException @@ -1718,10 +1735,9 @@ public class ObjectInputStream extends InputStream implements ObjectInput, ObjectStreamClass classDesc = readClassDesc(); if (classDesc != null) { - Integer newHandle = Integer.valueOf(nextHandle()); Class localClass = classDesc.forClass(); if (localClass != null) { - registerObjectRead(localClass, newHandle, unshared); + registerObjectRead(localClass, nextHandle(), unshared); } return localClass; } @@ -1753,11 +1769,9 @@ public class ObjectInputStream extends InputStream implements ObjectInput, ObjectStreamClass classDesc; primitiveData = input; Integer oldHandle = descriptorHandle; - descriptorHandle = Integer.valueOf(nextHandle()); + descriptorHandle = nextHandle(); classDesc = readClassDescriptor(); - if (descriptorHandle != null) { - registerObjectRead(classDesc, descriptorHandle, false); - } + registerObjectRead(classDesc, descriptorHandle, false); descriptorHandle = oldHandle; primitiveData = emptyStream; classDesc.setClass(resolveClass(classDesc)); @@ -1789,7 +1803,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput, ClassNotFoundException, IOException { // read classdesc for Enum first ObjectStreamClass classDesc = readEnumDesc(); - Integer newHandle = Integer.valueOf(nextHandle()); + Integer newHandle = nextHandle(); // read name after class desc String name; byte tc = nextTC(); @@ -1818,11 +1832,11 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * Reads a new class descriptor from the receiver. It is assumed the class * descriptor has not been read yet (not a cyclic reference). Return the * class descriptor read. - * + * * @param unshared * read the object unshared * @return The {@code ObjectStreamClass} read from the stream. - * + * * @throws IOException * If an IO exception happened when reading the class * descriptor. @@ -1835,21 +1849,17 @@ public class ObjectInputStream extends InputStream implements ObjectInput, // subclasses during readClassDescriptor() primitiveData = input; Integer oldHandle = descriptorHandle; - descriptorHandle = Integer.valueOf(nextHandle()); + descriptorHandle = nextHandle(); ObjectStreamClass newClassDesc = readClassDescriptor(); - if (descriptorHandle != null) { - registerObjectRead(newClassDesc, descriptorHandle, unshared); - } + registerObjectRead(newClassDesc, descriptorHandle, unshared); descriptorHandle = oldHandle; primitiveData = emptyStream; // We need to map classDesc to class. try { newClassDesc.setClass(resolveClass(newClassDesc)); - // Check SUIDs - verifySUID(newClassDesc); - // Check base name of the class - verifyBaseName(newClassDesc); + // Check SUIDs & base name of the class + verifyAndInit(newClassDesc); } catch (ClassNotFoundException e) { if (mustResolve) { throw e; @@ -1877,9 +1887,9 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * Reads a new proxy class descriptor from the receiver. It is assumed the * proxy class descriptor has not been read yet (not a cyclic reference). * Return the proxy class descriptor read. - * + * * @return The {@code Class} read from the stream. - * + * * @throws IOException * If an IO exception happened when reading the class * descriptor. @@ -1901,33 +1911,33 @@ public class ObjectInputStream extends InputStream implements ObjectInput, /** * Reads a class descriptor from the source stream. - * + * * @return the class descriptor read from the source stream. * @throws ClassNotFoundException * if a class for one of the objects cannot be found. * @throws IOException * if an error occurs while reading from the source stream. - * @since Android 1.0 */ protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException { ObjectStreamClass newClassDesc = new ObjectStreamClass(); String name = input.readUTF(); - if ("".equals(name)) { - throw new IOException("The stream is corrupted."); + if (name.length() == 0) { + // luni.07 = The stream is corrupted + throw new IOException(Messages.getString("luni.07")); //$NON-NLS-1$ } newClassDesc.setName(name); newClassDesc.setSerialVersionUID(input.readLong()); newClassDesc.setFlags(input.readByte()); - // We must register the class descriptor before reading field - // descriptors. - // if called outside of readObject, the descriptorHandle might be null - descriptorHandle = (null == descriptorHandle ? Integer - .valueOf(nextHandle()) : descriptorHandle); + /* + * We must register the class descriptor before reading field + * descriptors. If called outside of readObject, the descriptorHandle + * might be null. + */ + descriptorHandle = (null == descriptorHandle ? nextHandle() : descriptorHandle); registerObjectRead(newClassDesc, descriptorHandle, false); - descriptorHandle = null; readFieldDescriptors(newClassDesc); return newClassDesc; @@ -1936,7 +1946,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput, /** * Creates the proxy class that implements the interfaces specified in * {@code interfaceNames}. - * + * * @param interfaceNames * the interfaces used to create the proxy class. * @return the proxy class. @@ -1946,16 +1956,15 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * @throws IOException * if an error occurs while reading from the source stream. * @see ObjectOutputStream#annotateProxyClass(Class) - * @since Android 1.0 */ protected Class resolveProxyClass(String[] interfaceNames) throws IOException, ClassNotFoundException { - // BEGIN android-removed + // TODO: This method is opportunity for performance enhancement + // We can cache the classloader and recently used interfaces. + // BEGIN android-changed // ClassLoader loader = VM.getNonBootstrapClassLoader(); - // END android-removed - // BEGIN android-added ClassLoader loader = ClassLoader.getSystemClassLoader(); - // END android-added + // END android-changed Class[] interfaces = new Class[interfaceNames.length]; for (int i = 0; i < interfaceNames.length; i++) { interfaces[i] = Class.forName(interfaceNames[i], false, loader); @@ -1969,61 +1978,19 @@ public class ObjectInputStream extends InputStream implements ObjectInput, /** * Write a new handle describing a cyclic reference from the stream. - * + * * @return the handle read - * + * * @throws IOException * If an IO exception happened when reading the handle */ - private Integer readNewHandle() throws IOException { - return Integer.valueOf(input.readInt()); + private int readNewHandle() throws IOException { + return input.readInt(); } - /** - * Read a new object from the stream. It is assumed the object has not been - * loaded yet (not a cyclic reference). Return the object read. - * - * If the object implements {@code Externalizable} its - * {@code readExternal} is called. Otherwise, all fields described by - * the class hierarchy are loaded. Each class can define how its declared - * instance fields are loaded by defining a private method - * {@code readObject} - * - * @param unshared - * read the object unshared - * @return the object read - * - * @throws IOException - * If an IO exception happened when reading the object. - * @throws OptionalDataException - * If optional data could not be found when reading the object - * graph - * @throws ClassNotFoundException - * If a class for one of the objects could not be found - */ - private Object readNewObject(boolean unshared) - throws OptionalDataException, ClassNotFoundException, IOException { - ObjectStreamClass classDesc = readClassDesc(); + private Class resolveConstructorClass(Class objectClass, boolean wasSerializable, boolean wasExternalizable) + throws OptionalDataException, ClassNotFoundException, IOException { - if (classDesc == null) { - throw new InvalidClassException(Msg.getString("K00d1")); //$NON-NLS-1$ - } - - Integer newHandle = Integer.valueOf(nextHandle()); - - // Note that these values come from the Stream, and in fact it could be - // that the classes have been changed so that the info below now - // conflicts with the newer class - boolean wasExternalizable = (classDesc.getFlags() & SC_EXTERNALIZABLE) > 0; - boolean wasSerializable = (classDesc.getFlags() & SC_SERIALIZABLE) > 0; - - // Maybe we should cache the values above in classDesc ? It may be the - // case that when reading classDesc we may need to read more stuff - // depending on the values above - Class objectClass = classDesc.forClass(); - - Object result, registeredResult = null; - if (objectClass != null) { // The class of the instance may not be the same as the class of the // constructor to run // This is the constructor to run if Externalizable @@ -2083,9 +2050,71 @@ public class ObjectInputStream extends InputStream implements ObjectInput, } } + return constructorClass; + } + + /** + * Read a new object from the stream. It is assumed the object has not been + * loaded yet (not a cyclic reference). Return the object read. + * + * If the object implements Externalizable its + * readExternal is called. Otherwise, all fields described by + * the class hierarchy are loaded. Each class can define how its declared + * instance fields are loaded by defining a private method + * readObject + * + * @param unshared + * read the object unshared + * @return the object read + * + * @throws IOException + * If an IO exception happened when reading the object. + * @throws OptionalDataException + * If optional data could not be found when reading the object + * graph + * @throws ClassNotFoundException + * If a class for one of the objects could not be found + */ + private Object readNewObject(boolean unshared) + throws OptionalDataException, ClassNotFoundException, IOException { + ObjectStreamClass classDesc = readClassDesc(); + + if (classDesc == null) { + throw new InvalidClassException(Msg.getString("K00d1")); //$NON-NLS-1$ + } + + Integer newHandle = nextHandle(); + + // Note that these values come from the Stream, and in fact it could be + // that the classes have been changed so that the info below now + // conflicts with the newer class + boolean wasExternalizable = (classDesc.getFlags() & SC_EXTERNALIZABLE) > 0; + boolean wasSerializable = (classDesc.getFlags() & SC_SERIALIZABLE) > 0; + + + // Maybe we should cache the values above in classDesc ? It may be the + // case that when reading classDesc we may need to read more stuff + // depending on the values above + Class objectClass = classDesc.forClass(); + + Object result, registeredResult = null; + if (objectClass != null) { + + // BEGIN android-changed + // long constructor = classDesc.getConstructor(); + // if (constructor == ObjectStreamClass.CONSTRUCTOR_IS_NOT_RESOLVED) { + // constructor = accessor.getMethodID(resolveConstructorClass(objectClass, wasSerializable, wasExternalizable), null, new Class[0]); + // classDesc.setConstructor(constructor); + // } + Class constructorClass = resolveConstructorClass(objectClass, wasSerializable, wasExternalizable); + // END android-changed + // Now we know which class to instantiate and which constructor to // run. We are allowed to run the constructor. + // BEGIN android-changed + // result = accessor.newInstance(objectClass, constructor, null); result = newInstance(objectClass, constructorClass); + // END android-changed registerObjectRead(result, newHandle, unshared); registeredResult = result; @@ -2131,38 +2160,23 @@ public class ObjectInputStream extends InputStream implements ObjectInput, } if (objectClass != null) { - Object readResolveMethod = readResolveCache.get(objectClass); - if (readResolveMethod != this) { - if (readResolveMethod == null) { - final Method readResolve = ObjectStreamClass - .methodReadResolve(objectClass); - if (readResolve == null) { - readResolveCache.put(objectClass, this); - readResolveMethod = null; + + if (classDesc.hasMethodReadResolve()){ + Method methodReadResolve = classDesc.getMethodReadResolve(); + try { + result = methodReadResolve.invoke(result, (Object[]) null); + } catch (IllegalAccessException iae) { + } catch (InvocationTargetException ite) { + Throwable target = ite.getTargetException(); + if (target instanceof ObjectStreamException) { + throw (ObjectStreamException) target; + } else if (target instanceof Error) { + throw (Error) target; } else { - // Has replacement method - AccessController.doPrivileged(new PriviAction( - readResolve)); - readResolveCache.put(objectClass, readResolve); - readResolveMethod = readResolve; - } - } - if (readResolveMethod != null) { - try { - result = ((Method) readResolveMethod).invoke(result, - (Object[]) null); - } catch (IllegalAccessException iae) { - } catch (InvocationTargetException ite) { - Throwable target = ite.getTargetException(); - if (target instanceof ObjectStreamException) { - throw (ObjectStreamException) target; - } else if (target instanceof Error) { - throw (Error) target; - } else { - throw (RuntimeException) target; - } + throw (RuntimeException) target; } } + } } // We get here either if class-based replacement was not needed or if it @@ -2183,7 +2197,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput, /** * Read a string encoded in {@link DataInput modified UTF-8} from the * receiver. Return the string read. - * + * * @param unshared * read the object unshared * @return the string just read. @@ -2195,8 +2209,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput, if (enableResolve) { result = resolveObject(result); } - int newHandle = nextHandle(); - registerObjectRead(result, Integer.valueOf(newHandle), unshared); + registerObjectRead(result, nextHandle(), unshared); return result; } @@ -2204,11 +2217,11 @@ public class ObjectInputStream extends InputStream implements ObjectInput, /** * Read a new String in UTF format from the receiver. Return the string * read. - * + * * @param unshared * read the object unshared * @return the string just read. - * + * * @throws IOException * If an IO exception happened when reading the String. */ @@ -2218,15 +2231,14 @@ public class ObjectInputStream extends InputStream implements ObjectInput, if (enableResolve) { result = resolveObject(result); } - int newHandle = nextHandle(); - registerObjectRead(result, Integer.valueOf(newHandle), unshared); + registerObjectRead(result, nextHandle(), unshared); return result; } /** * Reads the next object from the source stream. - * + * * @return the object read from the source stream. * @throws ClassNotFoundException * if the class of one of the objects in the object graph cannot @@ -2236,7 +2248,6 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * @throws OptionalDataException * if primitive data types were found instead of an object. * @see ObjectOutputStream#writeObject(Object) - * @since Android 1.0 */ public final Object readObject() throws OptionalDataException, ClassNotFoundException, IOException { @@ -2245,7 +2256,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput, /** * Reads the next unshared object from the source stream. - * + * * @return the new object read. * @throws ClassNotFoundException * if the class of one of the objects in the object graph cannot @@ -2253,7 +2264,6 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * @throws IOException * if an error occurs while reading from the source stream. * @see ObjectOutputStream#writeUnshared - * @since Android 1.0 */ public Object readUnshared() throws IOException, ClassNotFoundException { return readObject(true); @@ -2347,7 +2357,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput, /** * Method to be overriden by subclasses to read the next object from the * source stream. - * + * * @return the object read from the source stream. * @throws ClassNotFoundException * if the class of one of the objects in the object graph cannot @@ -2357,7 +2367,6 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * @throws OptionalDataException * if primitive data types were found instead of an object. * @see ObjectOutputStream#writeObjectOverride - * @since Android 1.0 */ protected Object readObjectOverride() throws OptionalDataException, ClassNotFoundException, IOException { @@ -2370,11 +2379,10 @@ public class ObjectInputStream extends InputStream implements ObjectInput, /** * Reads a short (16 bit) from the source stream. - * + * * @return the short value read from the source stream. * @throws IOException * if an error occurs while reading from the source stream. - * @since Android 1.0 */ public short readShort() throws IOException { return primitiveTypes.readShort(); @@ -2382,13 +2390,12 @@ public class ObjectInputStream extends InputStream implements ObjectInput, /** * Reads and validates the ObjectInputStream header from the source stream. - * + * * @throws IOException * if an error occurs while reading from the source stream. * @throws StreamCorruptedException * if the source stream does not contain readable serialized * objects. - * @since Android 1.0 */ protected void readStreamHeader() throws IOException, StreamCorruptedException { @@ -2401,7 +2408,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput, /** * Reads an unsigned byte (8 bit) from the source stream. - * + * * @return the unsigned byte value read from the source stream packaged in * an integer. * @throws EOFException @@ -2409,7 +2416,6 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * request can be satisfied. * @throws IOException * if an error occurs while reading from the source stream. - * @since Android 1.0 */ public int readUnsignedByte() throws IOException { return primitiveTypes.readUnsignedByte(); @@ -2417,7 +2423,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput, /** * Reads an unsigned short (16 bit) from the source stream. - * + * * @return the unsigned short value read from the source stream packaged in * an integer. * @throws EOFException @@ -2425,7 +2431,6 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * request can be satisfied. * @throws IOException * if an error occurs while reading from the source stream. - * @since Android 1.0 */ public int readUnsignedShort() throws IOException { return primitiveTypes.readUnsignedShort(); @@ -2434,7 +2439,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput, /** * Reads a string encoded in {@link DataInput modified UTF-8} from the * source stream. - * + * * @return the string encoded in {@link DataInput modified UTF-8} read from * the source stream. * @throws EOFException @@ -2442,7 +2447,6 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * request can be satisfied. * @throws IOException * if an error occurs while reading from the source stream. - * @since Android 1.0 */ public String readUTF() throws IOException { return primitiveTypes.readUTF(); @@ -2450,11 +2454,11 @@ public class ObjectInputStream extends InputStream implements ObjectInput, /** * Return the object previously read tagged with handle {@code handle}. - * + * * @param handle * The handle that this object was assigned when it was read. * @return the object previously read. - * + * * @throws InvalidObjectException * If there is no previously read object with this handle */ @@ -2472,14 +2476,14 @@ public class ObjectInputStream extends InputStream implements ObjectInput, /** * Assume object {@code obj} has been read, and assign a handle to * it, {@code handle}. - * + * * @param obj * Non-null object being loaded. * @param handle * An Integer, the handle to this object * @param unshared * Boolean, indicates that caller is reading in unshared mode - * + * * @see #nextHandle */ private void registerObjectRead(Object obj, Integer handle, boolean unshared) { @@ -2494,7 +2498,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * a class that implements "special" deserialization rules. It can be called * multiple times. Validation callbacks are then done in order of decreasing * priority, defined by {@code priority}. - * + * * @param object * an object that can validate itself by receiving a callback. * @param priority @@ -2512,7 +2516,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput, Object instanceBeingRead = this.currentObject; // We can't be called from just anywhere. There are rules. - if (instanceBeingRead == null) { + if (instanceBeingRead == null && nestedLevels == 0) { throw new NotActiveException(); } if (object == null) { @@ -2552,7 +2556,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * Reset the collection of objects already loaded by the receiver. */ private void resetSeenObjects() { - objectsRead = new Hashtable(); + objectsRead = new HashMap(); currentHandle = baseWireHandle; primitiveData = emptyStream; } @@ -2572,7 +2576,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput, /** * Loads the Java class corresponding to the class descriptor {@code * osClass} that has just been read from the source stream. - * + * * @param osClass * an ObjectStreamClass read from the source stream. * @return a Class corresponding to the descriptor {@code osClass}. @@ -2581,18 +2585,24 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * @throws IOException * if an I/O error occurs while creating the class. * @see ObjectOutputStream#annotateClass(Class) - * @since Android 1.0 */ protected Class resolveClass(ObjectStreamClass osClass) throws IOException, ClassNotFoundException { - String className = osClass.getName(); - // if it is primitive class, for example, long.class - Class cls = PRIMITIVE_CLASSES.get(className); + // fastpath: obtain cached value + Class cls = osClass.forClass(); if (null == cls) { - // not primitive class - // Use the first non-null ClassLoader on the stack. If null, use the - // system class loader - return Class.forName(className, true, callerClassLoader); + // slowpath: resolve the class + String className = osClass.getName(); + + // if it is primitive class, for example, long.class + cls = PRIMITIVE_CLASSES.get(className); + + if (null == cls) { + // not primitive class + // Use the first non-null ClassLoader on the stack. If null, use + // the system class loader + cls = Class.forName(className, true, callerClassLoader); + } } return cls; } @@ -2602,7 +2612,7 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * object} with a new object. Object substitution has to be activated first * with calling {@code enableResolveObject(true)}. This implementation just * returns {@code object}. - * + * * @param object * the original object for which a replacement may be defined. * @return the replacement object for {@code object}. @@ -2612,262 +2622,71 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * @see #enableResolveObject * @see ObjectOutputStream#enableReplaceObject * @see ObjectOutputStream#replaceObject - * @since Android 1.0 */ protected Object resolveObject(Object object) throws IOException { // By default no object replacement. Subclasses can override return object; } - /** - * Set a given declared field named {@code fieldName} of - * {@code instance} to the new {@code byte} value - * {@code value}. - * - * This method could be implemented non-natively on top of java.lang.reflect - * implementations that support the {@code setAccessible} API, at the - * expense of extra object creation (java.lang.reflect.Field). Otherwise - * Serialization could not set private fields, except by the use of a native - * method like this one. - * - * @param instance - * Object whose field to set - * @param declaringClass - * {@code instance}'s declaring class - * @param fieldName - * Name of the field to set - * @param value - * New value for the field - * - * @throws NoSuchFieldError - * If the field does not exist. + // BEGIN android-added + + /* + * These methods set the value of a field named fieldName of instance. The + * field is declared by declaringClass. The field is the same type as the + * value parameter. + * + * these methods could be implemented non-natively on top of + * java.lang.reflect at the expense of extra object creation + * (java.lang.reflect.Field). Otherwise Serialization could not fetch + * private fields, except by the use of a native method like this one. + * + * @throws NoSuchFieldError If the field does not exist. */ private static native void setField(Object instance, Class declaringClass, String fieldName, byte value) throws NoSuchFieldError; - /** - * Set a given declared field named {@code fieldName} of - * {@code instance} to the new {@code char} value - * {@code value}. - * - * This method could be implemented non-natively on top of java.lang.reflect - * implementations that support the {@code setAccessible} API, at the - * expense of extra object creation (java.lang.reflect.Field). Otherwise - * Serialization could not set private fields, except by the use of a native - * method like this one. - * - * @param instance - * Object whose field to set - * @param declaringClass - * {@code instance}'s declaring class - * @param fieldName - * Name of the field to set - * @param value - * New value for the field - * - * @throws NoSuchFieldError - * If the field does not exist. - */ + private static native void setField(Object instance, Class declaringClass, String fieldName, char value) throws NoSuchFieldError; - /** - * Set a given declared field named {@code fieldName} of - * {@code instance} to the new {@code double} value - * {@code value}. - * - * This method could be implemented non-natively on top of java.lang.reflect - * implementations that support the {@code setAccessible} API, at the - * expense of extra object creation (java.lang.reflect.Field). Otherwise - * Serialization could not set private fields, except by the use of a native - * method like this one. - * - * @param instance - * Object whose field to set - * @param declaringClass - * {@code instance}'s declaring class - * @param fieldName - * Name of the field to set - * @param value - * New value for the field - * - * @throws NoSuchFieldError - * If the field does not exist. - */ + private static native void setField(Object instance, Class declaringClass, String fieldName, double value) throws NoSuchFieldError; - /** - * Set a given declared field named {@code fieldName} of - * {@code instance} to the new {@code float} value - * {@code value}. - * - * This method could be implemented non-natively on top of java.lang.reflect - * implementations that support the {@code setAccessible} API, at the - * expense of extra object creation (java.lang.reflect.Field). Otherwise - * Serialization could not set private fields, except by the use of a native - * method like this one. - * - * @param instance - * Object whose field to set - * @param declaringClass - * {@code instance}'s declaring class - * @param fieldName - * Name of the field to set - * @param value - * New value for the field - * - * @throws NoSuchFieldError - * If the field does not exist. - */ private static native void setField(Object instance, Class declaringClass, String fieldName, float value) throws NoSuchFieldError; - /** - * Set a given declared field named {@code fieldName} of - * {@code instance} to the new {@code int} value - * {@code value}. - * - * This method could be implemented non-natively on top of java.lang.reflect - * implementations that support the {@code setAccessible} API, at the - * expense of extra object creation (java.lang.reflect.Field). Otherwise - * Serialization could not set private fields, except by the use of a native - * method like this one. - * - * @param instance - * Object whose field to set - * @param declaringClass - * {@code instance}'s declaring class - * @param fieldName - * Name of the field to set - * @param value - * New value for the field - * - * @throws NoSuchFieldError - * If the field does not exist. - */ private static native void setField(Object instance, Class declaringClass, String fieldName, int value) throws NoSuchFieldError; - /** - * Set a given declared field named {@code fieldName} of - * {@code instance} to the new {@code long} value - * {@code value}. - * - * This method could be implemented non-natively on top of java.lang.reflect - * implementations that support the {@code setAccessible} API, at the - * expense of extra object creation (java.lang.reflect.Field). Otherwise - * Serialization could not set private fields, except by the use of a native - * method like this one. - * - * @param instance - * Object whose field to set - * @param declaringClass - * {@code instance}'s declaring class - * @param fieldName - * Name of the field to set - * @param value - * New value for the field - * - * @throws NoSuchFieldError - * If the field does not exist. - */ private static native void setField(Object instance, Class declaringClass, String fieldName, long value) throws NoSuchFieldError; - /** - * Set a given declared field named {@code fieldName} of - * {@code instance} to the new value {@code value}. - * - * This method could be implemented non-natively on top of java.lang.reflect - * implementations that support the {@code setAccessible} API, at the - * expense of extra object creation (java.lang.reflect.Field). Otherwise - * Serialization could not set private fields, except by the use of a native - * method like this one. - * - * @param instance - * Object whose field to set - * @param declaringClass - * Class which declares the field - * @param fieldName - * Name of the field to set - * @param fieldTypeName - * Name of the class defining the type of the field - * @param value - * New value for the field - * - * @throws NoSuchFieldError - * If the field does not exist. - */ private static native void objSetField(Object instance, Class declaringClass, String fieldName, String fieldTypeName, Object value) throws NoSuchFieldError; - /** - * Set a given declared field named {@code fieldName} of - * {@code instance} to the new {@code short} value - * {@code value}. - * - * This method could be implemented non-natively on top of java.lang.reflect - * implementations that support the {@code setAccessible} API, at the - * expense of extra object creation (java.lang.reflect.Field). Otherwise - * Serialization could not set private fields, except by the use of a native - * method like this one. - * - * @param instance - * Object whose field to set - * @param declaringClass - * {@code instance}'s declaring class - * @param fieldName - * Name of the field to set - * @param value - * New value for the field - * - * @throws NoSuchFieldError - * If the field does not exist. - */ private static native void setField(Object instance, Class declaringClass, String fieldName, short value) throws NoSuchFieldError; - /** - * Set a given declared field named {@code fieldName} of - * {@code instance} to the new {@code boolean} value - * {@code value}. - * - * This method could be implemented non-natively on top of java.lang.reflect - * implementations that support the {@code setAccessible} API, at the - * expense of extra object creation (java.lang.reflect.Field). Otherwise - * Serialization could not set private fields, except by the use of a native - * method like this one. - * - * @param instance - * Object whose field to set - * @param declaringClass - * {@code instance}'s declaring class - * @param fieldName - * Name of the field to set - * @param value - * New value for the field - * - * @throws NoSuchFieldError - * If the field does not exist. - */ private static native void setField(Object instance, Class declaringClass, String fieldName, boolean value) throws NoSuchFieldError; + // END android-added + /** * Skips {@code length} bytes on the source stream. This method should not * be used to skip bytes at any arbitrary position, just when reading * primitive data types (int, char etc). - * + * * @param length * the number of bytes to skip. * @return the number of bytes actually skipped. @@ -2875,7 +2694,6 @@ public class ObjectInputStream extends InputStream implements ObjectInput, * if an error occurs while skipping bytes on the source stream. * @throws NullPointerException * if the source stream is {@code null}. - * @since Android 1.0 */ public int skipBytes(int length) throws IOException { // To be used with available. Ok to call if reading primitive buffer @@ -2896,50 +2714,31 @@ public class ObjectInputStream extends InputStream implements ObjectInput, } /** - * Verify if the SUID for descriptor {@code loadedStreamClass}matches - * the SUID of the corresponding loaded class. - * + * Verify if the SUID & the base name for descriptor + * loadedStreamClassmatches + * the SUID & the base name of the corresponding loaded class and + * init private fields. + * * @param loadedStreamClass * An ObjectStreamClass that was loaded from the stream. - * + * * @throws InvalidClassException * If the SUID of the stream class does not match the VM class */ - private void verifySUID(ObjectStreamClass loadedStreamClass) + private void verifyAndInit(ObjectStreamClass loadedStreamClass) throws InvalidClassException { + Class localClass = loadedStreamClass.forClass(); - // Instances of java.lang.Class are always Serializable, even if their - // instances aren't (e.g. java.lang.Object.class). We cannot call lookup - // because it returns null if the parameter represents instances that - // cannot be serialized, and that is not what we want. If we are loading - // an instance of java.lang.Class, we better have the corresponding - // ObjectStreamClass. ObjectStreamClass localStreamClass = ObjectStreamClass .lookupStreamClass(localClass); + if (loadedStreamClass.getSerialVersionUID() != localStreamClass .getSerialVersionUID()) { throw new InvalidClassException(loadedStreamClass.getName(), Msg .getString("K00da", loadedStreamClass, //$NON-NLS-1$ localStreamClass)); } - } - /** - * Verify if the base name for descriptor {@code loadedStreamClass} - * matches the base name of the corresponding loaded class. - * - * @param loadedStreamClass - * An ObjectStreamClass that was loaded from the stream. - * - * @throws InvalidClassException - * If the base name of the stream class does not match the VM - * class - */ - private void verifyBaseName(ObjectStreamClass loadedStreamClass) - throws InvalidClassException { - Class localClass = loadedStreamClass.forClass(); - ObjectStreamClass localStreamClass = ObjectStreamClass - .lookupStreamClass(localClass); String loadedClassBaseName = getBaseName(loadedStreamClass.getName()); String localClassBaseName = getBaseName(localStreamClass.getName()); @@ -2948,10 +2747,12 @@ public class ObjectInputStream extends InputStream implements ObjectInput, .getString("KA015", loadedClassBaseName, //$NON-NLS-1$ localClassBaseName)); } + + loadedStreamClass.initPrivateFields(localStreamClass); } private static String getBaseName(String fullName) { - int k = fullName.lastIndexOf("."); //$NON-NLS-1$ + int k = fullName.lastIndexOf('.'); if (k == -1 || k == (fullName.length() - 1)) { return fullName; diff --git a/libcore/luni/src/main/java/java/io/ObjectInputValidation.java b/libcore/luni/src/main/java/java/io/ObjectInputValidation.java index f1cae743a..3dce15e60 100644 --- a/libcore/luni/src/main/java/java/io/ObjectInputValidation.java +++ b/libcore/luni/src/main/java/java/io/ObjectInputValidation.java @@ -22,10 +22,7 @@ package java.io; * example, the validation of a whole graph of objects after all of them have * been loaded. * - * @see ObjectInputStream * @see ObjectInputStream#registerValidation(ObjectInputValidation, int) - * - * @since Android 1.0 */ public interface ObjectInputValidation { /** @@ -33,7 +30,6 @@ public interface ObjectInputValidation { * * @throws InvalidObjectException * if this object fails to validate itself. - * @since Android 1.0 */ public void validateObject() throws InvalidObjectException; } diff --git a/libcore/luni/src/main/java/java/io/ObjectOutput.java b/libcore/luni/src/main/java/java/io/ObjectOutput.java index cd68439a7..6680457ea 100644 --- a/libcore/luni/src/main/java/java/io/ObjectOutput.java +++ b/libcore/luni/src/main/java/java/io/ObjectOutput.java @@ -22,8 +22,6 @@ package java.io; * * @see ObjectOutputStream * @see ObjectInput - * - * @since Android 1.0 */ public interface ObjectOutput extends DataOutput { /** @@ -32,7 +30,6 @@ public interface ObjectOutput extends DataOutput { * * @throws IOException * if an error occurs while closing the target stream. - * @since Android 1.0 */ public void close() throws IOException; @@ -42,7 +39,6 @@ public interface ObjectOutput extends DataOutput { * * @throws IOException * if an error occurs while flushing the target stream. - * @since Android 1.0 */ public void flush() throws IOException; @@ -54,7 +50,6 @@ public interface ObjectOutput extends DataOutput { * the buffer to write. * @throws IOException * if an error occurs while writing to the target stream. - * @since Android 1.0 */ public void write(byte[] buffer) throws IOException; @@ -72,7 +67,6 @@ public interface ObjectOutput extends DataOutput { * stream. * @throws IOException * if an error occurs while writing to the target stream. - * @since Android 1.0 */ public void write(byte[] buffer, int offset, int count) throws IOException; @@ -85,7 +79,6 @@ public interface ObjectOutput extends DataOutput { * the byte to write. * @throws IOException * if an error occurs while writing to the target stream. - * @since Android 1.0 */ public void write(int value) throws IOException; @@ -96,7 +89,6 @@ public interface ObjectOutput extends DataOutput { * the object to write. * @throws IOException * if an error occurs while writing to the target stream. - * @since Android 1.0 */ public void writeObject(Object obj) throws IOException; } diff --git a/libcore/luni/src/main/java/java/io/ObjectOutputStream.java b/libcore/luni/src/main/java/java/io/ObjectOutputStream.java index 5da495026..e3c14719d 100644 --- a/libcore/luni/src/main/java/java/io/ObjectOutputStream.java +++ b/libcore/luni/src/main/java/java/io/ObjectOutputStream.java @@ -20,26 +20,39 @@ package java.io; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; -import java.security.AccessController; import java.util.IdentityHashMap; -import org.apache.harmony.luni.util.PriviAction; + +// BEGIN android-note +// Harmony uses ObjectAccessors to access fields through JNI. Android has not +// yet migrated that API. As a consequence, there's a lot of changes here... +// END android-note + +// BEGIN android-removed +// import org.apache.harmony.misc.accessors.ObjectAccessor; +// import org.apache.harmony.misc.accessors.AccessorFactory; +// END android-removed + +import org.apache.harmony.luni.util.Msg; /** * A specialized {@link OutputStream} that is able to write (serialize) Java * objects as well as primitive data types (int, byte, char etc.). The data can * later be loaded using an ObjectInputStream. - * + * * @see ObjectInputStream * @see ObjectOutput * @see Serializable * @see Externalizable - * - * @since Android 1.0 */ public class ObjectOutputStream extends OutputStream implements ObjectOutput, ObjectStreamConstants { + /* + * Mask to zero SC_BLOC_DATA bit. + */ + private static final byte NOT_SC_BLOCK_DATA = (byte) (SC_BLOCK_DATA ^ 0xFF); + /* * How many nested levels to writeObject. We may not need this. */ @@ -107,136 +120,129 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, */ private boolean subclassOverridingImplementation; + + // BEGIN android-removed + // private ObjectAccessor accessor = AccessorFactory.getObjectAccessor(); + // END android-removed + /* - * cache for writeReplace methods + * Descriptor for java.lang.reflect.Proxy */ - private IdentityHashMap, Object> writeReplaceCache; + private final ObjectStreamClass proxyClassDesc = ObjectStreamClass.lookup(Proxy.class); /** * PutField is an inner class to provide access to the persistent fields * that are written to the target stream. - * - * @since Android 1.0 */ public static abstract class PutField { /** * Puts the value of the boolean field identified by {@code name} to the * persistent field. - * + * * @param name * the name of the field to serialize. * @param value * the value that is put to the persistent field. - * @since Android 1.0 */ public abstract void put(String name, boolean value); /** * Puts the value of the character field identified by {@code name} to * the persistent field. - * + * * @param name * the name of the field to serialize. * @param value * the value that is put to the persistent field. - * @since Android 1.0 */ public abstract void put(String name, char value); /** * Puts the value of the byte field identified by {@code name} to the * persistent field. - * + * * @param name * the name of the field to serialize. * @param value * the value that is put to the persistent field. - * @since Android 1.0 */ public abstract void put(String name, byte value); /** * Puts the value of the short field identified by {@code name} to the * persistent field. - * + * * @param name * the name of the field to serialize. * @param value * the value that is put to the persistent field. - * @since Android 1.0 */ public abstract void put(String name, short value); /** * Puts the value of the integer field identified by {@code name} to the * persistent field. - * + * * @param name * the name of the field to serialize. * @param value * the value that is put to the persistent field. - * @since Android 1.0 */ public abstract void put(String name, int value); /** * Puts the value of the long field identified by {@code name} to the * persistent field. - * + * * @param name * the name of the field to serialize. * @param value * the value that is put to the persistent field. - * @since Android 1.0 */ public abstract void put(String name, long value); /** * Puts the value of the float field identified by {@code name} to the * persistent field. - * + * * @param name * the name of the field to serialize. * @param value * the value that is put to the persistent field. - * @since Android 1.0 */ public abstract void put(String name, float value); /** * Puts the value of the double field identified by {@code name} to the * persistent field. - * + * * @param name * the name of the field to serialize. * @param value * the value that is put to the persistent field. - * @since Android 1.0 */ public abstract void put(String name, double value); /** * Puts the value of the Object field identified by {@code name} to the * persistent field. - * + * * @param name * the name of the field to serialize. * @param value * the value that is put to the persistent field. - * @since Android 1.0 */ public abstract void put(String name, Object value); /** * Writes the fields to the target stream {@code out}. - * + * * @param out * the target stream * @throws IOException * if an error occurs while writing to the target stream. * @deprecated This method is unsafe and may corrupt the target stream. * Use ObjectOutputStream#writeFields() instead. - * @since Android 1.0 */ @Deprecated public abstract void write(ObjectOutput out) throws IOException; @@ -246,14 +252,13 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, * Constructs a new {@code ObjectOutputStream}. This default constructor can * be used by subclasses that do not want to use the public constructor if * it allocates unneeded data. - * + * * @throws IOException * if an error occurs when creating this stream. * @throws SecurityException * if a security manager is installed and it denies subclassing * this class. * @see SecurityManager#checkPermission(java.security.Permission) - * @since Android 1.0 */ protected ObjectOutputStream() throws IOException, SecurityException { super(); @@ -271,17 +276,16 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, /** * Constructs a new ObjectOutputStream that writes to the OutputStream * {@code output}. - * + * * @param output * the non-null OutputStream to filter writes on. - * + * * @throws IOException * if an error occurs while writing the object stream * header * @throws SecurityException * if a security manager is installed and it denies subclassing * this class. - * @since Android 1.0 */ public ObjectOutputStream(OutputStream output) throws IOException { Class implementationClass = getClass(); @@ -316,7 +320,6 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, this.enableReplace = false; this.protocolVersion = PROTOCOL_VERSION_2; this.subclassOverridingImplementation = false; - this.writeReplaceCache = new IdentityHashMap, Object>(); resetState(); this.nestedException = new StreamCorruptedException(); @@ -333,13 +336,12 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, * stream. This optional data can be read when deserializing the class * descriptor (ObjectStreamClass) for this class from an input stream. By * default, no extra data is saved. - * + * * @param aClass * the class to annotate. * @throws IOException * if an error occurs while writing to the target stream. * @see ObjectInputStream#resolveClass(ObjectStreamClass) - * @since Android 1.0 */ protected void annotateClass(Class aClass) throws IOException { // By default no extra info is saved. Subclasses can override @@ -349,13 +351,12 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, * Writes optional information for a proxy class to the target stream. This * optional data can be read when deserializing the proxy class from an * input stream. By default, no extra data is saved. - * + * * @param aClass * the proxy class to annotate. * @throws IOException * if an error occurs while writing to the target stream. * @see ObjectInputStream#resolveProxyClass(String[]) - * @since Android 1.0 */ protected void annotateProxyClass(Class aClass) throws IOException { // By default no extra info is saved. Subclasses can override @@ -377,10 +378,9 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, /** * Closes this stream. Any buffered data is flushed. This implementation * closes the target stream. - * + * * @throws IOException * if an error occurs while closing this stream. - * @since Android 1.0 */ @Override public void close() throws IOException { @@ -393,7 +393,7 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, * Computes the collection of emulated fields that users can manipulate to * store a representation different than the one declared by the class of * the object being dumped. - * + * * @see #writeFields * @see #writeFieldValues(EmulatedFieldsForDumping) */ @@ -405,13 +405,12 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, * Default method to write objects to this stream. Serializable fields * defined in the object's class and superclasses are written to the output * stream. - * + * * @throws IOException * if an error occurs while writing to the target stream. * @throws NotActiveException * if this method is not called from {@code writeObject()}. * @see ObjectInputStream#defaultReadObject - * @since Android 1.0 */ public void defaultWriteObject() throws IOException { // We can't be called from just anywhere. There are rules. @@ -424,13 +423,12 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, /** * Writes buffered data to the target stream. This is similar to {@code * flush} but the flush is not propagated to the target stream. - * + * * @throws IOException * if an error occurs while writing to the target stream. - * @since Android 1.0 */ protected void drain() throws IOException { - if (primitiveTypes == null) { + if (primitiveTypes == null || primitiveTypesBuffer == null) { return; } @@ -462,7 +460,7 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, /** * Dumps the parameter {@code obj} only if it is {@code null} * or an object that has already been dumped previously. - * + * * @param obj * Object to check if an instance previously dumped by this * stream. @@ -470,14 +468,14 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, * method does nothing). Integer, if {@code obj} is an * instance which has been dumped already. In this case this method * saves the cyclic reference. - * + * * @throws IOException * If an error occurs attempting to save {@code null} or * a cyclic reference. */ private Integer dumpCycle(Object obj) throws IOException { // If the object has been saved already, save its handle only - Integer handle = registeredObjectHandleFor(obj); + Integer handle = objectsWritten.get(obj); if (handle != null) { writeCyclicReference(handle); return handle; @@ -489,7 +487,7 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, * Enables object replacement for this stream. By default this is not * enabled. Only trusted subclasses (loaded with system class loader) are * allowed to change this status. - * + * * @param enable * {@code true} to enable object replacement; {@code false} to * disable it. @@ -499,7 +497,6 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, * object replacement for this stream. * @see #replaceObject * @see ObjectInputStream#enableResolveObject - * @since Android 1.0 */ protected boolean enableReplaceObject(boolean enable) throws SecurityException { @@ -519,11 +516,10 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, /** * Writes buffered data to the target stream and calls the {@code flush} * method of the target stream. - * + * * @throws IOException * if an error occurs while writing to or flushing the output * stream. - * @since Android 1.0 */ @Override public void flush() throws IOException { @@ -531,212 +527,64 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, output.flush(); } - /** - * Get the value of field named - * {@code fieldName of object instance}. The - * field is declared by class {@code declaringClass}. The field is supposed to be - * a boolean. - * - * This method could be implemented non-natively on top of java.lang.reflect implementations - * that support the {@code setAccessible} API, at the expense of extra object creation - * (java.lang.reflect.Field). Otherwise Serialization could not fetch private fields, except - * by the use of a native method like this one. + // BEGIN android-added + /* + * These methods get the value of a field named fieldName of object + * instance. The field is declared by declaringClass. The field is the same + * type as the method return value. * - * @param instance Object whose field value we want to fetch - * @param declaringClass The class that declares the field - * @param fieldName Name of the field we want to fetch - * @return the value of the field + * these methods could be implemented non-natively on top of + * java.lang.reflect at the expense of extra object creation + * (java.lang.reflect.Field). Otherwise Serialization could not fetch + * private fields, except by the use of a native method like this one. * * @throws NoSuchFieldError If the field does not exist. */ + private static native boolean getFieldBool(Object instance, Class declaringClass, String fieldName); - /** - * Get the value of field named - * {@code fieldName of object instance}. The - * field is declared by class {@code declaringClass}. The field is supposed to be - * a byte - * - * This method could be implemented non-natively on top of java.lang.reflect implementations - * that support the {@code setAccessible} API, at the expense of extra object creation - * (java.lang.reflect.Field). Otherwise Serialization could not fetch private fields, except - * by the use of a native method like this one. - * - * @param instance Object whose field value we want to fetch - * @param declaringClass The class that declares the field - * @param fieldName Name of the field we want to fetch - * @return the value of the field - * - * @throws NoSuchFieldError If the field does not exist. - */ private static native byte getFieldByte(Object instance, Class declaringClass, String fieldName); - /** - * Get the value of field named - * {@code fieldName of object instance}. The - * field is declared by class {@code declaringClass}. The field is supposed to be - * a char. - * - * This method could be implemented non-natively on top of java.lang.reflect implementations - * that support the {@code setAccessible} API, at the expense of extra object creation - * (java.lang.reflect.Field). Otherwise Serialization could not fetch private fields, except - * by the use of a native method like this one. - * - * @param instance Object whose field value we want to fetch - * @param declaringClass The class that declares the field - * @param fieldName Name of the field we want to fetch - * @return the value of the field - * - * @throws NoSuchFieldError If the field does not exist. - */ private static native char getFieldChar(Object instance, Class declaringClass, String fieldName); - /** - * Get the value of field named - * {@code fieldName of object instance}. The - * field is declared by class {@code declaringClass}. The field is supposed to be - * a double. - * - * This method could be implemented non-natively on top of java.lang.reflect implementations - * that support the {@code setAccessible} API, at the expense of extra object creation - * (java.lang.reflect.Field). Otherwise Serialization could not fetch private fields, except - * by the use of a native method like this one. - * - * @param instance Object whose field value we want to fetch - * @param declaringClass The class that declares the field - * @param fieldName Name of the field we want to fetch - * @return the value of the field - * - * @throws NoSuchFieldError If the field does not exist. - */ private static native double getFieldDouble(Object instance, Class declaringClass, String fieldName); - /** - * Get the value of field named - * {@code fieldName of object instance}. The - * field is declared by class {@code declaringClass}. The field is supposed to be - * a float. - * - * This method could be implemented non-natively on top of java.lang.reflect implementations - * that support the {@code setAccessible} API, at the expense of extra object creation - * (java.lang.reflect.Field). Otherwise Serialization could not fetch private fields, except - * by the use of a native method like this one. - * - * @param instance Object whose field value we want to fetch - * @param declaringClass The class that declares the field - * @param fieldName Name of the field we want to fetch - * @return the value of the field - * - * @throws NoSuchFieldError If the field does not exist. - */ private static native float getFieldFloat(Object instance, Class declaringClass, String fieldName); - /** - * Get the value of field named - * {@code fieldName of object instance}. The - * field is declared by class {@code declaringClass}. The field is supposed to be - * an int. - * - * This method could be implemented non-natively on top of java.lang.reflect implementations - * that support the {@code setAccessible} API, at the expense of extra object creation - * (java.lang.reflect.Field). Otherwise Serialization could not fetch private fields, except - * by the use of a native method like this one. - * - * @param instance Object whose field value we want to fetch - * @param declaringClass The class that declares the field - * @param fieldName Name of the field we want to fetch - * @return the value of the field - * - * @throws NoSuchFieldError If the field does not exist. - */ private static native int getFieldInt(Object instance, Class declaringClass, String fieldName); - /** - * Get the value of field named - * {@code fieldName of object instance}. The - * field is declared by class {@code declaringClass}. The field is supposed to be - * a long. - * - * This method could be implemented non-natively on top of java.lang.reflect implementations - * that support the {@code setAccessible} API, at the expense of extra object creation - * (java.lang.reflect.Field). Otherwise Serialization could not fetch private fields, except - * by the use of a native method like this one. - * - * @param instance Object whose field value we want to fetch - * @param declaringClass The class that declares the field - * @param fieldName Name of the field we want to fetch - * @return the value of the field - * - * @throws NoSuchFieldError If the field does not exist. - */ private static native long getFieldLong(Object instance, Class declaringClass, String fieldName); - /** - * Get the value of field named - * {@code fieldName of object instance}. The - * field is declared by class {@code declaringClass}. The field is supposed to be - * an Object type whose name is {@code fieldTypeName}. - * - * This method could be implemented non-natively on top of java.lang.reflect implementations - * that support the {@code setAccessible} API, at the expense of extra object creation - * (java.lang.reflect.Field). Otherwise Serialization could not fetch private fields, except - * by the use of a native method like this one. - * - * @param instance Object whose field value we want to fetch - * @param declaringClass The class that declares the field - * @param fieldName Name of the field we want to fetch - * @param fieldTypeName Name of the class that defines the type of this field - * @return the value of the field - * - * @throws NoSuchFieldError If the field does not exist. - */ private static native Object getFieldObj(Object instance, Class declaringClass, String fieldName, String fieldTypeName); - /** - * Get the value of field named - * {@code fieldName of object instance}. The - * field is declared by class {@code declaringClass}. The field is supposed to be - * a short. - * - * This method could be implemented non-natively on top of java.lang.reflect implementations - * that support the {@code setAccessible} API, at the expense of extra object creation - * (java.lang.reflect.Field). Otherwise Serialization could not fetch private fields, except - * by the use of a native method like this one. - * - * @param instance Object whose field value we want to fetch - * @param declaringClass The class that declares the field - * @param fieldName Name of the field we want to fetch - * @return the value of the field - * - * @throws NoSuchFieldError If the field does not exist. - */ private static native short getFieldShort(Object instance, Class declaringClass, String fieldName); + // END android-added /** - * Return the next {@code int} handle to be used to indicate cyclic + * Return the next Integer handle to be used to indicate cyclic * references being saved to the stream. - * - * @return int, the next handle to represent the next cyclic reference + * + * @return the next handle to represent the next cyclic reference */ - private int nextHandle() { - return this.currentHandle++; + private Integer nextHandle() { + return Integer.valueOf(this.currentHandle++); } /** * Gets this stream's {@code PutField} object. This object provides access * to the persistent fields that are eventually written to the output * stream. It is used to transfer the values from the fields of the object - * that is currently being written to the persistent fields. - * + * that is currently being written to the persistent fields. + * * @return the PutField object from which persistent fields can be accessed * by name. * @throws IOException @@ -744,7 +592,6 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, * @throws NotActiveException * if this method is not called from {@code writeObject()}. * @see ObjectInputStream#defaultReadObject - * @since Android 1.0 */ public PutField putFields() throws IOException { // We can't be called from just anywhere. There are rules. @@ -757,40 +604,26 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, return currentPutField; } - /** - * Return the {@code Integer} handle used to tag object - * {@code obj} as an instance that has been dumped already. Return - * {@code null} if object {@code obj} has not been saved yet. - * - * @param obj - * the object - * @return null if object {@code obj} has not been saved yet. Integer - * The handle that this object was assigned when it was saved. - */ - private Integer registeredObjectHandleFor(Object obj) { - return objectsWritten.get(obj); - } - /** * Assume object {@code obj} has not been dumped yet, and assign a * handle to it - * + * * @param obj * Non-null object being dumped. * @return the handle that this object is being assigned. - * + * * @see #nextHandle */ private Integer registerObjectWritten(Object obj) { - Integer handle = Integer.valueOf(nextHandle()); - registerObjectWritten(obj, handle); + Integer handle = nextHandle(); + objectsWritten.put(obj, handle); return handle; } /** * Remove the unshared object from the table, and restore any previous * handle. - * + * * @param obj * Non-null object being dumped. * @param previousHandle @@ -798,33 +631,18 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, */ private void removeUnsharedReference(Object obj, Integer previousHandle) { if (previousHandle != null) { - registerObjectWritten(obj, previousHandle); + objectsWritten.put(obj, previousHandle); } else { objectsWritten.remove(obj); } } - /** - * Assume object {@code obj} has not been dumped yet, and assign a - * handle to it, {@code handle}. - * - * @param obj - * Non-null object being dumped. - * @param handle - * An Integer, the handle to this object - * - * @see #nextHandle - */ - private void registerObjectWritten(Object obj, Integer handle) { - objectsWritten.put(obj, handle); - } - /** * Allows trusted subclasses to substitute the specified original {@code * object} with a new object. Object substitution has to be activated first * with calling {@code enableReplaceObject(true)}. This implementation just * returns {@code object}. - * + * * @param object * the original object for which a replacement may be defined. * @return the replacement object for {@code object}. @@ -834,7 +652,6 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, * @see #enableReplaceObject * @see ObjectInputStream#enableResolveObject * @see ObjectInputStream#resolveObject - * @since Android 1.0 */ protected Object replaceObject(Object object) throws IOException { // By default no object replacement. Subclasses can override @@ -847,11 +664,10 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, * point. Objects previously written are no longer remembered, so they will * be written again (instead of a cyclical reference) if found in the object * graph. - * + * * @throws IOException * if {@code reset()} is called during the serialization of an * object. - * @since Android 1.0 */ public void reset() throws IOException { // First we flush what we have @@ -869,7 +685,7 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, * Reset the collection of objects already dumped by the receiver. If the * objects are found again in the object graph, the receiver will dump them * again, instead of a handle (cyclic reference). - * + * */ private void resetSeenObjects() { objectsWritten = new IdentityHashMap(); @@ -880,7 +696,7 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, * Reset the receiver. The collection of objects already dumped by the * receiver is reset, and internal structures are also reset so that the * receiver knows it is in a fresh clean state. - * + * */ private void resetState() { resetSeenObjects(); @@ -889,7 +705,7 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, /** * Sets the specified protocol version to be used by this stream. - * + * * @param version * the protocol version to be used. Use a {@code * PROTOCOL_VERSION_x} constant from {@code @@ -900,13 +716,16 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, * if an I/O error occurs. * @see ObjectStreamConstants#PROTOCOL_VERSION_1 * @see ObjectStreamConstants#PROTOCOL_VERSION_2 - * @since Android 1.0 */ public void useProtocolVersion(int version) throws IOException { + if (!objectsWritten.isEmpty()) { + // KA028=Cannot set protocol version when stream in use + throw new IllegalStateException(Msg.getString("KA028")); //$NON-NLS-1$ + } if (version != ObjectStreamConstants.PROTOCOL_VERSION_1 && version != ObjectStreamConstants.PROTOCOL_VERSION_2) { - throw new IllegalArgumentException(org.apache.harmony.luni.util.Msg - .getString("K00b3", version)); //$NON-NLS-1$ + // K00b3=Unknown protocol\: {0} + throw new IllegalArgumentException(Msg.getString("K00b3", version)); //$NON-NLS-1$ } protocolVersion = version; } @@ -914,12 +733,11 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, /** * Writes the entire contents of the byte array {@code buffer} to the output * stream. Blocks until all bytes are written. - * + * * @param buffer * the buffer to write. * @throws IOException * if an error occurs while writing to the target stream. - * @since Android 1.0 */ @Override public void write(byte[] buffer) throws IOException { @@ -931,7 +749,7 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, * Writes {@code count} bytes from the byte array {@code buffer} starting at * offset {@code index} to the target stream. Blocks until all bytes are * written. - * + * * @param buffer * the buffer to write. * @param offset @@ -941,7 +759,6 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, * stream. * @throws IOException * if an error occurs while writing to the target stream. - * @since Android 1.0 */ @Override public void write(byte[] buffer, int offset, int length) throws IOException { @@ -953,12 +770,11 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, * Writes a single byte to the target stream. Only the least significant * byte of the integer {@code value} is written to the stream. Blocks until * the byte is actually written. - * + * * @param value * the byte to write. * @throws IOException * if an error occurs while writing to the target stream. - * @since Android 1.0 */ @Override public void write(int value) throws IOException { @@ -968,12 +784,11 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, /** * Writes a boolean to the target stream. - * + * * @param value * the boolean value to write to the target stream. * @throws IOException * if an error occurs while writing to the target stream. - * @since Android 1.0 */ public void writeBoolean(boolean value) throws IOException { checkWritePrimitiveTypes(); @@ -982,12 +797,11 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, /** * Writes a byte (8 bit) to the target stream. - * + * * @param value * the byte to write to the target stream. * @throws IOException * if an error occurs while writing to the target stream. - * @since Android 1.0 */ public void writeByte(int value) throws IOException { checkWritePrimitiveTypes(); @@ -998,12 +812,11 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, * Writes the string {@code value} as a sequence of bytes to the target * stream. Only the least significant byte of each character in the string * is written. - * + * * @param value * the string to write to the target stream. * @throws IOException * if an error occurs while writing to the target stream. - * @since Android 1.0 */ public void writeBytes(String value) throws IOException { checkWritePrimitiveTypes(); @@ -1012,12 +825,11 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, /** * Writes a character (16 bit) to the target stream. - * + * * @param value * the character to write to the target stream. * @throws IOException * if an error occurs while writing to the target stream. - * @since Android 1.0 */ public void writeChar(int value) throws IOException { checkWritePrimitiveTypes(); @@ -1027,12 +839,11 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, /** * Writes the string {@code value} as a sequence of characters to the target * stream. - * + * * @param value * the string to write to the target stream. * @throws IOException * if an error occurs while writing to the target stream. - * @since Android 1.0 */ public void writeChars(String value) throws IOException { checkWritePrimitiveTypes(); @@ -1042,14 +853,14 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, /** * Write a class descriptor {@code classDesc} (an * {@code ObjectStreamClass}) to the stream. - * + * * @param classDesc * The class descriptor (an {@code ObjectStreamClass}) to * be dumped * @param unshared * Write the object unshared * @return the handle assigned to the class descriptor - * + * * @throws IOException * If an IO exception happened when writing the class * descriptor. @@ -1066,12 +877,16 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, } if (handle == null) { Class classToWrite = classDesc.forClass(); - Integer previousHandle = objectsWritten.get(classDesc); + Integer previousHandle = null; + if (unshared) { + previousHandle = objectsWritten.get(classDesc); + } // If we got here, it is a new (non-null) classDesc that will have // to be registered as well - handle = registerObjectWritten(classDesc); + handle = nextHandle(); + objectsWritten.put(classDesc, handle); - if (Proxy.isProxyClass(classToWrite)) { + if (classDesc.isProxy()) { output.writeByte(TC_PROXYCLASSDESC); Class[] interfaces = classToWrite.getInterfaces(); output.writeInt(interfaces.length); @@ -1080,7 +895,7 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, } annotateProxyClass(classToWrite); output.writeByte(TC_ENDBLOCKDATA); - writeClassDescForClass(Proxy.class); + writeClassDesc(proxyClassDesc, false); if (unshared) { // remove reference to unshared object removeUnsharedReference(classDesc, previousHandle); @@ -1111,32 +926,13 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, return handle; } - /** - * Writes a class descriptor (an {@code ObjectStreamClass}) that - * corresponds to the {@code java.lang.Class objClass} to the stream. - * - * @param objClass - * The class for which a class descriptor (an - * {@code ObjectStreamClass}) will be dumped. - * @return the handle assigned to the class descriptor - * - * @throws IOException - * If an IO exception happened when writing the class - * descriptor. - * - */ - private Integer writeClassDescForClass(Class objClass) - throws IOException { - return writeClassDesc(ObjectStreamClass.lookup(objClass), false); - } - /** * Writes a handle representing a cyclic reference (object previously * dumped). - * + * * @param handle * The Integer handle that represents an object previously seen - * + * * @throws IOException * If an IO exception happened when writing the cyclic * reference. @@ -1148,12 +944,11 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, /** * Writes a double (64 bit) to the target stream. - * + * * @param value * the double to write to the target stream. * @throws IOException * if an error occurs while writing to the target stream. - * @since Android 1.0 */ public void writeDouble(double value) throws IOException { checkWritePrimitiveTypes(); @@ -1164,17 +959,17 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, * Writes a collection of field descriptors (name, type name, etc) for the * class descriptor {@code classDesc} (an * {@code ObjectStreamClass}) - * + * * @param classDesc * The class descriptor (an {@code ObjectStreamClass}) * for which to write field information * @param externalizable * true if the descriptors are externalizable - * + * * @throws IOException * If an IO exception happened when writing the field * descriptors. - * + * * @see #writeObject(Object) */ private void writeFieldDescriptors(ObjectStreamClass classDesc, @@ -1207,13 +1002,12 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, * Writes the fields of the object currently being written to the target * stream. The field values are buffered in the currently active {@code * PutField} object, which can be accessed by calling {@code putFields()}. - * + * * @throws IOException * if an error occurs while writing to the target stream. * @throws NotActiveException * if there are no fields to write to the target stream. * @see #putFields - * @since Android 1.0 */ public void writeFields() throws IOException { // Has to have fields to write @@ -1226,14 +1020,14 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, /** * Writes a collection of field values for the emulated fields * {@code emulatedFields} - * + * * @param emulatedFields * an {@code EmulatedFieldsForDumping}, concrete subclass * of {@code PutField} - * + * * @throws IOException * If an IO exception happened when writing the field values. - * + * * @see #writeFields * @see #writeObject(Object) */ @@ -1279,32 +1073,37 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, } } + /** * Writes a collection of field values for the fields described by class * descriptor {@code classDesc} (an {@code ObjectStreamClass}). * This is the default mechanism, when emulated fields (an * {@code PutField}) are not used. Actual values to dump are fetched * directly from object {@code obj}. - * + * * @param obj * Instance from which to fetch field values to dump. * @param classDesc * A class descriptor (an {@code ObjectStreamClass}) * defining which fields should be dumped. - * + * * @throws IOException * If an IO exception happened when writing the field values. - * + * * @see #writeObject(Object) */ private void writeFieldValues(Object obj, ObjectStreamClass classDesc) throws IOException { ObjectStreamField[] fields = classDesc.fields(); Class declaringClass = classDesc.forClass(); - for (int i = 0; i < fields.length; i++) { + for(ObjectStreamField fieldDesc : fields) { try { + + // BEGIN android-changed + // // get associated Field + // long fieldID = fieldDesc.getFieldID(accessor, declaringClass); + // Code duplication starts, just because Java is typed - ObjectStreamField fieldDesc = fields[i]; if (fieldDesc.isPrimitive()) { switch (fieldDesc.getTypeCode()) { case 'B': @@ -1346,14 +1145,15 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, } } else { // Object type (array included). - Object field = getFieldObj(obj, declaringClass, fieldDesc + Object objField = getFieldObj(obj, declaringClass, fieldDesc .getName(), fieldDesc.getTypeString()); if (fieldDesc.isUnshared()) { - writeUnshared(field); + writeUnshared(objField); } else { - writeObject(field); + writeObject(objField); } } + // END android-changed } catch (NoSuchFieldError nsf) { // The user defined serialPersistentFields but did not provide // the glue to transfer values, @@ -1366,12 +1166,11 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, /** * Writes a float (32 bit) to the target stream. - * + * * @param value * the float to write to the target stream. * @throws IOException * if an error occurs while writing to the target stream. - * @since Android 1.0 */ public void writeFloat(float value) throws IOException { checkWritePrimitiveTypes(); @@ -1386,19 +1185,19 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, * (corresponding to class descriptor {@code classDesc}) defines * private instance method {@code writeObject} it will be used to * dump field values. - * + * * @param object * Instance from which to fetch field values to dump. * @param classDesc * A class descriptor (an {@code ObjectStreamClass}) * defining which fields should be dumped. - * + * * @throws IOException * If an IO exception happened when writing the field values in * the hierarchy. * @throws NotActiveException * If the given object is not active - * + * * @see #defaultWriteObject * @see #writeObject(Object) */ @@ -1423,14 +1222,9 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, // See if the object has a writeObject method. If so, run it boolean executed = false; - Class targetClass = classDesc.forClass(); try { - final Method method = ObjectStreamClass - .getPrivateWriteObjectMethod(targetClass); - if (method != null) { - // We have to be able to fetch its value, even if it is - // private - AccessController.doPrivileged(new PriviAction(method)); + if (classDesc.hasMethodWriteObject()){ + final Method method = classDesc.getMethodWriteObject(); try { method.invoke(object, new Object[] { this }); executed = true; @@ -1447,6 +1241,7 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, } } + if (executed) { drain(); output.writeByte(TC_ENDBLOCKDATA); @@ -1466,12 +1261,11 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, /** * Writes an integer (32 bit) to the target stream. - * + * * @param value * the integer to write to the target stream. * @throws IOException * if an error occurs while writing to the target stream. - * @since Android 1.0 */ public void writeInt(int value) throws IOException { checkWritePrimitiveTypes(); @@ -1480,12 +1274,11 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, /** * Writes a long (64 bit) to the target stream. - * + * * @param value * the long to write to the target stream. * @throws IOException * if an error occurs while writing to the target stream. - * @since Android 1.0 */ public void writeLong(long value) throws IOException { checkWritePrimitiveTypes(); @@ -1497,7 +1290,7 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, * component type {@code componentType} into the receiver. It is * assumed the array has not been dumped yet. Return an {@code Integer} * that represents the handle for this object (array) which is dumped here. - * + * * @param array * The array object to dump * @param arrayClass @@ -1507,20 +1300,19 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, * A {@code java.lang.Class} representing the array * component type * @return the handle assigned to the array - * + * * @throws IOException * If an IO exception happened when writing the array. */ - private Integer writeNewArray(Object array, Class arrayClass, + private Integer writeNewArray(Object array, Class arrayClass, ObjectStreamClass arrayClDesc, Class componentType, boolean unshared) throws IOException { output.writeByte(TC_ARRAY); - writeClassDescForClass(arrayClass); + writeClassDesc(arrayClDesc, false); - Integer previousHandle = objectsWritten.get(array); - Integer handle = registerObjectWritten(array); - if (unshared) { - // remove reference to unshared object - removeUnsharedReference(array, previousHandle); + Integer handle = nextHandle(); + + if (!unshared) { + objectsWritten.put(array, handle); } // Now we have code duplication just because Java is typed. We have to @@ -1585,6 +1377,10 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, Object[] objectArray = (Object[]) array; output.writeInt(objectArray.length); for (int i = 0; i < objectArray.length; i++) { + // TODO: This place is the opportunity for enhancement + // We can implement writing elements through fast-path, + // without setting up the context (see writeObject()) for + // each element with public API writeObject(objectArray[i]); } } @@ -1597,11 +1393,11 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, * descriptor ({@code ObjectStreamClass}) that corresponds to them. * Return an {@code Integer} that represents the handle for this * object (class) which is dumped here. - * + * * @param object * The {@code java.lang.Class} object to dump * @return the handle assigned to the class being dumped - * + * * @throws IOException * If an IO exception happened when writing the class. */ @@ -1614,21 +1410,20 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, // We cannot call lookup because it returns null if the parameter // represents instances that cannot be serialized, and that is not what // we want. + ObjectStreamClass clDesc = ObjectStreamClass.lookupStreamClass(object); // The handle for the classDesc is NOT the handle for the class object // being dumped. We must allocate a new handle and return it. - if (object.isEnum()) { - writeEnumDesc(object, unshared); + if (clDesc.isEnum()) { + writeEnumDesc(object, clDesc, unshared); } else { - writeClassDesc(ObjectStreamClass.lookupStreamClass(object), - unshared); + writeClassDesc(clDesc, unshared); } - Integer previousHandle = objectsWritten.get(object); - Integer handle = registerObjectWritten(object); - if (unshared) { - // remove reference to unshared object - removeUnsharedReference(object, previousHandle); + Integer handle = nextHandle(); + + if (!unshared) { + objectsWritten.put(object, handle); } return handle; @@ -1640,10 +1435,10 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, * descriptors for the superclass chain will be dumped as well. Return an * {@code Integer} that represents the handle for this object (class * descriptor) which is dumped here. - * + * * @param classDesc * The {@code ObjectStreamClass} object to dump - * + * * @throws IOException * If an IO exception happened when writing the class * descriptor. @@ -1653,15 +1448,15 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, output.writeUTF(classDesc.getName()); output.writeLong(classDesc.getSerialVersionUID()); byte flags = classDesc.getFlags(); - boolean externalizable = false; - externalizable = ObjectStreamClass.isExternalizable(classDesc - .forClass()); - if (protocolVersion != PROTOCOL_VERSION_1) { - // Change for 1.2. Objects can be saved in old format - // (PROTOCOL_VERSION_1) or in the 1.2 format (PROTOCOL_VERSION_2). - // Nested "if" check to optimize checking. Second check is more - // expensive. - if (externalizable) { + + boolean externalizable = classDesc.isExternalizable(); + + if (externalizable) { + if (protocolVersion == PROTOCOL_VERSION_1) { + flags &= NOT_SC_BLOCK_DATA; + } else { + // Change for 1.2. Objects can be saved in old format + // (PROTOCOL_VERSION_1) or in the 1.2 format (PROTOCOL_VERSION_2). flags |= SC_BLOCK_DATA; } } @@ -1676,12 +1471,11 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, /** * Writes a class descriptor to the target stream. - * + * * @param classDesc * the class descriptor to write to the target stream. * @throws IOException * if an error occurs while writing to the target stream. - * @since Android 1.0 */ protected void writeClassDescriptor(ObjectStreamClass classDesc) throws IOException { @@ -1695,14 +1489,14 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, * This is used to dump the exception instance that happened (if any) when * dumping the original object graph. The set of seen objects will be reset * just before and just after dumping this exception object. - * + * * When exceptions are found normally in the object graph, they are dumped * as a regular object, and not by this method. In that case, the set of * "known objects" is not reset. - * + * * @param ex * Exception object to dump - * + * * @throws IOException * If an IO exception happened when writing the exception * object. @@ -1719,13 +1513,13 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, * the receiver. It is assumed the object has not been dumped yet. Return an * {@code Integer} that represents the handle for this object which * is dumped here. - * + * * If the object implements {@code Externalizable} its * {@code writeExternal} is called. Otherwise, all fields described * by the class hierarchy is dumped. Each class can define how its declared * instance fields are dumped by defining a private method * {@code writeObject} - * + * * @param object * The object to dump * @param theClass @@ -1734,11 +1528,11 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, * @param unshared * Write the object unshared * @return the handle assigned to the object - * + * * @throws IOException * If an IO exception happened when writing the object. */ - private Integer writeNewObject(Object object, Class theClass, + private Integer writeNewObject(Object object, Class theClass, ObjectStreamClass clDesc, boolean unshared) throws IOException { // Not String, not null, not array, not cyclic reference @@ -1746,8 +1540,8 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, currentPutField = null; // null it, to make sure one will be computed if // needed - boolean externalizable = ObjectStreamClass.isExternalizable(theClass); - boolean serializable = ObjectStreamClass.isSerializable(theClass); + boolean externalizable = clDesc.isExternalizable(); + boolean serializable = clDesc.isSerializable(); if (!externalizable && !serializable) { // Object is neither externalizable nor serializable. Error throw new NotSerializableException(theClass.getName()); @@ -1755,16 +1549,20 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, // Either serializable or externalizable, now we can save info output.writeByte(TC_OBJECT); - writeClassDescForClass(theClass); - Integer previousHandle = objectsWritten.get(object); - Integer handle = registerObjectWritten(object); + writeClassDesc(clDesc, false); + Integer previousHandle = null; + if (unshared) { + previousHandle = objectsWritten.get(object); + } + Integer handle = nextHandle(); + objectsWritten.put(object, handle); // This is how we know what to do in defaultWriteObject. And it is also // used by defaultWriteObject to check if it was called from an invalid // place. // It allows writeExternal to call defaultWriteObject and have it work. currentObject = object; - currentClass = ObjectStreamClass.lookup(theClass); + currentClass = clDesc; try { if (externalizable) { boolean noBlockData = protocolVersion == PROTOCOL_VERSION_1; @@ -1808,11 +1606,11 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, * String has not been dumped yet. Return an {@code Integer} that * represents the handle for this object (String) which is dumped here. * Strings are saved encoded with {@link DataInput modified UTF-8}. - * + * * @param object * the string to dump. * @return the handle assigned to the String being dumped - * + * * @throws IOException * If an IO exception happened when writing the String. */ @@ -1828,19 +1626,19 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, } output.writeUTFBytes(object, count); - Integer previousHandle = objectsWritten.get(object); - Integer handle = registerObjectWritten(object); - if (unshared) { - // remove reference to unshared object - removeUnsharedReference(object, previousHandle); + Integer handle = nextHandle(); + + if (!unshared) { + objectsWritten.put(object, handle); } + return handle; } /** * Write a special tag that indicates the value {@code null} into the * receiver. - * + * * @throws IOException * If an IO exception happened when writing the tag for * {@code null}. @@ -1851,13 +1649,12 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, /** * Writes an object to the target stream. - * + * * @param object * the object to write to the target stream. * @throws IOException * if an error occurs while writing to the target stream. * @see ObjectInputStream#readObject() - * @since Android 1.0 */ public final void writeObject(Object object) throws IOException { writeObject(object, false); @@ -1868,13 +1665,12 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, * to {@code writeObject}, except that it always writes a new object to the * stream versus the use of back-referencing for identical objects by * {@code writeObject}. - * + * * @param object * the object to write to the target stream. * @throws IOException * if an error occurs while writing to the target stream. * @see ObjectInputStream#readUnshared() - * @since Android 1.0 */ public void writeUnshared(Object object) throws IOException { writeObject(object, true); @@ -1919,7 +1715,7 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, /** * Write object {@code object} into the receiver's underlying stream. - * + * * @param object * The object to write * @param unshared @@ -1931,10 +1727,10 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, * A boolean indicating if stream-based replacement should be * computed (if supported) for the object. * @return the handle assigned to the final object being dumped - * + * * @throws IOException * If an IO exception happened when writing the object - * + * * @see ObjectInputStream#readObject() */ private Integer writeObjectInternal(Object object, boolean unshared, @@ -1955,6 +1751,8 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, // Non-null object, first time seen... Class objClass = object.getClass(); + ObjectStreamClass clDesc = ObjectStreamClass.lookupStreamClass(objClass); + nestedLevels++; try { @@ -1969,58 +1767,41 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, } } - if (ObjectStreamClass.isSerializable(object.getClass()) + if (clDesc.isSerializable() && computeClassBasedReplacement) { - Object writeReplaceMethod = writeReplaceCache.get(objClass); - if (writeReplaceMethod != this) { - if (writeReplaceMethod == null) { - final Method writeReplace = ObjectStreamClass - .methodWriteReplace(objClass); - if (writeReplace == null) { - writeReplaceCache.put(objClass, this); - writeReplaceMethod = null; + if(clDesc.hasMethodWriteReplace()){ + Method methodWriteReplace = clDesc.getMethodWriteReplace(); + Object replObj = null; + try { + replObj = methodWriteReplace.invoke(object, (Object[]) null); + } catch (IllegalAccessException iae) { + replObj = object; + } catch (InvocationTargetException ite) { + // WARNING - Not sure this is the right thing to do + // if we can't run the method + Throwable target = ite.getTargetException(); + if (target instanceof ObjectStreamException) { + throw (ObjectStreamException) target; + } else if (target instanceof Error) { + throw (Error) target; } else { - // Has replacement method - AccessController - .doPrivileged(new PriviAction( - writeReplace)); - writeReplaceCache.put(objClass, writeReplace); - writeReplaceMethod = writeReplace; + throw (RuntimeException) target; } } - if (writeReplaceMethod != null) { - Object classBasedReplacement; - try { - classBasedReplacement = ((Method) writeReplaceMethod) - .invoke(object, (Object[]) null); - } catch (IllegalAccessException iae) { - classBasedReplacement = object; - } catch (InvocationTargetException ite) { - // WARNING - Not sure this is the right thing to do - // if we can't run the method - Throwable target = ite.getTargetException(); - if (target instanceof ObjectStreamException) { - throw (ObjectStreamException) target; - } else if (target instanceof Error) { - throw (Error) target; - } else { - throw (RuntimeException) target; - } - } - if (classBasedReplacement != object) { - // All over, class-based replacement off this time. - Integer replacementHandle = writeObjectInternal( - classBasedReplacement, false, false, - computeStreamReplacement); - // Make the original object also map to the same - // handle. - if (replacementHandle != null) { - registerObjectWritten(object, replacementHandle); - } - return replacementHandle; + if (replObj != object) { + // All over, class-based replacement off this time. + Integer replacementHandle = writeObjectInternal( + replObj, false, false, + computeStreamReplacement); + // Make the original object also map to the same + // handle. + if (replacementHandle != null) { + objectsWritten.put(object, replacementHandle); } + return replacementHandle; } } + } // We get here either if class-based replacement was not needed or @@ -2036,7 +1817,7 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, computeClassBasedReplacement, false); // Make the original object also map to the same handle. if (replacementHandle != null) { - registerObjectWritten(object, replacementHandle); + objectsWritten.put(object, replacementHandle); } return replacementHandle; } @@ -2061,7 +1842,7 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, // Is it an Array ? if (objClass.isArray()) { - return writeNewArray(object, objClass, objClass + return writeNewArray(object, objClass, clDesc, objClass .getComponentType(), unshared); } @@ -2070,20 +1851,23 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, } // Not a String or Class or Array. Default procedure. - return writeNewObject(object, objClass, unshared); + return writeNewObject(object, objClass, clDesc, unshared); } finally { nestedLevels--; } } // write for Enum Class Desc only, which is different from other classes - private ObjectStreamClass writeEnumDesc(Class theClass, boolean unshared) + private ObjectStreamClass writeEnumDesc(Class theClass, ObjectStreamClass classDesc, boolean unshared) throws IOException { // write classDesc, classDesc for enum is different - ObjectStreamClass classDesc = ObjectStreamClass.lookup(theClass); + // set flag for enum, the flag is (SC_SERIALIZABLE | SC_ENUM) classDesc.setFlags((byte) (SC_SERIALIZABLE | SC_ENUM)); - Integer previousHandle = objectsWritten.get(classDesc); + Integer previousHandle = null; + if (unshared) { + previousHandle = objectsWritten.get(classDesc); + } Integer handle = null; if (!unshared) { handle = dumpCycle(classDesc); @@ -2092,7 +1876,7 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, Class classToWrite = classDesc.forClass(); // If we got here, it is a new (non-null) classDesc that will have // to be registered as well - registerObjectWritten(classDesc); + objectsWritten.put(classDesc, nextHandle()); output.writeByte(TC_CLASSDESC); if (protocolVersion == PROTOCOL_VERSION_1) { @@ -2109,11 +1893,11 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, drain(); // flush primitive types in the annotation output.writeByte(TC_ENDBLOCKDATA); // write super class - ObjectStreamClass superClass = classDesc.getSuperclass(); - if (null != superClass) { + ObjectStreamClass superClassDesc = classDesc.getSuperclass(); + if (null != superClassDesc) { // super class is also enum - superClass.setFlags((byte) (SC_SERIALIZABLE | SC_ENUM)); - writeEnumDesc(superClass.forClass(), unshared); + superClassDesc.setFlags((byte) (SC_SERIALIZABLE | SC_ENUM)); + writeEnumDesc(superClassDesc.forClass(), superClassDesc, unshared); } else { output.writeByte(TC_NULL); } @@ -2137,18 +1921,26 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, // write enum only theClass = theClass.getSuperclass(); } - ObjectStreamClass classDesc = writeEnumDesc(theClass, unshared); + ObjectStreamClass classDesc = ObjectStreamClass.lookup(theClass); + writeEnumDesc(theClass, classDesc, unshared); - Integer previousHandle = objectsWritten.get(object); - Integer handle = registerObjectWritten(object); + Integer previousHandle = null; + if (unshared) { + previousHandle = objectsWritten.get(object); + } + Integer handle = nextHandle(); + objectsWritten.put(object, handle); ObjectStreamField[] fields = classDesc.getSuperclass().fields(); Class declaringClass = classDesc.getSuperclass().forClass(); // Only write field "name" for enum class, which is the second field of // enum, that is fields[1]. Ignore all non-fields and fields.length < 2 if (null != fields && fields.length > 1) { + // BEGIN android-changed String str = (String) getFieldObj(object, declaringClass, fields[1] .getName(), fields[1].getTypeString()); + // END android-changed + Integer strhandle = null; if (!unshared) { strhandle = dumpCycle(str); @@ -2169,31 +1961,26 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, /** * Method to be overridden by subclasses to write {@code object} to the * target stream. - * + * * @param object * the object to write to the target stream. * @throws IOException * if an error occurs while writing to the target stream. - * @since Android 1.0 */ protected void writeObjectOverride(Object object) throws IOException { - // BEGIN android-changed - // copied from newer version of harmony if (!subclassOverridingImplementation) { // Subclasses must override. throw new IOException(); } - // END android-changed } /** * Writes a short (16 bit) to the target stream. - * + * * @param value * the short to write to the target stream. * @throws IOException * if an error occurs while writing to the target stream. - * @since Android 1.0 */ public void writeShort(int value) throws IOException { checkWritePrimitiveTypes(); @@ -2202,10 +1989,9 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, /** * Writes the {@link ObjectOutputStream} header to the target stream. - * + * * @throws IOException * if an error occurs while writing to the target stream. - * @since Android 1.0 */ protected void writeStreamHeader() throws IOException { output.writeShort(STREAM_MAGIC); @@ -2215,12 +2001,11 @@ public class ObjectOutputStream extends OutputStream implements ObjectOutput, /** * Writes a string encoded with {@link DataInput modified UTF-8} to the * target stream. - * + * * @param value * the string to write to the target stream. * @throws IOException * if an error occurs while writing to the target stream. - * @since Android 1.0 */ public void writeUTF(String value) throws IOException { checkWritePrimitiveTypes(); diff --git a/libcore/luni/src/main/java/java/io/ObjectStreamClass.java b/libcore/luni/src/main/java/java/io/ObjectStreamClass.java index 37b1f1765..ac356eab0 100644 --- a/libcore/luni/src/main/java/java/io/ObjectStreamClass.java +++ b/libcore/luni/src/main/java/java/io/ObjectStreamClass.java @@ -22,6 +22,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.lang.reflect.Proxy; import java.security.AccessController; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -33,18 +34,17 @@ import java.util.WeakHashMap; import org.apache.harmony.luni.util.Msg; import org.apache.harmony.luni.util.PriviAction; +import org.apache.harmony.luni.util.ThreadLocalCache; /** * Represents a descriptor for identifying a class during serialization and * deserialization. Information contained in the descriptor includes the name * and SUID of the class as well as field names and types. Information inherited * from the superclasses is also taken into account. - * + * * @see ObjectOutputStream * @see ObjectInputStream * @see java.lang.Class - * - * @since Android 1.0 */ public class ObjectStreamClass implements Serializable { @@ -55,6 +55,8 @@ public class ObjectStreamClass implements Serializable { // Name of the field that contains the SUID value (if present) private static final String UID_FIELD_NAME = "serialVersionUID"; //$NON-NLS-1$ + static final long CONSTRUCTOR_IS_NOT_RESOLVED = -1; + private static final int CLASS_MODIFIERS_MASK; private static final int FIELD_MODIFIERS_MASK; @@ -98,8 +100,6 @@ public class ObjectStreamClass implements Serializable { /** * Constant indicating that the class has no Serializable fields. - * - * @since Android 1.0 */ public static final ObjectStreamField[] NO_FIELDS = new ObjectStreamField[0]; @@ -135,9 +135,36 @@ public class ObjectStreamClass implements Serializable { static final Class OBJECTSTREAMCLASSCLASS = ObjectStreamClass.class; - // Table mapping instances of java.lang.Class to to corresponding instances - // of ObjectStreamClass - private static final WeakHashMap, ObjectStreamClass> classesAndDescriptors = new WeakHashMap, ObjectStreamClass>(); + private transient Method methodWriteReplace; + + private transient Method methodReadResolve; + + private transient Method methodWriteObject; + + private transient Method methodReadObject; + + private transient Method methodReadObjectNoData; + + /** + * Indicates whether the class properties resolved + * + * @see #resolveProperties() + */ + private transient boolean arePropertiesResolved; + + /** + * Cached class properties + * + * @see #resolveProperties() + * @see #isSerializable() + * @see #isExternalizable() + * @see #isProxy() + * @see #isEnum() + */ + private transient boolean isSerializable; + private transient boolean isExternalizable; + private transient boolean isProxy; + private transient boolean isEnum; // ClassDesc // @@ -167,6 +194,17 @@ public class ObjectStreamClass implements Serializable { // Array of ObjectStreamField describing the serialized fields of this class private transient ObjectStreamField[] loadFields; + // MethodID for deserialization constructor + private transient long constructor = CONSTRUCTOR_IS_NOT_RESOLVED; + + void setConstructor(long newConstructor) { + constructor = newConstructor; + } + + long getConstructor() { + return constructor; + } + /* * If an ObjectStreamClass describes an Externalizable class, it (the * descriptor) should not have field descriptors (ObjectStreamField) at all. @@ -183,22 +221,24 @@ public class ObjectStreamClass implements Serializable { } /** - * Adds an extra entry mapping a given class {@code cl} to its class - * descriptor, which will be computed (an ObjectStreamClass). If - * {@code computeSUID} is true, this method will compute the SUID for - * this class. - * + * Compute class descriptor for a given class cl. + * * @param cl * a java.langClass for which to compute the corresponding * descriptor - * @param computeSUID - * a boolean indicating if SUID should be computed or not. * @return the computer class descriptor */ - private static ObjectStreamClass addToCache(Class cl, boolean computeSUID) { + private static ObjectStreamClass createClassDesc(Class cl) { ObjectStreamClass result = new ObjectStreamClass(); + boolean isArray = cl.isArray(); + boolean serializable = isSerializable(cl); + boolean externalizable = isExternalizable(cl); + + result.isSerializable = serializable; + result.isExternalizable = externalizable; + // Now we fill in the values result.setName(cl.getName()); result.setClass(cl); @@ -208,45 +248,73 @@ public class ObjectStreamClass implements Serializable { } Field[] declaredFields = null; - if (computeSUID) { - // Lazy computation, to save speed & space - declaredFields = cl.getDeclaredFields(); - result.setSerialVersionUID((cl.isEnum() || (cl == Enum.class)) ? 0 - : computeSerialVersionUID(cl, declaredFields)); + + // Compute the SUID + if(serializable || externalizable) { + if (result.isEnum() || result.isProxy()) { + result.setSerialVersionUID(0L); + } else { + declaredFields = cl.getDeclaredFields(); + result.setSerialVersionUID(computeSerialVersionUID(cl, + declaredFields)); + } } - boolean serializable = isSerializable(cl); // Serializables need field descriptors - if (serializable && !cl.isArray()) { + if (serializable && !isArray) { if (declaredFields == null) { - declaredFields = cl.getDeclaredFields(); } result.buildFieldDescriptors(declaredFields); } else { // Externalizables or arrays do not need FieldDesc info - result.setFields(new ObjectStreamField[0]); + result.setFields(NO_FIELDS); + } + + // Copy all fields to loadFields - they should be read by default in + // ObjectInputStream.defaultReadObject() method + ObjectStreamField[] fields = result.getFields(); + + if (fields != null) { + ObjectStreamField[] loadFields = new ObjectStreamField[fields.length]; + + for (int i = 0; i < fields.length; ++i) { + loadFields[i] = new ObjectStreamField(fields[i].getName(), + fields[i].getType(), fields[i].isUnshared()); + + // resolve type string to init typeString field in + // ObjectStreamField + loadFields[i].getTypeString(); + } + result.setLoadFields(loadFields); } byte flags = 0; - boolean externalizable = isExternalizable(cl); if (externalizable) { flags |= ObjectStreamConstants.SC_EXTERNALIZABLE; + flags |= ObjectStreamConstants.SC_BLOCK_DATA; // use protocol version 2 by default } else if (serializable) { flags |= ObjectStreamConstants.SC_SERIALIZABLE; } - if (getPrivateWriteObjectMethod(cl) != null) { + result.methodWriteReplace = findMethod(cl, "writeReplace"); //$NON-NLS-1$ + result.methodReadResolve = findMethod(cl, "readResolve"); //$NON-NLS-1$ + result.methodWriteObject = findPrivateMethod(cl, "writeObject", //$NON-NLS-1$ + WRITE_PARAM_TYPES); + result.methodReadObject = findPrivateMethod(cl, "readObject", //$NON-NLS-1$ + READ_PARAM_TYPES); + result.methodReadObjectNoData = findPrivateMethod(cl, + "readObjectNoData", EMPTY_CONSTRUCTOR_PARAM_TYPES); //$NON-NLS-1$ + if (result.hasMethodWriteObject()) { flags |= ObjectStreamConstants.SC_WRITE_METHOD; } result.setFlags(flags); - classesAndDescriptors.put(cl, result); return result; } /** * Builds the collection of field descriptors for the receiver - * + * * @param declaredFields * collection of java.lang.reflect.Field for which to compute * field descriptors @@ -315,12 +383,12 @@ public class ObjectStreamClass implements Serializable { } fields = _fields; } - + /** * Compute and return the Serial Version UID of the class {@code cl}. * The value is computed based on the class name, superclass chain, field * names, method names, modifiers, etc. - * + * * @param cl * a java.lang.Class for which to compute the SUID * @param fields @@ -477,7 +545,7 @@ public class ObjectStreamClass implements Serializable { /* * write name, modifier & "descriptor" of all but private * ones - * + * * constructor.getName() returns the constructor name as * typed, not the VM name */ @@ -534,7 +602,7 @@ public class ObjectStreamClass implements Serializable { /** * Returns what the serializaton specification calls "descriptor" given a * field signature. - * + * * @param signature * a field signature * @return containing the descriptor @@ -546,7 +614,7 @@ public class ObjectStreamClass implements Serializable { /** * Return what the serializaton specification calls "descriptor" given a * method/constructor signature. - * + * * @param signature * a method or constructor signature * @return containing the descriptor @@ -558,7 +626,7 @@ public class ObjectStreamClass implements Serializable { /** * Return the java.lang.reflect.Field {@code serialPersistentFields} * if class {@code cl} implements it. Return null otherwise. - * + * * @param cl * a java.lang.Class which to test * @return {@code java.lang.reflect.Field} if the class has @@ -583,10 +651,9 @@ public class ObjectStreamClass implements Serializable { /** * Returns the class (java.lang.Class) for this descriptor. - * + * * @return the class in the local VM that this descriptor represents; * {@code null} if there is no corresponding class. - * @since Android 1.0 */ public Class forClass() { if (resolvedClass != null) { @@ -597,7 +664,7 @@ public class ObjectStreamClass implements Serializable { /** * Return a String representing the signature for a Constructor {@code c}. - * + * * @param c * a java.lang.reflect.Constructor for which to compute the * signature @@ -608,12 +675,11 @@ public class ObjectStreamClass implements Serializable { /** * Gets a field descriptor of the class represented by this class * descriptor. - * + * * @param name * the name of the desired field. * @return the field identified by {@code name} or {@code null} if there is * no such field. - * @since Android 1.0 */ public ObjectStreamField getField(String name) { ObjectStreamField[] allFields = getFields(); @@ -629,20 +695,18 @@ public class ObjectStreamClass implements Serializable { /** * Returns the collection of field descriptors for the fields of the * corresponding class - * + * * @return the receiver's collection of declared fields for the class it * represents */ ObjectStreamField[] fields() { if (fields == null) { - synchronized(this){ - Class forCl = forClass(); - if (forCl != null && isSerializable(forCl) && !forCl.isArray()) { - buildFieldDescriptors(forCl.getDeclaredFields()); - } else { - // Externalizables or arrays do not need FieldDesc info - setFields(new ObjectStreamField[0]); - } + Class forCl = forClass(); + if (forCl != null && isSerializable() && !forCl.isArray()) { + buildFieldDescriptors(forCl.getDeclaredFields()); + } else { + // Externalizables or arrays do not need FieldDesc info + setFields(NO_FIELDS); } } return fields; @@ -651,10 +715,9 @@ public class ObjectStreamClass implements Serializable { /** * Returns a collection of field descriptors for the serialized fields of * the class represented by this class descriptor. - * + * * @return an array of field descriptors or an array of length zero if there * are no fields in this descriptor's class. - * @since Android 1.0 */ public ObjectStreamField[] getFields() { copyFieldAttributes(); @@ -662,9 +725,9 @@ public class ObjectStreamClass implements Serializable { } /** - * If a Class uses "serialPersistentFields" to define the serialized fields, + * If a Class uses "serialPersistentFields" to define the serialized fields, * this.loadFields cannot get the "unshared" information when deserializing - * fields using current implementation of ObjectInputStream. This method + * fields using current implementation of ObjectInputStream. This method * provides a way to copy the "unshared" attribute from this.fields. * */ @@ -672,7 +735,7 @@ public class ObjectStreamClass implements Serializable { if ((loadFields == null) || fields == null) { return; } - + for (int i = 0; i < loadFields.length; i++) { ObjectStreamField loadField = loadFields[i]; String name = loadField.getName(); @@ -690,7 +753,7 @@ public class ObjectStreamClass implements Serializable { /** * Returns the collection of field descriptors for the input fields of the * corresponding class - * + * * @return the receiver's collection of input fields for the class it * represents */ @@ -700,7 +763,7 @@ public class ObjectStreamClass implements Serializable { /** * Return a String representing the signature for a field {@code f}. - * + * * @param f * a java.lang.reflect.Field for which to compute the signature * @return the field's signature @@ -709,11 +772,11 @@ public class ObjectStreamClass implements Serializable { /** * Returns the flags for this descriptor, where possible combined values are - * + * * ObjectStreamConstants.SC_WRITE_METHOD * ObjectStreamConstants.SC_SERIALIZABLE * ObjectStreamConstants.SC_EXTERNALIZABLE - * + * * @return byte the receiver's flags for the class it represents */ byte getFlags() { @@ -722,7 +785,7 @@ public class ObjectStreamClass implements Serializable { /** * Return a String representing the signature for a method {@code m}. - * + * * @param m * a java.lang.reflect.Method for which to compute the signature * @return the method's signature @@ -731,9 +794,8 @@ public class ObjectStreamClass implements Serializable { /** * Returns the name of the class represented by this descriptor. - * + * * @return the fully qualified name of the class this descriptor represents. - * @since Android 1.0 */ public String getName() { return className; @@ -742,9 +804,8 @@ public class ObjectStreamClass implements Serializable { /** * Returns the Serial Version User ID of the class represented by this * descriptor. - * + * * @return the SUID for the class represented by this descriptor. - * @since Android 1.0 */ public long getSerialVersionUID() { return svUID; @@ -753,7 +814,7 @@ public class ObjectStreamClass implements Serializable { /** * Returns the descriptor (ObjectStreamClass) of the superclass of the class * represented by the receiver. - * + * * @return an ObjectStreamClass representing the superclass of the class * represented by the receiver. */ @@ -767,7 +828,7 @@ public class ObjectStreamClass implements Serializable { * compiler-generated, it is used by the serialization code to compute SUID. * This is unfortunate, since it may depend on compiler optimizations in * some cases. - * + * * @param cl * a java.lang.Class which to test * @return {@code true} if the class has {@code false} @@ -775,85 +836,16 @@ public class ObjectStreamClass implements Serializable { */ private static native boolean hasClinit(Class cl); - /** - * Return true if the given class {@code cl} implements private - * method {@code readObject()}. - * - * @param cl - * a java.lang.Class which to test - * @return {@code true} if the class implements readObject - * {@code false} if the class does not implement readObject - */ - static Method getPrivateReadObjectMethod(Class cl) { - try { - Method method = cl - .getDeclaredMethod("readObject", READ_PARAM_TYPES); //$NON-NLS-1$ - if (Modifier.isPrivate(method.getModifiers()) - && method.getReturnType() == VOID_CLASS) { - return method; - } - } catch (NoSuchMethodException nsm) { - // Ignored - } - return null; - } - - /** - * Return true if the given class {@code cl} implements private - * method {@code readObject()}. - * - * @param cl - * a java.lang.Class which to test - * @return {@code true} if the class implements readObject - * {@code false} if the class does not implement readObject - */ - static Method getPrivateReadObjectNoDataMethod(Class cl) { - try { - Method method = cl.getDeclaredMethod("readObjectNoData", //$NON-NLS-1$ - EMPTY_CONSTRUCTOR_PARAM_TYPES); - if (Modifier.isPrivate(method.getModifiers()) - && method.getReturnType() == VOID_CLASS) { - return method; - } - } catch (NoSuchMethodException nsm) { - // Ignored - } - return null; - } - - /** - * Return true if the given class {@code cl} implements private - * method {@code writeObject()}. - * - * @param cl - * a java.lang.Class which to test - * @return {@code true} if the class implements writeObject - * {@code false} if the class does not implement writeObject - */ - static Method getPrivateWriteObjectMethod(Class cl) { - try { - Method method = cl.getDeclaredMethod("writeObject", //$NON-NLS-1$ - WRITE_PARAM_TYPES); - if (Modifier.isPrivate(method.getModifiers()) - && method.getReturnType() == VOID_CLASS) { - return method; - } - } catch (NoSuchMethodException nsm) { - // Ignored - } - return null; - } - /** * Return true if instances of class {@code cl} are Externalizable, * false otherwise. - * + * * @param cl * a java.lang.Class which to test * @return {@code true} if instances of the class are Externalizable * {@code false} if instances of the class are not * Externalizable - * + * * @see Object#hashCode */ static boolean isExternalizable(Class cl) { @@ -865,7 +857,7 @@ public class ObjectStreamClass implements Serializable { * typecode describes a primitive type * * @param typecode a char describing the typecode - * @return {@code true} if the typecode represents a primitive type + * @return {@code true} if the typecode represents a primitive type * {@code false} if the typecode represents an Object type (including arrays) * * @see Object#hashCode @@ -877,22 +869,79 @@ public class ObjectStreamClass implements Serializable { /** * Return true if instances of class {@code cl} are Serializable, * false otherwise. - * + * * @param cl * a java.lang.Class which to test * @return {@code true} if instances of the class are Serializable * {@code false} if instances of the class are not * Serializable - * + * * @see Object#hashCode */ static boolean isSerializable(Class cl) { return SERIALIZABLE.isAssignableFrom(cl); } + /** + * Resolves the class properties, if they weren't already + */ + private void resolveProperties() { + if (arePropertiesResolved) { + return; + } + + Class cl = forClass(); + isProxy = Proxy.isProxyClass(cl); + isEnum = Enum.class.isAssignableFrom(cl); + isSerializable = isSerializable(cl); + isExternalizable = isExternalizable(cl); + + arePropertiesResolved = true; + } + + /** + * Answers whether the class for this descriptor is serializable + * + * @return true if class implements Serializable + */ + boolean isSerializable() { + resolveProperties(); + return isSerializable; + } + + /** + * Answers whether the class for this descriptor is serializable + * + * @return true if class implements Serializable + */ + boolean isExternalizable() { + resolveProperties(); + return isExternalizable; + } + + /** + * Answers whether the class for this descriptor is proxied class + * + * @return true if class is proxied + */ + boolean isProxy() { + resolveProperties(); + return isProxy; + } + + /** + * Answers whether the class for this descriptor is subclass of Enum + * + * @return true if class is subclass of Enum + */ + boolean isEnum() { + resolveProperties(); + return isEnum; + } + /** * Return a little endian long stored in a given position of the buffer - * + * * @param buffer * a byte array with the byte representation of the number * @param position @@ -911,83 +960,67 @@ public class ObjectStreamClass implements Serializable { * Returns the descriptor corresponding to the class {@code cl}. If the * class is not serializable or externalizable then {@code null} is * returned. - * + * * @param cl * a java.langClass for which to obtain the corresponding * descriptor * @return the corresponding descriptor if the {@code cl} is serializable or * externalizable; {@code null} otherwise. - * @since Android 1.0 */ public static ObjectStreamClass lookup(Class cl) { - boolean serializable = isSerializable(cl); - boolean externalizable = isExternalizable(cl); + ObjectStreamClass osc = lookupStreamClass(cl); - // Has to be either Serializable or Externalizable - if (!serializable && !externalizable) { - return null; + if (osc.isSerializable() || osc.isExternalizable()) { + return osc; } - return lookupStreamClass(cl, true); + return null; } /** * Return the descriptor (ObjectStreamClass) corresponding to the class * {@code cl}. Returns an ObjectStreamClass even if instances of the * class cannot be serialized - * + * * @param cl * a java.langClass for which to obtain the corresponding * descriptor * @return the corresponding descriptor */ static ObjectStreamClass lookupStreamClass(Class cl) { - return lookupStreamClass(cl, isSerializable(cl) || isExternalizable(cl)); - } - /** - * Return the descriptor (ObjectStreamClass) corresponding to the class - * {@code cl}. Returns an ObjectStreamClass even if instances of the - * class cannot be serialized - * - * @param cl - * a {@code java.langClass} for which to obtain the - * corresponding descriptor - * @param computeSUID - * a boolean indicating if SUID should be computed or not. - * @return the corresponding descriptor - */ - private static synchronized ObjectStreamClass lookupStreamClass( - Class cl, boolean computeSUID) { - // Synchronized because of the lookup table 'classesAndDescriptors' - ObjectStreamClass cachedValue = classesAndDescriptors.get(cl); - if (cachedValue != null) { - return cachedValue; + WeakHashMap,ObjectStreamClass> tlc = OSCThreadLocalCache.oscWeakHashMap.get(); + + ObjectStreamClass cachedValue = tlc.get(cl); + if (cachedValue == null) { + cachedValue = createClassDesc(cl); + tlc.put(cl, cachedValue); } - return addToCache(cl, computeSUID); + return cachedValue; + } /** - * Return the java.lang.reflect.Method {@code readResolve} if class - * {@code cl} implements it. Return null otherwise. - * + * Return the java.lang.reflect.Method if class cl implements + * methodName . Return null otherwise. + * * @param cl * a java.lang.Class which to test - * @return {@code java.lang.reflect.Method} if the class implements - * readResolve {@code null} if the class does not implement - * readResolve + * @return java.lang.reflect.Method if the class implements + * writeReplace null if the class does not implement + * writeReplace */ - static Method methodReadResolve(Class cl) { + static Method findMethod(Class cl, String methodName) { Class search = cl; + Method method = null; while (search != null) { try { - Method method = search.getDeclaredMethod( - "readResolve", (Class[]) null); //$NON-NLS-1$ + method = search.getDeclaredMethod(methodName, (Class[]) null); if (search == cl || (method.getModifiers() & Modifier.PRIVATE) == 0) { + method.setAccessible(true); return method; } - return null; } catch (NoSuchMethodException nsm) { } search = search.getSuperclass(); @@ -996,37 +1029,81 @@ public class ObjectStreamClass implements Serializable { } /** - * Return the java.lang.reflect.Method {@code writeReplace} if class - * {@code cl} implements it. Return null otherwise. - * + * Return the java.lang.reflect.Method if class cl implements + * private methodName . Return null otherwise. + * * @param cl * a java.lang.Class which to test * @return {@code java.lang.reflect.Method} if the class implements * writeReplace {@code null} if the class does not implement * writeReplace */ - static Method methodWriteReplace(Class cl) { - Class search = cl; - while (search != null) { - try { - Method method = search.getDeclaredMethod( - "writeReplace", (Class[]) null); //$NON-NLS-1$ - if (search == cl - || (method.getModifiers() & Modifier.PRIVATE) == 0) { - return method; - } - return null; - } catch (NoSuchMethodException nsm) { - // Ignored + static Method findPrivateMethod(Class cl, String methodName, + Class[] param) { + try { + Method method = cl.getDeclaredMethod(methodName, param); + if (Modifier.isPrivate(method.getModifiers()) + && method.getReturnType() == VOID_CLASS) { + method.setAccessible(true); + return method; } - search = search.getSuperclass(); + } catch (NoSuchMethodException nsm) { + // Ignored } return null; } + boolean hasMethodWriteReplace() { + return (methodWriteReplace != null); + } + + Method getMethodWriteReplace() { + return methodWriteReplace; + } + + boolean hasMethodReadResolve() { + return (methodReadResolve != null); + } + + Method getMethodReadResolve() { + return methodReadResolve; + } + + boolean hasMethodWriteObject() { + return (methodWriteObject != null); + } + + Method getMethodWriteObject() { + return methodWriteObject; + } + + boolean hasMethodReadObject() { + return (methodReadObject != null); + } + + Method getMethodReadObject() { + return methodReadObject; + } + + boolean hasMethodReadObjectNoData() { + return (methodReadObjectNoData != null); + } + + Method getMethodReadObjectNoData() { + return methodReadObjectNoData; + } + + void initPrivateFields(ObjectStreamClass desc) { + methodWriteReplace = desc.methodWriteReplace; + methodReadResolve = desc.methodReadResolve; + methodWriteObject = desc.methodWriteObject; + methodReadObject = desc.methodReadObject; + methodReadObjectNoData = desc.methodReadObjectNoData; + } + /** * Set the class (java.lang.Class) that the receiver represents - * + * * @param c * aClass, the new class that the receiver describes */ @@ -1037,7 +1114,7 @@ public class ObjectStreamClass implements Serializable { /** * Set the collection of field descriptors for the fields of the * corresponding class - * + * * @param f * ObjectStreamField[], the receiver's new collection of declared * fields for the class it represents @@ -1049,7 +1126,7 @@ public class ObjectStreamClass implements Serializable { /** * Set the collection of field descriptors for the input fields of the * corresponding class - * + * * @param f * ObjectStreamField[], the receiver's new collection of input * fields for the class it represents @@ -1060,11 +1137,11 @@ public class ObjectStreamClass implements Serializable { /** * Set the flags for this descriptor, where possible combined values are - * + * * ObjectStreamConstants.SC_WRITE_METHOD * ObjectStreamConstants.SC_SERIALIZABLE * ObjectStreamConstants.SC_EXTERNALIZABLE - * + * * @param b * byte, the receiver's new flags for the class it represents */ @@ -1074,7 +1151,7 @@ public class ObjectStreamClass implements Serializable { /** * Set the name of the class represented by the receiver - * + * * @param newName * a String, the new fully qualified name of the class the * receiver represents @@ -1085,7 +1162,7 @@ public class ObjectStreamClass implements Serializable { /** * Set the Serial Version User ID of the class represented by the receiver - * + * * @param l * a long, the new SUID for the class represented by the receiver */ @@ -1096,7 +1173,7 @@ public class ObjectStreamClass implements Serializable { /** * Set the descriptor for the superclass of the class described by the * receiver - * + * * @param c * an ObjectStreamClass, the new ObjectStreamClass for the * superclass of the class represented by the receiver @@ -1124,13 +1201,24 @@ public class ObjectStreamClass implements Serializable { /** * Returns a string containing a concise, human-readable description of this * descriptor. - * + * * @return a printable representation of this descriptor. - * @since Android 1.0 */ @Override public String toString() { return getName() + ": static final long serialVersionUID =" //$NON-NLS-1$ + getSerialVersionUID() + "L;"; //$NON-NLS-1$ } + + static class OSCThreadLocalCache extends ThreadLocalCache { + + // thread-local cache for ObjectStreamClass.lookup + public static ThreadLocalCache,ObjectStreamClass>> oscWeakHashMap = new ThreadLocalCache,ObjectStreamClass>>() { + protected WeakHashMap,ObjectStreamClass> initialValue() { + return new WeakHashMap,ObjectStreamClass>(); + } + }; + + } + } diff --git a/libcore/luni/src/main/java/java/io/ObjectStreamConstants.java b/libcore/luni/src/main/java/java/io/ObjectStreamConstants.java index 8f7ad6654..01b010960 100644 --- a/libcore/luni/src/main/java/java/io/ObjectStreamConstants.java +++ b/libcore/luni/src/main/java/java/io/ObjectStreamConstants.java @@ -19,168 +19,122 @@ package java.io; /** * A helper interface with constants used by the serialization implementation. - * - * @since Android 1.0 */ public abstract interface ObjectStreamConstants { /** * The stream header's magic number. - * - * @since Android 1.0 */ public static final short STREAM_MAGIC = (short) 0xaced; /** * The stream header's version number. - * - * @since Android 1.0 */ public static final short STREAM_VERSION = 5; // These are tags to indicate the stream contents - + /** * The minimum tag value. - * - * @since Android 1.0 */ public static final byte TC_BASE = 0x70; /** * Tag to mark a {@code null} object reference. - * - * @since Android 1.0 */ public static final byte TC_NULL = (byte) 0x70; /** * Tag to mark a reference to an object that has already been written to the * stream. - * - * @since Android 1.0 */ public static final byte TC_REFERENCE = (byte) 0x71; /** * Tag to mark a new class descriptor. - * - * @since Android 1.0 */ public static final byte TC_CLASSDESC = (byte) 0x72; /** * Tag to mark a new object. - * - * @since Android 1.0 */ public static final byte TC_OBJECT = (byte) 0x73; /** * Tag to mark a new string. - * - * @since Android 1.0 */ public static final byte TC_STRING = (byte) 0x74; /** * Tag to mark a new array. - * - * @since Android 1.0 */ public static final byte TC_ARRAY = (byte) 0x75; /** * Tag to mark a reference to a class. - * - * @since Android 1.0 */ public static final byte TC_CLASS = (byte) 0x76; /** * Tag to mark a block of optional data. The byte following this tag * indicates the size of the block. - * - * @since Android 1.0 */ public static final byte TC_BLOCKDATA = (byte) 0x77; /** * Tag to mark the end of block data blocks for an object. - * - * @since Android 1.0 */ public static final byte TC_ENDBLOCKDATA = (byte) 0x78; /** * Tag to mark a stream reset. - * - * @since Android 1.0 */ public static final byte TC_RESET = (byte) 0x79; /** * Tag to mark a long block of data. The long following this tag * indicates the size of the block. - * - * @since Android 1.0 */ public static final byte TC_BLOCKDATALONG = (byte) 0x7A; /** * Tag to mark an exception. - * - * @since Android 1.0 */ public static final byte TC_EXCEPTION = (byte) 0x7B; /** * Tag to mark a long string. - * - * @since Android 1.0 */ public static final byte TC_LONGSTRING = (byte) 0x7C; /** * Tag to mark a new proxy class descriptor. - * - * @since Android 1.0 */ public static final byte TC_PROXYCLASSDESC = (byte) 0x7D; /** * The maximum tag value. - * - * @since Android 1.0 */ public static final byte TC_MAX = 0x7E; /** * Handle for the first object that gets serialized. - * - * @since Android 1.0 */ public static final int baseWireHandle = 0x007e0000; /** * Stream protocol version 1. - * - * @since Android 1.0 */ public static final int PROTOCOL_VERSION_1 = 1; /** * Stream protocol version 2. - * - * @since Android 1.0 */ public static final int PROTOCOL_VERSION_2 = 2; /** * Permission constant to enable subclassing of ObjectInputStream and * ObjectOutputStream. - * - * @since Android 1.0 */ public static final SerializablePermission SUBCLASS_IMPLEMENTATION_PERMISSION = new SerializablePermission( "enableSubclassImplementation"); //$NON-NLS-1$ @@ -188,8 +142,6 @@ public abstract interface ObjectStreamConstants { /** * Permission constant to enable object substitution during serialization * and deserialization. - * - * @since Android 1.0 */ public static final SerializablePermission SUBSTITUTION_PERMISSION = new SerializablePermission( "enableSubstitution"); //$NON-NLS-1$ @@ -199,47 +151,35 @@ public abstract interface ObjectStreamConstants { /** * Bit mask for the {@code flag} field in ObjectStreamClass. Indicates * that a serializable class has its own {@code writeObject} method. - * - * @since Android 1.0 */ public static final byte SC_WRITE_METHOD = 0x01; // If SC_SERIALIZABLE /** * Bit mask for the {@code flag} field in ObjectStreamClass. Indicates * that a class is serializable. - * - * @since Android 1.0 */ public static final byte SC_SERIALIZABLE = 0x02; /** * Bit mask for the {@code flag} field in ObjectStreamClass. Indicates * that a class is externalizable. - * - * @since Android 1.0 */ public static final byte SC_EXTERNALIZABLE = 0x04; /** * Bit mask for the {@code flag} field in ObjectStreamClass. Indicates * that an externalizable class is written in block data mode. - * - * @since Android 1.0 */ public static final byte SC_BLOCK_DATA = 0x08; // If SC_EXTERNALIZABLE /** * Tag to mark a new enum. - * - * @since Android 1.0 */ public static final byte TC_ENUM = 0x7E; /** * Bit mask for the {@code flag} field in ObjectStreamClass. Indicates * that a class is an enum type. - * - * @since Android 1.0 */ public static final byte SC_ENUM = 0x10; } diff --git a/libcore/luni/src/main/java/java/io/ObjectStreamException.java b/libcore/luni/src/main/java/java/io/ObjectStreamException.java index a06b102b8..c3ad7dc8e 100644 --- a/libcore/luni/src/main/java/java/io/ObjectStreamException.java +++ b/libcore/luni/src/main/java/java/io/ObjectStreamException.java @@ -28,8 +28,6 @@ package java.io; * @see OptionalDataException * @see StreamCorruptedException * @see WriteAbortedException - * - * @since Android 1.0 */ public abstract class ObjectStreamException extends IOException { @@ -38,8 +36,6 @@ public abstract class ObjectStreamException extends IOException { /** * Constructs a new {@code ObjectStreamException} with its stack trace * filled in. - * - * @since Android 1.0 */ protected ObjectStreamException() { super(); @@ -51,7 +47,6 @@ public abstract class ObjectStreamException extends IOException { * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ protected ObjectStreamException(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/io/ObjectStreamField.java b/libcore/luni/src/main/java/java/io/ObjectStreamField.java index 95deb7b63..3baebf701 100644 --- a/libcore/luni/src/main/java/java/io/ObjectStreamField.java +++ b/libcore/luni/src/main/java/java/io/ObjectStreamField.java @@ -17,22 +17,32 @@ package java.io; +// BEGIN android-note +// Harmony uses ObjectAccessors to access fields through JNI. Android has not +// yet migrated that API. +// END android-note + import java.lang.ref.WeakReference; import java.util.Arrays; import java.util.Comparator; +// BEGIN android-removed +// import org.apache.harmony.misc.accessors.ObjectAccessor; +// END android-removed + /** * Describes a field for the purpose of serialization. Classes can define the * collection of fields that are serialized, which may be different from the set * of all declared fields. - * + * * @see ObjectOutputStream#writeFields() * @see ObjectInputStream#readFields() - * - * @since Android 1.0 */ public class ObjectStreamField implements Comparable { + static final int FIELD_IS_NOT_RESOLVED = -1; + static final int FIELD_IS_ABSENT = -2; + // Declared name of the field private String name; @@ -49,16 +59,32 @@ public class ObjectStreamField implements Comparable { private boolean isDeserialized; + private long assocFieldID = FIELD_IS_NOT_RESOLVED; + + // BEGIN android-removed + // long getFieldID(ObjectAccessor accessor, Class declaringClass) { + // if (assocFieldID != FIELD_IS_NOT_RESOLVED) { + // return assocFieldID; + // } else { + // try { + // assocFieldID = accessor.getFieldID(declaringClass, name); + // } catch(NoSuchFieldError e) { + // assocFieldID = FIELD_IS_ABSENT; + // } + // return assocFieldID; + // } + // } + // END android-removed + /** * Constructs an ObjectStreamField with the specified name and type. - * + * * @param name * the name of the field. * @param cl * the type of the field. * @throws NullPointerException * if {@code name} or {@code cl} is {@code null}. - * @since Android 1.0 */ public ObjectStreamField(String name, Class cl) { if (name == null || cl == null) { @@ -71,7 +97,7 @@ public class ObjectStreamField implements Comparable { /** * Constructs an ObjectStreamField with the specified name, type and the * indication if it is unshared. - * + * * @param name * the name of the field. * @param cl @@ -82,7 +108,6 @@ public class ObjectStreamField implements Comparable { * @throws NullPointerException * if {@code name} or {@code cl} is {@code null}. * @see ObjectOutputStream#writeUnshared(Object) - * @since Android 1.0 */ public ObjectStreamField(String name, Class cl, boolean unshared) { if (name == null || cl == null) { @@ -97,7 +122,7 @@ public class ObjectStreamField implements Comparable { /** * Constructs an ObjectStreamField with the given name and the given type. * The type may be null. - * + * * @param signature * A String representing the type of the field * @param name @@ -117,13 +142,12 @@ public class ObjectStreamField implements Comparable { * of the compared fields has a primitive type and the other one not. If so, * the field with the primitive type is considered to be "smaller". If both * fields are equal, their names are compared. - * + * * @param o * the object to compare with. * @return -1 if this field is "smaller" than field {@code o}, 0 if both * fields are equal; 1 if this field is "greater" than field {@code * o}. - * @since Android 1.0 */ public int compareTo(Object o) { ObjectStreamField f = (ObjectStreamField) o; @@ -139,34 +163,14 @@ public class ObjectStreamField implements Comparable { // Either both primitives or both not primitives. Compare based on name. return this.getName().compareTo(f.getName()); } - + // BEGIN android-removed // There shouldn't be an implementation of these methods. - // /** - // * Indicates if this field descriptor is equal to {@code arg0}. Field - // * descriptors are equal if their name is equal. - // * - // * @param arg0 - // * the object to check equality with. - // * @return {@code true} if the name of this field descriptor is equal to the - // * name of {@code arg0}, {@code false} otherwise. - // * @since Android 1.0 - // */ // @Override // public boolean equals(Object arg0) { - // // BEGIN android-changed - // // copied from newer harmony version - // return (arg0 instanceof ObjectStreamField) && compareTo(arg0) == 0; - // // END android-changed + // return (arg0 instanceof ObjectStreamField) && (compareTo(arg0) == 0); // } - // - // /** - // * Returns a hash code for this field descriptor. The hash code of this - // * field's name is returned. - // * - // * @return the field's hash code. - // * @since Android 1.0 - // */ + // // @Override // public int hashCode() { // return getName().hashCode(); @@ -175,9 +179,8 @@ public class ObjectStreamField implements Comparable { /** * Gets the name of this field. - * + * * @return the field's name. - * @since Android 1.0 */ public String getName() { return name; @@ -185,9 +188,8 @@ public class ObjectStreamField implements Comparable { /** * Gets the offset of this field in the object. - * + * * @return this field's offset. - * @since Android 1.0 */ public int getOffset() { return offset; @@ -196,7 +198,7 @@ public class ObjectStreamField implements Comparable { /** * Return the type of the field the receiver represents, this is an internal * method - * + * * @return A Class object representing the type of the field */ // BEGIN android-note @@ -211,9 +213,8 @@ public class ObjectStreamField implements Comparable { /** * Gets the type of this field. - * + * * @return a {@code Class} object representing the type of the field. - * @since Android 1.0 */ public Class getType() { Class cl = getTypeInternal(); @@ -226,7 +227,7 @@ public class ObjectStreamField implements Comparable { /** * Gets a character code for the type of this field. The following codes are * used: - * + * *
                * B     byte
                * C     char
          @@ -239,9 +240,8 @@ public class ObjectStreamField implements Comparable {
                * Z     boolean
                * [     array
                * 
          -     * 
          +     *
                * @return the field's type code.
          -     * @since Android 1.0
                */
               public char getTypeCode() {
                   Class t = getTypeInternal();
          @@ -278,10 +278,9 @@ public class ObjectStreamField implements Comparable {
               /**
                * Gets the type signature used by the VM to represent the type of this
                * field.
          -     * 
          +     *
                * @return the signature of this field's class or {@code null} if this
                *         field's type is primitive.
          -     * @since Android 1.0
                */
               public String getTypeString() {
                   if (isPrimitive()) {
          @@ -298,10 +297,9 @@ public class ObjectStreamField implements Comparable {
           
               /**
                * Indicates whether this field's type is a primitive type.
          -     * 
          +     *
                * @return {@code true} if this field's type is primitive; {@code false} if
                *         the type of this field is a regular class.
          -     * @since Android 1.0
                */
               public boolean isPrimitive() {
                   Class t = getTypeInternal();
          @@ -310,10 +308,9 @@ public class ObjectStreamField implements Comparable {
           
               /**
                * Sets this field's offset in the object.
          -     * 
          +     *
                * @param newValue
                *            the field's new offset.
          -     * @since Android 1.0
                */
               protected void setOffset(int newValue) {
                   this.offset = newValue;
          @@ -322,9 +319,8 @@ public class ObjectStreamField implements Comparable {
               /**
                * Returns a string containing a concise, human-readable description of this
                * field descriptor.
          -     * 
          +     *
                * @return a printable representation of this descriptor.
          -     * @since Android 1.0
                */
               @Override
               public String toString() {
          @@ -335,7 +331,7 @@ public class ObjectStreamField implements Comparable {
               /**
                * Sorts the fields for dumping. Primitive types come first, then regular
                * types.
          -     * 
          +     *
                * @param fields
                *            ObjectStreamField[] fields to be sorted
                */
          @@ -396,9 +392,8 @@ public class ObjectStreamField implements Comparable {
           
               /**
                * Indicats whether this field is unshared.
          -     * 
          +     *
                * @return {@code true} if this field is unshared, {@code false} otherwise.
          -     * @since Android 1.0
                */
               public boolean isUnshared() {
                   return unshared;
          diff --git a/libcore/luni/src/main/java/java/io/OptionalDataException.java b/libcore/luni/src/main/java/java/io/OptionalDataException.java
          index 891bfdd04..39d3d5dbb 100644
          --- a/libcore/luni/src/main/java/java/io/OptionalDataException.java
          +++ b/libcore/luni/src/main/java/java/io/OptionalDataException.java
          @@ -25,8 +25,6 @@ package java.io;
            * @see ObjectInputStream#available()
            * @see ObjectInputStream#readObject()
            * @see ObjectInputStream#skipBytes(int)
          - * 
          - * @since Android 1.0
            */
           public class OptionalDataException extends ObjectStreamException {
           
          @@ -34,24 +32,18 @@ public class OptionalDataException extends ObjectStreamException {
           
               /**
                * {@code true} indicates that there is no more primitive data available.
          -     * 
          -     * @since Android 1.0
                */
               public boolean eof;
           
               /**
                * The number of bytes of primitive data (int, char, long etc.) that are
                * available.
          -     * 
          -     * @since Android 1.0
                */
               public int length;
           
               /**
                * Constructs a new {@code OptionalDataException} with its stack trace
                * filled in.
          -     * 
          -     * @since Android 1.0
                */
               OptionalDataException() {
                   super();
          @@ -63,7 +55,6 @@ public class OptionalDataException extends ObjectStreamException {
                * 
                * @param detailMessage
                *            the detail message for this exception.
          -     * @since Android 1.0
                */
               OptionalDataException(String detailMessage) {
                   super(detailMessage);
          diff --git a/libcore/luni/src/main/java/java/io/OutputStream.java b/libcore/luni/src/main/java/java/io/OutputStream.java
          index 5531ab524..2172411e7 100644
          --- a/libcore/luni/src/main/java/java/io/OutputStream.java
          +++ b/libcore/luni/src/main/java/java/io/OutputStream.java
          @@ -32,17 +32,13 @@ import org.apache.harmony.luni.util.Msg;
            * 

          * Many specialized output streams for purposes like writing to a file already * exist in this package. - * + * * @see InputStream - * - * @since Android 1.0 */ public abstract class OutputStream implements Closeable, Flushable { /** * Default constructor. - * - * @since Android 1.0 */ public OutputStream() { super(); @@ -51,10 +47,9 @@ public abstract class OutputStream implements Closeable, Flushable { /** * Closes this stream. Implementations of this method should free any * resources used by the stream. This implementation does nothing. - * + * * @throws IOException * if an error occurs while closing this stream. - * @since Android 1.0 */ public void close() throws IOException { /* empty */ @@ -63,10 +58,9 @@ public abstract class OutputStream implements Closeable, Flushable { /** * Flushes this stream. Implementations of this method should ensure that * any buffered data is written out. This implementation does nothing. - * + * * @throws IOException * if an error occurs while flushing this stream. - * @since Android 1.0 */ public void flush() throws IOException { /* empty */ @@ -75,12 +69,11 @@ public abstract class OutputStream implements Closeable, Flushable { /** * Writes the entire contents of the byte array {@code buffer} to this * stream. - * + * * @param buffer * the buffer to be written. * @throws IOException * if an error occurs while writing to this stream. - * @since Android 1.0 */ public void write(byte[] buffer) throws IOException { // BEGIN android-note @@ -92,7 +85,7 @@ public abstract class OutputStream implements Closeable, Flushable { /** * Writes {@code count} bytes from the byte array {@code buffer} starting at * position {@code offset} to this stream. - * + * * @param buffer * the buffer to be written. * @param offset @@ -106,7 +99,6 @@ public abstract class OutputStream implements Closeable, Flushable { * if {@code offset < 0} or {@code count < 0}, or if * {@code offset + count} is bigger than the length of * {@code buffer}. - * @since Android 1.0 */ public void write(byte[] buffer, int offset, int count) throws IOException { // BEGIN android-note @@ -134,12 +126,11 @@ public abstract class OutputStream implements Closeable, Flushable { /** * Writes a single byte to this stream. Only the least significant byte of * the integer {@code oneByte} is written to the stream. - * + * * @param oneByte * the byte to be written. * @throws IOException * if an error occurs while writing to this stream. - * @since Android 1.0 */ public abstract void write(int oneByte) throws IOException; } diff --git a/libcore/luni/src/main/java/java/io/OutputStreamWriter.java b/libcore/luni/src/main/java/java/io/OutputStreamWriter.java index d6cc91eb3..6e6cc5033 100644 --- a/libcore/luni/src/main/java/java/io/OutputStreamWriter.java +++ b/libcore/luni/src/main/java/java/io/OutputStreamWriter.java @@ -25,6 +25,7 @@ import java.nio.charset.CoderResult; import java.nio.charset.CodingErrorAction; import java.security.AccessController; +import org.apache.harmony.luni.util.HistoricalNamesUtil; import org.apache.harmony.luni.util.Msg; import org.apache.harmony.luni.util.PriviAction; @@ -35,10 +36,8 @@ import org.apache.harmony.luni.util.PriviAction; * "file.encoding" system property. {@code OutputStreamWriter} contains a buffer * of bytes to be written to target stream and converts these into characters as * needed. The buffer size is 8K. - * + * * @see InputStreamReader - * - * @since Android 1.0 */ public class OutputStreamWriter extends Writer { @@ -52,10 +51,9 @@ public class OutputStreamWriter extends Writer { * Constructs a new OutputStreamWriter using {@code out} as the target * stream to write converted characters to. The default character encoding * is used. - * + * * @param out * the non-null target stream to write converted bytes to. - * @since Android 1.0 */ public OutputStreamWriter(OutputStream out) { super(out); @@ -73,7 +71,7 @@ public class OutputStreamWriter extends Writer { * stream to write converted characters to and {@code enc} as the character * encoding. If the encoding cannot be found, an * UnsupportedEncodingException error is thrown. - * + * * @param out * the target stream to write converted bytes to. * @param enc @@ -82,7 +80,6 @@ public class OutputStreamWriter extends Writer { * if {@code enc} is {@code null}. * @throws UnsupportedEncodingException * if the encoding specified by {@code enc} cannot be found. - * @since Android 1.0 */ public OutputStreamWriter(OutputStream out, final String enc) throws UnsupportedEncodingException { @@ -104,12 +101,11 @@ public class OutputStreamWriter extends Writer { * Constructs a new OutputStreamWriter using {@code out} as the target * stream to write converted characters to and {@code cs} as the character * encoding. - * + * * @param out * the target stream to write converted bytes to. * @param cs * the {@code Charset} that specifies the character encoding. - * @since Android 1.0 */ public OutputStreamWriter(OutputStream out, Charset cs) { super(out); @@ -123,12 +119,11 @@ public class OutputStreamWriter extends Writer { * Constructs a new OutputStreamWriter using {@code out} as the target * stream to write converted characters to and {@code enc} as the character * encoder. - * + * * @param out * the target stream to write converted bytes to. * @param enc * the character encoder used for character conversion. - * @since Android 1.0 */ public OutputStreamWriter(OutputStream out, CharsetEncoder enc) { super(out); @@ -144,11 +139,9 @@ public class OutputStreamWriter extends Writer { *

          * Only the first invocation of this method has any effect. Subsequent calls * do nothing. - *

          - * + * * @throws IOException * if an error occurs while closing this writer. - * @since Android 1.0 */ @Override public void close() throws IOException { @@ -168,10 +161,9 @@ public class OutputStreamWriter extends Writer { * Flushes this writer. This implementation ensures that all buffered bytes * are written to the target stream. After writing the bytes, the target * stream is flushed as well. - * + * * @throws IOException * if an error occurs while flushing this writer. - * @since Android 1.0 */ @Override public void flush() throws IOException { @@ -197,17 +189,15 @@ public class OutputStreamWriter extends Writer { /** * Gets the name of the encoding that is used to convert characters to * bytes. - * + * * @return the string describing the converter or {@code null} if this * writer is closed. - * @since Android 1.0 */ public String getEncoding() { if (encoder == null) { return null; } - return InputStreamReader.HistoricalNamesUtil.getHistoricalName(encoder - .charset().name()); + return HistoricalNamesUtil.getHistoricalName(encoder.charset().name()); } /** @@ -215,7 +205,7 @@ public class OutputStreamWriter extends Writer { * to this writer. The characters are immediately converted to bytes by the * character converter and stored in a local buffer. If the buffer gets full * as a result of the conversion, this writer is flushed. - * + * * @param buf * the array containing characters to write. * @param offset @@ -229,7 +219,6 @@ public class OutputStreamWriter extends Writer { * @throws IOException * if this writer has already been closed or another I/O error * occurs. - * @since Android 1.0 */ @Override public void write(char[] buf, int offset, int count) throws IOException { @@ -273,12 +262,11 @@ public class OutputStreamWriter extends Writer { * of the integer {@code oneChar} are immediately converted to bytes by the * character converter and stored in a local buffer. If the buffer gets full * by converting this character, this writer is flushed. - * + * * @param oneChar * the character to write. * @throws IOException * if this writer is closed or another I/O error occurs. - * @since Android 1.0 */ @Override public void write(int oneChar) throws IOException { @@ -294,7 +282,7 @@ public class OutputStreamWriter extends Writer { * to this writer. The characters are immediately converted to bytes by the * character converter and stored in a local buffer. If the buffer gets full * as a result of the conversion, this writer is flushed. - * + * * @param str * the string containing characters to write. * @param offset @@ -308,7 +296,6 @@ public class OutputStreamWriter extends Writer { * if {@code offset < 0} or {@code count < 0}, or if * {@code offset + count} is bigger than the length of * {@code str}. - * @since Android 1.0 */ @Override public void write(String str, int offset, int count) throws IOException { diff --git a/libcore/luni/src/main/java/java/io/PipedInputStream.java b/libcore/luni/src/main/java/java/io/PipedInputStream.java index 5562c5834..a6b033667 100644 --- a/libcore/luni/src/main/java/java/io/PipedInputStream.java +++ b/libcore/luni/src/main/java/java/io/PipedInputStream.java @@ -23,10 +23,8 @@ import org.apache.harmony.luni.util.Msg; * Receives information from a communications pipe. When two threads want to * pass data back and forth, one creates a piped output stream and the other one * creates a piped input stream. - * + * * @see PipedOutputStream - * - * @since Android 1.0 */ public class PipedInputStream extends InputStream { @@ -35,30 +33,37 @@ public class PipedInputStream extends InputStream { private boolean isClosed = false; /** - * The circular buffer through which data is passed. - * - * @since Android 1.0 + * The circular buffer through which data is passed. Data is read from the + * range {@code [out, in)} and written to the range {@code [in, out)}. + * Data in the buffer is either sequential:
          +     *     { - - - X X X X X X X - - - - - }
          +     *             ^             ^
          +     *             |             |
          +     *            out           in
          + * ...or wrapped around the buffer's end:
          +     *     { X X X X - - - - - - - - X X X }
          +     *               ^               ^
          +     *               |               |
          +     *              in              out
          + * When the buffer is empty, {@code in == -1}. Reading when the buffer is + * empty will block until data is available. When the buffer is full, + * {@code in == out}. Writing when the buffer is full will block until free + * space is available. */ protected byte buffer[]; /** * The index in {@code buffer} where the next byte will be written. - * - * @since Android 1.0 */ protected int in = -1; /** * The index in {@code buffer} where the next byte will be read. - * - * @since Android 1.0 */ protected int out = 0; /** * The size of the default pipe in bytes. - * - * @since Android 1.0 */ protected static final int PIPE_SIZE = 1024; @@ -71,8 +76,6 @@ public class PipedInputStream extends InputStream { * Constructs a new unconnected {@code PipedInputStream}. The resulting * stream must be connected to a {@link PipedOutputStream} before data may * be read from it. - * - * @since Android 1.0 */ public PipedInputStream() { /* empty */ @@ -82,12 +85,11 @@ public class PipedInputStream extends InputStream { * Constructs a new {@code PipedInputStream} connected to the * {@link PipedOutputStream} {@code out}. Any data written to the output * stream can be read from the this input stream. - * + * * @param out * the piped output stream to connect to. * @throws IOException * if this stream or {@code out} are already connected. - * @since Android 1.0 */ public PipedInputStream(PipedOutputStream out) throws IOException { connect(out); @@ -97,11 +99,10 @@ public class PipedInputStream extends InputStream { * Returns the number of bytes that are available before this stream will * block. This implementation returns the number of bytes written to this * pipe that have not been read yet. - * + * * @return the number of bytes available before blocking. * @throws IOException * if an error occurs in this stream. - * @since Android 1.0 */ @Override public synchronized int available() throws IOException { @@ -114,10 +115,9 @@ public class PipedInputStream extends InputStream { /** * Closes this stream. This implementation releases the buffer used for the * pipe and notifies all threads waiting to read or write. - * + * * @throws IOException * if an error occurs while closing this stream. - * @since Android 1.0 */ @Override public void close() throws IOException { @@ -134,12 +134,11 @@ public class PipedInputStream extends InputStream { * Connects this {@code PipedInputStream} to a {@link PipedOutputStream}. * Any data written to the output stream becomes readable in this input * stream. - * + * * @param src * the source output stream. * @throws IOException * if either stream is already connected. - * @since Android 1.0 */ public void connect(PipedOutputStream src) throws IOException { src.connect(this); @@ -155,24 +154,33 @@ public class PipedInputStream extends InputStream { * Separate threads should be used to read from a {@code PipedInputStream} * and to write to the connected {@link PipedOutputStream}. If the same * thread is used, a deadlock may occur. - *

          - * + * * @return the byte read or -1 if the end of the source stream has been * reached. * @throws IOException * if this stream is closed or not connected to an output * stream, or if the thread writing to the connected output * stream is no longer alive. - * @since Android 1.0 */ @Override public synchronized int read() throws IOException { if (!isConnected) { + // K0074=Not connected throw new IOException(Msg.getString("K0074")); //$NON-NLS-1$ } if (buffer == null) { + // K0075=InputStream is closed throw new IOException(Msg.getString("K0075")); //$NON-NLS-1$ } + + // BEGIN android-removed + // eagerly throwing prevents checking isClosed and returning normally + // if (lastWriter != null && !lastWriter.isAlive() && (in < 0)) { + // // KA030=Write end dead + // throw new IOException(Msg.getString("KA030")); //$NON-NLS-1$ + // } + // END android-removed + /** * Set the last thread to be reading on this PipedInputStream. If * lastReader dies while someone is waiting to write an IOException of @@ -180,16 +188,16 @@ public class PipedInputStream extends InputStream { */ lastReader = Thread.currentThread(); try { - boolean first = true; + int attempts = 3; while (in == -1) { // Are we at end of stream? if (isClosed) { return -1; } - if (!first && lastWriter != null && !lastWriter.isAlive()) { + if ((attempts-- <= 0) && lastWriter != null && !lastWriter.isAlive()) { + // K0076=Pipe broken throw new IOException(Msg.getString("K0076")); //$NON-NLS-1$ } - first = false; // Notify callers of receive() notifyAll(); wait(1000); @@ -198,7 +206,8 @@ public class PipedInputStream extends InputStream { throw new InterruptedIOException(); } - byte result = buffer[out++]; + // BEGIN android-changed + int result = buffer[out++] & 0xff; if (out == buffer.length) { out = 0; } @@ -207,7 +216,12 @@ public class PipedInputStream extends InputStream { in = -1; out = 0; } - return result & 0xff; + + // let blocked writers write to the newly available buffer space + notifyAll(); + + return result; + // END android-changed } /** @@ -219,8 +233,7 @@ public class PipedInputStream extends InputStream { * Separate threads should be used to read from a {@code PipedInputStream} * and to write to the connected {@link PipedOutputStream}. If the same * thread is used, a deadlock may occur. - *

          - * + * * @param bytes * the array in which to store the bytes read. * @param offset @@ -264,13 +277,23 @@ public class PipedInputStream extends InputStream { } if (!isConnected) { + // K0074=Not connected throw new IOException(Msg.getString("K0074")); //$NON-NLS-1$ } if (buffer == null) { + // K0075=InputStream is closed throw new IOException(Msg.getString("K0075")); //$NON-NLS-1$ } + // BEGIN android-removed + // eagerly throwing prevents checking isClosed and returning normally + // if (lastWriter != null && !lastWriter.isAlive() && (in < 0)) { + // // KA030=Write end dead + // throw new IOException(Msg.getString("KA030")); //$NON-NLS-1$ + // } + // END android-removed + /** * Set the last thread to be reading on this PipedInputStream. If * lastReader dies while someone is waiting to write an IOException of @@ -278,16 +301,16 @@ public class PipedInputStream extends InputStream { */ lastReader = Thread.currentThread(); try { - boolean first = true; + int attempts = 3; while (in == -1) { // Are we at end of stream? if (isClosed) { return -1; } - if (!first && lastWriter != null && !lastWriter.isAlive()) { + if ((attempts-- <= 0) && lastWriter != null && !lastWriter.isAlive()) { + // K0076=Pipe broken throw new IOException(Msg.getString("K0076")); //$NON-NLS-1$ } - first = false; // Notify callers of receive() notifyAll(); wait(1000); @@ -296,13 +319,15 @@ public class PipedInputStream extends InputStream { throw new InterruptedIOException(); } - int copyLength = 0; - /* Copy bytes from out to end of buffer first */ + // BEGIN android-changed + int totalCopied = 0; + + // copy bytes from out thru the end of buffer if (out >= in) { - copyLength = count > (buffer.length - out) ? buffer.length - out - : count; - System.arraycopy(buffer, out, bytes, offset, copyLength); - out += copyLength; + int leftInBuffer = buffer.length - out; + int length = leftInBuffer < count ? leftInBuffer : count; + System.arraycopy(buffer, out, bytes, offset, length); + out += length; if (out == buffer.length) { out = 0; } @@ -311,28 +336,29 @@ public class PipedInputStream extends InputStream { in = -1; out = 0; } + totalCopied += length; } - /* - * Did the read fully succeed in the previous copy or is the buffer - * empty? - */ - if (copyLength == count || in == -1) { - return copyLength; + // copy bytes from out thru in + if (totalCopied < count && in != -1) { + int leftInBuffer = in - out; + int leftToCopy = count - totalCopied; + int length = leftToCopy < leftInBuffer ? leftToCopy : leftInBuffer; + System.arraycopy(buffer, out, bytes, offset + totalCopied, length); + out += length; + if (out == in) { + // empty buffer + in = -1; + out = 0; + } + totalCopied += length; } - int bytesCopied = copyLength; - /* Copy bytes from 0 to the number of available bytes */ - copyLength = in - out > (count - bytesCopied) ? count - bytesCopied - : in - out; - System.arraycopy(buffer, out, bytes, offset + bytesCopied, copyLength); - out += copyLength; - if (out == in) { - // empty buffer - in = -1; - out = 0; - } - return bytesCopied + copyLength; + // let blocked writers write to the newly available buffer space + notifyAll(); + + return totalCopied; + // END android-changed } /** @@ -342,8 +368,7 @@ public class PipedInputStream extends InputStream { * {@code in} in the {@code buffer}. *

          * This method blocks as long as {@code buffer} is full. - *

          - * + * * @param oneByte * the byte to store in this pipe. * @throws InterruptedIOException @@ -357,9 +382,12 @@ public class PipedInputStream extends InputStream { if (buffer == null || isClosed) { throw new IOException(Msg.getString("K0078")); //$NON-NLS-1$ } - if (lastReader != null && !lastReader.isAlive()) { - throw new IOException(Msg.getString("K0076")); //$NON-NLS-1$ - } + // BEGIN android-removed + // eagerly throwing causes us to fail even if the buffer's not full + // if (lastReader != null && !lastReader.isAlive()) { + // throw new IOException(Msg.getString("K0076")); //$NON-NLS-1$ + // } + // END android-removed /** * Set the last thread to be writing on this PipedInputStream. If * lastWriter dies while someone is waiting to read an IOException of @@ -368,11 +396,14 @@ public class PipedInputStream extends InputStream { lastWriter = Thread.currentThread(); try { while (buffer != null && out == in) { - notifyAll(); - wait(1000); + // BEGIN android-changed + // moved has-last-reader-died check to be before wait() if (lastReader != null && !lastReader.isAlive()) { throw new IOException(Msg.getString("K0076")); //$NON-NLS-1$ } + notifyAll(); + wait(1000); + // END android-changed } } catch (InterruptedException e) { throw new InterruptedIOException(); @@ -385,7 +416,11 @@ public class PipedInputStream extends InputStream { if (in == buffer.length) { in = 0; } - return; + + // BEGIN android-added + // let blocked readers read the newly available data + notifyAll(); + // END android-added } } diff --git a/libcore/luni/src/main/java/java/io/PipedOutputStream.java b/libcore/luni/src/main/java/java/io/PipedOutputStream.java index 3041f70bc..15ee9309e 100644 --- a/libcore/luni/src/main/java/java/io/PipedOutputStream.java +++ b/libcore/luni/src/main/java/java/io/PipedOutputStream.java @@ -23,10 +23,8 @@ import org.apache.harmony.luni.util.Msg; * Places information on a communications pipe. When two threads want to pass * data back and forth, one creates a piped output stream and the other one * creates a piped input stream. - * + * * @see PipedInputStream - * - * @since Android 1.0 */ public class PipedOutputStream extends OutputStream { @@ -39,8 +37,6 @@ public class PipedOutputStream extends OutputStream { * Constructs a new unconnected {@code PipedOutputStream}. The resulting * stream must be connected to a {@link PipedInputStream} before data can be * written to it. - * - * @since Android 1.0 */ public PipedOutputStream() { super(); @@ -50,12 +46,11 @@ public class PipedOutputStream extends OutputStream { * Constructs a new {@code PipedOutputStream} connected to the * {@link PipedInputStream} {@code dest}. Any data written to this stream * can be read from the target stream. - * + * * @param dest * the piped input stream to connect to. * @throws IOException * if this stream or {@code dest} are already connected. - * @since Android 1.0 */ public PipedOutputStream(PipedInputStream dest) throws IOException { super(); @@ -65,10 +60,9 @@ public class PipedOutputStream extends OutputStream { /** * Closes this stream. If this stream is connected to an input stream, the * input stream is closed and the pipe is disconnected. - * + * * @throws IOException * if an error occurs while closing this stream. - * @since Android 1.0 */ @Override public void close() throws IOException { @@ -82,12 +76,11 @@ public class PipedOutputStream extends OutputStream { /** * Connects this stream to a {@link PipedInputStream}. Any data written to * this output stream becomes readable in the input stream. - * + * * @param stream * the destination input stream. * @throws IOException * if either stream is already connected. - * @since Android 1.0 */ public void connect(PipedInputStream stream) throws IOException { if (null == stream) { @@ -109,10 +102,9 @@ public class PipedOutputStream extends OutputStream { /** * Notifies the readers of this {@link PipedInputStream} that bytes can be * read. This method does nothing if this stream is not connected. - * + * * @throws IOException * if an I/O error occurs while flushing this stream. - * @since Android 1.0 */ @Override public void flush() throws IOException { @@ -131,8 +123,7 @@ public class PipedOutputStream extends OutputStream { * Separate threads should be used to write to a {@code PipedOutputStream} * and to read from the connected {@link PipedInputStream}. If the same * thread is used, a deadlock may occur. - *

          - * + * * @param buffer * the buffer to write. * @param offset @@ -151,7 +142,6 @@ public class PipedOutputStream extends OutputStream { * if this stream is not connected, if the target stream is * closed or if the thread reading from the target stream is no * longer alive. This case is currently not handled correctly. - * @since Android 1.0 */ @Override public void write(byte[] buffer, int offset, int count) throws IOException { @@ -173,8 +163,7 @@ public class PipedOutputStream extends OutputStream { * Separate threads should be used to write to a {@code PipedOutputStream} * and to read from the connected {@link PipedInputStream}. If the same * thread is used, a deadlock may occur. - *

          - * + * * @param oneByte * the byte to write. * @throws InterruptedIOException @@ -185,7 +174,6 @@ public class PipedOutputStream extends OutputStream { * if this stream is not connected, if the target stream is * closed or if the thread reading from the target stream is no * longer alive. This case is currently not handled correctly. - * @since Android 1.0 */ @Override public void write(int oneByte) throws IOException { diff --git a/libcore/luni/src/main/java/java/io/PipedReader.java b/libcore/luni/src/main/java/java/io/PipedReader.java index a0af9a50f..251ac9027 100644 --- a/libcore/luni/src/main/java/java/io/PipedReader.java +++ b/libcore/luni/src/main/java/java/io/PipedReader.java @@ -23,10 +23,8 @@ import org.apache.harmony.luni.util.Msg; * Receives information on a communications pipe. When two threads want to pass * data back and forth, one creates a piped writer and the other creates a piped * reader. - * + * * @see PipedWriter - * - * @since Android 1.0 */ public class PipedReader extends Reader { @@ -66,9 +64,8 @@ public class PipedReader extends Reader { * Constructs a new unconnected {@code PipedReader}. The resulting reader * must be connected to a {@code PipedWriter} before data may be read from * it. - * + * * @see PipedWriter - * @since Android 1.0 */ public PipedReader() { data = new char[PIPE_SIZE]; @@ -78,12 +75,11 @@ public class PipedReader extends Reader { * Constructs a new {@code PipedReader} connected to the {@link PipedWriter} * {@code out}. Any data written to the writer can be read from the this * reader. - * + * * @param out * the {@code PipedWriter} to connect to. * @throws IOException * if {@code out} is already connected. - * @since Android 1.0 */ public PipedReader(PipedWriter out) throws IOException { this(); @@ -93,10 +89,9 @@ public class PipedReader extends Reader { /** * Closes this reader. This implementation releases the buffer used for * the pipe and notifies all threads waiting to read or write. - * + * * @throws IOException * if an error occurs while closing this reader. - * @since Android 1.0 */ @Override public void close() throws IOException { @@ -112,13 +107,12 @@ public class PipedReader extends Reader { /** * Connects this {@code PipedReader} to a {@link PipedWriter}. Any data * written to the writer becomes readable in this reader. - * + * * @param src * the writer to connect to. * @throws IOException * if this reader is closed or already connected, or if {@code * src} is already connected. - * @since Android 1.0 */ public void connect(PipedWriter src) throws IOException { synchronized (lock) { @@ -128,7 +122,7 @@ public class PipedReader extends Reader { /** * Establishes the connection to the PipedWriter. - * + * * @throws IOException * If this Reader is already connected. */ @@ -154,13 +148,11 @@ public class PipedReader extends Reader { * Separate threads should be used to read from a {@code PipedReader} and to * write to the connected {@link PipedWriter}. If the same thread is used, * a deadlock may occur. - *

          - * + * * @return the character read or -1 if the end of the reader has been * reached. * @throws IOException * if this reader is closed or some other I/O error occurs. - * @since Android 1.0 */ @Override public int read() throws IOException { @@ -179,8 +171,7 @@ public class PipedReader extends Reader { * Separate threads should be used to read from a {@code PipedReader} and to * write to the connected {@link PipedWriter}. If the same thread is used, a * deadlock may occur. - *

          - * + * * @param buffer * the character array in which to store the characters read. * @param offset @@ -298,14 +289,13 @@ public class PipedReader extends Reader { * called, {@code false} if unknown or blocking will occur. This * implementation returns {@code true} if the internal buffer contains * characters that can be read. - * + * * @return always {@code false}. * @throws IOException * if this reader is closed or not connected, or if some other * I/O error occurs. * @see #read() * @see #read(char[], int, int) - * @since Android 1.0 */ @Override public boolean ready() throws IOException { @@ -326,10 +316,10 @@ public class PipedReader extends Reader { *

          * If the buffer is full and the thread sending #receive is interrupted, the * InterruptedIOException will be thrown. - * + * * @param oneChar * the char to store into the pipe. - * + * * @throws IOException * If the stream is already closed or another IOException * occurs. @@ -380,14 +370,14 @@ public class PipedReader extends Reader { *

          * If the buffer is full and the thread sending #receive is interrupted, the * InterruptedIOException will be thrown. - * + * * @param chars * the char array to store into the pipe. * @param offset * offset to start reading from * @param count * total characters to read - * + * * @throws IOException * If the stream is already closed or another IOException * occurs. diff --git a/libcore/luni/src/main/java/java/io/PipedWriter.java b/libcore/luni/src/main/java/java/io/PipedWriter.java index 98a73c93a..5fc968ddc 100644 --- a/libcore/luni/src/main/java/java/io/PipedWriter.java +++ b/libcore/luni/src/main/java/java/io/PipedWriter.java @@ -23,10 +23,8 @@ import org.apache.harmony.luni.util.Msg; * Places information on a communications pipe. When two threads want to pass * data back and forth, one creates a piped writer and the other creates a piped * reader. - * + * * @see PipedReader - * - * @since Android 1.0 */ public class PipedWriter extends Writer { /** @@ -40,9 +38,8 @@ public class PipedWriter extends Writer { * Constructs a new unconnected {@code PipedWriter}. The resulting writer * must be connected to a {@code PipedReader} before data may be written to * it. - * + * * @see PipedReader - * @since Android 1.0 */ public PipedWriter() { super(); @@ -52,12 +49,11 @@ public class PipedWriter extends Writer { * Constructs a new {@code PipedWriter} connected to the {@link PipedReader} * {@code dest}. Any data written to this writer can be read from {@code * dest}. - * + * * @param dest * the {@code PipedReader} to connect to. * @throws IOException * if {@code dest} is already connected. - * @since Android 1.0 */ public PipedWriter(PipedReader dest) throws IOException { super(dest); @@ -68,10 +64,9 @@ public class PipedWriter extends Writer { * Closes this writer. If a {@link PipedReader} is connected to this writer, * it is closed as well and the pipe is disconnected. Any data buffered in * the reader can still be read. - * + * * @throws IOException * if an error occurs while closing this writer. - * @since Android 1.0 */ @Override public void close() throws IOException { @@ -88,13 +83,12 @@ public class PipedWriter extends Writer { /** * Connects this {@code PipedWriter} to a {@link PipedReader}. Any data * written to this writer becomes readable in the reader. - * + * * @param stream * the reader to connect to. * @throws IOException * if this writer is closed or already connected, or if {@code * stream} is already connected. - * @since Android 1.0 */ public void connect(PipedReader stream) throws IOException { synchronized (lock) { @@ -112,10 +106,9 @@ public class PipedWriter extends Writer { /** * Notifies the readers of this {@code PipedReader} that characters can be read. This * method does nothing if this Writer is not connected. - * + * * @throws IOException * if an I/O error occurs while flushing this writer. - * @since Android 1.0 */ @Override public void flush() throws IOException { @@ -132,8 +125,7 @@ public class PipedWriter extends Writer { * Separate threads should be used to write to a {@code PipedWriter} and to * read from the connected {@code PipedReader}. If the same thread is used, * a deadlock may occur. - *

          - * + * * @param buffer * the buffer to write. * @param offset @@ -155,7 +147,6 @@ public class PipedWriter extends Writer { * correctly. * @throws NullPointerException * if {@code buffer} is {@code null}. - * @since Android 1.0 */ @Override public void write(char[] buffer, int offset, int count) throws IOException { @@ -194,8 +185,7 @@ public class PipedWriter extends Writer { * Separate threads should be used to write to a {@code PipedWriter} and to * read from the connected {@code PipedReader}. If the same thread is used, * a deadlock may occur. - *

          - * + * * @param c * the character to write. * @throws InterruptedIOException @@ -207,7 +197,6 @@ public class PipedWriter extends Writer { * reader is closed or if the thread reading from the target * reader is no longer alive. This case is currently not handled * correctly. - * @since Android 1.0 */ @Override public void write(int c) throws IOException { diff --git a/libcore/luni/src/main/java/java/io/PrintStream.java b/libcore/luni/src/main/java/java/io/PrintStream.java index 0aae645c3..29d460eb8 100644 --- a/libcore/luni/src/main/java/java/io/PrintStream.java +++ b/libcore/luni/src/main/java/java/io/PrintStream.java @@ -18,6 +18,7 @@ package java.io; import java.nio.charset.Charset; +import java.nio.charset.IllegalCharsetNameException; import java.security.AccessController; import java.util.Formatter; import java.util.IllegalFormatException; @@ -33,8 +34,6 @@ import org.apache.harmony.luni.util.PriviAction; * so that they can be read back in. No {@code IOException} is thrown by this * class. Instead, callers should use {@link #checkError()} to see if a problem * has occurred in this stream. - * - * @since Android 1.0 */ public class PrintStream extends FilterOutputStream implements Appendable, Closeable { @@ -63,12 +62,11 @@ public class PrintStream extends FilterOutputStream implements Appendable, * Constructs a new {@code PrintStream} with {@code out} as its target * stream. By default, the new print stream does not automatically flush its * contents to the target stream when a newline is encountered. - * + * * @param out * the target output stream. * @throws NullPointerException * if {@code out} is {@code null}. - * @since Android 1.0 */ public PrintStream(OutputStream out) { super(out); @@ -82,7 +80,7 @@ public class PrintStream extends FilterOutputStream implements Appendable, * stream. The parameter {@code autoflush} determines if the print stream * automatically flushes its contents to the target stream when a newline is * encountered. - * + * * @param out * the target output stream. * @param autoflush @@ -90,7 +88,6 @@ public class PrintStream extends FilterOutputStream implements Appendable, * newline sequence. * @throws NullPointerException * if {@code out} is {@code null}. - * @since Android 1.0 */ public PrintStream(OutputStream out, boolean autoflush) { super(out); @@ -105,7 +102,7 @@ public class PrintStream extends FilterOutputStream implements Appendable, * stream and using the character encoding {@code enc} while writing. The * parameter {@code autoflush} determines if the print stream automatically * flushes its contents to the target stream when a newline is encountered. - * + * * @param out * the target output stream. * @param autoflush @@ -117,7 +114,6 @@ public class PrintStream extends FilterOutputStream implements Appendable, * if {@code out} or {@code enc} are {@code null}. * @throws UnsupportedEncodingException * if the encoding specified by {@code enc} is not supported. - * @since Android 1.0 */ public PrintStream(OutputStream out, boolean autoflush, String enc) throws UnsupportedEncodingException { @@ -126,7 +122,11 @@ public class PrintStream extends FilterOutputStream implements Appendable, throw new NullPointerException(); } this.autoflush = autoflush; - if (!Charset.isSupported(enc)) { + try { + if (!Charset.isSupported(enc)) { + throw new UnsupportedEncodingException(enc); + } + } catch (IllegalCharsetNameException e) { throw new UnsupportedEncodingException(enc); } encoding = enc; @@ -135,7 +135,7 @@ public class PrintStream extends FilterOutputStream implements Appendable, /** * Constructs a new {@code PrintStream} with {@code file} as its target. The * virtual machine's default character set is used for character encoding. - * + * * @param file * the target file. If the file already exists, its contents are * removed, otherwise a new file is created. @@ -144,7 +144,6 @@ public class PrintStream extends FilterOutputStream implements Appendable, * @throws SecurityException * if a security manager exists and it denies writing to the * target file. - * @since Android 1.0 */ public PrintStream(File file) throws FileNotFoundException { super(new FileOutputStream(file)); @@ -153,7 +152,7 @@ public class PrintStream extends FilterOutputStream implements Appendable, /** * Constructs a new {@code PrintStream} with {@code file} as its target. The * character set named {@code csn} is used for character encoding. - * + * * @param file * the target file. If the file already exists, its contents are * removed, otherwise a new file is created. @@ -168,7 +167,6 @@ public class PrintStream extends FilterOutputStream implements Appendable, * target file. * @throws UnsupportedEncodingException * if the encoding specified by {@code csn} is not supported. - * @since Android 1.0 */ public PrintStream(File file, String csn) throws FileNotFoundException, UnsupportedEncodingException { @@ -186,7 +184,7 @@ public class PrintStream extends FilterOutputStream implements Appendable, * Constructs a new {@code PrintStream} with the file identified by * {@code fileName} as its target. The virtual machine's default character * set is used for character encoding. - * + * * @param fileName * the target file's name. If the file already exists, its * contents are removed, otherwise a new file is created. @@ -195,7 +193,6 @@ public class PrintStream extends FilterOutputStream implements Appendable, * @throws SecurityException * if a security manager exists and it denies writing to the * target file. - * @since Android 1.0 */ public PrintStream(String fileName) throws FileNotFoundException { this(new File(fileName)); @@ -205,7 +202,7 @@ public class PrintStream extends FilterOutputStream implements Appendable, * Constructs a new {@code PrintStream} with the file identified by * {@code fileName} as its target. The character set named {@code csn} is * used for character encoding. - * + * * @param fileName * the target file's name. If the file already exists, its * contents are removed, otherwise a new file is created. @@ -220,7 +217,6 @@ public class PrintStream extends FilterOutputStream implements Appendable, * target file. * @throws UnsupportedEncodingException * if the encoding specified by {@code csn} is not supported. - * @since Android 1.0 */ public PrintStream(String fileName, String csn) throws FileNotFoundException, UnsupportedEncodingException { @@ -229,12 +225,11 @@ public class PrintStream extends FilterOutputStream implements Appendable, /** * Flushes this stream and returns the value of the error flag. - * + * * @return {@code true} if either an {@code IOException} has been thrown * previously or if {@code setError()} has been called; * {@code false} otherwise. - * @see #setError() - * @since Android 1.0 + * @see #setError() */ public boolean checkError() { if (out != null) { @@ -247,8 +242,6 @@ public class PrintStream extends FilterOutputStream implements Appendable, * Closes this print stream. Flushes this stream and then closes the target * stream. If an I/O error occurs, this stream's error state is set to * {@code true}. - * - * @since Android 1.0 */ @Override public synchronized void close() { @@ -267,8 +260,6 @@ public class PrintStream extends FilterOutputStream implements Appendable, * Ensures that all pending data is sent out to the target stream. It also * flushes the target stream. If an I/O error occurs, this stream's error * state is set to {@code true}. - * - * @since Android 1.0 */ @Override public synchronized void flush() { @@ -288,7 +279,7 @@ public class PrintStream extends FilterOutputStream implements Appendable, * target stream using the specified format string and arguments. For the * locale, the default value of the current virtual machine instance is * used. - * + * * @param format * the format string used for {@link java.util.Formatter#format}. * @param args @@ -302,7 +293,6 @@ public class PrintStream extends FilterOutputStream implements Appendable, * error regarding the format string or arguments is detected. * @throws NullPointerException * if {@code format} is {@code null}. - * @since Android 1.0 */ public PrintStream format(String format, Object... args) { return format(Locale.getDefault(), format, args); @@ -311,7 +301,7 @@ public class PrintStream extends FilterOutputStream implements Appendable, /** * Writes a string formatted by an intermediate {@link Formatter} to this * stream using the specified locale, format string and arguments. - * + * * @param l * the locale used in the method. No localization will be applied * if {@code l} is {@code null}. @@ -328,7 +318,6 @@ public class PrintStream extends FilterOutputStream implements Appendable, * error regarding the format string or arguments is detected. * @throws NullPointerException * if {@code format} is {@code null}. - * @since Android 1.0 */ public PrintStream format(Locale l, String format, Object... args) { if (format == null) { @@ -342,7 +331,7 @@ public class PrintStream extends FilterOutputStream implements Appendable, * Prints a formatted string. The behavior of this method is the same as * this stream's {@code #format(String, Object...)} method. For the locale, * the default value of the current virtual machine instance is used. - * + * * @param format * the format string used for * {@link java.util.Formatter#format}. @@ -357,7 +346,6 @@ public class PrintStream extends FilterOutputStream implements Appendable, * error regarding the format string or arguments is detected. * @throws NullPointerException * if {@code format} is {@code null}. - * @since Android 1.0 */ public PrintStream printf(String format, Object... args) { return format(format, args); @@ -366,7 +354,7 @@ public class PrintStream extends FilterOutputStream implements Appendable, /** * Prints a formatted string. The behavior of this method is the same as * this stream's {@code #format(Locale, String, Object...)} method. - * + * * @param l * the locale used in the method. No localization will be applied * if {@code l} is {@code null}. @@ -383,7 +371,6 @@ public class PrintStream extends FilterOutputStream implements Appendable, * error regarding the format string or arguments is detected. * @throws NullPointerException * if {@code format} is {@code null}. - * @since Android 1.0 */ public PrintStream printf(Locale l, String format, Object... args) { return format(l, format, args); @@ -399,11 +386,10 @@ public class PrintStream extends FilterOutputStream implements Appendable, /** * Prints the string representation of the specified character array * to the target stream. - * + * * @param charArray * the character array to print to the target stream. * @see #print(String) - * @since Android 1.0 */ public void print(char[] charArray) { print(new String(charArray, 0, charArray.length)); @@ -412,11 +398,10 @@ public class PrintStream extends FilterOutputStream implements Appendable, /** * Prints the string representation of the specified character to the target * stream. - * + * * @param ch * the character to print to the target stream. * @see #print(String) - * @since Android 1.0 */ public void print(char ch) { print(String.valueOf(ch)); @@ -425,11 +410,10 @@ public class PrintStream extends FilterOutputStream implements Appendable, /** * Prints the string representation of the specified double to the target * stream. - * + * * @param dnum * the double value to print to the target stream. * @see #print(String) - * @since Android 1.0 */ public void print(double dnum) { print(String.valueOf(dnum)); @@ -438,12 +422,11 @@ public class PrintStream extends FilterOutputStream implements Appendable, /** * Prints the string representation of the specified float to the target * stream. - * + * * @param fnum * the float value to print to the target stream. * @see #print(String) - * @since Android 1.0 - */ + */ public void print(float fnum) { print(String.valueOf(fnum)); } @@ -451,12 +434,11 @@ public class PrintStream extends FilterOutputStream implements Appendable, /** * Prints the string representation of the specified integer to the target * stream. - * + * * @param inum * the integer value to print to the target stream. * @see #print(String) - * @since Android 1.0 - */ + */ public void print(int inum) { print(String.valueOf(inum)); } @@ -464,12 +446,11 @@ public class PrintStream extends FilterOutputStream implements Appendable, /** * Prints the string representation of the specified long to the target * stream. - * + * * @param lnum * the long value to print to the target stream. * @see #print(String) - * @since Android 1.0 - */ + */ public void print(long lnum) { print(String.valueOf(lnum)); } @@ -477,12 +458,11 @@ public class PrintStream extends FilterOutputStream implements Appendable, /** * Prints the string representation of the specified object to the target * stream. - * + * * @param obj * the object to print to the target stream. * @see #print(String) - * @since Android 1.0 - */ + */ public void print(Object obj) { print(String.valueOf(obj)); } @@ -494,12 +474,10 @@ public class PrintStream extends FilterOutputStream implements Appendable, * {@code write(int)}. *

          * If an I/O error occurs, this stream's error state is set to {@code true}. - *

          - * + * * @param str * the string to print to the target stream. * @see #write(int) - * @since Android 1.0 */ public synchronized void print(String str) { if (out == null) { @@ -525,12 +503,11 @@ public class PrintStream extends FilterOutputStream implements Appendable, /** * Prints the string representation of the specified boolean to the target * stream. - * + * * @param bool * the boolean value to print the target stream. * @see #print(String) - * @since Android 1.0 - */ + */ public void print(boolean bool) { print(String.valueOf(bool)); } @@ -538,8 +515,6 @@ public class PrintStream extends FilterOutputStream implements Appendable, /** * Prints the string representation of the system property * {@code "line.separator"} to the target stream. - * - * @since Android 1.0 */ public void println() { newline(); @@ -549,11 +524,10 @@ public class PrintStream extends FilterOutputStream implements Appendable, * Prints the string representation of the specified character array * followed by the system property {@code "line.separator"} to the target * stream. - * + * * @param charArray * the character array to print to the target stream. * @see #print(String) - * @since Android 1.0 */ public void println(char[] charArray) { println(new String(charArray, 0, charArray.length)); @@ -562,11 +536,10 @@ public class PrintStream extends FilterOutputStream implements Appendable, /** * Prints the string representation of the specified character followed by * the system property {@code "line.separator"} to the target stream. - * + * * @param ch * the character to print to the target stream. * @see #print(String) - * @since Android 1.0 */ public void println(char ch) { println(String.valueOf(ch)); @@ -575,11 +548,10 @@ public class PrintStream extends FilterOutputStream implements Appendable, /** * Prints the string representation of the specified double followed by the * system property {@code "line.separator"} to the target stream. - * + * * @param dnum * the double value to print to the target stream. * @see #print(String) - * @since Android 1.0 */ public void println(double dnum) { println(String.valueOf(dnum)); @@ -588,12 +560,11 @@ public class PrintStream extends FilterOutputStream implements Appendable, /** * Prints the string representation of the specified float followed by the * system property {@code "line.separator"} to the target stream. - * + * * @param fnum * the float value to print to the target stream. * @see #print(String) - * @since Android 1.0 - */ + */ public void println(float fnum) { println(String.valueOf(fnum)); } @@ -601,12 +572,12 @@ public class PrintStream extends FilterOutputStream implements Appendable, /** * Prints the string representation of the specified integer followed by the * system property {@code "line.separator"} to the target stream. - * + * * @param inum * the integer value to print to the target stream. * @see #print(String) * @since Android 1.0 - */ + */ public void println(int inum) { println(String.valueOf(inum)); } @@ -614,12 +585,12 @@ public class PrintStream extends FilterOutputStream implements Appendable, /** * Prints the string representation of the specified long followed by the * system property {@code "line.separator"} to the target stream. - * + * * @param lnum * the long value to print to the target stream. * @see #print(String) * @since Android 1.0 - */ + */ public void println(long lnum) { println(String.valueOf(lnum)); } @@ -627,12 +598,12 @@ public class PrintStream extends FilterOutputStream implements Appendable, /** * Prints the string representation of the specified object followed by the * system property {@code "line.separator"} to the target stream. - * + * * @param obj * the object to print to the target stream. * @see #print(String) * @since Android 1.0 - */ + */ public void println(Object obj) { println(String.valueOf(obj)); } @@ -645,7 +616,7 @@ public class PrintStream extends FilterOutputStream implements Appendable, *

          * If an I/O error occurs, this stream's error state is set to {@code true}. *

          - * + * * @param str * the string to print to the target stream. * @see #write(int) @@ -659,20 +630,17 @@ public class PrintStream extends FilterOutputStream implements Appendable, /** * Prints the string representation of the specified boolean followed by the * system property {@code "line.separator"} to the target stream. - * + * * @param bool * the boolean value to print to the target stream. * @see #print(String) - * @since Android 1.0 - */ + */ public void println(boolean bool) { println(String.valueOf(bool)); } /** * Sets the error flag of this print stream to {@code true}. - * - * @since Android 1.0 */ protected void setError() { ioError = true; @@ -685,8 +653,7 @@ public class PrintStream extends FilterOutputStream implements Appendable, *

          * This stream's error flag is set to {@code true} if this stream is closed * or an I/O error occurs. - *

          - * + * * @param buffer * the buffer to be written. * @param offset @@ -697,7 +664,6 @@ public class PrintStream extends FilterOutputStream implements Appendable, * if {@code offset < 0} or {@code count < 0}, or if {@code * offset + count} is bigger than the length of {@code buffer}. * @see #flush() - * @since Android 1.0 */ @Override public void write(byte[] buffer, int offset, int count) { @@ -738,11 +704,9 @@ public class PrintStream extends FilterOutputStream implements Appendable, *

          * This stream's error flag is set to {@code true} if it is closed or an I/O * error occurs. - *

          - * + * * @param oneByte * the byte to be written - * @since Android 1.0 */ @Override public synchronized void write(int oneByte) { @@ -763,11 +727,10 @@ public class PrintStream extends FilterOutputStream implements Appendable, /** * Appends the character {@code c} to the target stream. This method works * the same way as {@link #print(char)}. - * + * * @param c * the character to append to the target stream. * @return this stream. - * @since Android 1.0 */ public PrintStream append(char c) { print(c); @@ -779,11 +742,10 @@ public class PrintStream extends FilterOutputStream implements Appendable, * method works the same way as {@code PrintStream.print(csq.toString())}. * If {@code csq} is {@code null}, then the string "null" is written to the * target stream. - * + * * @param csq * the character sequence appended to the target stream. * @return this stream. - * @since Android 1.0 */ public PrintStream append(CharSequence csq) { if (null == csq) { @@ -800,7 +762,7 @@ public class PrintStream extends FilterOutputStream implements Appendable, * PrintStream.print(csq.subsequence(start, end).toString())}. If {@code * csq} is {@code null}, then the specified subsequence of the string "null" * will be written to the target stream. - * + * * @param csq * the character sequence appended to the target stream. * @param start @@ -814,7 +776,6 @@ public class PrintStream extends FilterOutputStream implements Appendable, * if {@code start > end}, {@code start < 0}, {@code end < 0} or * either {@code start} or {@code end} are greater or equal than * the length of {@code csq}. - * @since Android 1.0 */ public PrintStream append(CharSequence csq, int start, int end) { if (null == csq) { diff --git a/libcore/luni/src/main/java/java/io/PrintWriter.java b/libcore/luni/src/main/java/java/io/PrintWriter.java index 0c5189db1..f87707d46 100644 --- a/libcore/luni/src/main/java/java/io/PrintWriter.java +++ b/libcore/luni/src/main/java/java/io/PrintWriter.java @@ -25,24 +25,16 @@ import java.util.Locale; import org.apache.harmony.luni.util.Msg; import org.apache.harmony.luni.util.PriviAction; -// BEGIN android-note -// added @return comment to #printf(Locale l, String format, Object... args) -// END android-note - /** * Wraps either an existing {@link OutputStream} or an existing {@link Writer} * and provides convenience methods for printing common data types in a human * readable format. No {@code IOException} is thrown by this class. Instead, * callers should use {@link #checkError()} to see if a problem has occurred in * this writer. - * - * @since Android 1.0 */ public class PrintWriter extends Writer { /** * The writer to print data to. - * - * @since Android 1.0 */ protected Writer out; @@ -64,12 +56,11 @@ public class PrintWriter extends Writer { * Constructs a new {@code PrintWriter} with {@code out} as its target * stream. By default, the new print writer does not automatically flush its * contents to the target stream when a newline is encountered. - * + * * @param out * the target output stream. * @throws NullPointerException * if {@code out} is {@code null}. - * @since Android 1.0 */ public PrintWriter(OutputStream out) { this(new OutputStreamWriter(out), false); @@ -80,7 +71,7 @@ public class PrintWriter extends Writer { * stream. The parameter {@code autoflush} determines if the print writer * automatically flushes its contents to the target stream when a newline is * encountered. - * + * * @param out * the target output stream. * @param autoflush @@ -88,7 +79,6 @@ public class PrintWriter extends Writer { * newline sequence. * @throws NullPointerException * if {@code out} is {@code null}. - * @since Android 1.0 */ public PrintWriter(OutputStream out, boolean autoflush) { this(new OutputStreamWriter(out), autoflush); @@ -98,12 +88,11 @@ public class PrintWriter extends Writer { * Constructs a new {@code PrintWriter} with {@code wr} as its target * writer. By default, the new print writer does not automatically flush its * contents to the target writer when a newline is encountered. - * + * * @param wr * the target writer. * @throws NullPointerException * if {@code wr} is {@code null}. - * @since Android 1.0 */ public PrintWriter(Writer wr) { this(wr, false); @@ -114,7 +103,7 @@ public class PrintWriter extends Writer { * writer. The parameter {@code autoflush} determines if the print writer * automatically flushes its contents to the target writer when a newline is * encountered. - * + * * @param wr * the target writer. * @param autoflush @@ -122,7 +111,6 @@ public class PrintWriter extends Writer { * newline sequence. * @throws NullPointerException * if {@code out} is {@code null}. - * @since Android 1.0 */ public PrintWriter(Writer wr, boolean autoflush) { super(wr); @@ -135,7 +123,7 @@ public class PrintWriter extends Writer { * virtual machine's default character set is used for character encoding. * The print writer does not automatically flush its contents to the target * file when a newline is encountered. The output to the file is buffered. - * + * * @param file * the target file. If the file already exists, its contents are * removed, otherwise a new file is created. @@ -144,7 +132,6 @@ public class PrintWriter extends Writer { * @throws SecurityException * if a security manager exists and it denies writing to the * target file. - * @since Android 1.0 */ public PrintWriter(File file) throws FileNotFoundException { // BEGIN android-modified @@ -160,7 +147,7 @@ public class PrintWriter extends Writer { * character set named {@code csn} is used for character encoding. * The print writer does not automatically flush its contents to the target * file when a newline is encountered. The output to the file is buffered. - * + * * @param file * the target file. If the file already exists, its contents are * removed, otherwise a new file is created. @@ -175,7 +162,6 @@ public class PrintWriter extends Writer { * target file. * @throws UnsupportedEncodingException * if the encoding specified by {@code csn} is not supported. - * @since Android 1.0 */ public PrintWriter(File file, String csn) throws FileNotFoundException, UnsupportedEncodingException { @@ -193,7 +179,7 @@ public class PrintWriter extends Writer { * used for character encoding. The print writer does not automatically * flush its contents to the target file when a newline is encountered. The * output to the file is buffered. - * + * * @param fileName * the target file's name. If the file already exists, its * contents are removed, otherwise a new file is created. @@ -202,7 +188,6 @@ public class PrintWriter extends Writer { * @throws SecurityException * if a security manager exists and it denies writing to the * target file. - * @since Android 1.0 */ public PrintWriter(String fileName) throws FileNotFoundException { // BEGIN android-modified @@ -219,7 +204,7 @@ public class PrintWriter extends Writer { * character encoding. The print writer does not automatically flush its * contents to the target file when a newline is encountered. The output to * the file is buffered. - * + * * @param fileName * the target file's name. If the file already exists, its * contents are removed, otherwise a new file is created. @@ -234,7 +219,6 @@ public class PrintWriter extends Writer { * target file. * @throws UnsupportedEncodingException * if the encoding specified by {@code csn} is not supported. - * @since Android 1.0 */ public PrintWriter(String fileName, String csn) throws FileNotFoundException, UnsupportedEncodingException { @@ -248,12 +232,11 @@ public class PrintWriter extends Writer { /** * Flushes this writer and returns the value of the error flag. - * + * * @return {@code true} if either an {@code IOException} has been thrown * previously or if {@code setError()} has been called; * {@code false} otherwise. - * @see #setError() - * @since Android 1.0 + * @see #setError() */ public boolean checkError() { if (out != null) { @@ -265,8 +248,6 @@ public class PrintWriter extends Writer { /** * Closes this print writer. Flushes this writer and then closes the target. * If an I/O error occurs, this writer's error flag is set to {@code true}. - * - * @since Android 1.0 */ @Override public void close() { @@ -286,8 +267,6 @@ public class PrintWriter extends Writer { * Ensures that all pending data is sent out to the target. It also * flushes the target. If an I/O error occurs, this writer's error * state is set to {@code true}. - * - * @since Android 1.0 */ @Override public void flush() { @@ -309,7 +288,7 @@ public class PrintWriter extends Writer { * target using the specified format string and arguments. For the locale, * the default value of the current virtual machine instance is used. If * automatic flushing is enabled then the buffer is flushed as well. - * + * * @param format * the format string used for {@link java.util.Formatter#format}. * @param args @@ -323,7 +302,6 @@ public class PrintWriter extends Writer { * error regarding the format string or arguments is detected. * @throws NullPointerException * if {@code format} is {@code null}. - * @since Android 1.0 */ public PrintWriter format(String format, Object... args) { return format(Locale.getDefault(), format, args); @@ -333,7 +311,7 @@ public class PrintWriter extends Writer { * Writes a string formatted by an intermediate {@code Formatter} to the * target using the specified locale, format string and arguments. If * automatic flushing is enabled then this writer is flushed. - * + * * @param l * the locale used in the method. No localization will be applied * if {@code l} is {@code null}. @@ -350,7 +328,6 @@ public class PrintWriter extends Writer { * error regarding the format string or arguments is detected. * @throws NullPointerException * if {@code format} is {@code null}. - * @since Android 1.0 */ public PrintWriter format(Locale l, String format, Object... args) { if (format == null) { @@ -367,7 +344,7 @@ public class PrintWriter extends Writer { * Prints a formatted string. The behavior of this method is the same as * this writer's {@code #format(String, Object...)} method. For the locale, * the default value of the current virtual machine instance is used. - * + * * @param format * the format string used for {@link java.util.Formatter#format}. * @param args @@ -381,7 +358,6 @@ public class PrintWriter extends Writer { * error regarding the format string or arguments is detected. * @throws NullPointerException * if {@code format} is {@code null}. - * @since Android 1.0 */ public PrintWriter printf(String format, Object... args) { return format(format, args); @@ -390,7 +366,7 @@ public class PrintWriter extends Writer { /** * Prints a formatted string. The behavior of this method is the same as * this writer's {@code #format(Locale, String, Object...)} method. - * + * * @param l * the locale used in the method. No localization will be applied * if {@code l} is {@code null}. @@ -407,7 +383,6 @@ public class PrintWriter extends Writer { * error regarding the format string or arguments is detected. * @throws NullPointerException * if {@code format} is {@code null}. - * @since Android 1.0 */ public PrintWriter printf(Locale l, String format, Object... args) { return format(l, format, args); @@ -426,11 +401,10 @@ public class PrintWriter extends Writer { /** * Prints the string representation of the specified character array * to the target. - * + * * @param charArray * the character array to print to the target. * @see #print(String) - * @since Android 1.0 */ public void print(char[] charArray) { print(new String(charArray, 0, charArray.length)); @@ -439,11 +413,10 @@ public class PrintWriter extends Writer { /** * Prints the string representation of the specified character to the * target. - * + * * @param ch * the character to print to the target. * @see #print(String) - * @since Android 1.0 */ public void print(char ch) { print(String.valueOf(ch)); @@ -451,23 +424,21 @@ public class PrintWriter extends Writer { /** * Prints the string representation of the specified double to the target. - * + * * @param dnum * the double value to print to the target. * @see #print(String) - * @since Android 1.0 */ public void print(double dnum) { print(String.valueOf(dnum)); } - /** + /** * Prints the string representation of the specified float to the target. - * + * * @param fnum * the float value to print to the target. * @see #print(String) - * @since Android 1.0 */ public void print(float fnum) { print(String.valueOf(fnum)); @@ -475,23 +446,21 @@ public class PrintWriter extends Writer { /** * Prints the string representation of the specified integer to the target. - * + * * @param inum * the integer value to print to the target. * @see #print(String) - * @since Android 1.0 - */ + */ public void print(int inum) { print(String.valueOf(inum)); } /** * Prints the string representation of the specified long to the target. - * + * * @param lnum * the long value to print to the target. * @see #print(String) - * @since Android 1.0 */ public void print(long lnum) { print(String.valueOf(lnum)); @@ -499,12 +468,11 @@ public class PrintWriter extends Writer { /** * Prints the string representation of the specified object to the target. - * + * * @param obj * the object to print to the target. * @see #print(String) - * @since Android 1.0 - */ + */ public void print(Object obj) { print(String.valueOf(obj)); } @@ -515,12 +483,10 @@ public class PrintWriter extends Writer { * The bytes are then written to the target with {@code write(int)}. *

          * If an I/O error occurs, this writer's error flag is set to {@code true}. - *

          - * + * * @param str * the string to print to the target. * @see #write(int) - * @since Android 1.0 */ public void print(String str) { write(str != null ? str : String.valueOf((Object) null)); @@ -528,12 +494,11 @@ public class PrintWriter extends Writer { /** * Prints the string representation of the specified boolean to the target. - * + * * @param bool * the boolean value to print the target. * @see #print(String) - * @since Android 1.0 - */ + */ public void print(boolean bool) { print(String.valueOf(bool)); } @@ -542,8 +507,6 @@ public class PrintWriter extends Writer { * Prints the string representation of the system property {@code * "line.separator"} to the target. Flushes this writer if the autoflush * flag is set to {@code true}. - * - * @since Android 1.0 */ public void println() { synchronized (lock) { @@ -555,11 +518,10 @@ public class PrintWriter extends Writer { * Prints the string representation of the specified character array * followed by the system property {@code "line.separator"} to the target. * Flushes this writer if the autoflush flag is set to {@code true}. - * + * * @param charArray * the character array to print to the target. * @see #print(String) - * @since Android 1.0 */ public void println(char[] charArray) { println(new String(charArray, 0, charArray.length)); @@ -569,11 +531,10 @@ public class PrintWriter extends Writer { * Prints the string representation of the specified character followed by * the system property {@code "line.separator"} to the target. Flushes this * writer if the autoflush flag is set to {@code true}. - * + * * @param ch * the character to print to the target. * @see #print(String) - * @since Android 1.0 */ public void println(char ch) { println(String.valueOf(ch)); @@ -583,11 +544,10 @@ public class PrintWriter extends Writer { * Prints the string representation of the specified double followed by the * system property {@code "line.separator"} to the target. Flushes this * writer if the autoflush flag is set to {@code true}. - * + * * @param dnum * the double value to print to the target. * @see #print(String) - * @since Android 1.0 */ public void println(double dnum) { println(String.valueOf(dnum)); @@ -597,12 +557,11 @@ public class PrintWriter extends Writer { * Prints the string representation of the specified float followed by the * system property {@code "line.separator"} to the target. Flushes this * writer if the autoflush flag is set to {@code true}. - * + * * @param fnum * the float value to print to the target. * @see #print(String) - * @since Android 1.0 - */ + */ public void println(float fnum) { println(String.valueOf(fnum)); } @@ -611,12 +570,11 @@ public class PrintWriter extends Writer { * Prints the string representation of the specified integer followed by the * system property {@code "line.separator"} to the target. Flushes this * writer if the autoflush flag is set to {@code true}. - * + * * @param inum * the integer value to print to the target. * @see #print(String) - * @since Android 1.0 - */ + */ public void println(int inum) { println(String.valueOf(inum)); } @@ -625,12 +583,11 @@ public class PrintWriter extends Writer { * Prints the string representation of the specified long followed by the * system property {@code "line.separator"} to the target. Flushes this * writer if the autoflush flag is set to {@code true}. - * + * * @param lnum * the long value to print to the target. * @see #print(String) - * @since Android 1.0 - */ + */ public void println(long lnum) { println(String.valueOf(lnum)); } @@ -639,12 +596,11 @@ public class PrintWriter extends Writer { * Prints the string representation of the specified object followed by the * system property {@code "line.separator"} to the target. Flushes this * writer if the autoflush flag is set to {@code true}. - * + * * @param obj * the object to print to the target. * @see #print(String) - * @since Android 1.0 - */ + */ public void println(Object obj) { println(String.valueOf(obj)); } @@ -657,12 +613,10 @@ public class PrintWriter extends Writer { * is flushed if the autoflush flag is set to {@code true}. *

          * If an I/O error occurs, this writer's error flag is set to {@code true}. - *

          - * + * * @param str * the string to print to the target. * @see #write(int) - * @since Android 1.0 */ public void println(String str) { synchronized (lock) { @@ -675,20 +629,17 @@ public class PrintWriter extends Writer { * Prints the string representation of the specified boolean followed by the * system property {@code "line.separator"} to the target. Flushes this * writer if the autoflush flag is set to {@code true}. - * + * * @param bool * the boolean value to print to the target. * @see #print(String) - * @since Android 1.0 - */ + */ public void println(boolean bool) { println(String.valueOf(bool)); } /** * Sets the error flag of this writer to {@code true}. - * - * @since Android 1.0 */ protected void setError() { synchronized (lock) { @@ -698,10 +649,9 @@ public class PrintWriter extends Writer { /** * Writes the character buffer {@code buf} to the target. - * + * * @param buf * the non-null array containing characters to write. - * @since Android 1.0 */ @Override public void write(char[] buf) { @@ -717,8 +667,7 @@ public class PrintWriter extends Writer { *

          * This writer's error flag is set to {@code true} if this writer is closed * or an I/O error occurs. - *

          - * + * * @param buf * the buffer to write to the target. * @param offset @@ -728,7 +677,6 @@ public class PrintWriter extends Writer { * @throws IndexOutOfBoundsException * if {@code offset < 0} or {@code count < 0}, or if {@code * offset + count} is greater than the length of {@code buf}. - * @since Android 1.0 */ @Override public void write(char[] buf, int offset, int count) { @@ -744,11 +692,9 @@ public class PrintWriter extends Writer { *

          * This writer's error flag is set to {@code true} if this writer is closed * or an I/O error occurs. - *

          - * + * * @param oneChar * the character to write to the target. - * @since Android 1.0 */ @Override public void write(int oneChar) { @@ -774,10 +720,9 @@ public class PrintWriter extends Writer { /** * Writes the characters from the specified string to the target. - * + * * @param str * the non-null string containing the characters to write. - * @since Android 1.0 */ @Override public void write(String str) { @@ -787,7 +732,7 @@ public class PrintWriter extends Writer { /** * Writes {@code count} characters from {@code str} starting at {@code * offset} to the target. - * + * * @param str * the non-null string containing the characters to write. * @param offset @@ -797,7 +742,6 @@ public class PrintWriter extends Writer { * @throws IndexOutOfBoundsException * if {@code offset < 0} or {@code count < 0}, or if {@code * offset + count} is greater than the length of {@code str}. - * @since Android 1.0 */ @Override public void write(String str, int offset, int count) { @@ -805,12 +749,11 @@ public class PrintWriter extends Writer { } /** - * Appends the character {@code c} to the target. - * + * Appends the character {@code c} to the target. + * * @param c * the character to append to the target. * @return this writer. - * @since Android 1.0 */ @Override public PrintWriter append(char c) { @@ -823,11 +766,10 @@ public class PrintWriter extends Writer { * method works the same way as {@code PrintWriter.print(csq.toString())}. * If {@code csq} is {@code null}, then the string "null" is written * to the target. - * + * * @param csq * the character sequence appended to the target. * @return this writer. - * @since Android 1.0 */ @Override public PrintWriter append(CharSequence csq) { @@ -845,7 +787,7 @@ public class PrintWriter extends Writer { * PrintWriter.print(csq.subsequence(start, end).toString())}. If {@code * csq} is {@code null}, then the specified subsequence of the string "null" * will be written to the target. - * + * * @param csq * the character sequence appended to the target. * @param start @@ -859,7 +801,6 @@ public class PrintWriter extends Writer { * if {@code start > end}, {@code start < 0}, {@code end < 0} or * either {@code start} or {@code end} are greater or equal than * the length of {@code csq}. - * @since Android 1.0 */ @Override public PrintWriter append(CharSequence csq, int start, int end) { diff --git a/libcore/luni/src/main/java/java/io/PushbackInputStream.java b/libcore/luni/src/main/java/java/io/PushbackInputStream.java index c397c866e..4600fa257 100644 --- a/libcore/luni/src/main/java/java/io/PushbackInputStream.java +++ b/libcore/luni/src/main/java/java/io/PushbackInputStream.java @@ -25,14 +25,10 @@ import org.apache.harmony.luni.util.Msg; * this useful. The number of bytes which may be pushed back can be specified * during construction. If the buffer of pushed back bytes is empty, bytes are * read from the underlying input stream. - * - * @since Android 1.0 */ public class PushbackInputStream extends FilterInputStream { /** * The buffer that contains pushed-back bytes. - * - * @since Android 1.0 */ protected byte[] buf; @@ -40,8 +36,6 @@ public class PushbackInputStream extends FilterInputStream { * The current position within {@code buf}. A value equal to * {@code buf.length} indicates that no bytes are available. A value of 0 * indicates that the buffer is full. - * - * @since Android 1.0 */ protected int pos; @@ -49,10 +43,9 @@ public class PushbackInputStream extends FilterInputStream { * Constructs a new {@code PushbackInputStream} with the specified input * stream as source. The size of the pushback buffer is set to the default * value of 1 byte. - * + * * @param in * the source input stream. - * @since Android 1.0 */ public PushbackInputStream(InputStream in) { super(in); @@ -63,14 +56,13 @@ public class PushbackInputStream extends FilterInputStream { /** * Constructs a new {@code PushbackInputStream} with {@code in} as source * input stream. The size of the pushback buffer is set to {@code size}. - * + * * @param in * the source input stream. * @param size * the size of the pushback buffer. * @throws IllegalArgumentException * if {@code size} is negative. - * @since Android 1.0 */ public PushbackInputStream(InputStream in, int size) { super(in); @@ -85,12 +77,11 @@ public class PushbackInputStream extends FilterInputStream { * Returns the number of bytes that are available before this stream will * block. This is the sum of the bytes available in the pushback buffer and * those available from the source stream. - * + * * @return the number of bytes available before blocking. * @throws IOException * if this stream is closed or an I/O error occurs in the source * stream. - * @since Android 1.0 */ @Override public int available() throws IOException { @@ -103,10 +94,9 @@ public class PushbackInputStream extends FilterInputStream { /** * Closes this stream. This implementation closes the source stream * and releases the pushback buffer. - * + * * @throws IOException * if an error occurs while closing this stream. - * @since Android 1.0 */ @Override public void close() throws IOException { @@ -121,11 +111,10 @@ public class PushbackInputStream extends FilterInputStream { * Indicates whether this stream supports the {@code mark(int)} and * {@code reset()} methods. {@code PushbackInputStream} does not support * them, so it returns {@code false}. - * + * * @return always {@code false}. * @see #mark(int) * @see #reset() - * @since Android 1.0 */ @Override public boolean markSupported() { @@ -138,13 +127,12 @@ public class PushbackInputStream extends FilterInputStream { * available bytes then a byte from the source input stream is returned. * Blocks until one byte has been read, the end of the source stream is * detected or an exception is thrown. - * + * * @return the byte read or -1 if the end of the source stream has been * reached. * @throws IOException * if this stream is closed or an I/O error occurs while reading * from this stream. - * @since Android 1.0 */ @Override public int read() throws IOException { @@ -166,7 +154,7 @@ public class PushbackInputStream extends FilterInputStream { * from the pushback buffer first, then from the source stream if more bytes * are required. Blocks until {@code count} bytes have been read, the end of * the source stream is detected or an exception is thrown. - * + * * @param buffer * the array in which to store the bytes read from this stream. * @param offset @@ -185,7 +173,6 @@ public class PushbackInputStream extends FilterInputStream { * reading from this stream. * @throws NullPointerException * if {@code buffer} is {@code null}. - * @since Android 1.0 */ @Override public int read(byte[] buffer, int offset, int length) throws IOException { @@ -234,13 +221,12 @@ public class PushbackInputStream extends FilterInputStream { /** * Skips {@code count} bytes in this stream. This implementation skips bytes * in the pushback buffer first and then in the source stream if necessary. - * + * * @param count * the number of bytes to skip. * @return the number of bytes actually skipped. * @throws IOException * if this stream is closed or another I/O error occurs. - * @since Android 1.0 */ @Override public long skip(long count) throws IOException { @@ -270,14 +256,12 @@ public class PushbackInputStream extends FilterInputStream { * contents of {@code buffer}, an {@code IOException} is thrown. Parts of * {@code buffer} may have already been copied to the pushback buffer when * the exception is thrown. - *

          - * + * * @param buffer * the buffer containing the bytes to push back to this stream. * @throws IOException * if the free space in the internal pushback buffer is not * sufficient to store the contents of {@code buffer}. - * @since Android 1.0 */ public void unread(byte[] buffer) throws IOException { unread(buffer, 0, buffer.length); @@ -294,8 +278,7 @@ public class PushbackInputStream extends FilterInputStream { * subset of {@code buffer}, an {@code IOException} is thrown. Parts of * {@code buffer} may have already been copied to the pushback buffer when * the exception is thrown. - *

          - * + * * @param buffer * the buffer containing the bytes to push back to this stream. * @param offset @@ -309,7 +292,6 @@ public class PushbackInputStream extends FilterInputStream { * @throws IOException * if the free space in the internal pushback buffer is not * sufficient to store the selected contents of {@code buffer}. - * @since Android 1.0 */ public void unread(byte[] buffer, int offset, int length) throws IOException { @@ -332,9 +314,11 @@ public class PushbackInputStream extends FilterInputStream { } // END android-changed - for (int i = offset + length - 1; i >= offset; i--) { - unread(buffer[i]); + if (buf == null) { + throw new IOException(); } + System.arraycopy(buffer, offset, buf, pos - length, length); + pos = pos - length; } /** @@ -345,14 +329,12 @@ public class PushbackInputStream extends FilterInputStream { *

          * If this stream's internal pushback buffer cannot store the byte, an * {@code IOException} is thrown. - *

          - * + * * @param oneByte * the byte to push back to this stream. * @throws IOException * if this stream is closed or the internal pushback buffer is * full. - * @since Android 1.0 */ public void unread(int oneByte) throws IOException { if (buf == null) { @@ -367,11 +349,10 @@ public class PushbackInputStream extends FilterInputStream { /** * Marks the current position in this stream. Setting a mark is not * supported in this class; this implementation does nothing. - * + * * @param readlimit * the number of bytes that can be read from this stream before * the mark is invalidated; this parameter is ignored. - * @since Android 1.0 */ @Override public void mark(int readlimit) { @@ -382,10 +363,9 @@ public class PushbackInputStream extends FilterInputStream { * Resets this stream to the last marked position. Resetting the stream is * not supported in this class; this implementation always throws an * {@code IOException}. - * + * * @throws IOException * if this method is called. - * @since Android 1.0 */ @Override public void reset() throws IOException { diff --git a/libcore/luni/src/main/java/java/io/PushbackReader.java b/libcore/luni/src/main/java/java/io/PushbackReader.java index 484caf34b..606ecb40e 100644 --- a/libcore/luni/src/main/java/java/io/PushbackReader.java +++ b/libcore/luni/src/main/java/java/io/PushbackReader.java @@ -25,8 +25,6 @@ import org.apache.harmony.luni.util.Msg; * find this useful. The number of characters which may be pushed back can be * specified during construction. If the buffer of pushed back bytes is empty, * characters are read from the underlying reader. - * - * @since Android 1.0 */ public class PushbackReader extends FilterReader { /** @@ -45,10 +43,9 @@ public class PushbackReader extends FilterReader { * Constructs a new {@code PushbackReader} with the specified reader as * source. The size of the pushback buffer is set to the default value of 1 * character. - * + * * @param in * the source reader. - * @since Android 1.0 */ public PushbackReader(Reader in) { super(in); @@ -59,14 +56,13 @@ public class PushbackReader extends FilterReader { /** * Constructs a new {@code PushbackReader} with {@code in} as source reader. * The size of the pushback buffer is set to {@code size}. - * + * * @param in * the source reader. * @param size * the size of the pushback buffer. * @throws IllegalArgumentException * if {@code size} is negative. - * @since Android 1.0 */ public PushbackReader(Reader in, int size) { super(in); @@ -80,10 +76,9 @@ public class PushbackReader extends FilterReader { /** * Closes this reader. This implementation closes the source reader * and releases the pushback buffer. - * + * * @throws IOException * if an error occurs while closing this reader. - * @since Android 1.0 */ @Override public void close() throws IOException { @@ -97,13 +92,12 @@ public class PushbackReader extends FilterReader { * Marks the current position in this stream. Setting a mark is not * supported in this class; this implementation always throws an * {@code IOException}. - * + * * @param readAheadLimit * the number of character that can be read from this reader * before the mark is invalidated; this parameter is ignored. * @throws IOException * if this method is called. - * @since Android 1.0 */ @Override public void mark(int readAheadLimit) throws IOException { @@ -114,11 +108,10 @@ public class PushbackReader extends FilterReader { * Indicates whether this reader supports the {@code mark(int)} and * {@code reset()} methods. {@code PushbackReader} does not support them, so * it returns {@code false}. - * + * * @return always {@code false}. * @see #mark(int) * @see #reset() - * @since Android 1.0 */ @Override public boolean markSupported() { @@ -132,13 +125,12 @@ public class PushbackReader extends FilterReader { * available characters then a character from the source reader is returned. * Blocks until one character has been read, the end of the source reader is * detected or an exception is thrown. - * + * * @return the character read or -1 if the end of the source reader has been * reached. * @throws IOException * if this reader is closed or an I/O error occurs while reading * from this reader. - * @since Android 1.0 */ @Override public int read() throws IOException { @@ -164,7 +156,7 @@ public class PushbackReader extends FilterReader { * read from the pushback buffer first, then from the source reader if more * bytes are required. Blocks until {@code count} characters have been read, * the end of the source reader is detected or an exception is thrown. - * + * * @param buffer * the array in which to store the characters read from this * reader. @@ -182,7 +174,6 @@ public class PushbackReader extends FilterReader { * @throws IOException * if this reader is closed or another I/O error occurs while * reading from this reader. - * @since Android 1.0 */ @Override public int read(char[] buffer, int offset, int count) throws IOException { @@ -236,7 +227,7 @@ public class PushbackReader extends FilterReader { * Indicates whether this reader is ready to be read without blocking. * Returns {@code true} if this reader will not block when {@code read} is * called, {@code false} if unknown or blocking will occur. - * + * * @return {@code true} if the receiver will not block when * {@code read()} is called, {@code false} if unknown * or blocking will occur. @@ -244,7 +235,6 @@ public class PushbackReader extends FilterReader { * if this reader is closed or some other I/O error occurs. * @see #read() * @see #read(char[], int, int) - * @since Android 1.0 */ @Override public boolean ready() throws IOException { @@ -260,10 +250,9 @@ public class PushbackReader extends FilterReader { * Resets this reader to the last marked position. Resetting the reader is * not supported in this class; this implementation always throws an * {@code IOException}. - * + * * @throws IOException * if this method is called. - * @since Android 1.0 */ @Override public void reset() throws IOException { @@ -279,8 +268,7 @@ public class PushbackReader extends FilterReader { * contents of {@code buffer}, an {@code IOException} is thrown. Parts of * {@code buffer} may have already been copied to the pushback buffer when * the exception is thrown. - *

          - * + * * @param buffer * the buffer containing the characters to push back to this * reader. @@ -288,7 +276,6 @@ public class PushbackReader extends FilterReader { * if this reader is closed or the free space in the internal * pushback buffer is not sufficient to store the contents of * {@code buffer}. - * @since Android 1.0 */ public void unread(char[] buffer) throws IOException { unread(buffer, 0, buffer.length); @@ -305,8 +292,7 @@ public class PushbackReader extends FilterReader { * subset of {@code buffer}, an {@code IOException} is thrown. Parts of * {@code buffer} may have already been copied to the pushback buffer when * the exception is thrown. - *

          - * + * * @param buffer * the buffer containing the characters to push back to this * reader. @@ -324,7 +310,6 @@ public class PushbackReader extends FilterReader { * contents of {@code buffer}. * @throws NullPointerException * if {@code buffer} is {@code null}. - * @since Android 1.0 */ public void unread(char[] buffer, int offset, int count) throws IOException { synchronized (lock) { @@ -361,14 +346,12 @@ public class PushbackReader extends FilterReader { *

          * If this reader's internal pushback buffer cannot store the character, an * {@code IOException} is thrown. - *

          - * + * * @param oneChar * the character to push back to this stream. * @throws IOException * if this reader is closed or the internal pushback buffer is * full. - * @since Android 1.0 */ public void unread(int oneChar) throws IOException { synchronized (lock) { @@ -386,14 +369,13 @@ public class PushbackReader extends FilterReader { * Skips {@code count} characters in this reader. This implementation skips * characters in the pushback buffer first and then in the source reader if * necessary. - * + * * @param count * the number of characters to skip. * @return the number of characters actually skipped. * @throws IllegalArgumentException if {@code count < 0}. * @throws IOException * if this reader is closed or another I/O error occurs. - * @since Android 1.0 */ @Override public long skip(long count) throws IOException { diff --git a/libcore/luni/src/main/java/java/io/RandomAccessFile.java b/libcore/luni/src/main/java/java/io/RandomAccessFile.java index 7152116f8..37d77f5a9 100644 --- a/libcore/luni/src/main/java/java/io/RandomAccessFile.java +++ b/libcore/luni/src/main/java/java/io/RandomAccessFile.java @@ -33,8 +33,6 @@ import org.apache.harmony.nio.FileChannelFactory; * opened in read/write mode, write operations are available as well. The * position of the next read or write operation can be moved forwards and * backwards after every operation. - * - * @since Android 1.0 */ public class RandomAccessFile implements DataInput, DataOutput, Closeable { /** @@ -51,7 +49,7 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { private IFileSystem fileSystem = Platform.getFileSystem(); private boolean isReadOnly; - + // BEGIN android-added private int options; // END android-added @@ -61,12 +59,11 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { private Object repositionLock = new RepositionLock(); - // BEGIN android-changed /** * Constructs a new {@code RandomAccessFile} based on {@code file} and opens - * it according to the access string in {@code mode}. + * it according to the access string in {@code mode}. *

          - * {@code mode} may have one of following values: + * {@code mode} may have one of following values: * * * @@ -90,7 +87,7 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { * file's content must be written synchronously to the target device. * *
          {@code "r"}
          - *

          + * * @param file * the file to open. * @param mode @@ -107,16 +104,17 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { * access request according to {@code mode}. * @see java.lang.SecurityManager#checkRead(FileDescriptor) * @see java.lang.SecurityManager#checkWrite(FileDescriptor) - * @since Android 1.0 */ public RandomAccessFile(File file, String mode) throws FileNotFoundException { super(); + // BEGIN android-changed options = 0; - + // END android-changed + fd = new FileDescriptor(); - + if (mode.equals("r")) { //$NON-NLS-1$ isReadOnly = true; fd.readOnly = true; @@ -143,8 +141,12 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { security.checkWrite(file.getPath()); } } - + fd.descriptor = fileSystem.open(file.properPath(true), options); + // BEGIN android-removed + // channel = FileChannelFactory.getFileChannel(this, fd.descriptor, + // options); + // END android-removed // if we are in "rws" mode, attempt to sync file+metadata if (syncMetadata) { @@ -155,14 +157,13 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { } } } - // END android-changed /** * Constructs a new {@code RandomAccessFile} based on the file named {@code * fileName} and opens it according to the access string in {@code mode}. * The file path may be specified absolutely or relative to the system * property {@code "user.dir"}. - * + * * @param fileName * the name of the file to open. * @param mode @@ -179,7 +180,6 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { * access request according to {@code mode}. * @see java.lang.SecurityManager#checkRead(FileDescriptor) * @see java.lang.SecurityManager#checkWrite(FileDescriptor) - * @since Android 1.0 */ public RandomAccessFile(String fileName, String mode) throws FileNotFoundException { @@ -188,10 +188,9 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { /** * Closes this file. - * + * * @throws IOException * if an error occurs while closing this file. - * @since Android 1.0 */ public void close() throws IOException { // BEGIN android-changed @@ -208,39 +207,34 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { // END android-changed } - // BEGIN android-changed /** * Gets this file's {@link FileChannel} object. *

          - * The file channel's {@link FileChannel.#position() position} is the same + * The file channel's {@link FileChannel#position() position} is the same * as this file's file pointer offset (see {@link #getFilePointer()}). Any * changes made to this file's file pointer offset are also visible in the * file channel's position and vice versa. - *

          - * + * * @return this file's file channel instance. - * @since Android 1.0 */ - public final FileChannel getChannel() { - synchronized(this) { - if(channel == null) { - channel = FileChannelFactory.getFileChannel(this, fd.descriptor, - options); - } - return channel; + public final synchronized FileChannel getChannel() { + // BEGIN android-added + if(channel == null) { + channel = FileChannelFactory.getFileChannel(this, fd.descriptor, + options); } + // END android-added + return channel; } - // END android-changed /** * Gets this file's {@link FileDescriptor}. This represents the operating * system resource for this random access file. - * + * * @return this file's file descriptor object. * @throws IOException * if an error occurs while getting the file descriptor of this * file. - * @since Android 1.0 */ public final FileDescriptor getFD() throws IOException { return fd; @@ -249,13 +243,12 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { /** * Gets the current position within this file. All reads and * writes take place at the current file pointer position. - * + * * @return the current offset in bytes from the beginning of the file. - * + * * @throws IOException * if an error occurs while getting the file pointer of this * file. - * @since Android 1.0 */ public long getFilePointer() throws IOException { openCheck(); @@ -265,7 +258,7 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { /** * Checks to see if the file is currently open. Returns silently if it is, * and throws an exception if it is not. - * + * * @throws IOException * the receiver is closed. */ @@ -277,11 +270,10 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { /** * Returns the length of this file in bytes. - * + * * @return the file's length in bytes. * @throws IOException * if this file is closed or some other I/O error occurs. - * @since Android 1.0 */ public long length() throws IOException { openCheck(); @@ -301,11 +293,10 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { * as an integer in the range from 0 to 255. Returns -1 if the end of the * file has been reached. Blocks until one byte has been read, the end of * the file is detected or an exception is thrown. - * + * * @return the byte read or -1 if the end of the file has been reached. * @throws IOException * if this file is closed or another I/O error occurs. - * @since Android 1.0 */ public int read() throws IOException { openCheck(); @@ -321,14 +312,13 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { * byte array {@code buffer}. The maximum number of bytes read corresponds * to the size of {@code buffer}. Blocks until at least one byte has been * read. - * + * * @param buffer * the byte array in which to store the bytes read. * @return the number of bytes actually read or -1 if the end of the file * has been reached. * @throws IOException * if this file is closed or another I/O error occurs. - * @since Android 1.0 */ public int read(byte[] buffer) throws IOException { return read(buffer, 0, buffer.length); @@ -339,7 +329,7 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { * and stores them in the byte array {@code buffer} starting at {@code * offset}. Blocks until {@code count} bytes have been read, the end of the * file is reached or an exception is thrown. - * + * * @param buffer * the array in which to store the bytes read from this file. * @param offset @@ -354,7 +344,6 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { * offset + count} is greater than the size of {@code buffer}. * @throws IOException * if this file is closed or another I/O error occurs. - * @since Android 1.0 */ public int read(byte[] buffer, int offset, int count) throws IOException { // have to have four comparisions to not miss integer overflow cases @@ -383,14 +372,13 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { * Reads a boolean from the current position in this file. Blocks until one * byte has been read, the end of the file is reached or an exception is * thrown. - * + * * @return the next boolean value from this file. * @throws EOFException * if the end of this file is detected. * @throws IOException * if this file is closed or another I/O error occurs. * @see #writeBoolean(boolean) - * @since Android 1.0 */ public final boolean readBoolean() throws IOException { int temp = this.read(); @@ -404,14 +392,13 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { * Reads an 8-bit byte from the current position in this file. Blocks until * one byte has been read, the end of the file is reached or an exception is * thrown. - * + * * @return the next signed 8-bit byte value from this file. * @throws EOFException * if the end of this file is detected. * @throws IOException * if this file is closed or another I/O error occurs. * @see #writeBoolean(boolean) - * @since Android 1.0 */ public final byte readByte() throws IOException { int temp = this.read(); @@ -425,15 +412,14 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { * Reads a 16-bit character from the current position in this file. Blocks until * two bytes have been read, the end of the file is reached or an exception is * thrown. - * + * * @return the next char value from this file. * @throws EOFException * if the end of this file is detected. * @throws IOException * if this file is closed or another I/O error occurs. * @see #writeChar(int) - * @since Android 1.0 - */ + */ public final char readChar() throws IOException { byte[] buffer = new byte[2]; if (read(buffer, 0, buffer.length) != buffer.length) { @@ -446,14 +432,13 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { * Reads a 64-bit double from the current position in this file. Blocks * until eight bytes have been read, the end of the file is reached or an * exception is thrown. - * + * * @return the next double value from this file. * @throws EOFException * if the end of this file is detected. * @throws IOException * if this file is closed or another I/O error occurs. * @see #writeDouble(double) - * @since Android 1.0 */ public final double readDouble() throws IOException { return Double.longBitsToDouble(readLong()); @@ -463,14 +448,13 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { * Reads a 32-bit float from the current position in this file. Blocks * until four bytes have been read, the end of the file is reached or an * exception is thrown. - * + * * @return the next float value from this file. * @throws EOFException * if the end of this file is detected. * @throws IOException * if this file is closed or another I/O error occurs. * @see #writeFloat(float) - * @since Android 1.0 */ public final float readFloat() throws IOException { return Float.intBitsToFloat(readInt()); @@ -480,7 +464,7 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { * Reads bytes from this file into {@code buffer}. Blocks until {@code * buffer.length} number of bytes have been read, the end of the file is * reached or an exception is thrown. - * + * * @param buffer * the buffer to read bytes into. * @throws EOFException @@ -489,7 +473,6 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { * if this file is closed or another I/O error occurs. * @throws NullPointerException * if {@code buffer} is {@code null}. - * @since Android 1.0 */ public final void readFully(byte[] buffer) throws IOException { readFully(buffer, 0, buffer.length); @@ -499,7 +482,7 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { * Read bytes from this file into {@code buffer} starting at offset {@code * offset}. This method blocks until {@code count} number of bytes have been * read. - * + * * @param buffer * the buffer to read bytes into. * @param offset @@ -516,7 +499,6 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { * if this file is closed or another I/O error occurs. * @throws NullPointerException * if {@code buffer} is {@code null}. - * @since Android 1.0 */ public final void readFully(byte[] buffer, int offset, int count) throws IOException { @@ -547,14 +529,13 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { * Reads a 32-bit integer from the current position in this file. Blocks * until four bytes have been read, the end of the file is reached or an * exception is thrown. - * + * * @return the next int value from this file. * @throws EOFException * if the end of this file is detected. * @throws IOException * if this file is closed or another I/O error occurs. * @see #writeInt(int) - * @since Android 1.0 */ public final int readInt() throws IOException { byte[] buffer = new byte[4]; @@ -573,13 +554,11 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { *

          * Blocks until a line terminating sequence has been read, the end of the * file is reached or an exception is thrown. - *

          - * + * * @return the contents of the line or {@code null} if no characters have * been read before the end of the file has been reached. * @throws IOException * if this file is closed or another I/O error occurs. - * @since Android 1.0 */ public final String readLine() throws IOException { StringBuilder line = new StringBuilder(80); // Typical line length @@ -615,14 +594,13 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { * Reads a 64-bit long from the current position in this file. Blocks until * eight bytes have been read, the end of the file is reached or an * exception is thrown. - * + * * @return the next long value from this file. * @throws EOFException * if the end of this file is detected. * @throws IOException * if this file is closed or another I/O error occurs. * @see #writeLong(long) - * @since Android 1.0 */ public final long readLong() throws IOException { byte[] buffer = new byte[8]; @@ -641,14 +619,13 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { * Reads a 16-bit short from the current position in this file. Blocks until * two bytes have been read, the end of the file is reached or an exception * is thrown. - * + * * @return the next short value from this file. * @throws EOFException * if the end of this file is detected. * @throws IOException * if this file is closed or another I/O error occurs. * @see #writeShort(int) - * @since Android 1.0 */ public final short readShort() throws IOException { byte[] buffer = new byte[2]; @@ -662,14 +639,13 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { * Reads an unsigned 8-bit byte from the current position in this file and * returns it as an integer. Blocks until one byte has been read, the end of * the file is reached or an exception is thrown. - * + * * @return the next unsigned byte value from this file as an int. * @throws EOFException * if the end of this file is detected. * @throws IOException * if this file is closed or another I/O error occurs. * @see #writeByte(int) - * @since Android 1.0 */ public final int readUnsignedByte() throws IOException { int temp = this.read(); @@ -683,14 +659,13 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { * Reads an unsigned 16-bit short from the current position in this file and * returns it as an integer. Blocks until two bytes have been read, the end of * the file is reached or an exception is thrown. - * + * * @return the next unsigned short value from this file as an int. * @throws EOFException * if the end of this file is detected. * @throws IOException * if this file is closed or another I/O error occurs. * @see #writeShort(int) - * @since Android 1.0 */ public final int readUnsignedShort() throws IOException { byte[] buffer = new byte[2]; @@ -706,7 +681,7 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { * is determined by the first two bytes read from the file. Blocks until all * required bytes have been read, the end of the file is reached or an * exception is thrown. - * + * * @return the next string encoded in {@link DataInput modified UTF-8} from * this file. * @throws EOFException @@ -716,7 +691,6 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { * @throws UTFDataFormatException * if the bytes read cannot be decoded into a character string. * @see #writeUTF(String) - * @since Android 1.0 */ public final String readUTF() throws IOException { int utfSize = readUnsignedShort(); @@ -736,13 +710,12 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { * position may be greater than the current length of the file, but the * file's length will only change if the moving of the pointer is followed * by a {@code write} operation. - * + * * @param pos * the new file pointer position. * @throws IOException * if this file is closed, {@code pos < 0} or another I/O error * occurs. - * @since Android 1.0 */ public void seek(long pos) throws IOException { if (pos < 0) { @@ -761,14 +734,13 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { * file to the new end are undefined. The file is truncated if its current * size is bigger than {@code newLength}. If the current file pointer * position is in the truncated part, it is set to the end of the file. - * + * * @param newLength * the new file length in bytes. * @throws IllegalArgumentException * if {@code newLength < 0}. * @throws IOException * if this file is closed or another I/O error occurs. - * @since Android 1.0 */ public void setLength(long newLength) throws IOException { openCheck(); @@ -793,13 +765,12 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { * bytes are skipped if the end of the file is reached or an exception is * thrown during the operation. Nothing is done if {@code count} is * negative. - * + * * @param count * the number of bytes to skip. * @return the number of bytes actually skipped. * @throws IOException * if this file is closed or another I/O error occurs. - * @since Android 1.0 */ public int skipBytes(int count) throws IOException { if (count > 0) { @@ -815,7 +786,7 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { /** * Writes the entire contents of the byte array {@code buffer} to this file, * starting at the current file pointer. - * + * * @param buffer * the buffer to write. * @throws IOException @@ -824,7 +795,6 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { * @see #read(byte[],int,int) * @see #readFully(byte[]) * @see #readFully(byte[],int,int) - * @since Android 1.0 */ public void write(byte[] buffer) throws IOException { write(buffer, 0, buffer.length); @@ -834,7 +804,7 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { * Writes {@code count} bytes from the byte array {@code buffer} to this * file, starting at the current file pointer and using {@code offset} as * the first position within {@code buffer} to get bytes. - * + * * @param buffer * the buffer to write to this file. * @param offset @@ -848,7 +818,6 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { * if an I/O error occurs while writing to this file. * @see #read(byte[], int, int) * @see #readFully(byte[], int, int) - * @since Android 1.0 */ public void write(byte[] buffer, int offset, int count) throws IOException { // BEGIN android-changed @@ -883,13 +852,12 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { /** * Writes a byte to this file, starting at the current file pointer. Only * the least significant byte of the integer {@code oneByte} is written. - * + * * @param oneByte * the byte to write to this file. * @throws IOException * if this file is closed or another I/O error occurs. * @see #read() - * @since Android 1.0 */ public void write(int oneByte) throws IOException { openCheck(); @@ -907,13 +875,12 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { /** * Writes a boolean to this file, starting at the current file pointer. - * + * * @param val * the boolean to write to this file. * @throws IOException * if this file is closed or another I/O error occurs. * @see #readBoolean() - * @since Android 1.0 */ public final void writeBoolean(boolean val) throws IOException { write(val ? 1 : 0); @@ -922,14 +889,13 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { /** * Writes an 8-bit byte to this file, starting at the current file pointer. * Only the least significant byte of the integer {@code val} is written. - * + * * @param val * the byte to write to this file. * @throws IOException * if this file is closed or another I/O error occurs. * @see #readByte() * @see #readUnsignedByte() - * @since Android 1.0 */ public final void writeByte(int val) throws IOException { write(val & 0xFF); @@ -938,7 +904,7 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { /** * Writes the low order 8-bit bytes from a string to this file, starting at * the current file pointer. - * + * * @param str * the string containing the bytes to write to this file * @throws IOException @@ -947,7 +913,6 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { * @see #read(byte[],int,int) * @see #readFully(byte[]) * @see #readFully(byte[],int,int) - * @since Android 1.0 */ public final void writeBytes(String str) throws IOException { byte bytes[] = new byte[str.length()]; @@ -957,18 +922,16 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { write(bytes); } - /** * Writes a 16-bit character to this file, starting at the current file * pointer. Only the two least significant bytes of the integer {@code val} * are written, with the high byte first. - * + * * @param val * the char to write to this file. * @throws IOException * if an I/O error occurs while writing to this file. * @see #readChar() - * @since Android 1.0 */ public final void writeChar(int val) throws IOException { byte[] buffer = new byte[2]; @@ -981,13 +944,12 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { * Writes the 16-bit characters from a string to this file, starting at the * current file pointer. Each character is written in the same way as with * {@link #writeChar(int)}, with its high byte first. - * + * * @param str * the string to write to this file. * @throws IOException * if an I/O error occurs while writing to this file. * @see #readChar() - * @since Android 1.0 */ public final void writeChars(String str) throws IOException { byte newBytes[] = new byte[str.length() * 2]; @@ -1003,13 +965,12 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { * Writes a 64-bit double to this file, starting at the current file * pointer. The eight bytes returned by * {@link Double#doubleToLongBits(double)} are written to this file. - * + * * @param val * the double to write to this file. * @throws IOException * if an I/O error occurs while writing to this file. * @see #readDouble() - * @since Android 1.0 */ public final void writeDouble(double val) throws IOException { writeLong(Double.doubleToLongBits(val)); @@ -1019,13 +980,12 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { * Writes a 32-bit float to this file, starting at the current file pointer. * The four bytes returned by {@link Float#floatToIntBits(float)} are * written to this file. - * + * * @param val * the float to write to this file. * @throws IOException * if an I/O error occurs while writing to this file. * @see #readFloat() - * @since Android 1.0 */ public final void writeFloat(float val) throws IOException { writeInt(Float.floatToIntBits(val)); @@ -1035,13 +995,12 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { * Writes a 32-bit integer to this file, starting at the current file * pointer. The four bytes of the integer are written with the highest byte * first. - * + * * @param val * the int to write to this file. * @throws IOException * if an I/O error occurs while writing to this file. * @see #readInt() - * @since Android 1.0 */ public final void writeInt(int val) throws IOException { byte[] buffer = new byte[4]; @@ -1056,13 +1015,12 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { * Writes a 64-bit long to this file, starting at the current file * pointer. The eight bytes of the long are written with the highest byte * first. - * + * * @param val * the long to write to this file. * @throws IOException * if an I/O error occurs while writing to this file. * @see #readLong() - * @since Android 1.0 */ public final void writeLong(long val) throws IOException { byte[] buffer = new byte[8]; @@ -1082,13 +1040,13 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { * Writes a 16-bit short to this file, starting at the current file * pointer. Only the two least significant bytes of the integer {@code val} * are written, with the high byte first. - * + * * @param val * the short to write to this file. * @throws IOException * if an I/O error occurs while writing to this file. * @see #readShort() - * @since Android 1.0 + * @see DataInput#readUnsignedShort() */ public final void writeShort(int val) throws IOException { writeChar(val); @@ -1097,7 +1055,7 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { /** * Writes a string encoded with {@link DataInput modified UTF-8} to this * file, starting at the current file pointer. - * + * * @param str * the string to write in {@link DataInput modified UTF-8} * format. @@ -1106,7 +1064,6 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable { * @throws UTFDataFormatException * if the encoded string is longer than 65535 bytes. * @see #readUTF() - * @since Android 1.0 */ public final void writeUTF(String str) throws IOException { int utfCount = 0, length = str.length(); diff --git a/libcore/luni/src/main/java/java/io/Reader.java b/libcore/luni/src/main/java/java/io/Reader.java index 42fb9eef4..1f78f5a7d 100644 --- a/libcore/luni/src/main/java/java/io/Reader.java +++ b/libcore/luni/src/main/java/java/io/Reader.java @@ -33,24 +33,18 @@ import java.nio.ReadOnlyBufferException; *

          * Many specialized readers for purposes like reading from a file already exist * in this package. - * + * * @see Writer - * - * @since Android 1.0 */ public abstract class Reader implements Readable, Closeable { /** * The object used to synchronize access to the reader. - * - * @since Android 1.0 */ protected Object lock; /** * Constructs a new {@code Reader} with {@code this} as the object used to * synchronize critical sections. - * - * @since Android 1.0 */ protected Reader() { super(); @@ -60,12 +54,11 @@ public abstract class Reader implements Readable, Closeable { /** * Constructs a new {@code Reader} with {@code lock} used to synchronize * critical sections. - * + * * @param lock * the {@code Object} used to synchronize critical sections. * @throws NullPointerException * if {@code lock} is {@code null}. - * @since Android 1.0 */ protected Reader(Object lock) { if (lock == null) { @@ -77,10 +70,9 @@ public abstract class Reader implements Readable, Closeable { /** * Closes this reader. Implementations of this method should free any * resources associated with the reader. - * + * * @throws IOException * if an error occurs while closing this reader. - * @since Android 1.0 */ public abstract void close() throws IOException; @@ -92,8 +84,7 @@ public abstract class Reader implements Readable, Closeable { *

          * This default implementation simply throws an {@code IOException}; * subclasses must provide their own implementation. - *

          - * + * * @param readLimit * the number of characters that can be read before the mark is * invalidated. @@ -103,7 +94,6 @@ public abstract class Reader implements Readable, Closeable { * if an error occurs while setting a mark in this reader. * @see #markSupported() * @see #reset() - * @since Android 1.0 */ public void mark(int readLimit) throws IOException { throw new IOException(); @@ -113,9 +103,8 @@ public abstract class Reader implements Readable, Closeable { * Indicates whether this reader supports the {@code mark()} and * {@code reset()} methods. This default implementation returns * {@code false}. - * + * * @return always {@code false}. - * @since Android 1.0 */ public boolean markSupported() { return false; @@ -125,12 +114,11 @@ public abstract class Reader implements Readable, Closeable { * Reads a single character from this reader and returns it as an integer * with the two higher-order bytes set to 0. Returns -1 if the end of the * reader has been reached. - * + * * @return the character read or -1 if the end of the reader has been * reached. * @throws IOException * if this reader is closed or some other I/O error occurs. - * @since Android 1.0 */ public int read() throws IOException { synchronized (lock) { @@ -146,14 +134,13 @@ public abstract class Reader implements Readable, Closeable { * Reads characters from this reader and stores them in the character array * {@code buf} starting at offset 0. Returns the number of characters * actually read or -1 if the end of the reader has been reached. - * + * * @param buf * character array to store the characters read. * @return the number of characters read or -1 if the end of the reader has * been reached. * @throws IOException * if this reader is closed or some other I/O error occurs. - * @since Android 1.0 */ public int read(char[] buf) throws IOException { // BEGIN android-note @@ -167,7 +154,7 @@ public abstract class Reader implements Readable, Closeable { * at {@code offset} in the character array {@code buf}. Returns the number * of characters actually read or -1 if the end of the reader has been * reached. - * + * * @param buf * the character array to store the characters read. * @param offset @@ -179,7 +166,6 @@ public abstract class Reader implements Readable, Closeable { * been reached. * @throws IOException * if this reader is closed or some other I/O error occurs. - * @since Android 1.0 */ public abstract int read(char[] buf, int offset, int count) throws IOException; @@ -192,14 +178,13 @@ public abstract class Reader implements Readable, Closeable { * Returns {@code true} if this reader will not block when {@code read} is * called, {@code false} if unknown or blocking will occur. This default * implementation always returns {@code false}. - * + * * @return always {@code false}. * @throws IOException * if this reader is closed or some other I/O error occurs. * @see #read() * @see #read(char[]) * @see #read(char[], int, int) - * @since Android 1.0 */ public boolean ready() throws IOException { return false; @@ -211,12 +196,11 @@ public abstract class Reader implements Readable, Closeable { * location. If this reader has not been marked, the behavior of * {@code reset()} is implementation specific. This default * implementation throws an {@code IOException}. - * + * * @throws IOException * always thrown in this default implementation. * @see #mark(int) * @see #markSupported() - * @since Android 1.0 */ public void reset() throws IOException { throw new IOException(); @@ -227,7 +211,7 @@ public abstract class Reader implements Readable, Closeable { * {@code read} methods will not return these characters unless {@code * reset()} is used. This method may perform multiple reads to read {@code * count} characters. - * + * * @param count * the maximum number of characters to skip. * @return the number of characters actually skipped. @@ -238,7 +222,6 @@ public abstract class Reader implements Readable, Closeable { * @see #mark(int) * @see #markSupported() * @see #reset() - * @since Android 1.0 */ public long skip(long count) throws IOException { if (count < 0) { @@ -267,7 +250,7 @@ public abstract class Reader implements Readable, Closeable { /** * Reads characters and puts them into the {@code target} character buffer. - * + * * @param target * the destination character buffer. * @return the number of characters put into {@code target} or -1 if the end @@ -278,7 +261,6 @@ public abstract class Reader implements Readable, Closeable { * if {@code target} is {@code null}. * @throws ReadOnlyBufferException * if {@code target} is read-only. - * @since Android 1.0 */ public int read(CharBuffer target) throws IOException { if (null == target) { diff --git a/libcore/luni/src/main/java/java/io/SequenceInputStream.java b/libcore/luni/src/main/java/java/io/SequenceInputStream.java index 1b946b54f..37190213c 100644 --- a/libcore/luni/src/main/java/java/io/SequenceInputStream.java +++ b/libcore/luni/src/main/java/java/io/SequenceInputStream.java @@ -28,8 +28,6 @@ import org.apache.harmony.luni.util.Msg; * Concatenates two or more existing {@link InputStream}s. Reads are taken from * the first stream until it ends, then the next stream is used, until the last * stream returns end of file. - * - * @since Android 1.0 */ public class SequenceInputStream extends InputStream { /** @@ -45,14 +43,13 @@ public class SequenceInputStream extends InputStream { /** * Constructs a new {@code SequenceInputStream} using the two streams * {@code s1} and {@code s2} as the sequence of streams to read from. - * + * * @param s1 * the first stream to get bytes from. * @param s2 * the second stream to get bytes from. * @throws NullPointerException * if {@code s1} is {@code null}. - * @since Android 1.0 */ public SequenceInputStream(InputStream s1, InputStream s2) { if (s1 == null) { @@ -68,12 +65,11 @@ public class SequenceInputStream extends InputStream { * Constructs a new SequenceInputStream using the elements returned from * Enumeration {@code e} as the stream sequence. The instances returned by * {@code e.nextElement()} must be of type {@link InputStream}. - * + * * @param e * the enumeration of {@code InputStreams} to get bytes from. * @throws NullPointerException * if any of the elements in {@code e} is {@code null}. - * @since Android 1.0 */ public SequenceInputStream(Enumeration e) { this.e = e; @@ -87,12 +83,11 @@ public class SequenceInputStream extends InputStream { /** * Returns the number of bytes that are available before the current input stream will - * block. - * + * block. + * * @return the number of bytes available in the current input stream before blocking. * @throws IOException * if an I/O error occurs in the current input stream. - * @since Android 1.0 */ @Override public int available() throws IOException { @@ -103,11 +98,10 @@ public class SequenceInputStream extends InputStream { } /** - * Closes all streams in this sequence of input stream. - * + * Closes all streams in this sequence of input stream. + * * @throws IOException * if an error occurs while closing any of the input streams. - * @since Android 1.0 */ @Override public void close() throws IOException { @@ -119,7 +113,7 @@ public class SequenceInputStream extends InputStream { /** * Sets up the next InputStream or leaves it alone if there are none left. - * + * * @throws IOException */ private void nextStream() throws IOException { @@ -142,14 +136,13 @@ public class SequenceInputStream extends InputStream { * stream first; if the end of this stream has been reached, it reads from * the next one. Blocks until one byte has been read, the end of the last * input stream in the sequence has been reached, or an exception is thrown. - * + * * @return the byte read or -1 if either the end of the last stream in the * sequence has been reached or this input stream sequence is * closed. * @throws IOException * if an error occurs while reading the current source input * stream. - * @since Android 1.0 */ @Override public int read() throws IOException { @@ -179,7 +172,7 @@ public class SequenceInputStream extends InputStream { * If a substream has already reached the end when this call is made, it * will close that substream and start with the next one. If there are no * more substreams it will return -1. - * + * * @param buffer * the array in which to store the bytes read. * @param offset @@ -197,7 +190,6 @@ public class SequenceInputStream extends InputStream { * if an I/O error occurs. * @throws NullPointerException * if {@code buffer} is {@code null}. - * @since Android 1.0 */ @Override public int read(byte[] buffer, int offset, int count) throws IOException { diff --git a/libcore/luni/src/main/java/java/io/Serializable.java b/libcore/luni/src/main/java/java/io/Serializable.java index 17690e3a8..9cd78161d 100644 --- a/libcore/luni/src/main/java/java/io/Serializable.java +++ b/libcore/luni/src/main/java/java/io/Serializable.java @@ -28,13 +28,9 @@ package java.io; *

          * {@code private void writeObject(java.io.ObjectOutputStream out) throws * IOException} - *

          *

          * {@code private void readObject(java.io.ObjectInputStream in) throws * IOException, ClassNotFoundException} - *

          - * - * @since Android 1.0 */ public interface Serializable { /* empty */ diff --git a/libcore/luni/src/main/java/java/io/StreamCorruptedException.java b/libcore/luni/src/main/java/java/io/StreamCorruptedException.java index 11815072d..9d25727bb 100644 --- a/libcore/luni/src/main/java/java/io/StreamCorruptedException.java +++ b/libcore/luni/src/main/java/java/io/StreamCorruptedException.java @@ -25,8 +25,6 @@ package java.io; * * @see ObjectInputStream * @see OptionalDataException - * - * @since Android 1.0 */ public class StreamCorruptedException extends ObjectStreamException { @@ -35,8 +33,6 @@ public class StreamCorruptedException extends ObjectStreamException { /** * Constructs a new {@code StreamCorruptedException} with its stack trace * filled in. - * - * @since Android 1.0 */ public StreamCorruptedException() { super(); @@ -48,7 +44,6 @@ public class StreamCorruptedException extends ObjectStreamException { * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public StreamCorruptedException(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/io/StreamTokenizer.java b/libcore/luni/src/main/java/java/io/StreamTokenizer.java index 391fca865..c87a0537d 100644 --- a/libcore/luni/src/main/java/java/io/StreamTokenizer.java +++ b/libcore/luni/src/main/java/java/io/StreamTokenizer.java @@ -23,51 +23,37 @@ package java.io; * and different comment styles. The class can be used for limited processing * of source code of programming languages like Java, although it is nowhere * near a full parser. - * - * @since Android 1.0 */ public class StreamTokenizer { /** * Contains a number if the current token is a number ({@code ttype} == * {@code TT_NUMBER}). - * - * @since Android 1.0 */ public double nval; /** * Contains a string if the current token is a word ({@code ttype} == * {@code TT_WORD}). - * - * @since Android 1.0 */ public String sval; /** * The constant representing the end of the stream. - * - * @since Android 1.0 */ public static final int TT_EOF = -1; /** * The constant representing the end of the line. - * - * @since Android 1.0 */ public static final int TT_EOL = '\n'; /** * The constant representing a number token. - * - * @since Android 1.0 */ public static final int TT_NUMBER = -2; /** * The constant representing a word token. - * - * @since Android 1.0 */ public static final int TT_WORD = -3; @@ -89,8 +75,6 @@ public class StreamTokenizer { * whether {@code eolIsSignificant} is {@code true}. *
        • {@code TT_EOF} - the end of the stream has been reached.
        • * - * - * @since Android 1.0 */ public int ttype = TT_UNKNOWN; @@ -135,7 +119,7 @@ public class StreamTokenizer { * specification. */ private StreamTokenizer() { - /** + /* * Initialize the default state per specification. All byte values 'A' * through 'Z', 'a' through 'z', and '\u00A0' through '\u00FF' are * considered to be alphabetic. @@ -176,7 +160,6 @@ public class StreamTokenizer { * @throws NullPointerException * if {@code is} is {@code null}. * @deprecated Use {@link #StreamTokenizer(Reader)} - * @since Android 1.0 */ @Deprecated public StreamTokenizer(InputStream is) { @@ -205,7 +188,6 @@ public class StreamTokenizer { * * @param r * the source reader from which to parse tokens. - * @since Android 1.0 */ public StreamTokenizer(Reader r) { this(); @@ -221,7 +203,6 @@ public class StreamTokenizer { * * @param ch * the character to be considered a comment character. - * @since Android 1.0 */ public void commentChar(int ch) { if (0 <= ch && ch < tokenTypes.length) { @@ -235,7 +216,6 @@ public class StreamTokenizer { * * @param flag * {@code true} if EOL is significant, {@code false} otherwise. - * @since Android 1.0 */ public void eolIsSignificant(boolean flag) { isEOLSignificant = flag; @@ -245,7 +225,6 @@ public class StreamTokenizer { * Returns the current line number. * * @return this tokenizer's current line number. - * @since Android 1.0 */ public int lineno() { return lineNumber; @@ -258,7 +237,6 @@ public class StreamTokenizer { * @param flag * {@code true} if {@code sval} should be converted to lower * case, {@code false} otherwise. - * @since Android 1.0 */ public void lowerCaseMode(boolean flag) { forceLowercase = flag; @@ -272,7 +250,6 @@ public class StreamTokenizer { * @return the value of {@code ttype}. * @throws IOException * if an I/O error occurs while parsing the next token. - * @since Android 1.0 */ public int nextToken() throws IOException { if (pushBackToken) { @@ -359,7 +336,7 @@ public class StreamTokenizer { } // Check for words if ((currentType & TOKEN_WORD) != 0) { - StringBuffer word = new StringBuffer(20); + StringBuilder word = new StringBuilder(20); while (true) { word.append((char) currentChar); currentChar = read(); @@ -376,7 +353,7 @@ public class StreamTokenizer { // Check for quoted character if (currentType == TOKEN_QUOTE) { int matchQuote = currentChar; - StringBuffer quoteString = new StringBuffer(); + StringBuilder quoteString = new StringBuilder(); int peekOne = read(); while (peekOne >= 0 && peekOne != matchQuote && peekOne != '\r' && peekOne != '\n') { @@ -507,7 +484,6 @@ public class StreamTokenizer { * * @param ch * the character to be considered an ordinary character. - * @since Android 1.0 */ public void ordinaryChar(int ch) { if (0 <= ch && ch < tokenTypes.length) { @@ -525,7 +501,6 @@ public class StreamTokenizer { * the first character in the range of ordinary characters. * @param hi * the last character in the range of ordinary characters. - * @since Android 1.0 */ public void ordinaryChars(int low, int hi) { if (low < 0) { @@ -541,8 +516,6 @@ public class StreamTokenizer { /** * Specifies that this tokenizer shall parse numbers. - * - * @since Android 1.0 */ public void parseNumbers() { for (int i = '0'; i <= '9'; i++) { @@ -555,8 +528,6 @@ public class StreamTokenizer { /** * Indicates that the current token should be pushed back and returned again * the next time {@code nextToken()} is called. - * - * @since Android 1.0 */ public void pushBack() { pushBackToken = true; @@ -568,7 +539,6 @@ public class StreamTokenizer { * * @param ch * the character to be considered a quote character. - * @since Android 1.0 */ public void quoteChar(int ch) { if (0 <= ch && ch < tokenTypes.length) { @@ -586,8 +556,6 @@ public class StreamTokenizer { /** * Specifies that all characters shall be treated as ordinary characters. - * - * @since Android 1.0 */ public void resetSyntax() { for (int i = 0; i < 256; i++) { @@ -602,7 +570,6 @@ public class StreamTokenizer { * @param flag * {@code true} if {@code //} should be recognized as the start * of a comment, {@code false} otherwise. - * @since Android 1.0 */ public void slashSlashComments(boolean flag) { slashSlashComments = flag; @@ -616,7 +583,6 @@ public class StreamTokenizer { * @param flag * {@code true} if {@code /*} should be recognized as the start * of a comment, {@code false} otherwise. - * @since Android 1.0 */ public void slashStarComments(boolean flag) { slashStarComments = flag; @@ -626,7 +592,6 @@ public class StreamTokenizer { * Returns the state of this tokenizer in a readable format. * * @return the current state of this tokenizer. - * @since Android 1.0 */ @Override public String toString() { @@ -648,8 +613,6 @@ public class StreamTokenizer { result.append(sval); break; default: - // BEGIN android-changed - // copied from a newer version of harmony if (ttype == TT_UNKNOWN || tokenTypes[ttype] == TOKEN_QUOTE) { result.append(sval); } else { @@ -657,7 +620,6 @@ public class StreamTokenizer { result.append((char) ttype); result.append('\''); } - // END android-changed } result.append("], line "); //$NON-NLS-1$ result.append(lineNumber); @@ -672,7 +634,6 @@ public class StreamTokenizer { * the first character in the range of whitespace characters. * @param hi * the last character in the range of whitespace characters. - * @since Android 1.0 */ public void whitespaceChars(int low, int hi) { if (low < 0) { @@ -695,7 +656,6 @@ public class StreamTokenizer { * the first character in the range of word characters. * @param hi * the last character in the range of word characters. - * @since Android 1.0 */ public void wordChars(int low, int hi) { if (low < 0) { diff --git a/libcore/luni/src/main/java/java/io/StringBufferInputStream.java b/libcore/luni/src/main/java/java/io/StringBufferInputStream.java index 6e20ab6c5..cc4ab8160 100644 --- a/libcore/luni/src/main/java/java/io/StringBufferInputStream.java +++ b/libcore/luni/src/main/java/java/io/StringBufferInputStream.java @@ -22,31 +22,23 @@ import org.apache.harmony.luni.util.Msg; /** * A specialized {@link InputStream} that reads bytes from a {@code String} in * a sequential manner. - * + * * @deprecated Use {@link StringReader} - * - * @since Android 1.0 */ @Deprecated public class StringBufferInputStream extends InputStream { /** * The source string containing the data to read. - * - * @since Android 1.0 */ protected String buffer; /** * The total number of characters in the source string. - * - * @since Android 1.0 */ protected int count; /** * The current position within the source string. - * - * @since Android 1.0 */ protected int pos; @@ -54,12 +46,11 @@ public class StringBufferInputStream extends InputStream { * Construct a new {@code StringBufferInputStream} with {@code str} as * source. The size of the stream is set to the {@code length()} of the * string. - * + * * @param str * the source string for this stream. * @throws NullPointerException * if {@code str} is {@code null}. - * @since Android 1.0 */ public StringBufferInputStream(String str) { if (str == null) { @@ -72,9 +63,8 @@ public class StringBufferInputStream extends InputStream { /** * Returns the number of bytes that are available before this stream will * block. - * + * * @return the number of bytes available before blocking. - * @since Android 1.0 */ @Override public synchronized int available() { @@ -85,10 +75,9 @@ public class StringBufferInputStream extends InputStream { * Reads a single byte from the source string and returns it as an integer * in the range from 0 to 255. Returns -1 if the end of the source string * has been reached. - * + * * @return the byte read or -1 if the end of the source string has been * reached. - * @since Android 1.0 */ @Override public synchronized int read() { @@ -98,7 +87,7 @@ public class StringBufferInputStream extends InputStream { /** * Reads at most {@code length} bytes from the source string and stores them * in the byte array {@code b} starting at {@code offset}. - * + * * @param b * the byte array in which to store the bytes read. * @param offset @@ -114,7 +103,6 @@ public class StringBufferInputStream extends InputStream { * {@code b}. * @throws NullPointerException * if {@code b} is {@code null}. - * @since Android 1.0 */ @Override public synchronized int read(byte[] b, int offset, int length) { @@ -153,8 +141,6 @@ public class StringBufferInputStream extends InputStream { /** * Resets this stream to the beginning of the source string. - * - * @since Android 1.0 */ @Override public synchronized void reset() { @@ -166,11 +152,10 @@ public class StringBufferInputStream extends InputStream { * returns 0 if {@code n} is negative. Less than {@code n} characters are * skipped if the end of the source string is reached before the operation * completes. - * + * * @param n * the number of characters to skip. * @return the number of characters actually skipped. - * @since Android 1.0 */ @Override public synchronized long skip(long n) { diff --git a/libcore/luni/src/main/java/java/io/StringReader.java b/libcore/luni/src/main/java/java/io/StringReader.java index 3a1942996..0a9e9bbd5 100644 --- a/libcore/luni/src/main/java/java/io/StringReader.java +++ b/libcore/luni/src/main/java/java/io/StringReader.java @@ -22,10 +22,8 @@ import org.apache.harmony.luni.util.Msg; /** * A specialized {@link Reader} that reads characters from a {@code String} in * a sequential manner. - * + * * @see StringWriter - * - * @since Android 1.0 */ public class StringReader extends Reader { private String str; @@ -40,13 +38,12 @@ public class StringReader extends Reader { * Construct a new {@code StringReader} with {@code str} as source. The size * of the reader is set to the {@code length()} of the string and the Object * to synchronize access through is set to {@code str}. - * + * * @param str * the source string for this reader. - * @since Android 1.0 */ public StringReader(String str) { - super(str); + super(); this.str = str; this.count = str.length(); } @@ -55,22 +52,15 @@ public class StringReader extends Reader { * Closes this reader. Once it is closed, read operations on this reader * will throw an {@code IOException}. Only the first invocation of this * method has any effect. - * - * @since Android 1.0 */ @Override public void close() { - synchronized (lock) { - if (isClosed()) { - return; - } - str = null; - } + str = null; } /** * Returns a boolean indicating whether this reader is closed. - * + * * @return {@code true} if closed, otherwise {@code false}. */ private boolean isClosed() { @@ -81,7 +71,7 @@ public class StringReader extends Reader { * Sets a mark position in this reader. The parameter {@code readLimit} is * ignored for this class. Calling {@code reset()} will reposition the * reader back to the marked position. - * + * * @param readLimit * ignored for {@code StringReader} instances. * @throws IllegalArgumentException @@ -90,7 +80,6 @@ public class StringReader extends Reader { * if this reader is closed. * @see #markSupported() * @see #reset() - * @since Android 1.0 */ @Override public void mark(int readLimit) throws IOException { @@ -109,9 +98,8 @@ public class StringReader extends Reader { /** * Indicates whether this reader supports the {@code mark()} and {@code * reset()} methods. This implementation returns {@code true}. - * + * * @return always {@code true}. - * @since Android 1.0 */ @Override public boolean markSupported() { @@ -122,12 +110,11 @@ public class StringReader extends Reader { * Reads a single character from the source string and returns it as an * integer with the two higher-order bytes set to 0. Returns -1 if the end * of the source string has been reached. - * + * * @return the character read or -1 if the end of the source string has been * reached. * @throws IOException * if this reader is closed. - * @since Android 1.0 */ @Override public int read() throws IOException { @@ -147,7 +134,7 @@ public class StringReader extends Reader { * them at {@code offset} in the character array {@code buf}. Returns the * number of characters actually read or -1 if the end of the source string * has been reached. - * + * * @param buf * the character array to store the characters read. * @param offset @@ -162,14 +149,12 @@ public class StringReader extends Reader { * {@code offset + len} is greater than the size of {@code buf}. * @throws IOException * if this reader is closed. - * @since Android 1.0 */ @Override public int read(char[] buf, int offset, int len) throws IOException { // BEGIN android-note // changed array notation to be consistent with the rest of harmony // END android-note - // avoid int overflow // BEGIN android-changed // Exception priorities (in case of multiple errors) differ from // RI, but are spec-compliant. @@ -178,14 +163,18 @@ public class StringReader extends Reader { if (buf == null) { throw new NullPointerException(Msg.getString("K0047")); //$NON-NLS-1$ } - if ((offset | len) < 0 || len > buf.length - offset) { - throw new ArrayIndexOutOfBoundsException(Msg.getString("K002f")); //$NON-NLS-1$ - } - // END android-changed synchronized (lock) { + // avoid int overflow + if ((offset | len) < 0 || len > buf.length - offset) { + throw new ArrayIndexOutOfBoundsException(Msg.getString("K002f")); //$NON-NLS-1$ + } + // END android-changed if (isClosed()) { throw new IOException(Msg.getString("K0083")); //$NON-NLS-1$ } + if (len == 0) { + return 0; + } if (pos == this.count) { return -1; } @@ -200,13 +189,12 @@ public class StringReader extends Reader { /** * Indicates whether this reader is ready to be read without blocking. This * implementation always returns {@code true}. - * + * * @return always {@code true}. * @throws IOException * if this reader is closed. * @see #read() * @see #read(char[], int, int) - * @since Android 1.0 */ @Override public boolean ready() throws IOException { @@ -223,12 +211,11 @@ public class StringReader extends Reader { * Invocations of {@code read()} and {@code skip()} will occur from this new * location. If this reader has not been marked, it is reset to the * beginning of the source string. - * + * * @throws IOException * if this reader is closed. * @see #mark(int) * @see #markSupported() - * @since Android 1.0 */ @Override public void reset() throws IOException { @@ -244,7 +231,7 @@ public class StringReader extends Reader { * Skips {@code amount} characters in the source string. Subsequent calls of * {@code read} methods will not return these characters unless {@code * reset()} is used. - * + * * @param ns * the maximum number of characters to skip. * @return the number of characters actually skipped or 0 if {@code ns < 0}. @@ -253,7 +240,6 @@ public class StringReader extends Reader { * @see #mark(int) * @see #markSupported() * @see #reset() - * @since Android 1.0 */ @Override public long skip(long ns) throws IOException { diff --git a/libcore/luni/src/main/java/java/io/StringWriter.java b/libcore/luni/src/main/java/java/io/StringWriter.java index 01a6db82b..45f6c1633 100644 --- a/libcore/luni/src/main/java/java/io/StringWriter.java +++ b/libcore/luni/src/main/java/java/io/StringWriter.java @@ -26,10 +26,8 @@ import org.apache.harmony.luni.util.Msg; * in a sequential manner, appending them in the process. The result can later * be queried using the {@link #StringWriter(int)} or {@link #toString()} * methods. - * + * * @see StringReader - * - * @since Android 1.0 */ public class StringWriter extends Writer { @@ -40,8 +38,6 @@ public class StringWriter extends Writer { * allocated with the default size of 16 characters. The {@code * StringBuffer} is also the {@code lock} used to synchronize access to this * writer. - * - * @since Android 1.0 */ public StringWriter() { super(); @@ -54,10 +50,9 @@ public class StringWriter extends Writer { * allocated with a size of {@code initialSize} characters. The {@code * StringBuffer} is also the {@code lock} used to synchronize access to this * writer. - * + * * @param initialSize * the intial size of the target string buffer. - * @since Android 1.0 */ public StringWriter(int initialSize) { if (initialSize < 0) { @@ -71,10 +66,9 @@ public class StringWriter extends Writer { * Calling this method has no effect. In contrast to most {@code Writer} subclasses, * the other methods in {@code StringWriter} do not throw an {@code IOException} if * {@code close()} has been called. - * + * * @throws IOException * if an error occurs while closing this writer. - * @since Android 1.0 */ @Override public void close() throws IOException { @@ -83,8 +77,6 @@ public class StringWriter extends Writer { /** * Calling this method has no effect. - * - * @since Android 1.0 */ @Override public void flush() { @@ -94,33 +86,27 @@ public class StringWriter extends Writer { /** * Gets a reference to this writer's internal {@link StringBuffer}. Any * changes made to the returned buffer are reflected in this writer. - * + * * @return a reference to this writer's internal {@code StringBuffer}. - * @since Android 1.0 */ public StringBuffer getBuffer() { - synchronized (lock) { - return buf; - } + return buf; } /** * Gets a copy of the contents of this writer as a string. - * + * * @return this writer's contents as a string. - * @since Android 1.0 */ @Override public String toString() { - synchronized (lock) { - return buf.toString(); - } + return buf.toString(); } /** * Writes {@code count} characters starting at {@code offset} in {@code buf} * to this writer's {@code StringBuffer}. - * + * * @param cbuf * the non-null character array to write. * @param offset @@ -130,7 +116,6 @@ public class StringWriter extends Writer { * @throws IndexOutOfBoundsException * if {@code offset < 0} or {@code count < 0}, or if {@code * offset + count} is greater than the size of {@code buf}. - * @since Android 1.0 */ @Override public void write(char[] cbuf, int offset, int count) { @@ -147,45 +132,40 @@ public class StringWriter extends Writer { throw new IndexOutOfBoundsException(Msg.getString("K002f")); //$NON-NLS-1$ } // END android-changed - synchronized (lock) { - this.buf.append(cbuf, offset, count); + if (count == 0) { + return; } + buf.append(cbuf, offset, count); } /** * Writes one character to this writer's {@code StringBuffer}. Only the two * least significant bytes of the integer {@code oneChar} are written. - * + * * @param oneChar * the character to write to this writer's {@code StringBuffer}. - * @since Android 1.0 */ @Override public void write(int oneChar) { - synchronized (lock) { - buf.append((char) oneChar); - } + buf.append((char) oneChar); } /** * Writes the characters from the specified string to this writer's {@code * StringBuffer}. - * + * * @param str * the non-null string containing the characters to write. - * @since Android 1.0 */ @Override public void write(String str) { - synchronized (lock) { - buf.append(str); - } + buf.append(str); } /** * Writes {@code count} characters from {@code str} starting at {@code * offset} to this writer's {@code StringBuffer}. - * + * * @param str * the non-null string containing the characters to write. * @param offset @@ -195,24 +175,20 @@ public class StringWriter extends Writer { * @throws StringIndexOutOfBoundsException * if {@code offset < 0} or {@code count < 0}, or if {@code * offset + count} is greater than the length of {@code str}. - * @since Android 1.0 */ @Override public void write(String str, int offset, int count) { String sub = str.substring(offset, offset + count); - synchronized (lock) { - buf.append(sub); - } + buf.append(sub); } /** * Appends the character {@code c} to this writer's {@code StringBuffer}. * This method works the same way as {@link #write(int)}. - * + * * @param c * the character to append to the target stream. * @return this writer. - * @since Android 1.0 */ @Override public StringWriter append(char c) { @@ -225,18 +201,17 @@ public class StringWriter extends Writer { * StringBuffer}. This method works the same way as {@code * StringWriter.write(csq.toString())}. If {@code csq} is {@code null}, then * the string "null" is written to the target stream. - * + * * @param csq * the character sequence appended to the target. * @return this writer. - * @since Android 1.0 */ @Override public StringWriter append(CharSequence csq) { if (null == csq) { - append(TOKEN_NULL, 0, TOKEN_NULL.length()); + write(TOKEN_NULL); } else { - append(csq, 0, csq.length()); + write(csq.toString()); } return this; } @@ -247,7 +222,7 @@ public class StringWriter extends Writer { * StringWriter.writer(csq.subsequence(start, end).toString())}. If {@code * csq} is {@code null}, then the specified subsequence of the string "null" * will be written to the target. - * + * * @param csq * the character sequence appended to the target. * @param start @@ -261,7 +236,6 @@ public class StringWriter extends Writer { * if {@code start > end}, {@code start < 0}, {@code end < 0} or * either {@code start} or {@code end} are greater or equal than * the length of {@code csq}. - * @since Android 1.0 */ @Override public StringWriter append(CharSequence csq, int start, int end) { diff --git a/libcore/luni/src/main/java/java/io/SyncFailedException.java b/libcore/luni/src/main/java/java/io/SyncFailedException.java index 2f8d9c70d..c4f734073 100644 --- a/libcore/luni/src/main/java/java/io/SyncFailedException.java +++ b/libcore/luni/src/main/java/java/io/SyncFailedException.java @@ -20,8 +20,6 @@ package java.io; /** * Signals that the {@link FileDescriptor#sync()} method has failed to * complete. - * - * @since Android 1.0 */ public class SyncFailedException extends IOException { @@ -33,7 +31,6 @@ public class SyncFailedException extends IOException { * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public SyncFailedException(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/io/UTFDataFormatException.java b/libcore/luni/src/main/java/java/io/UTFDataFormatException.java index 20a7abe7d..215e3e2ed 100644 --- a/libcore/luni/src/main/java/java/io/UTFDataFormatException.java +++ b/libcore/luni/src/main/java/java/io/UTFDataFormatException.java @@ -22,9 +22,7 @@ package java.io; * likely while reading some {@link DataInputStream}. * * @see DataInputStream#readUTF() - * - * @since Android 1.0 -*/ + */ public class UTFDataFormatException extends IOException { private static final long serialVersionUID = 420743449228280612L; @@ -32,8 +30,6 @@ public class UTFDataFormatException extends IOException { /** * Constructs a new {@code UTFDataFormatException} with its stack trace * filled in. - * - * @since Android 1.0 */ public UTFDataFormatException() { super(); @@ -45,7 +41,6 @@ public class UTFDataFormatException extends IOException { * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public UTFDataFormatException(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/io/UnsupportedEncodingException.java b/libcore/luni/src/main/java/java/io/UnsupportedEncodingException.java index deeab293d..eca6c8135 100644 --- a/libcore/luni/src/main/java/java/io/UnsupportedEncodingException.java +++ b/libcore/luni/src/main/java/java/io/UnsupportedEncodingException.java @@ -18,11 +18,8 @@ package java.io; /** - * Signals that a requested character encoding is not available, either because - * it is not included a specific Android system, or because the encoding name - * is simply incorrect. - * - * @since Android 1.0 + * Thrown when a program asks for a particular character converter that is + * unavailable. */ public class UnsupportedEncodingException extends IOException { @@ -31,8 +28,6 @@ public class UnsupportedEncodingException extends IOException { /** * Constructs a new {@code UnsupportedEncodingException} with its stack * trace filled in. - * - * @since Android 1.0 */ public UnsupportedEncodingException() { super(); @@ -44,7 +39,6 @@ public class UnsupportedEncodingException extends IOException { * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public UnsupportedEncodingException(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/io/WriteAbortedException.java b/libcore/luni/src/main/java/java/io/WriteAbortedException.java index 02c2f70f2..11ee1fe60 100644 --- a/libcore/luni/src/main/java/java/io/WriteAbortedException.java +++ b/libcore/luni/src/main/java/java/io/WriteAbortedException.java @@ -26,8 +26,6 @@ package java.io; * object. * * @see ObjectInputStream#readObject() - * - * @since Android 1.0 */ public class WriteAbortedException extends ObjectStreamException { @@ -36,8 +34,6 @@ public class WriteAbortedException extends ObjectStreamException { /** * The exception that occured when writeObject() was attempting to serialize * the object. - * - * @since Android 1.0 */ public Exception detail; @@ -50,7 +46,6 @@ public class WriteAbortedException extends ObjectStreamException { * the detail message for this exception. * @param rootCause * the exception that was thrown when serializing the object. - * @since Android 1.0 */ public WriteAbortedException(String detailMessage, Exception rootCause) { super(detailMessage); @@ -64,7 +59,6 @@ public class WriteAbortedException extends ObjectStreamException { * time. * * @return the exception message. - * @since Android 1.0 */ @Override public String getMessage() { @@ -79,7 +73,6 @@ public class WriteAbortedException extends ObjectStreamException { * Gets the cause of this exception or {@code null} if there is no cause. * * @return the exception cause. - * @since Android 1.0 */ @Override public Throwable getCause() { diff --git a/libcore/luni/src/main/java/java/io/Writer.java b/libcore/luni/src/main/java/java/io/Writer.java index 5505e809e..dfb37c575 100644 --- a/libcore/luni/src/main/java/java/io/Writer.java +++ b/libcore/luni/src/main/java/java/io/Writer.java @@ -31,10 +31,8 @@ package java.io; *

          * Many specialized readers for purposes like reading from a file already exist * in this package. - * + * * @see Reader - * - * @since Android 1.0 */ public abstract class Writer implements Appendable, Closeable, Flushable { @@ -42,16 +40,12 @@ public abstract class Writer implements Appendable, Closeable, Flushable { /** * The object used to synchronize access to the writer. - * - * @since Android 1.0 */ protected Object lock; /** * Constructs a new {@code Writer} with {@code this} as the object used to * synchronize critical sections. - * - * @since Android 1.0 */ protected Writer() { super(); @@ -61,12 +55,11 @@ public abstract class Writer implements Appendable, Closeable, Flushable { /** * Constructs a new {@code Writer} with {@code lock} used to synchronize * critical sections. - * + * * @param lock * the {@code Object} used to synchronize critical sections. * @throws NullPointerException * if {@code lock} is {@code null}. - * @since Android 1.0 */ protected Writer(Object lock) { if (lock == null) { @@ -78,31 +71,28 @@ public abstract class Writer implements Appendable, Closeable, Flushable { /** * Closes this writer. Implementations of this method should free any * resources associated with the writer. - * + * * @throws IOException * if an error occurs while closing this writer. - * @since Android 1.0 */ public abstract void close() throws IOException; /** * Flushes this writer. Implementations of this method should ensure that * all buffered characters are written to the target. - * + * * @throws IOException * if an error occurs while flushing this writer. - * @since Android 1.0 */ public abstract void flush() throws IOException; /** * Writes the entire character buffer {@code buf} to the target. - * + * * @param buf * the non-null array containing characters to write. * @throws IOException * if this writer is closed or another I/O error occurs. - * @since Android 1.0 */ public void write(char[] buf) throws IOException { // BEGIN android-note @@ -114,7 +104,7 @@ public abstract class Writer implements Appendable, Closeable, Flushable { /** * Writes {@code count} characters starting at {@code offset} in {@code buf} * to the target. - * + * * @param buf * the non-null character array to write. * @param offset @@ -126,7 +116,6 @@ public abstract class Writer implements Appendable, Closeable, Flushable { * offset + count} is greater than the size of {@code buf}. * @throws IOException * if this writer is closed or another I/O error occurs. - * @since Android 1.0 */ public abstract void write(char[] buf, int offset, int count) throws IOException; @@ -137,12 +126,11 @@ public abstract class Writer implements Appendable, Closeable, Flushable { /** * Writes one character to the target. Only the two least significant bytes * of the integer {@code oneChar} are written. - * + * * @param oneChar * the character to write to the target. * @throws IOException * if this writer is closed or another I/O error occurs. - * @since Android 1.0 */ public void write(int oneChar) throws IOException { synchronized (lock) { @@ -154,12 +142,11 @@ public abstract class Writer implements Appendable, Closeable, Flushable { /** * Writes the characters from the specified string to the target. - * + * * @param str * the non-null string containing the characters to write. * @throws IOException * if this writer is closed or another I/O error occurs. - * @since Android 1.0 */ public void write(String str) throws IOException { char buf[] = new char[str.length()]; @@ -172,7 +159,7 @@ public abstract class Writer implements Appendable, Closeable, Flushable { /** * Writes {@code count} characters from {@code str} starting at {@code * offset} to the target. - * + * * @param str * the non-null string containing the characters to write. * @param offset @@ -184,7 +171,6 @@ public abstract class Writer implements Appendable, Closeable, Flushable { * @throws IndexOutOfBoundsException * if {@code offset < 0} or {@code count < 0}, or if {@code * offset + count} is greater than the length of {@code str}. - * @since Android 1.0 */ public void write(String str, int offset, int count) throws IOException { if (count < 0) { // other cases tested by getChars() @@ -201,13 +187,12 @@ public abstract class Writer implements Appendable, Closeable, Flushable { /** * Appends the character {@code c} to the target. This method works the same * way as {@link #write(int)}. - * + * * @param c * the character to append to the target stream. * @return this writer. * @throws IOException * if this writer is closed or another I/O error occurs. - * @since Android 1.0 */ public Writer append(char c) throws IOException { write(c); @@ -219,13 +204,12 @@ public abstract class Writer implements Appendable, Closeable, Flushable { * works the same way as {@code Writer.write(csq.toString())}. If {@code * csq} is {@code null}, then the string "null" is written to the target * stream. - * + * * @param csq * the character sequence appended to the target. * @return this writer. * @throws IOException * if this writer is closed or another I/O error occurs. - * @since Android 1.0 */ public Writer append(CharSequence csq) throws IOException { if (null == csq) { @@ -242,7 +226,7 @@ public abstract class Writer implements Appendable, Closeable, Flushable { * Writer.writer(csq.subsequence(start, end).toString())}. If {@code * csq} is {@code null}, then the specified subsequence of the string "null" * will be written to the target. - * + * * @param csq * the character sequence appended to the target. * @param start @@ -258,7 +242,6 @@ public abstract class Writer implements Appendable, Closeable, Flushable { * if {@code start > end}, {@code start < 0}, {@code end < 0} or * either {@code start} or {@code end} are greater or equal than * the length of {@code csq}. - * @since Android 1.0 */ public Writer append(CharSequence csq, int start, int end) throws IOException { diff --git a/libcore/luni/src/main/java/java/lang/AbstractMethodError.java b/libcore/luni/src/main/java/java/lang/AbstractMethodError.java index cdfa0ec72..4d5238ad0 100644 --- a/libcore/luni/src/main/java/java/lang/AbstractMethodError.java +++ b/libcore/luni/src/main/java/java/lang/AbstractMethodError.java @@ -22,8 +22,6 @@ package java.lang; *

          * Note that this can only occur when inconsistent class files have been loaded, * since invoking an abstract method is a compile time error. - *

          - * @since Android 1.0 */ public class AbstractMethodError extends IncompatibleClassChangeError { @@ -32,8 +30,6 @@ public class AbstractMethodError extends IncompatibleClassChangeError { /** * Constructs a new {@code AbstractMethodError} that includes the current * stack trace. - * - * @since Android 1.0 */ public AbstractMethodError() { super(); @@ -42,10 +38,9 @@ public class AbstractMethodError extends IncompatibleClassChangeError { /** * Constructs a new {@code AbstractMethodError} with the current stack trace * and the specified detail message. - * + * * @param detailMessage * the detail message for this error. - * @since Android 1.0 */ public AbstractMethodError(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/lang/AbstractStringBuilder.java b/libcore/luni/src/main/java/java/lang/AbstractStringBuilder.java index 39459a026..c481e118d 100644 --- a/libcore/luni/src/main/java/java/lang/AbstractStringBuilder.java +++ b/libcore/luni/src/main/java/java/lang/AbstractStringBuilder.java @@ -23,15 +23,13 @@ import java.util.Arrays; import org.apache.harmony.luni.util.Msg; /** - *

          * A modifiable {@link CharSequence sequence of characters} for use in creating * and modifying Strings. This class is intended as a base class for * {@link StringBuffer} and {@link StringBuilder}. - *

          * * @see StringBuffer * @see StringBuilder - * @since Android 1.0 + * @since 1.5 */ abstract class AbstractStringBuilder { @@ -95,8 +93,8 @@ abstract class AbstractStringBuilder { } private void enlargeBuffer(int min) { - int twice = (value.length << 1) + 2; - char[] newData = new char[min > twice ? min : twice]; + int newSize = ((value.length >> 1) + value.length) + 2; + char[] newData = new char[min > newSize ? min : newSize]; System.arraycopy(value, 0, newData, 0, count); value = newData; shared = false; @@ -106,9 +104,6 @@ abstract class AbstractStringBuilder { int newSize = count + 4; if (newSize > value.length) { enlargeBuffer(newSize); - } else if (shared) { - value = value.clone(); - shared = false; } value[count++] = 'n'; value[count++] = 'u'; @@ -120,9 +115,6 @@ abstract class AbstractStringBuilder { int newSize = count + chars.length; if (newSize > value.length) { enlargeBuffer(newSize); - } else if (shared) { - value = value.clone(); - shared = false; } System.arraycopy(chars, 0, value, count, chars.length); count = newSize; @@ -137,9 +129,6 @@ abstract class AbstractStringBuilder { int newSize = count + length; if (newSize > value.length) { enlargeBuffer(newSize); - } else if (shared) { - value = value.clone(); - shared = false; } System.arraycopy(chars, start, value, count, length); count = newSize; @@ -152,10 +141,6 @@ abstract class AbstractStringBuilder { if (count == value.length) { enlargeBuffer(count + 1); } - if (shared) { - value = value.clone(); - shared = false; - } value[count++] = ch; } @@ -168,9 +153,6 @@ abstract class AbstractStringBuilder { int newSize = count + adding; if (newSize > value.length) { enlargeBuffer(newSize); - } else if (shared) { - value = value.clone(); - shared = false; } // BEGIN android-changed string._getChars(0, adding, value, count); @@ -213,7 +195,7 @@ abstract class AbstractStringBuilder { /** * Returns the number of characters that can be held without growing. - * + * * @return the capacity * @see #ensureCapacity * @see #length @@ -224,7 +206,7 @@ abstract class AbstractStringBuilder { /** * Retrieves the character at the {@code index}. - * + * * @param index * the index of the character to retrieve. * @return the char value. @@ -249,7 +231,7 @@ abstract class AbstractStringBuilder { } if (end > start) { int length = count - end; - if (length > 0) { + if (length >= 0) { if (!shared) { System.arraycopy(value, end, value, start, length); } else { @@ -296,20 +278,21 @@ abstract class AbstractStringBuilder { * value of either the {@code minimumCapacity} or the current capacity * multiplied by two plus two. Although this is the general policy, there is * no guarantee that the capacity will change. - * + * * @param min * the new minimum capacity to set. */ public void ensureCapacity(int min) { if (min > value.length) { - enlargeBuffer(min); + int twice = (value.length << 1) + 2; + enlargeBuffer(twice > min ? twice : min); } } /** * Copies the requested sequence of characters to the {@code char[]} passed * starting at {@code destStart}. - * + * * @param start * the inclusive start index of the characters to copy. * @param end @@ -343,7 +326,7 @@ abstract class AbstractStringBuilder { } } - final void insert0(int index, char chars[], int start, int length) { + final void insert0(int index, char[] chars, int start, int length) { if (0 <= index && index <= count) { // start + length could overflow, start/length maybe MaxInt if (start >= 0 && 0 <= length && length <= chars.length - start) { @@ -354,8 +337,9 @@ abstract class AbstractStringBuilder { } return; } - throw new StringIndexOutOfBoundsException("offset " + start - + ", len " + length + ", array.length " + chars.length); + throw new StringIndexOutOfBoundsException("offset " + start //$NON-NLS-1$ + + ", length " + length //$NON-NLS-1$ + + ", char[].length " + chars.length); //$NON-NLS-1$ } throw new StringIndexOutOfBoundsException(index); } @@ -401,7 +385,7 @@ abstract class AbstractStringBuilder { /** * The current length. - * + * * @return the number of characters contained in this instance. */ public int length() { @@ -481,15 +465,72 @@ abstract class AbstractStringBuilder { return; } if (!shared) { - for (int i = 0, end = count, mid = count / 2; i < mid; i++) { - char temp = value[--end]; - value[end] = value[i]; - value[i] = temp; + int end = count - 1; + char frontHigh = value[0]; + char endLow = value[end]; + boolean allowFrontSur = true, allowEndSur = true; + for (int i = 0, mid = count / 2; i < mid; i++, --end) { + char frontLow = value[i + 1]; + char endHigh = value[end - 1]; + boolean surAtFront = allowFrontSur && frontLow >= 0xdc00 + && frontLow <= 0xdfff && frontHigh >= 0xd800 + && frontHigh <= 0xdbff; + if (surAtFront && (count < 3)) { + return; + } + boolean surAtEnd = allowEndSur && endHigh >= 0xd800 + && endHigh <= 0xdbff && endLow >= 0xdc00 + && endLow <= 0xdfff; + allowFrontSur = allowEndSur = true; + if (surAtFront == surAtEnd) { + if (surAtFront) { + // both surrogates + value[end] = frontLow; + value[end - 1] = frontHigh; + value[i] = endHigh; + value[i + 1] = endLow; + frontHigh = value[i + 2]; + endLow = value[end - 2]; + i++; + end--; + } else { + // neither surrogates + value[end] = frontHigh; + value[i] = endLow; + frontHigh = frontLow; + endLow = endHigh; + } + } else { + if (surAtFront) { + // surrogate only at the front + value[end] = frontLow; + value[i] = endLow; + endLow = endHigh; + allowFrontSur = false; + } else { + // surrogate only at the end + value[end] = frontHigh; + value[i] = endHigh; + frontHigh = frontLow; + allowEndSur = false; + } + } + } + if ((count & 1) == 1 && (!allowFrontSur || !allowEndSur)) { + value[end] = allowFrontSur ? endLow : frontHigh; } } else { char[] newData = new char[value.length]; for (int i = 0, end = count; i < count; i++) { - newData[--end] = value[i]; + char high = value[i]; + if ((i + 1) < count && high >= 0xd800 && high <= 0xdbff) { + char low = value[i + 1]; + if (low >= 0xdc00 && low <= 0xdfff) { + newData[--end] = low; + i++; + } + } + newData[--end] = high; } value = newData; shared = false; @@ -498,7 +539,7 @@ abstract class AbstractStringBuilder { /** * Sets the character at the {@code index}. - * + * * @param index * the zero-based index of the character to replace. * @param ch @@ -522,7 +563,7 @@ abstract class AbstractStringBuilder { * Sets the current length to a new value. If the new length is larger than * the current length, then the new characters at the end of this object * will contain the {@code char} value of {@code \u0000}. - * + * * @param length * the new length of this StringBuffer. * @exception IndexOutOfBoundsException @@ -533,16 +574,16 @@ abstract class AbstractStringBuilder { if (length < 0) { throw new StringIndexOutOfBoundsException(length); } - if (count < length) { - if (length > value.length) { - enlargeBuffer(length); + if (length > value.length) { + enlargeBuffer(length); + } else { + if (shared) { + char[] newData = new char[value.length]; + System.arraycopy(value, 0, newData, 0, count); + value = newData; + shared = false; } else { - if (shared) { - char[] newData = new char[value.length]; - System.arraycopy(value, 0, newData, 0, count); - value = newData; - shared = false; - } else { + if (count < length) { Arrays.fill(value, count, length, (char) 0); } } @@ -553,7 +594,7 @@ abstract class AbstractStringBuilder { /** * Returns the String value of the subsequence from the {@code start} index * to the current end. - * + * * @param start * the inclusive start index to begin the subsequence. * @return a String containing the subsequence. @@ -567,8 +608,8 @@ abstract class AbstractStringBuilder { return ""; //$NON-NLS-1$ } - shared = true; - return new String(start, count - start, value); + // Remove String sharing for more performance + return new String(value, start, count - start); } throw new StringIndexOutOfBoundsException(start); } @@ -576,7 +617,7 @@ abstract class AbstractStringBuilder { /** * Returns the String value of the subsequence from the {@code start} index * to the {@code end} index. - * + * * @param start * the inclusive start index to begin the subsequence. * @param end @@ -592,7 +633,7 @@ abstract class AbstractStringBuilder { return ""; //$NON-NLS-1$ } - shared = true; + // Remove String sharing for more performance return new String(value, start, end - start); } throw new StringIndexOutOfBoundsException(); @@ -600,7 +641,7 @@ abstract class AbstractStringBuilder { /** * Returns the current String representation. - * + * * @return a String containing the characters in this instance. */ @Override @@ -608,8 +649,10 @@ abstract class AbstractStringBuilder { if (count == 0) { return ""; //$NON-NLS-1$ } - - if (count >= 256 && count <= (value.length >> 1)) { + // Optimize String sharing for more performance + int wasted = value.length - count; + if (wasted >= 256 + || (wasted >= INITIAL_CAPACITY && wasted >= (count >> 1))) { return new String(value, 0, count); } shared = true; @@ -619,7 +662,7 @@ abstract class AbstractStringBuilder { /** * Returns a {@code CharSequence} of the subsequence from the {@code start} * index to the {@code end} index. - * + * * @param start * the inclusive start index to begin the subsequence. * @param end @@ -628,6 +671,7 @@ abstract class AbstractStringBuilder { * @throws IndexOutOfBoundsException * if {@code start} is negative, greater than {@code end} or if * {@code end} is greater than the current {@link #length()}. + * @since 1.4 */ public CharSequence subSequence(int start, int end) { return substring(start, end); @@ -636,12 +680,13 @@ abstract class AbstractStringBuilder { /** * Searches for the first index of the specified character. The search for * the character starts at the beginning and moves towards the end. - * + * * @param string * the string to find. * @return the index of the specified character, -1 if the character isn't * found. * @see #lastIndexOf(String) + * @since 1.4 */ public int indexOf(String string) { return indexOf(string, 0); @@ -650,7 +695,7 @@ abstract class AbstractStringBuilder { /** * Searches for the index of the specified character. The search for the * character starts at the specified offset and moves towards the end. - * + * * @param subString * the string to find. * @param start @@ -658,6 +703,7 @@ abstract class AbstractStringBuilder { * @return the index of the specified character, -1 if the character isn't * found * @see #lastIndexOf(String,int) + * @since 1.4 */ public int indexOf(String subString, int start) { if (start < 0) { @@ -698,12 +744,15 @@ abstract class AbstractStringBuilder { /** * Searches for the last index of the specified character. The search for * the character starts at the end and moves towards the beginning. - * + * * @param string * the string to find. * @return the index of the specified character, -1 if the character isn't * found. - * @see String#lastIndexOf(String) + * @throws NullPointerException + * if {@code string} is {@code null}. + * @see String#lastIndexOf(java.lang.String) + * @since 1.4 */ public int lastIndexOf(String string) { return lastIndexOf(string, count); @@ -712,14 +761,17 @@ abstract class AbstractStringBuilder { /** * Searches for the index of the specified character. The search for the * character starts at the specified offset and moves towards the beginning. - * + * * @param subString * the string to find. * @param start * the starting offset. * @return the index of the specified character, -1 if the character isn't * found. + * @throws NullPointerException + * if {@code subString} is {@code null}. * @see String#lastIndexOf(String,int) + * @since 1.4 */ public int lastIndexOf(String subString, int start) { int subCount = subString.length(); @@ -762,6 +814,8 @@ abstract class AbstractStringBuilder { /** * Trims off any extra capacity beyond the current length. Note, this method * is NOT guaranteed to change the capacity of this object. + * + * @since 1.5 */ public void trimToSize() { if (count < value.length) { @@ -774,7 +828,7 @@ abstract class AbstractStringBuilder { /** * Retrieves the Unicode code point value at the {@code index}. - * + * * @param index * the index to the {@code char} code unit. * @return the Unicode code point value. @@ -783,6 +837,7 @@ abstract class AbstractStringBuilder { * {@link #length()}. * @see Character * @see Character#codePointAt(char[], int, int) + * @since 1.5 */ public int codePointAt(int index) { if (index < 0 || index >= count) { @@ -793,7 +848,7 @@ abstract class AbstractStringBuilder { /** * Retrieves the Unicode code point value that precedes the {@code index}. - * + * * @param index * the index to the {@code char} code unit within this object. * @return the Unicode code point value. @@ -801,7 +856,8 @@ abstract class AbstractStringBuilder { * if {@code index} is less than 1 or greater than * {@link #length()}. * @see Character - * @see Character#codePointBefore(char[], int) + * @see Character#codePointBefore(char[], int, int) + * @since 1.5 */ public int codePointBefore(int index) { if (index < 1 || index > count) { @@ -811,11 +867,9 @@ abstract class AbstractStringBuilder { } /** - *

          * Calculates the number of Unicode code points between {@code beginIndex} * and {@code endIndex}. - *

          - * + * * @param beginIndex * the inclusive beginning index of the subsequence. * @param endIndex @@ -827,6 +881,7 @@ abstract class AbstractStringBuilder { * {@link #length()}. * @see Character * @see Character#codePointCount(char[], int, int) + * @since 1.5 */ public int codePointCount(int beginIndex, int endIndex) { if (beginIndex < 0 || endIndex > count || beginIndex > endIndex) { @@ -837,11 +892,9 @@ abstract class AbstractStringBuilder { } /** - *

          * Returns the index that is offset {@code codePointOffset} code points from * {@code index}. - *

          - * + * * @param index * the index to calculate the offset from. * @param codePointOffset @@ -855,6 +908,7 @@ abstract class AbstractStringBuilder { * {@code codePointOffset}. * @see Character * @see Character#offsetByCodePoints(char[], int, int, int, int) + * @since 1.5 */ public int offsetByCodePoints(int index, int codePointOffset) { return Character.offsetByCodePoints(value, 0, count, index, diff --git a/libcore/luni/src/main/java/java/lang/Appendable.java b/libcore/luni/src/main/java/java/lang/Appendable.java index 1b4e702fe..ca4b53292 100644 --- a/libcore/luni/src/main/java/java/lang/Appendable.java +++ b/libcore/luni/src/main/java/java/lang/Appendable.java @@ -27,16 +27,13 @@ import java.io.IOException; *

          * {@code Appendable} itself does not guarantee thread safety. This * responsibility is up to the implementing class. - *

          *

          * Implementing classes can choose different exception handling mechanism. They * can choose to throw exceptions other than {@code IOException} or they do not * throw any exceptions at all and use error codes instead. - *

          - * @since Android 1.0 */ public interface Appendable { - + /** * Appends the specified character. * @@ -45,7 +42,6 @@ public interface Appendable { * @return this {@code Appendable}. * @throws IOException * if an I/O error occurs. - * @since Android 1.0 */ Appendable append(char c) throws IOException; @@ -55,14 +51,12 @@ public interface Appendable { * limited size. *

          * If {@code csq} is {@code null}, the characters "null" are appended. - *

          - * + * * @param csq * the character sequence to append. * @return this {@code Appendable}. * @throws IOException * if an I/O error occurs. - * @since Android 1.0 */ Appendable append(CharSequence csq) throws IOException; @@ -73,7 +67,6 @@ public interface Appendable { * to calling {@code append(csq.subSequence(start, end))}. *

          * If {@code csq} is {@code null}, the characters "null" are appended. - *

          * * @param csq * the character sequence to append. @@ -89,7 +82,6 @@ public interface Appendable { * or {@code end} is greater than the length of {@code csq}. * @throws IOException * if an I/O error occurs. - * @since Android 1.0 */ Appendable append(CharSequence csq, int start, int end) throws IOException; } diff --git a/libcore/luni/src/main/java/java/lang/ArithmeticException.java b/libcore/luni/src/main/java/java/lang/ArithmeticException.java index 74671fccc..a6ab2a098 100644 --- a/libcore/luni/src/main/java/java/lang/ArithmeticException.java +++ b/libcore/luni/src/main/java/java/lang/ArithmeticException.java @@ -19,8 +19,6 @@ package java.lang; /** * Thrown when the an invalid arithmetic operation is attempted. - * - * @since Android 1.0 */ public class ArithmeticException extends RuntimeException { @@ -29,8 +27,6 @@ public class ArithmeticException extends RuntimeException { /** * Constructs a new {@code ArithmeticException} that includes the current * stack trace. - * - * @since Android 1.0 */ public ArithmeticException() { super(); @@ -39,10 +35,9 @@ public class ArithmeticException extends RuntimeException { /** * Constructs a new {@code ArithmeticException} with the current stack trace * and the specified detail message. - * + * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public ArithmeticException(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/lang/ArrayIndexOutOfBoundsException.java b/libcore/luni/src/main/java/java/lang/ArrayIndexOutOfBoundsException.java index 0f231d3e6..d43a7e66f 100644 --- a/libcore/luni/src/main/java/java/lang/ArrayIndexOutOfBoundsException.java +++ b/libcore/luni/src/main/java/java/lang/ArrayIndexOutOfBoundsException.java @@ -22,8 +22,6 @@ import org.apache.harmony.luni.util.Msg; /** * Thrown when the an array is indexed with a value less than zero, or greater * than or equal to the size of the array. - * - * @since Android 1.0 */ public class ArrayIndexOutOfBoundsException extends IndexOutOfBoundsException { @@ -32,8 +30,6 @@ public class ArrayIndexOutOfBoundsException extends IndexOutOfBoundsException { /** * Constructs a new {@code ArrayIndexOutOfBoundsException} that includes the * current stack trace. - * - * @since Android 1.0 */ public ArrayIndexOutOfBoundsException() { super(); @@ -46,7 +42,6 @@ public class ArrayIndexOutOfBoundsException extends IndexOutOfBoundsException { * * @param index * the invalid index. - * @since Android 1.0 */ public ArrayIndexOutOfBoundsException(int index) { super(Msg.getString("K0052", index)); //$NON-NLS-1$ @@ -55,10 +50,9 @@ public class ArrayIndexOutOfBoundsException extends IndexOutOfBoundsException { /** * Constructs a new {@code ArrayIndexOutOfBoundsException} with the current * stack trace and the specified detail message. - * + * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public ArrayIndexOutOfBoundsException(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/lang/ArrayStoreException.java b/libcore/luni/src/main/java/java/lang/ArrayStoreException.java index b69697ac5..6cb9c8fb0 100644 --- a/libcore/luni/src/main/java/java/lang/ArrayStoreException.java +++ b/libcore/luni/src/main/java/java/lang/ArrayStoreException.java @@ -20,8 +20,6 @@ package java.lang; /** * Thrown when a program attempts to store an element of an incompatible type in * an array. - * - * @since Android 1.0 */ public class ArrayStoreException extends RuntimeException { @@ -30,8 +28,6 @@ public class ArrayStoreException extends RuntimeException { /** * Constructs a new {@code ArrayStoreException} that includes the current * stack trace. - * - * @since Android 1.0 */ public ArrayStoreException() { super(); @@ -40,10 +36,9 @@ public class ArrayStoreException extends RuntimeException { /** * Constructs a new {@code ArrayStoreException} with the current stack trace * and the specified detail message. - * + * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public ArrayStoreException(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/lang/AssertionError.java b/libcore/luni/src/main/java/java/lang/AssertionError.java index 8d3368b9f..e4d4ed07f 100644 --- a/libcore/luni/src/main/java/java/lang/AssertionError.java +++ b/libcore/luni/src/main/java/java/lang/AssertionError.java @@ -19,8 +19,8 @@ package java.lang; /** * Thrown when an assertion has failed. - * - * @since Android 1.0 + * + * @since 1.4 */ public class AssertionError extends Error { @@ -28,8 +28,6 @@ public class AssertionError extends Error { /** * Constructs a new {@code AssertionError} with no message. - * - * @since Android 1.0 */ public AssertionError() { super(); @@ -44,7 +42,6 @@ public class AssertionError extends Error { * @param detailMessage * the object to be converted into the detail message and * optionally the cause. - * @since Android 1.0 */ public AssertionError(Object detailMessage) { super(String.valueOf(detailMessage), @@ -58,7 +55,6 @@ public class AssertionError extends Error { * * @param detailMessage * the value to be converted into the message. - * @since Android 1.0 */ public AssertionError(boolean detailMessage) { this(String.valueOf(detailMessage)); @@ -70,7 +66,6 @@ public class AssertionError extends Error { * * @param detailMessage * the value to be converted into the message. - * @since Android 1.0 */ public AssertionError(char detailMessage) { this(String.valueOf(detailMessage)); @@ -82,7 +77,6 @@ public class AssertionError extends Error { * * @param detailMessage * the value to be converted into the message. - * @since Android 1.0 */ public AssertionError(int detailMessage) { this(Integer.toString(detailMessage)); @@ -94,7 +88,6 @@ public class AssertionError extends Error { * * @param detailMessage * the value to be converted into the message. - * @since Android 1.0 */ public AssertionError(long detailMessage) { this(Long.toString(detailMessage)); @@ -106,7 +99,6 @@ public class AssertionError extends Error { * * @param detailMessage * the value to be converted into the message. - * @since Android 1.0 */ public AssertionError(float detailMessage) { this(Float.toString(detailMessage)); @@ -118,7 +110,6 @@ public class AssertionError extends Error { * * @param detailMessage * the value to be converted into the message. - * @since Android 1.0 */ public AssertionError(double detailMessage) { this(Double.toString(detailMessage)); diff --git a/libcore/luni/src/main/java/java/lang/Boolean.java b/libcore/luni/src/main/java/java/lang/Boolean.java index c49c3849a..9c476a469 100644 --- a/libcore/luni/src/main/java/java/lang/Boolean.java +++ b/libcore/luni/src/main/java/java/lang/Boolean.java @@ -21,8 +21,8 @@ import java.io.Serializable; /** * The wrapper for the primitive type {@code boolean}. - * - * @since Android 1.0 + * + * @since 1.0 */ public final class Boolean implements Serializable, Comparable { @@ -36,8 +36,6 @@ public final class Boolean implements Serializable, Comparable { /** * The {@link Class} object that represents the primitive type {@code * boolean}. - * - * @since Android 1.0 */ @SuppressWarnings("unchecked") public static final Class TYPE = (Class) new boolean[0] @@ -49,16 +47,12 @@ public final class Boolean implements Serializable, Comparable { /** * The {@code Boolean} object that represents the primitive value * {@code true}. - * - * @since Android 1.0 */ public static final Boolean TRUE = new Boolean(true); /** * The {@code Boolean} object that represents the primitive value * {@code false}. - * - * @since Android 1.0 */ public static final Boolean FALSE = new Boolean(false); @@ -68,10 +62,9 @@ public final class Boolean implements Serializable, Comparable { * "true" using a non-case sensitive comparison, the result will be a * Boolean representing the primitive value {@code true}, otherwise it will * be a Boolean representing the primitive value {@code false}. - * + * * @param string * the string representing a boolean value. - * @since Android 1.0 */ public Boolean(String string) { this(parseBoolean(string)); @@ -80,10 +73,9 @@ public final class Boolean implements Serializable, Comparable { /** * Constructs a new {@code Boolean} with the specified primitive boolean * value. - * + * * @param value * the primitive boolean value, {@code true} or {@code false}. - * @since Android 1.0 */ public Boolean(boolean value) { this.value = value; @@ -92,9 +84,8 @@ public final class Boolean implements Serializable, Comparable { /** * Gets the primitive value of this boolean, either {@code true} or * {@code false}. - * + * * @return this object's primitive value, {@code true} or {@code false}. - * @since Android 1.0 */ public boolean booleanValue() { return value; @@ -104,12 +95,11 @@ public final class Boolean implements Serializable, Comparable { * Compares this instance with the specified object and indicates if they * are equal. In order to be equal, {@code o} must be an instance of * {@code Boolean} and have the same boolean value as this object. - * + * * @param o * the object to compare this boolean with. * @return {@code true} if the specified object is equal to this * {@code Boolean}; {@code false} otherwise. - * @since Android 1.0 */ @Override public boolean equals(Object o) { @@ -120,7 +110,7 @@ public final class Boolean implements Serializable, Comparable { /** * Compares this object to the specified boolean object to determine their * relative order. - * + * * @param that * the boolean object to compare this object to. * @return 0 if the value of this boolean and the value of {@code that} are @@ -129,7 +119,7 @@ public final class Boolean implements Serializable, Comparable { * negative value if the value if this boolean is {@code false} and * the value of {@code that} is {@code true}. * @see java.lang.Comparable - * @since Android 1.0 + * @since 1.5 */ public int compareTo(Boolean that) { if (that == null) { @@ -145,10 +135,9 @@ public final class Boolean implements Serializable, Comparable { /** * Returns an integer hash code for this boolean. - * + * * @return this boolean's hash code, which is {@code 1231} for {@code true} * values and {@code 1237} for {@code false} values. - * @since Android 1.0 */ @Override public int hashCode() { @@ -158,10 +147,9 @@ public final class Boolean implements Serializable, Comparable { /** * Returns a string containing a concise, human-readable description of this * boolean. - * + * * @return "true" if the value of this boolean is {@code true}, "false" * otherwise. - * @since Android 1.0 */ @Override public String toString() { @@ -171,14 +159,13 @@ public final class Boolean implements Serializable, Comparable { /** * Returns the {@code boolean} value of the system property identified by * {@code string}. - * + * * @param string * the name of the requested system property. * @return {@code true} if the system property named by {@code string} * exists and it is equal to "true" using case insensitive * comparison, {@code false} otherwise. * @see System#getProperty(String) - * @since Android 1.0 */ public static boolean getBoolean(String string) { if (string == null || string.length() == 0) { @@ -189,25 +176,24 @@ public final class Boolean implements Serializable, Comparable { /** * Parses the specified string as a {@code boolean}. - * + * * @param s * the string representation of a boolean value. * @return {@code true} if {@code s} is not {@code null} and is equal to * {@code "true"} using case insensitive comparison, {@code false} * otherwise. - * @since Android 1.0 + * @since 1.5 */ public static boolean parseBoolean(String s) { return "true".equalsIgnoreCase(s); //$NON-NLS-1$ } /** - * Converts the specified boolean to its string representation. - * + * Converts the specified boolean to its string representation. + * * @param value * the boolean to convert. * @return "true" if {@code value} is {@code true}, "false" otherwise. - * @since Android 1.0 */ public static String toString(boolean value) { return String.valueOf(value); @@ -215,13 +201,12 @@ public final class Boolean implements Serializable, Comparable { /** * Parses the specified string as a boolean value. - * + * * @param string * the string representation of a boolean value. * @return {@code Boolean.TRUE} if {@code string} is equal to "true" using * case insensitive comparison, {@code Boolean.FALSE} otherwise. * @see #parseBoolean(String) - * @since Android 1.0 */ public static Boolean valueOf(String string) { return parseBoolean(string) ? Boolean.TRUE : Boolean.FALSE; @@ -233,13 +218,11 @@ public final class Boolean implements Serializable, Comparable { * If it is not necessary to get a new {@code Boolean} instance, it is * recommended to use this method instead of the constructor, since it * returns its static instances, which results in better performance. - *

          - * + * * @param b * the boolean to convert to a {@code Boolean}. * @return {@code Boolean.TRUE} if {@code b} is equal to {@code true}, * {@code Boolean.FALSE} otherwise. - * @since Android 1.0 */ public static Boolean valueOf(boolean b) { return b ? Boolean.TRUE : Boolean.FALSE; diff --git a/libcore/luni/src/main/java/java/lang/Byte.java b/libcore/luni/src/main/java/java/lang/Byte.java index a74b53993..7e20493f9 100644 --- a/libcore/luni/src/main/java/java/lang/Byte.java +++ b/libcore/luni/src/main/java/java/lang/Byte.java @@ -19,10 +19,11 @@ package java.lang; /** * The wrapper for the primitive type {@code byte}. - * - * @since Android 1.0 + * + * @since 1.1 */ -public final class Byte extends Number implements Comparable { +public final class Byte extends Number implements Comparable { + private static final long serialVersionUID = -7183698231559129828L; /** @@ -32,30 +33,24 @@ public final class Byte extends Number implements Comparable { /** * The maximum {@code Byte} value, 27-1. - * - * @since Android 1.0 */ public static final byte MAX_VALUE = (byte) 0x7F; /** * The minimum {@code Byte} value, -27. - * - * @since Android 1.0 */ public static final byte MIN_VALUE = (byte) 0x80; - + /** * The number of bits needed to represent a {@code Byte} value in two's * complement form. - * - * @since Android 1.0 + * + * @since 1.5 */ public static final int SIZE = 8; /** * The {@link Class} object that represents the primitive type {@code byte}. - * - * @since Android 1.0 */ @SuppressWarnings("unchecked") public static final Class TYPE = (Class) new byte[0].getClass() @@ -63,7 +58,7 @@ public final class Byte extends Number implements Comparable { // Note: This can't be set to "byte.class", since *that* is // defined to be "java.lang.Byte.TYPE"; - + /** * A cache of instances used by {@link #valueOf(byte)} and auto-boxing. */ @@ -71,10 +66,9 @@ public final class Byte extends Number implements Comparable { /** * Constructs a new {@code Byte} with the specified primitive byte value. - * + * * @param value * the primitive byte value to store in the new instance. - * @since Android 1.0 */ public Byte(byte value) { this.value = value; @@ -82,13 +76,12 @@ public final class Byte extends Number implements Comparable { /** * Constructs a new {@code Byte} from the specified string. - * + * * @param string * the string representation of a single byte value. * @throws NumberFormatException * if {@code string} can not be decoded into a byte value. * @see #parseByte(String) - * @since Android 1.0 */ public Byte(String string) throws NumberFormatException { this(parseByte(string)); @@ -96,9 +89,8 @@ public final class Byte extends Number implements Comparable { /** * Gets the primitive value of this byte. - * + * * @return this object's primitive value. - * @since Android 1.0 */ @Override public byte byteValue() { @@ -108,7 +100,7 @@ public final class Byte extends Number implements Comparable { /** * Compares this object to the specified byte object to determine their * relative order. - * + * * @param object * the byte object to compare this object to. * @return a negative value if the value of this byte is less than the value @@ -116,7 +108,7 @@ public final class Byte extends Number implements Comparable { * {@code object} are equal; a positive value if the value of this * byte is greater than the value of {@code object}. * @see java.lang.Comparable - * @since Android 1.0 + * @since 1.2 */ public int compareTo(Byte object) { return value > object.value ? 1 : (value < object.value ? -1 : 0); @@ -127,13 +119,12 @@ public final class Byte extends Number implements Comparable { * string can be decoded into a single byte value. The string may be an * optional minus sign "-" followed by a hexadecimal ("0x..." or "#..."), * octal ("0..."), or decimal ("...") representation of a byte. - * + * * @param string * a string representation of a single byte value. * @return a {@code Byte} containing the value represented by {@code string}. * @throws NumberFormatException * if {@code string} can not be parsed as a byte value. - * @since Android 1.0 */ public static Byte decode(String string) throws NumberFormatException { int intValue = Integer.decode(string).intValue(); @@ -153,12 +144,11 @@ public final class Byte extends Number implements Comparable { * Compares this object with the specified object and indicates if they are * equal. In order to be equal, {@code object} must be an instance of * {@code Byte} and have the same byte value as this object. - * + * * @param object * the object to compare this byte with. * @return {@code true} if the specified object is equal to this * {@code Byte}; {@code false} otherwise. - * @since Android 1.0 */ @Override public boolean equals(Object object) { @@ -189,14 +179,13 @@ public final class Byte extends Number implements Comparable { /** * Parses the specified string as a signed decimal byte value. The ASCII * character \u002d ('-') is recognized as the minus sign. - * + * * @param string * the string representation of a single byte value. * @return the primitive byte value represented by {@code string}. * @throws NumberFormatException * if {@code string} is {@code null}, has a length of zero or * can not be parsed as a byte value. - * @since Android 1.0 */ public static byte parseByte(String string) throws NumberFormatException { int intValue = Integer.parseInt(string); @@ -210,7 +199,7 @@ public final class Byte extends Number implements Comparable { /** * Parses the specified string as a signed byte value using the specified * radix. The ASCII character \u002d ('-') is recognized as the minus sign. - * + * * @param string * the string representation of a single byte value. * @param radix @@ -222,7 +211,6 @@ public final class Byte extends Number implements Comparable { * {@code radix < Character.MIN_RADIX}, * {@code radix > Character.MAX_RADIX}, or if {@code string} * can not be parsed as a byte value. - * @since Android 1.0 */ public static byte parseByte(String string, int radix) throws NumberFormatException { @@ -247,11 +235,10 @@ public final class Byte extends Number implements Comparable { /** * Returns a string containing a concise, human-readable description of the * specified byte value. - * + * * @param value * the byte to convert to a string. * @return a printable representation of {@code value}. - * @since Android 1.0 */ public static String toString(byte value) { return Integer.toString(value); @@ -259,7 +246,7 @@ public final class Byte extends Number implements Comparable { /** * Parses the specified string as a signed decimal byte value. - * + * * @param string * the string representation of a single byte value. * @return a {@code Byte} instance containing the byte value represented by @@ -268,7 +255,6 @@ public final class Byte extends Number implements Comparable { * if {@code string} is {@code null}, has a length of zero or * can not be parsed as a byte value. * @see #parseByte(String) - * @since Android 1.0 */ public static Byte valueOf(String string) throws NumberFormatException { return valueOf(parseByte(string)); @@ -277,7 +263,7 @@ public final class Byte extends Number implements Comparable { /** * Parses the specified string as a signed byte value using the specified * radix. - * + * * @param string * the string representation of a single byte value. * @param radix @@ -290,25 +276,23 @@ public final class Byte extends Number implements Comparable { * {@code radix > Character.MAX_RADIX}, or if {@code string} * can not be parsed as a byte value. * @see #parseByte(String, int) - * @since Android 1.0 */ public static Byte valueOf(String string, int radix) throws NumberFormatException { return valueOf(parseByte(string, radix)); } - + /** * Returns a {@code Byte} instance for the specified byte value. *

          * If it is not necessary to get a new {@code Byte} instance, it is * recommended to use this method instead of the constructor, since it * maintains a cache of instances which may result in better performance. - *

          - * + * * @param b * the byte value to store in the instance. * @return a {@code Byte} instance containing {@code b}. - * @since Android 1.0 + * @since 1.5 */ public static Byte valueOf(byte b) { synchronized (CACHE) { diff --git a/libcore/luni/src/main/java/java/lang/CharSequence.java b/libcore/luni/src/main/java/java/lang/CharSequence.java index 190119215..fc1ecd3bc 100644 --- a/libcore/luni/src/main/java/java/lang/CharSequence.java +++ b/libcore/luni/src/main/java/java/lang/CharSequence.java @@ -21,37 +21,33 @@ package java.lang; /** * This interface represents an ordered set of characters and defines the * methods to probe them. - * - * @since Android 1.0 */ public interface CharSequence { /** * Returns the number of characters in this sequence. - * + * * @return the number of characters. - * @since Android 1.0 */ public int length(); /** * Returns the character at the specified index, with the first character * having index zero. - * + * * @param index * the index of the character to return. * @return the requested character. * @throws IndexOutOfBoundsException * if {@code index < 0} or {@code index} is greater than the * length of this sequence. - * @since Android 1.0 */ public char charAt(int index); /** * Returns a {@code CharSequence} from the {@code start} index (inclusive) * to the {@code end} index (exclusive) of this sequence. - * + * * @param start * the start offset of the sub-sequence. It is inclusive, that * is, the index of the first character that is included in the @@ -65,16 +61,14 @@ public interface CharSequence { * if {@code start < 0}, {@code end < 0}, {@code start > end}, * or if {@code start} or {@code end} are greater than the * length of this sequence. - * @since Android 1.0 */ public CharSequence subSequence(int start, int end); /** * Returns a string with the same characters in the same order as in this * sequence. - * + * * @return a string based on this sequence. - * @since Android 1.0 */ public String toString(); } diff --git a/libcore/luni/src/main/java/java/lang/Character.java b/libcore/luni/src/main/java/java/lang/Character.java index 564e37a8f..8e83b4807 100644 --- a/libcore/luni/src/main/java/java/lang/Character.java +++ b/libcore/luni/src/main/java/java/lang/Character.java @@ -21,7 +21,7 @@ import java.io.Serializable; // BEGIN android-removed // import java.util.SortedMap; // import java.util.TreeMap; -// +// // import org.apache.harmony.luni.util.BinarySearch; // END android-removed @@ -36,7 +36,6 @@ import com.ibm.icu4jni.lang.UCharacter; * Character data is based upon the Unicode Standard, 4.0. The Unicode * specification, character tables and other information are available at
          http://www.unicode.org/. - *

          *

          * Unicode characters are referred to as code points. The range of valid * code points is U+0000 to U+10FFFF. The Basic Multilingual Plane (BMP) @@ -47,14 +46,12 @@ import com.ibm.icu4jni.lang.UCharacter; * supplementary character are made up of a high surrogate with a value * range of 0xD800 to 0xDBFF and a low surrogate with a value range of * 0xDC00 to 0xDFFF. - *

          *

          * On the Java platform a {@code char} value represents either a single BMP code * point or a UTF-16 unit that's part of a surrogate pair. The {@code int} type * is used to represent all Unicode code points. - *

          - * - * @since Android 1.0 + * + * @since 1.0 */ public final class Character implements Serializable, Comparable { private static final long serialVersionUID = 3786198910865385080L; @@ -63,36 +60,26 @@ public final class Character implements Serializable, Comparable { /** * The minimum {@code Character} value. - * - * @since Android 1.0 */ public static final char MIN_VALUE = '\u0000'; /** * The maximum {@code Character} value. - * - * @since Android 1.0 */ public static final char MAX_VALUE = '\uffff'; /** * The minimum radix used for conversions between characters and integers. - * - * @since Android 1.0 */ public static final int MIN_RADIX = 2; /** * The maximum radix used for conversions between characters and integers. - * - * @since Android 1.0 */ public static final int MAX_RADIX = 36; /** * The {@link Class} object that represents the primitive type {@code char}. - * - * @since Android 1.0 */ @SuppressWarnings("unchecked") public static final Class TYPE = (Class) new char[0] @@ -103,437 +90,415 @@ public final class Character implements Serializable, Comparable { /** * Unicode category constant Cn. - * - * @since Android 1.0 */ public static final byte UNASSIGNED = 0; /** * Unicode category constant Lu. - * - * @since Android 1.0 */ public static final byte UPPERCASE_LETTER = 1; /** * Unicode category constant Ll. - * - * @since Android 1.0 */ public static final byte LOWERCASE_LETTER = 2; /** * Unicode category constant Lt. - * - * @since Android 1.0 */ public static final byte TITLECASE_LETTER = 3; /** * Unicode category constant Lm. - * - * @since Android 1.0 */ public static final byte MODIFIER_LETTER = 4; /** * Unicode category constant Lo. - * - * @since Android 1.0 */ public static final byte OTHER_LETTER = 5; /** * Unicode category constant Mn. - * - * @since Android 1.0 */ public static final byte NON_SPACING_MARK = 6; /** * Unicode category constant Me. - * - * @since Android 1.0 */ public static final byte ENCLOSING_MARK = 7; /** * Unicode category constant Mc. - * - * @since Android 1.0 */ public static final byte COMBINING_SPACING_MARK = 8; /** * Unicode category constant Nd. - * - * @since Android 1.0 */ public static final byte DECIMAL_DIGIT_NUMBER = 9; /** * Unicode category constant Nl. - * - * @since Android 1.0 */ public static final byte LETTER_NUMBER = 10; /** * Unicode category constant No. - * - * @since Android 1.0 */ public static final byte OTHER_NUMBER = 11; /** * Unicode category constant Zs. - * - * @since Android 1.0 */ public static final byte SPACE_SEPARATOR = 12; /** * Unicode category constant Zl. - * - * @since Android 1.0 */ public static final byte LINE_SEPARATOR = 13; /** * Unicode category constant Zp. - * - * @since Android 1.0 */ public static final byte PARAGRAPH_SEPARATOR = 14; /** * Unicode category constant Cc. - * - * @since Android 1.0 */ public static final byte CONTROL = 15; /** * Unicode category constant Cf. - * - * @since Android 1.0 */ public static final byte FORMAT = 16; /** * Unicode category constant Co. - * - * @since Android 1.0 */ public static final byte PRIVATE_USE = 18; /** * Unicode category constant Cs. - * - * @since Android 1.0 */ public static final byte SURROGATE = 19; /** * Unicode category constant Pd. - * - * @since Android 1.0 */ public static final byte DASH_PUNCTUATION = 20; /** * Unicode category constant Ps. - * - * @since Android 1.0 */ public static final byte START_PUNCTUATION = 21; /** * Unicode category constant Pe. - * - * @since Android 1.0 */ public static final byte END_PUNCTUATION = 22; /** * Unicode category constant Pc. - * - * @since Android 1.0 */ public static final byte CONNECTOR_PUNCTUATION = 23; /** * Unicode category constant Po. - * - * @since Android 1.0 */ public static final byte OTHER_PUNCTUATION = 24; /** * Unicode category constant Sm. - * - * @since Android 1.0 */ public static final byte MATH_SYMBOL = 25; /** * Unicode category constant Sc. - * - * @since Android 1.0 */ public static final byte CURRENCY_SYMBOL = 26; /** * Unicode category constant Sk. - * - * @since Android 1.0 */ public static final byte MODIFIER_SYMBOL = 27; /** * Unicode category constant So. - * - * @since Android 1.0 */ public static final byte OTHER_SYMBOL = 28; /** * Unicode category constant Pi. - * - * @since Android 1.0 + * + * @since 1.4 */ public static final byte INITIAL_QUOTE_PUNCTUATION = 29; /** * Unicode category constant Pf. - * - * @since Android 1.0 + * + * @since 1.4 */ public static final byte FINAL_QUOTE_PUNCTUATION = 30; /** * Unicode bidirectional constant. - * - * @since Android 1.0 + * + * @since 1.4 */ public static final byte DIRECTIONALITY_UNDEFINED = -1; /** * Unicode bidirectional constant L. - * - * @since Android 1.0 + * + * @since 1.4 */ public static final byte DIRECTIONALITY_LEFT_TO_RIGHT = 0; /** * Unicode bidirectional constant R. - * - * @since Android 1.0 + * + * @since 1.4 */ public static final byte DIRECTIONALITY_RIGHT_TO_LEFT = 1; /** * Unicode bidirectional constant AL. - * - * @since Android 1.0 + * + * @since 1.4 */ public static final byte DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC = 2; /** * Unicode bidirectional constant EN. - * - * @since Android 1.0 + * + * @since 1.4 */ public static final byte DIRECTIONALITY_EUROPEAN_NUMBER = 3; /** * Unicode bidirectional constant ES. - * - * @since Android 1.0 + * + * @since 1.4 */ public static final byte DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR = 4; /** * Unicode bidirectional constant ET. - * - * @since Android 1.0 + * + * @since 1.4 */ public static final byte DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR = 5; /** * Unicode bidirectional constant AN. - * - * @since Android 1.0 + * + * @since 1.4 */ public static final byte DIRECTIONALITY_ARABIC_NUMBER = 6; /** * Unicode bidirectional constant CS. - * - * @since Android 1.0 + * + * @since 1.4 */ public static final byte DIRECTIONALITY_COMMON_NUMBER_SEPARATOR = 7; /** * Unicode bidirectional constant NSM. * - * @since Android 1.0 + * @since 1.4 */ public static final byte DIRECTIONALITY_NONSPACING_MARK = 8; /** * Unicode bidirectional constant BN. - * - * @since Android 1.0 + * + * @since 1.4 */ public static final byte DIRECTIONALITY_BOUNDARY_NEUTRAL = 9; /** * Unicode bidirectional constant B. - * - * @since Android 1.0 + * + * @since 1.4 */ public static final byte DIRECTIONALITY_PARAGRAPH_SEPARATOR = 10; /** * Unicode bidirectional constant S. - * - * @since Android 1.0 + * + * @since 1.4 */ public static final byte DIRECTIONALITY_SEGMENT_SEPARATOR = 11; /** * Unicode bidirectional constant WS. - * - * @since Android 1.0 + * + * @since 1.4 */ public static final byte DIRECTIONALITY_WHITESPACE = 12; /** * Unicode bidirectional constant ON. - * - * @since Android 1.0 + * + * @since 1.4 */ public static final byte DIRECTIONALITY_OTHER_NEUTRALS = 13; /** * Unicode bidirectional constant LRE. - * - * @since Android 1.0 + * + * @since 1.4 */ public static final byte DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING = 14; /** * Unicode bidirectional constant LRO. - * - * @since Android 1.0 + * + * @since 1.4 */ public static final byte DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE = 15; /** * Unicode bidirectional constant RLE. - * - * @since Android 1.0 + * + * @since 1.4 */ public static final byte DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING = 16; /** * Unicode bidirectional constant RLO. - * - * @since Android 1.0 + * + * @since 1.4 */ public static final byte DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE = 17; /** * Unicode bidirectional constant PDF. - * - * @since Android 1.0 + * + * @since 1.4 */ public static final byte DIRECTIONALITY_POP_DIRECTIONAL_FORMAT = 18; - + /** * The minimum value of a high surrogate or leading surrogate unit in UTF-16 * encoding, {@code '\uD800'}. - * - * @since Android 1.0 + * + * @since 1.5 */ public static final char MIN_HIGH_SURROGATE = '\uD800'; /** * The maximum value of a high surrogate or leading surrogate unit in UTF-16 * encoding, {@code '\uDBFF'}. - * - * @since Android 1.0 + * + * @since 1.5 */ public static final char MAX_HIGH_SURROGATE = '\uDBFF'; /** * The minimum value of a low surrogate or trailing surrogate unit in UTF-16 * encoding, {@code '\uDC00'}. - * - * @since Android 1.0 + * + * @since 1.5 */ public static final char MIN_LOW_SURROGATE = '\uDC00'; /** * The maximum value of a low surrogate or trailing surrogate unit in UTF-16 * encoding, {@code '\uDFFF'}. - * - * @since Android 1.0 + * + * @since 1.5 */ public static final char MAX_LOW_SURROGATE = '\uDFFF'; /** * The minimum value of a surrogate unit in UTF-16 encoding, {@code '\uD800'}. - * - * @since Android 1.0 + * + * @since 1.5 */ public static final char MIN_SURROGATE = '\uD800'; /** * The maximum value of a surrogate unit in UTF-16 encoding, {@code '\uDFFF'}. - * - * @since Android 1.0 + * + * @since 1.5 */ public static final char MAX_SURROGATE = '\uDFFF'; /** * The minimum value of a supplementary code point, {@code U+010000}. - * - * @since Android 1.0 + * + * @since 1.5 */ public static final int MIN_SUPPLEMENTARY_CODE_POINT = 0x10000; /** * The minimum code point value, {@code U+0000}. - * - * @since Android 1.0 + * + * @since 1.5 */ public static final int MIN_CODE_POINT = 0x000000; /** * The maximum code point value, {@code U+10FFFF}. - * - * @since Android 1.0 + * + * @since 1.5 */ public static final int MAX_CODE_POINT = 0x10FFFF; /** - * The number of bits required to represent a {@code Character} value in - * two's compliment form. - * - * @since Android 1.0 + * The number of bits required to represent a {@code Character} value + * unsigned form. + * + * @since 1.5 */ public static final int SIZE = 16; // BEGIN android-removed - // removed strings containing information about chars that now are read from - // icu data. + // Unicode 3.0.1 (same as Unicode 3.0.0) + // private static final String bidiKeys = ... + + // private static final char[] bidiValues = ... + + // private static final char[] mirrored = ... + + // Unicode 3.0.1 (same as Unicode 3.0.0) + // private static final String typeKeys = ... + + // private static final char[] typeValues = ... + + // private static final int[] typeValuesCache = ... + + // Unicode 3.0.1 (same as Unicode 3.0.0) + // private static final String uppercaseKeys = ... + + // private static final char[] uppercaseValues = ... + + // private static final int[] uppercaseValuesCache = ... + + // private static final String lowercaseKeys = ... + + // private static final char[] lowercaseValues = ... + + // private static final int[] lowercaseValuesCache = ... + + // private static final String digitKeys = ... + + // private static final char[] digitValues = ... // END android-removed - + + // BEGIN android-note + // put this in a helper class so that it's only initialized on demand? + // END android-note private static final char[] typeTags = "\u0002\u0002\u0002\u0002\u0002\u0002\u0002\u0002\u0002\u0000\u0000\u0000\u0000\u0000\u0002\u0002\u0002\u0002\u0002\u0002\u0002\u0002\u0002\u0002\u0002\u0002\u0002\u0002\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0003\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0002\u0002\u0002\u0002\u0002\u0002\u0002\u0002\u0002\u0002\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0000\u0000\u0000\u0000\u0003\u0000\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0003\u0000\u0000\u0000\u0000\u0002" .getValue(); - + + // BEGIN android-note + // put this in a helper class so that it's only initialized on demand? + // END android-note private static final byte[] DIRECTIONALITY = new byte[] { DIRECTIONALITY_LEFT_TO_RIGHT, DIRECTIONALITY_RIGHT_TO_LEFT, DIRECTIONALITY_EUROPEAN_NUMBER, @@ -557,24 +522,28 @@ public final class Character implements Serializable, Comparable { private static final int ISJAVAPART = 2; // BEGIN android-removed - // removed strings containing information about chars that now are read from - // icu data. + // Unicode 3.0.1 (same as Unicode 3.0.0) + // private static final String titlecaseKeys = ... + + // private static final char[] titlecaseValues = ... + + // Unicode 3.0.0 (NOT the same as Unicode 3.0.1) + // private static final String numericKeys = ... + + // private static final char[] numericValues = ... // END android-removed - - /** + + /* * Represents a subset of the Unicode character set. - * - * @since Android 1.0 */ public static class Subset { String name; /** * Constructs a new {@code Subset}. - * + * * @param string * this subset's name. - * @since Android 1.0 */ protected Subset(String string) { if (string == null) { @@ -586,12 +555,11 @@ public final class Character implements Serializable, Comparable { /** * Compares this character subset with the specified object. Uses * {@link java.lang.Object#equals(Object)} to do the comparison. - * + * * @param object * the object to compare this character subset with. * @return {@code true} if {@code object} is this subset, that is, if * {@code object == this}; {@code false} otherwise. - * @since Android 1.0 */ @Override public final boolean equals(Object object) { @@ -600,10 +568,9 @@ public final class Character implements Serializable, Comparable { /** * Returns the integer hash code for this character subset. - * + * * @return this subset's hash code, which is the hash code computed by * {@link java.lang.Object#hashCode()}. - * @since Android 1.0 */ @Override public final int hashCode() { @@ -612,9 +579,8 @@ public final class Character implements Serializable, Comparable { /** * Returns the string representation of this subset. - * + * * @return this subset's name. - * @since Android 1.0 */ @Override public final String toString() { @@ -622,799 +588,796 @@ public final class Character implements Serializable, Comparable { } } - // BEGIN android-changed - /** * Represents a block of Unicode characters, as defined by the Unicode 4.0.1 * specification. - * - * @since Android 1.0 + * + * @since 1.2 */ public static final class UnicodeBlock extends Subset { /** * The "Surrogates Area" Unicode Block. - * + * * @deprecated As of Java 5, this block has been replaced by * {@link #HIGH_SURROGATES}, * {@link #HIGH_PRIVATE_USE_SURROGATES} and * {@link #LOW_SURROGATES}. - * @since Android 1.0 */ @Deprecated - public static final UnicodeBlock SURROGATES_AREA = new UnicodeBlock("SURROGATES_AREA"); + public static final UnicodeBlock SURROGATES_AREA = new UnicodeBlock("SURROGATES_AREA", 0x0, 0x0); /** - * The "Basic Latin" Unicode Block. + * The "Basic Latin" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock BASIC_LATIN = new UnicodeBlock("BASIC_LATIN"); + public static final UnicodeBlock BASIC_LATIN = new UnicodeBlock("BASIC_LATIN", 0x0, 0x7f); /** - * The "Latin-1 Supplement" Unicode Block. + * The "Latin-1 Supplement" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock LATIN_1_SUPPLEMENT = new UnicodeBlock("LATIN_1_SUPPLEMENT"); + public static final UnicodeBlock LATIN_1_SUPPLEMENT = new UnicodeBlock("LATIN_1_SUPPLEMENT", 0x80, 0xff); /** - * The "Latin Extended-A" Unicode Block. + * The "Latin Extended-A" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock LATIN_EXTENDED_A = new UnicodeBlock("LATIN_EXTENDED_A"); + public static final UnicodeBlock LATIN_EXTENDED_A = new UnicodeBlock("LATIN_EXTENDED_A", 0x100, 0x17f); /** - * The "Latin Extended-B" Unicode Block. + * The "Latin Extended-B" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock LATIN_EXTENDED_B = new UnicodeBlock("LATIN_EXTENDED_B"); + public static final UnicodeBlock LATIN_EXTENDED_B = new UnicodeBlock("LATIN_EXTENDED_B", 0x180, 0x24f); /** - * The "IPA Extensions" Unicode Block. + * The "IPA Extensions" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock IPA_EXTENSIONS = new UnicodeBlock("IPA_EXTENSIONS"); + public static final UnicodeBlock IPA_EXTENSIONS = new UnicodeBlock("IPA_EXTENSIONS", 0x250, 0x2af); /** - * The "Spacing Modifier Letters" Unicode Block. + * The "Spacing Modifier Letters" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock SPACING_MODIFIER_LETTERS = new UnicodeBlock("SPACING_MODIFIER_LETTERS"); + public static final UnicodeBlock SPACING_MODIFIER_LETTERS = new UnicodeBlock("SPACING_MODIFIER_LETTERS", 0x2b0, 0x2ff); /** - * The "Combining Diacritical Marks" Unicode Block. + * The "Combining Diacritical Marks" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock COMBINING_DIACRITICAL_MARKS = new UnicodeBlock("COMBINING_DIACRITICAL_MARKS"); + public static final UnicodeBlock COMBINING_DIACRITICAL_MARKS = new UnicodeBlock("COMBINING_DIACRITICAL_MARKS", 0x300, 0x36f); /** * The "Greek and Coptic" Unicode Block. Previously referred * to as "Greek". - * - * @since Android 1.0 + * + * @since 1.2 */ - public static final UnicodeBlock GREEK = new UnicodeBlock("GREEK"); + public static final UnicodeBlock GREEK = new UnicodeBlock("GREEK", 0x370, 0x3ff); /** - * The "Cyrillic" Unicode Block. + * The "Cyrillic" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock CYRILLIC = new UnicodeBlock("CYRILLIC"); + public static final UnicodeBlock CYRILLIC = new UnicodeBlock("CYRILLIC", 0x400, 0x4ff); /** * The "Cyrillic Supplement" Unicode Block. Previously * referred to as "Cyrillic Supplementary". - * - * @since Android 1.0 + * + * @since 1.5 */ - public static final UnicodeBlock CYRILLIC_SUPPLEMENTARY = new UnicodeBlock("CYRILLIC_SUPPLEMENTARY"); + public static final UnicodeBlock CYRILLIC_SUPPLEMENTARY = new UnicodeBlock("CYRILLIC_SUPPLEMENTARY", 0x500, 0x52f); /** - * The "Armenian" Unicode Block. + * The "Armenian" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock ARMENIAN = new UnicodeBlock("ARMENIAN"); + public static final UnicodeBlock ARMENIAN = new UnicodeBlock("ARMENIAN", 0x530, 0x58f); /** - * The "Hebrew" Unicode Block. + * The "Hebrew" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock HEBREW = new UnicodeBlock("HEBREW"); + public static final UnicodeBlock HEBREW = new UnicodeBlock("HEBREW", 0x590, 0x5ff); /** - * The "Arabic" Unicode Block. + * The "Arabic" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock ARABIC = new UnicodeBlock("ARABIC"); + public static final UnicodeBlock ARABIC = new UnicodeBlock("ARABIC", 0x600, 0x6ff); /** - * The "Syriac" Unicode Block. + * The "Syriac" Unicode Block. * - * @since Android 1.0 + * @since 1.4 */ - public static final UnicodeBlock SYRIAC = new UnicodeBlock("SYRIAC"); + public static final UnicodeBlock SYRIAC = new UnicodeBlock("SYRIAC", 0x700, 0x74f); /** - * The "Thaana" Unicode Block. + * The "Thaana" Unicode Block. * - * @since Android 1.0 + * @since 1.4 */ - public static final UnicodeBlock THAANA = new UnicodeBlock("THAANA"); + public static final UnicodeBlock THAANA = new UnicodeBlock("THAANA", 0x780, 0x7bf); /** - * The "Devanagari" Unicode Block. + * The "Devanagari" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock DEVANAGARI = new UnicodeBlock("DEVANAGARI"); + public static final UnicodeBlock DEVANAGARI = new UnicodeBlock("DEVANAGARI", 0x900, 0x97f); /** - * The "Bengali" Unicode Block. + * The "Bengali" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock BENGALI = new UnicodeBlock("BENGALI"); + public static final UnicodeBlock BENGALI = new UnicodeBlock("BENGALI", 0x980, 0x9ff); /** - * The "Gurmukhi" Unicode Block. + * The "Gurmukhi" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock GURMUKHI = new UnicodeBlock("GURMUKHI"); + public static final UnicodeBlock GURMUKHI = new UnicodeBlock("GURMUKHI", 0xa00, 0xa7f); /** - * The "Gujarati" Unicode Block. + * The "Gujarati" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock GUJARATI = new UnicodeBlock("GUJARATI"); + public static final UnicodeBlock GUJARATI = new UnicodeBlock("GUJARATI", 0xa80, 0xaff); /** - * The "Oriya" Unicode Block. + * The "Oriya" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock ORIYA = new UnicodeBlock("ORIYA"); + public static final UnicodeBlock ORIYA = new UnicodeBlock("ORIYA", 0xb00, 0xb7f); /** - * The "Tamil" Unicode Block. + * The "Tamil" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock TAMIL = new UnicodeBlock("TAMIL"); + public static final UnicodeBlock TAMIL = new UnicodeBlock("TAMIL", 0xb80, 0xbff); /** - * The "Telugu" Unicode Block. + * The "Telugu" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock TELUGU = new UnicodeBlock("TELUGU"); + public static final UnicodeBlock TELUGU = new UnicodeBlock("TELUGU", 0xc00, 0xc7f); /** - * The "Kannada" Unicode Block. + * The "Kannada" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock KANNADA = new UnicodeBlock("KANNADA"); + public static final UnicodeBlock KANNADA = new UnicodeBlock("KANNADA", 0xc80, 0xcff); /** - * The "Malayalam" Unicode Block. + * The "Malayalam" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock MALAYALAM = new UnicodeBlock("MALAYALAM"); + public static final UnicodeBlock MALAYALAM = new UnicodeBlock("MALAYALAM", 0xd00, 0xd7f); /** - * The "Sinhala" Unicode Block. + * The "Sinhala" Unicode Block. * - * @since Android 1.0 + * @since 1.4 */ - public static final UnicodeBlock SINHALA = new UnicodeBlock("SINHALA"); + public static final UnicodeBlock SINHALA = new UnicodeBlock("SINHALA", 0xd80, 0xdff); /** - * The "Thai" Unicode Block. + * The "Thai" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock THAI = new UnicodeBlock("THAI"); + public static final UnicodeBlock THAI = new UnicodeBlock("THAI", 0xe00, 0xe7f); /** - * The "Lao" Unicode Block. + * The "Lao" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock LAO = new UnicodeBlock("LAO"); + public static final UnicodeBlock LAO = new UnicodeBlock("LAO", 0xe80, 0xeff); /** - * The "Tibetan" Unicode Block. + * The "Tibetan" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock TIBETAN = new UnicodeBlock("TIBETAN"); + public static final UnicodeBlock TIBETAN = new UnicodeBlock("TIBETAN", 0xf00, 0xfff); /** - * The "Myanmar" Unicode Block. + * The "Myanmar" Unicode Block. * - * @since Android 1.0 + * @since 1.4 */ - public static final UnicodeBlock MYANMAR = new UnicodeBlock("MYANMAR"); + public static final UnicodeBlock MYANMAR = new UnicodeBlock("MYANMAR", 0x1000, 0x109f); /** - * The "Georgian" Unicode Block. + * The "Georgian" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock GEORGIAN = new UnicodeBlock("GEORGIAN"); + public static final UnicodeBlock GEORGIAN = new UnicodeBlock("GEORGIAN", 0x10a0, 0x10ff); /** - * The "Hangul Jamo" Unicode Block. + * The "Hangul Jamo" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock HANGUL_JAMO = new UnicodeBlock("HANGUL_JAMO"); + public static final UnicodeBlock HANGUL_JAMO = new UnicodeBlock("HANGUL_JAMO", 0x1100, 0x11ff); /** - * The "Ethiopic" Unicode Block. + * The "Ethiopic" Unicode Block. * - * @since Android 1.0 + * @since 1.4 */ - public static final UnicodeBlock ETHIOPIC = new UnicodeBlock("ETHIOPIC"); + public static final UnicodeBlock ETHIOPIC = new UnicodeBlock("ETHIOPIC", 0x1200, 0x137f); /** - * The "Cherokee" Unicode Block. + * The "Cherokee" Unicode Block. * - * @since Android 1.0 + * @since 1.4 */ - public static final UnicodeBlock CHEROKEE = new UnicodeBlock("CHEROKEE"); + public static final UnicodeBlock CHEROKEE = new UnicodeBlock("CHEROKEE", 0x13a0, 0x13ff); /** - * The "Unified Canadian Aboriginal Syllabics" Unicode Block. + * The "Unified Canadian Aboriginal Syllabics" Unicode Block. * - * @since Android 1.0 + * @since 1.4 */ - public static final UnicodeBlock UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS = new UnicodeBlock("UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS"); + public static final UnicodeBlock UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS = new UnicodeBlock("UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS", 0x1400, 0x167f); /** - * The "Ogham" Unicode Block. + * The "Ogham" Unicode Block. * - * @since Android 1.0 + * @since 1.4 */ - public static final UnicodeBlock OGHAM = new UnicodeBlock("OGHAM"); + public static final UnicodeBlock OGHAM = new UnicodeBlock("OGHAM", 0x1680, 0x169f); /** - * The "Runic" Unicode Block. + * The "Runic" Unicode Block. * - * @since Android 1.0 + * @since 1.4 */ - public static final UnicodeBlock RUNIC = new UnicodeBlock("RUNIC"); + public static final UnicodeBlock RUNIC = new UnicodeBlock("RUNIC", 0x16a0, 0x16ff); /** - * The "Tagalog" Unicode Block. + * The "Tagalog" Unicode Block. * - * @since Android 1.0 + * @since 1.5 */ - public static final UnicodeBlock TAGALOG = new UnicodeBlock("TAGALOG"); + public static final UnicodeBlock TAGALOG = new UnicodeBlock("TAGALOG", 0x1700, 0x171f); /** - * The "Hanunoo" Unicode Block. + * The "Hanunoo" Unicode Block. * - * @since Android 1.0 + * @since 1.5 */ - public static final UnicodeBlock HANUNOO = new UnicodeBlock("HANUNOO"); + public static final UnicodeBlock HANUNOO = new UnicodeBlock("HANUNOO", 0x1720, 0x173f); /** - * The "Buhid" Unicode Block. + * The "Buhid" Unicode Block. * - * @since Android 1.0 + * @since 1.5 */ - public static final UnicodeBlock BUHID = new UnicodeBlock("BUHID"); + public static final UnicodeBlock BUHID = new UnicodeBlock("BUHID", 0x1740, 0x175f); /** - * The "Tagbanwa" Unicode Block. + * The "Tagbanwa" Unicode Block. * - * @since Android 1.0 + * @since 1.5 */ - public static final UnicodeBlock TAGBANWA = new UnicodeBlock("TAGBANWA"); + public static final UnicodeBlock TAGBANWA = new UnicodeBlock("TAGBANWA", 0x1760, 0x177f); /** - * The "Khmer" Unicode Block. + * The "Khmer" Unicode Block. * - * @since Android 1.0 + * @since 1.4 */ - public static final UnicodeBlock KHMER = new UnicodeBlock("KHMER"); + public static final UnicodeBlock KHMER = new UnicodeBlock("KHMER", 0x1780, 0x17ff); /** - * The "Mongolian" Unicode Block. + * The "Mongolian" Unicode Block. * - * @since Android 1.0 + * @since 1.4 */ - public static final UnicodeBlock MONGOLIAN = new UnicodeBlock("MONGOLIAN"); + public static final UnicodeBlock MONGOLIAN = new UnicodeBlock("MONGOLIAN", 0x1800, 0x18af); /** - * The "Limbu" Unicode Block. + * The "Limbu" Unicode Block. * - * @since Android 1.0 + * @since 1.5 */ - public static final UnicodeBlock LIMBU = new UnicodeBlock("LIMBU"); + public static final UnicodeBlock LIMBU = new UnicodeBlock("LIMBU", 0x1900, 0x194f); /** - * The "Tai Le" Unicode Block. + * The "Tai Le" Unicode Block. * - * @since Android 1.0 + * @since 1.5 */ - public static final UnicodeBlock TAI_LE = new UnicodeBlock("TAI_LE"); + public static final UnicodeBlock TAI_LE = new UnicodeBlock("TAI_LE", 0x1950, 0x197f); /** - * The "Khmer Symbols" Unicode Block. + * The "Khmer Symbols" Unicode Block. * - * @since Android 1.0 + * @since 1.5 */ - public static final UnicodeBlock KHMER_SYMBOLS = new UnicodeBlock("KHMER_SYMBOLS"); + public static final UnicodeBlock KHMER_SYMBOLS = new UnicodeBlock("KHMER_SYMBOLS", 0x19e0, 0x19ff); /** - * The "Phonetic Extensions" Unicode Block. + * The "Phonetic Extensions" Unicode Block. * - * @since Android 1.0 + * @since 1.5 */ - public static final UnicodeBlock PHONETIC_EXTENSIONS = new UnicodeBlock("PHONETIC_EXTENSIONS"); + public static final UnicodeBlock PHONETIC_EXTENSIONS = new UnicodeBlock("PHONETIC_EXTENSIONS", 0x1d00, 0x1d7f); /** - * The "Latin Extended Additional" Unicode Block. + * The "Latin Extended Additional" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock LATIN_EXTENDED_ADDITIONAL = new UnicodeBlock("LATIN_EXTENDED_ADDITIONAL"); + public static final UnicodeBlock LATIN_EXTENDED_ADDITIONAL = new UnicodeBlock("LATIN_EXTENDED_ADDITIONAL", 0x1e00, 0x1eff); /** - * The "Greek Extended" Unicode Block. + * The "Greek Extended" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock GREEK_EXTENDED = new UnicodeBlock("GREEK_EXTENDED"); + public static final UnicodeBlock GREEK_EXTENDED = new UnicodeBlock("GREEK_EXTENDED", 0x1f00, 0x1fff); /** - * The "General Punctuation" Unicode Block. + * The "General Punctuation" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock GENERAL_PUNCTUATION = new UnicodeBlock("GENERAL_PUNCTUATION"); + public static final UnicodeBlock GENERAL_PUNCTUATION = new UnicodeBlock("GENERAL_PUNCTUATION", 0x2000, 0x206f); /** - * The "Superscripts and Subscripts" Unicode Block. + * The "Superscripts and Subscripts" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock SUPERSCRIPTS_AND_SUBSCRIPTS = new UnicodeBlock("SUPERSCRIPTS_AND_SUBSCRIPTS"); + public static final UnicodeBlock SUPERSCRIPTS_AND_SUBSCRIPTS = new UnicodeBlock("SUPERSCRIPTS_AND_SUBSCRIPTS", 0x2070, 0x209f); /** - * The "Currency Symbols" Unicode Block. + * The "Currency Symbols" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock CURRENCY_SYMBOLS = new UnicodeBlock("CURRENCY_SYMBOLS"); + public static final UnicodeBlock CURRENCY_SYMBOLS = new UnicodeBlock("CURRENCY_SYMBOLS", 0x20a0, 0x20cf); /** * The "Combining Diacritical Marks for Symbols" Unicode * Block. Previously referred to as "Combining Marks for * Symbols". - * - * @since Android 1.0 + * + * @since 1.2 */ - public static final UnicodeBlock COMBINING_MARKS_FOR_SYMBOLS = new UnicodeBlock("COMBINING_MARKS_FOR_SYMBOLS"); + public static final UnicodeBlock COMBINING_MARKS_FOR_SYMBOLS = new UnicodeBlock("COMBINING_MARKS_FOR_SYMBOLS", 0x20d0, 0x20ff); /** - * The "Letterlike Symbols" Unicode Block. + * The "Letterlike Symbols" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock LETTERLIKE_SYMBOLS = new UnicodeBlock("LETTERLIKE_SYMBOLS"); + public static final UnicodeBlock LETTERLIKE_SYMBOLS = new UnicodeBlock("LETTERLIKE_SYMBOLS", 0x2100, 0x214f); /** - * The "Number Forms" Unicode Block. + * The "Number Forms" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock NUMBER_FORMS = new UnicodeBlock("NUMBER_FORMS"); + public static final UnicodeBlock NUMBER_FORMS = new UnicodeBlock("NUMBER_FORMS", 0x2150, 0x218f); /** - * The "Arrows" Unicode Block. + * The "Arrows" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock ARROWS = new UnicodeBlock("ARROWS"); + public static final UnicodeBlock ARROWS = new UnicodeBlock("ARROWS", 0x2190, 0x21ff); /** - * The "Mathematical Operators" Unicode Block. + * The "Mathematical Operators" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock MATHEMATICAL_OPERATORS = new UnicodeBlock("MATHEMATICAL_OPERATORS"); + public static final UnicodeBlock MATHEMATICAL_OPERATORS = new UnicodeBlock("MATHEMATICAL_OPERATORS", 0x2200, 0x22ff); /** - * The "Miscellaneous Technical" Unicode Block. + * The "Miscellaneous Technical" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock MISCELLANEOUS_TECHNICAL = new UnicodeBlock("MISCELLANEOUS_TECHNICAL"); + public static final UnicodeBlock MISCELLANEOUS_TECHNICAL = new UnicodeBlock("MISCELLANEOUS_TECHNICAL", 0x2300, 0x23ff); /** - * The "Control Pictures" Unicode Block. + * The "Control Pictures" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock CONTROL_PICTURES = new UnicodeBlock("CONTROL_PICTURES"); + public static final UnicodeBlock CONTROL_PICTURES = new UnicodeBlock("CONTROL_PICTURES", 0x2400, 0x243f); /** - * The "Optical Character Recognition" Unicode Block. + * The "Optical Character Recognition" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock OPTICAL_CHARACTER_RECOGNITION = new UnicodeBlock("OPTICAL_CHARACTER_RECOGNITION"); + public static final UnicodeBlock OPTICAL_CHARACTER_RECOGNITION = new UnicodeBlock("OPTICAL_CHARACTER_RECOGNITION", 0x2440, 0x245f); /** - * The "Enclosed Alphanumerics" Unicode Block. + * The "Enclosed Alphanumerics" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock ENCLOSED_ALPHANUMERICS = new UnicodeBlock("ENCLOSED_ALPHANUMERICS"); + public static final UnicodeBlock ENCLOSED_ALPHANUMERICS = new UnicodeBlock("ENCLOSED_ALPHANUMERICS", 0x2460, 0x24ff); /** - * The "Box Drawing" Unicode Block. + * The "Box Drawing" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock BOX_DRAWING = new UnicodeBlock("BOX_DRAWING"); + public static final UnicodeBlock BOX_DRAWING = new UnicodeBlock("BOX_DRAWING", 0x2500, 0x257f); /** - * The "Block Elements" Unicode Block. + * The "Block Elements" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock BLOCK_ELEMENTS = new UnicodeBlock("BLOCK_ELEMENTS"); + public static final UnicodeBlock BLOCK_ELEMENTS = new UnicodeBlock("BLOCK_ELEMENTS", 0x2580, 0x259f); /** - * The "Geometric Shapes" Unicode Block. + * The "Geometric Shapes" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock GEOMETRIC_SHAPES = new UnicodeBlock("GEOMETRIC_SHAPES"); + public static final UnicodeBlock GEOMETRIC_SHAPES = new UnicodeBlock("GEOMETRIC_SHAPES", 0x25a0, 0x25ff); /** - * The "Miscellaneous Symbols" Unicode Block. + * The "Miscellaneous Symbols" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock MISCELLANEOUS_SYMBOLS = new UnicodeBlock("MISCELLANEOUS_SYMBOLS"); + public static final UnicodeBlock MISCELLANEOUS_SYMBOLS = new UnicodeBlock("MISCELLANEOUS_SYMBOLS", 0x2600, 0x26ff); /** - * The "Dingbats" Unicode Block. + * The "Dingbats" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock DINGBATS = new UnicodeBlock("DINGBATS"); + public static final UnicodeBlock DINGBATS = new UnicodeBlock("DINGBATS", 0x2700, 0x27bf); /** - * The "Miscellaneous Mathematical Symbols-A" Unicode Block. + * The "Miscellaneous Mathematical Symbols-A" Unicode Block. * - * @since Android 1.0 + * @since 1.5 */ - public static final UnicodeBlock MISCELLANEOUS_MATHEMATICAL_SYMBOLS_A = new UnicodeBlock("MISCELLANEOUS_MATHEMATICAL_SYMBOLS_A"); + public static final UnicodeBlock MISCELLANEOUS_MATHEMATICAL_SYMBOLS_A = new UnicodeBlock("MISCELLANEOUS_MATHEMATICAL_SYMBOLS_A", 0x27c0, 0x27ef); /** - * The "Supplemental Arrows-A" Unicode Block. + * The "Supplemental Arrows-A" Unicode Block. * - * @since Android 1.0 + * @since 1.5 */ - public static final UnicodeBlock SUPPLEMENTAL_ARROWS_A = new UnicodeBlock("SUPPLEMENTAL_ARROWS_A"); + public static final UnicodeBlock SUPPLEMENTAL_ARROWS_A = new UnicodeBlock("SUPPLEMENTAL_ARROWS_A", 0x27f0, 0x27ff); /** - * The "Braille Patterns" Unicode Block. + * The "Braille Patterns" Unicode Block. * - * @since Android 1.0 + * @since 1.4 */ - public static final UnicodeBlock BRAILLE_PATTERNS = new UnicodeBlock("BRAILLE_PATTERNS"); + public static final UnicodeBlock BRAILLE_PATTERNS = new UnicodeBlock("BRAILLE_PATTERNS", 0x2800, 0x28ff); /** - * The "Supplemental Arrows-B" Unicode Block. + * The "Supplemental Arrows-B" Unicode Block. * - * @since Android 1.0 + * @since 1.5 */ - public static final UnicodeBlock SUPPLEMENTAL_ARROWS_B = new UnicodeBlock("SUPPLEMENTAL_ARROWS_B"); + public static final UnicodeBlock SUPPLEMENTAL_ARROWS_B = new UnicodeBlock("SUPPLEMENTAL_ARROWS_B", 0x2900, 0x297f); /** - * The "Miscellaneous Mathematical Symbols-B" Unicode Block. + * The "Miscellaneous Mathematical Symbols-B" Unicode Block. * - * @since Android 1.0 + * @since 1.5 */ - public static final UnicodeBlock MISCELLANEOUS_MATHEMATICAL_SYMBOLS_B = new UnicodeBlock("MISCELLANEOUS_MATHEMATICAL_SYMBOLS_B"); + public static final UnicodeBlock MISCELLANEOUS_MATHEMATICAL_SYMBOLS_B = new UnicodeBlock("MISCELLANEOUS_MATHEMATICAL_SYMBOLS_B", 0x2980, 0x29ff); /** - * The "Supplemental Mathematical Operators" Unicode Block. + * The "Supplemental Mathematical Operators" Unicode Block. * - * @since Android 1.0 + * @since 1.5 */ - public static final UnicodeBlock SUPPLEMENTAL_MATHEMATICAL_OPERATORS = new UnicodeBlock("SUPPLEMENTAL_MATHEMATICAL_OPERATORS"); + public static final UnicodeBlock SUPPLEMENTAL_MATHEMATICAL_OPERATORS = new UnicodeBlock("SUPPLEMENTAL_MATHEMATICAL_OPERATORS", 0x2a00, 0x2aff); /** - * The "Miscellaneous Symbols and Arrows" Unicode Block. + * The "Miscellaneous Symbols and Arrows" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock MISCELLANEOUS_SYMBOLS_AND_ARROWS = new UnicodeBlock("MISCELLANEOUS_SYMBOLS_AND_ARROWS"); + public static final UnicodeBlock MISCELLANEOUS_SYMBOLS_AND_ARROWS = new UnicodeBlock("MISCELLANEOUS_SYMBOLS_AND_ARROWS", 0x2b00, 0x2bff); /** - * The "CJK Radicals Supplement" Unicode Block. + * The "CJK Radicals Supplement" Unicode Block. * - * @since Android 1.0 + * @since 1.4 */ - public static final UnicodeBlock CJK_RADICALS_SUPPLEMENT = new UnicodeBlock("CJK_RADICALS_SUPPLEMENT"); + public static final UnicodeBlock CJK_RADICALS_SUPPLEMENT = new UnicodeBlock("CJK_RADICALS_SUPPLEMENT", 0x2e80, 0x2eff); /** - * The "Kangxi Radicals" Unicode Block. + * The "Kangxi Radicals" Unicode Block. * - * @since Android 1.0 + * @since 1.4 */ - public static final UnicodeBlock KANGXI_RADICALS = new UnicodeBlock("KANGXI_RADICALS"); + public static final UnicodeBlock KANGXI_RADICALS = new UnicodeBlock("KANGXI_RADICALS", 0x2f00, 0x2fdf); /** - * The "Ideographic Description Characters" Unicode Block. + * The "Ideographic Description Characters" Unicode Block. * - * @since Android 1.0 + * @since 1.4 */ - public static final UnicodeBlock IDEOGRAPHIC_DESCRIPTION_CHARACTERS = new UnicodeBlock("IDEOGRAPHIC_DESCRIPTION_CHARACTERS"); + public static final UnicodeBlock IDEOGRAPHIC_DESCRIPTION_CHARACTERS = new UnicodeBlock("IDEOGRAPHIC_DESCRIPTION_CHARACTERS", 0x2ff0, 0x2fff); /** - * The "CJK Symbols and Punctuation" Unicode Block. + * The "CJK Symbols and Punctuation" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock CJK_SYMBOLS_AND_PUNCTUATION = new UnicodeBlock("CJK_SYMBOLS_AND_PUNCTUATION"); + public static final UnicodeBlock CJK_SYMBOLS_AND_PUNCTUATION = new UnicodeBlock("CJK_SYMBOLS_AND_PUNCTUATION", 0x3000, 0x303f); /** - * The "Hiragana" Unicode Block. + * The "Hiragana" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock HIRAGANA = new UnicodeBlock("HIRAGANA"); + public static final UnicodeBlock HIRAGANA = new UnicodeBlock("HIRAGANA", 0x3040, 0x309f); /** - * The "Katakana" Unicode Block. + * The "Katakana" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock KATAKANA = new UnicodeBlock("KATAKANA"); + public static final UnicodeBlock KATAKANA = new UnicodeBlock("KATAKANA", 0x30a0, 0x30ff); /** - * The "Bopomofo" Unicode Block. + * The "Bopomofo" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock BOPOMOFO = new UnicodeBlock("BOPOMOFO"); + public static final UnicodeBlock BOPOMOFO = new UnicodeBlock("BOPOMOFO", 0x3100, 0x312f); /** - * The "Hangul Compatibility Jamo" Unicode Block. + * The "Hangul Compatibility Jamo" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock HANGUL_COMPATIBILITY_JAMO = new UnicodeBlock("HANGUL_COMPATIBILITY_JAMO"); + public static final UnicodeBlock HANGUL_COMPATIBILITY_JAMO = new UnicodeBlock("HANGUL_COMPATIBILITY_JAMO", 0x3130, 0x318f); /** - * The "Kanbun" Unicode Block. + * The "Kanbun" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock KANBUN = new UnicodeBlock("KANBUN"); + public static final UnicodeBlock KANBUN = new UnicodeBlock("KANBUN", 0x3190, 0x319f); /** - * The "Bopomofo Extended" Unicode Block. + * The "Bopomofo Extended" Unicode Block. * - * @since Android 1.0 + * @since 1.4 */ - public static final UnicodeBlock BOPOMOFO_EXTENDED = new UnicodeBlock("BOPOMOFO_EXTENDED"); + public static final UnicodeBlock BOPOMOFO_EXTENDED = new UnicodeBlock("BOPOMOFO_EXTENDED", 0x31a0, 0x31bf); /** - * The "Katakana Phonetic Extensions" Unicode Block. + * The "Katakana Phonetic Extensions" Unicode Block. * - * @since Android 1.0 + * @since 1.5 */ - public static final UnicodeBlock KATAKANA_PHONETIC_EXTENSIONS = new UnicodeBlock("KATAKANA_PHONETIC_EXTENSIONS"); + public static final UnicodeBlock KATAKANA_PHONETIC_EXTENSIONS = new UnicodeBlock("KATAKANA_PHONETIC_EXTENSIONS", 0x31f0, 0x31ff); /** - * The "Enclosed CJK Letters and Months" Unicode Block. + * The "Enclosed CJK Letters and Months" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock ENCLOSED_CJK_LETTERS_AND_MONTHS = new UnicodeBlock("ENCLOSED_CJK_LETTERS_AND_MONTHS"); + public static final UnicodeBlock ENCLOSED_CJK_LETTERS_AND_MONTHS = new UnicodeBlock("ENCLOSED_CJK_LETTERS_AND_MONTHS", 0x3200, 0x32ff); /** - * The "CJK Compatibility" Unicode Block. + * The "CJK Compatibility" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock CJK_COMPATIBILITY = new UnicodeBlock("CJK_COMPATIBILITY"); + public static final UnicodeBlock CJK_COMPATIBILITY = new UnicodeBlock("CJK_COMPATIBILITY", 0x3300, 0x33ff); /** - * The "CJK Unified Ideographs Extension A" Unicode Block. + * The "CJK Unified Ideographs Extension A" Unicode Block. * - * @since Android 1.0 + * @since 1.4 */ - public static final UnicodeBlock CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A = new UnicodeBlock("CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A"); + public static final UnicodeBlock CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A = new UnicodeBlock("CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A", 0x3400, 0x4dbf); /** - * The "Yijing Hexagram Symbols" Unicode Block. + * The "Yijing Hexagram Symbols" Unicode Block. * - * @since Android 1.0 + * @since 1.5 */ - public static final UnicodeBlock YIJING_HEXAGRAM_SYMBOLS = new UnicodeBlock("YIJING_HEXAGRAM_SYMBOLS"); + public static final UnicodeBlock YIJING_HEXAGRAM_SYMBOLS = new UnicodeBlock("YIJING_HEXAGRAM_SYMBOLS", 0x4dc0, 0x4dff); /** - * The "CJK Unified Ideographs" Unicode Block. + * The "CJK Unified Ideographs" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock CJK_UNIFIED_IDEOGRAPHS = new UnicodeBlock("CJK_UNIFIED_IDEOGRAPHS"); + public static final UnicodeBlock CJK_UNIFIED_IDEOGRAPHS = new UnicodeBlock("CJK_UNIFIED_IDEOGRAPHS", 0x4e00, 0x9fff); /** - * The "Yi Syllables" Unicode Block. + * The "Yi Syllables" Unicode Block. * - * @since Android 1.0 + * @since 1.4 */ - public static final UnicodeBlock YI_SYLLABLES = new UnicodeBlock("YI_SYLLABLES"); + public static final UnicodeBlock YI_SYLLABLES = new UnicodeBlock("YI_SYLLABLES", 0xa000, 0xa48f); /** - * The "Yi Radicals" Unicode Block. + * The "Yi Radicals" Unicode Block. * - * @since Android 1.0 + * @since 1.4 */ - public static final UnicodeBlock YI_RADICALS = new UnicodeBlock("YI_RADICALS"); + public static final UnicodeBlock YI_RADICALS = new UnicodeBlock("YI_RADICALS", 0xa490, 0xa4cf); /** - * The "Hangul Syllables" Unicode Block. + * The "Hangul Syllables" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock HANGUL_SYLLABLES = new UnicodeBlock("HANGUL_SYLLABLES"); + public static final UnicodeBlock HANGUL_SYLLABLES = new UnicodeBlock("HANGUL_SYLLABLES", 0xac00, 0xd7af); /** * The "High Surrogates" Unicode Block. This block represents * code point values in the high surrogate range 0xD800 to 0xDB7F - * - * @since Android 1.0 */ - public static final UnicodeBlock HIGH_SURROGATES = new UnicodeBlock("HIGH_SURROGATES"); + public static final UnicodeBlock HIGH_SURROGATES = new UnicodeBlock("HIGH_SURROGATES", 0xd800, 0xdb7f); /** * The "High Private Use Surrogates" Unicode Block. This block * represents code point values in the high surrogate range 0xDB80 to * 0xDBFF - * - * @since Android 1.0 */ - public static final UnicodeBlock HIGH_PRIVATE_USE_SURROGATES = new UnicodeBlock("HIGH_PRIVATE_USE_SURROGATES"); + public static final UnicodeBlock HIGH_PRIVATE_USE_SURROGATES = new UnicodeBlock("HIGH_PRIVATE_USE_SURROGATES", 0xdb80, 0xdbff); /** * The "Low Surrogates" Unicode Block. This block represents * code point values in the low surrogate range 0xDC00 to 0xDFFF - * - * @since Android 1.0 */ - public static final UnicodeBlock LOW_SURROGATES = new UnicodeBlock("LOW_SURROGATES"); + public static final UnicodeBlock LOW_SURROGATES = new UnicodeBlock("LOW_SURROGATES", 0xdc00, 0xdfff); /** - * The "Private Use Area" Unicode Block. + * The "Private Use Area" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock PRIVATE_USE_AREA = new UnicodeBlock("PRIVATE_USE_AREA"); + public static final UnicodeBlock PRIVATE_USE_AREA = new UnicodeBlock("PRIVATE_USE_AREA", 0xe000, 0xf8ff); /** - * The "CJK Compatibility Ideographs" Unicode Block. + * The "CJK Compatibility Ideographs" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock CJK_COMPATIBILITY_IDEOGRAPHS = new UnicodeBlock("CJK_COMPATIBILITY_IDEOGRAPHS"); + public static final UnicodeBlock CJK_COMPATIBILITY_IDEOGRAPHS = new UnicodeBlock("CJK_COMPATIBILITY_IDEOGRAPHS", 0xf900, 0xfaff); /** - * The "Alphabetic Presentation Forms" Unicode Block. + * The "Alphabetic Presentation Forms" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock ALPHABETIC_PRESENTATION_FORMS = new UnicodeBlock("ALPHABETIC_PRESENTATION_FORMS"); + public static final UnicodeBlock ALPHABETIC_PRESENTATION_FORMS = new UnicodeBlock("ALPHABETIC_PRESENTATION_FORMS", 0xfb00, 0xfb4f); /** - * The "Arabic Presentation Forms-A" Unicode Block. + * The "Arabic Presentation Forms-A" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock ARABIC_PRESENTATION_FORMS_A = new UnicodeBlock("ARABIC_PRESENTATION_FORMS_A"); + public static final UnicodeBlock ARABIC_PRESENTATION_FORMS_A = new UnicodeBlock("ARABIC_PRESENTATION_FORMS_A", 0xfb50, 0xfdff); /** - * The "Variation Selectors" Unicode Block. + * The "Variation Selectors" Unicode Block. * - * @since Android 1.0 + * @since 1.5 */ - public static final UnicodeBlock VARIATION_SELECTORS = new UnicodeBlock("VARIATION_SELECTORS"); + public static final UnicodeBlock VARIATION_SELECTORS = new UnicodeBlock("VARIATION_SELECTORS", 0xfe00, 0xfe0f); /** - * The "Combining Half Marks" Unicode Block. + * The "Combining Half Marks" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock COMBINING_HALF_MARKS = new UnicodeBlock("COMBINING_HALF_MARKS"); + public static final UnicodeBlock COMBINING_HALF_MARKS = new UnicodeBlock("COMBINING_HALF_MARKS", 0xfe20, 0xfe2f); /** - * The "CJK Compatibility Forms" Unicode Block. + * The "CJK Compatibility Forms" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock CJK_COMPATIBILITY_FORMS = new UnicodeBlock("CJK_COMPATIBILITY_FORMS"); + public static final UnicodeBlock CJK_COMPATIBILITY_FORMS = new UnicodeBlock("CJK_COMPATIBILITY_FORMS", 0xfe30, 0xfe4f); /** - * The "Small Form Variants" Unicode Block. + * The "Small Form Variants" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock SMALL_FORM_VARIANTS = new UnicodeBlock("SMALL_FORM_VARIANTS"); + public static final UnicodeBlock SMALL_FORM_VARIANTS = new UnicodeBlock("SMALL_FORM_VARIANTS", 0xfe50, 0xfe6f); /** - * The "Arabic Presentation Forms-B" Unicode Block. + * The "Arabic Presentation Forms-B" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock ARABIC_PRESENTATION_FORMS_B = new UnicodeBlock("ARABIC_PRESENTATION_FORMS_B"); + public static final UnicodeBlock ARABIC_PRESENTATION_FORMS_B = new UnicodeBlock("ARABIC_PRESENTATION_FORMS_B", 0xfe70, 0xfeff); /** - * The "Halfwidth and Fullwidth Forms" Unicode Block. + * The "Halfwidth and Fullwidth Forms" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock HALFWIDTH_AND_FULLWIDTH_FORMS = new UnicodeBlock("HALFWIDTH_AND_FULLWIDTH_FORMS"); + public static final UnicodeBlock HALFWIDTH_AND_FULLWIDTH_FORMS = new UnicodeBlock("HALFWIDTH_AND_FULLWIDTH_FORMS", 0xff00, 0xffef); /** - * The "Specials" Unicode Block. + * The "Specials" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock SPECIALS = new UnicodeBlock("SPECIALS"); + public static final UnicodeBlock SPECIALS = new UnicodeBlock("SPECIALS", 0xfff0, 0xffff); /** - * The "Linear B Syllabary" Unicode Block. + * The "Linear B Syllabary" Unicode Block. * - * @since Android 1.0 + * @since 1.2 */ - public static final UnicodeBlock LINEAR_B_SYLLABARY = new UnicodeBlock("LINEAR_B_SYLLABARY"); + public static final UnicodeBlock LINEAR_B_SYLLABARY = new UnicodeBlock("LINEAR_B_SYLLABARY", 0x10000, 0x1007f); /** - * The "Linear B Ideograms" Unicode Block. + * The "Linear B Ideograms" Unicode Block. * - * @since Android 1.0 + * @since 1.5 */ - public static final UnicodeBlock LINEAR_B_IDEOGRAMS = new UnicodeBlock("LINEAR_B_IDEOGRAMS"); + public static final UnicodeBlock LINEAR_B_IDEOGRAMS = new UnicodeBlock("LINEAR_B_IDEOGRAMS", 0x10080, 0x100ff); /** - * The "Aegean Numbers" Unicode Block. + * The "Aegean Numbers" Unicode Block. * - * @since Android 1.0 + * @since 1.5 */ - public static final UnicodeBlock AEGEAN_NUMBERS = new UnicodeBlock("AEGEAN_NUMBERS"); + public static final UnicodeBlock AEGEAN_NUMBERS = new UnicodeBlock("AEGEAN_NUMBERS", 0x10100, 0x1013f); /** - * The "Old Italic" Unicode Block. + * The "Old Italic" Unicode Block. * - * @since Android 1.0 + * @since 1.5 */ - public static final UnicodeBlock OLD_ITALIC = new UnicodeBlock("OLD_ITALIC"); + public static final UnicodeBlock OLD_ITALIC = new UnicodeBlock("OLD_ITALIC", 0x10300, 0x1032f); /** - * The "Gothic" Unicode Block. + * The "Gothic" Unicode Block. * - * @since Android 1.0 + * @since 1.5 */ - public static final UnicodeBlock GOTHIC = new UnicodeBlock("GOTHIC"); + public static final UnicodeBlock GOTHIC = new UnicodeBlock("GOTHIC", 0x10330, 0x1034f); /** - * The "Ugaritic" Unicode Block. + * The "Ugaritic" Unicode Block. * - * @since Android 1.0 + * @since 1.5 */ - public static final UnicodeBlock UGARITIC = new UnicodeBlock("UGARITIC"); + public static final UnicodeBlock UGARITIC = new UnicodeBlock("UGARITIC", 0x10380, 0x1039f); /** - * The "Deseret" Unicode Block. + * The "Deseret" Unicode Block. * - * @since Android 1.0 + * @since 1.5 */ - public static final UnicodeBlock DESERET = new UnicodeBlock("DESERET"); + public static final UnicodeBlock DESERET = new UnicodeBlock("DESERET", 0x10400, 0x1044f); /** - * The "Shavian" Unicode Block. + * The "Shavian" Unicode Block. * - * @since Android 1.0 + * @since 1.5 */ - public static final UnicodeBlock SHAVIAN = new UnicodeBlock("SHAVIAN"); + public static final UnicodeBlock SHAVIAN = new UnicodeBlock("SHAVIAN", 0x10450, 0x1047f); /** - * The "Osmanya" Unicode Block. + * The "Osmanya" Unicode Block. * - * @since Android 1.0 + * @since 1.5 */ - public static final UnicodeBlock OSMANYA = new UnicodeBlock("OSMANYA"); + public static final UnicodeBlock OSMANYA = new UnicodeBlock("OSMANYA", 0x10480, 0x104af); /** - * The "Cypriot Syllabary" Unicode Block. + * The "Cypriot Syllabary" Unicode Block. * - * @since Android 1.0 + * @since 1.5 */ - public static final UnicodeBlock CYPRIOT_SYLLABARY = new UnicodeBlock("CYPRIOT_SYLLABARY"); + public static final UnicodeBlock CYPRIOT_SYLLABARY = new UnicodeBlock("CYPRIOT_SYLLABARY", 0x10800, 0x1083f); /** - * The "Byzantine Musical Symbols" Unicode Block. + * The "Byzantine Musical Symbols" Unicode Block. * - * @since Android 1.0 + * @since 1.5 */ - public static final UnicodeBlock BYZANTINE_MUSICAL_SYMBOLS = new UnicodeBlock("BYZANTINE_MUSICAL_SYMBOLS"); + public static final UnicodeBlock BYZANTINE_MUSICAL_SYMBOLS = new UnicodeBlock("BYZANTINE_MUSICAL_SYMBOLS", 0x1d000, 0x1d0ff); /** - * The "Musical Symbols" Unicode Block. + * The "Musical Symbols" Unicode Block. * - * @since Android 1.0 + * @since 1.5 */ - public static final UnicodeBlock MUSICAL_SYMBOLS = new UnicodeBlock("MUSICAL_SYMBOLS"); + public static final UnicodeBlock MUSICAL_SYMBOLS = new UnicodeBlock("MUSICAL_SYMBOLS", 0x1d100, 0x1d1ff); /** - * The "Tai Xuan Jing Symbols" Unicode Block. + * The "Tai Xuan Jing Symbols" Unicode Block. * - * @since Android 1.0 + * @since 1.5 */ - public static final UnicodeBlock TAI_XUAN_JING_SYMBOLS = new UnicodeBlock("TAI_XUAN_JING_SYMBOLS"); + public static final UnicodeBlock TAI_XUAN_JING_SYMBOLS = new UnicodeBlock("TAI_XUAN_JING_SYMBOLS", 0x1d300, 0x1d35f); /** - * The "Mathematical Alphanumeric Symbols" Unicode Block. + * The "Mathematical Alphanumeric Symbols" Unicode Block. * - * @since Android 1.0 + * @since 1.5 */ - public static final UnicodeBlock MATHEMATICAL_ALPHANUMERIC_SYMBOLS = new UnicodeBlock("MATHEMATICAL_ALPHANUMERIC_SYMBOLS"); + public static final UnicodeBlock MATHEMATICAL_ALPHANUMERIC_SYMBOLS = new UnicodeBlock("MATHEMATICAL_ALPHANUMERIC_SYMBOLS", 0x1d400, 0x1d7ff); /** - * The "CJK Unified Ideographs Extension B" Unicode Block. + * The "CJK Unified Ideographs Extension B" Unicode Block. * - * @since Android 1.0 + * @since 1.5 */ - public static final UnicodeBlock CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B = new UnicodeBlock("CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B"); + public static final UnicodeBlock CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B = new UnicodeBlock("CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B", 0x20000, 0x2a6df); /** - * The "CJK Compatibility Ideographs Supplement" Unicode Block. + * The "CJK Compatibility Ideographs Supplement" Unicode Block. * - * @since Android 1.0 + * @since 1.5 */ - public static final UnicodeBlock CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT = new UnicodeBlock("CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT"); + public static final UnicodeBlock CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT = new UnicodeBlock("CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT", 0x2f800, 0x2fa1f); /** - * The "Tags" Unicode Block. + * The "Tags" Unicode Block. * - * @since Android 1.0 + * @since 1.5 */ - public static final UnicodeBlock TAGS = new UnicodeBlock("TAGS"); + public static final UnicodeBlock TAGS = new UnicodeBlock("TAGS", 0xe0000, 0xe007f); /** - * The "Variation Selectors Supplement" Unicode Block. + * The "Variation Selectors Supplement" Unicode Block. * - * @since Android 1.0 + * @since 1.5 */ - public static final UnicodeBlock VARIATION_SELECTORS_SUPPLEMENT = new UnicodeBlock("VARIATION_SELECTORS_SUPPLEMENT"); + public static final UnicodeBlock VARIATION_SELECTORS_SUPPLEMENT = new UnicodeBlock("VARIATION_SELECTORS_SUPPLEMENT", 0xe0100, 0xe01ef); /** - * The "Supplementary Private Use Area-A" Unicode Block. + * The "Supplementary Private Use Area-A" Unicode Block. * - * @since Android 1.0 + * @since 1.5 */ - public static final UnicodeBlock SUPPLEMENTARY_PRIVATE_USE_AREA_A = new UnicodeBlock("SUPPLEMENTARY_PRIVATE_USE_AREA_A"); + public static final UnicodeBlock SUPPLEMENTARY_PRIVATE_USE_AREA_A = new UnicodeBlock("SUPPLEMENTARY_PRIVATE_USE_AREA_A", 0xf0000, 0xfffff); /** - * The "Supplementary Private Use Area-B" Unicode Block. + * The "Supplementary Private Use Area-B" Unicode Block. * - * @since Android 1.0 + * @since 1.5 */ - public static final UnicodeBlock SUPPLEMENTARY_PRIVATE_USE_AREA_B = new UnicodeBlock("SUPPLEMENTARY_PRIVATE_USE_AREA_B"); + public static final UnicodeBlock SUPPLEMENTARY_PRIVATE_USE_AREA_B = new UnicodeBlock("SUPPLEMENTARY_PRIVATE_USE_AREA_B", 0x100000, 0x10ffff); /* * All of the UnicodeBlocks with valid ranges in ascending order. */ private static UnicodeBlock[] BLOCKS; + // BEGIN android-changed + // /* + // * A SortedMap (String.CASE_INSENSITIVE_ORDER) with keys that represents + // * valid block names and values of the UnicodeBlock constant they map + // * to. + // */ + // private static final SortedMap BLOCKS_BY_NAME = ...; // END android-changed - + /** * Retrieves the constant that corresponds to the specified block name. * The block names are defined by the Unicode 4.0.1 specification in the * {@code Blocks-4.0.1.txt} file. *

          * Block names may be one of the following: - *

          *
            *
          • Canonical block name, as defined by the Unicode specification; * case-insensitive.
          • @@ -1424,13 +1387,15 @@ public final class Character implements Serializable, Comparable { * uppercasing the canonical name and replacing all spaces and hyphens * with underscores. *
          - * + * * @param blockName * the name of the block to retrieve. * @return the UnicodeBlock constant corresponding to {@code blockName}. + * @throws NullPointerException + * if {@code blockName} is {@code null}. * @throws IllegalArgumentException * if {@code blockName} is not a valid block name. - * @since Android 1.0 + * @since 1.5 */ public static final UnicodeBlock forName(String blockName) { // BEGIN android-note @@ -1449,7 +1414,7 @@ public final class Character implements Serializable, Comparable { return SURROGATES_AREA; } else if(blockName.equalsIgnoreCase("greek")) { return GREEK; - } else if(blockName.equals("COMBINING_MARKS_FOR_SYMBOLS") || + } else if(blockName.equals("COMBINING_MARKS_FOR_SYMBOLS") || blockName.equals("Combining Marks for Symbols") || blockName.equals("CombiningMarksforSymbols")) { return COMBINING_MARKS_FOR_SYMBOLS; @@ -1459,27 +1424,26 @@ public final class Character implements Serializable, Comparable { return BLOCKS[block]; // END android-changed } - + /** * Gets the constant for the Unicode block that contains the specified * character. - * + * * @param c * the character for which to get the {@code UnicodeBlock} * constant. * @return the {@code UnicodeBlock} constant for the block that contains * {@code c}, or {@code null} if {@code c} does not belong to * any defined block. - * @since Android 1.0 */ public static UnicodeBlock of(char c) { return of((int) c); } - + /** * Gets the constant for the Unicode block that contains the specified * Unicode code point. - * + * * @param codePoint * the Unicode code point for which to get the * {@code UnicodeBlock} constant. @@ -1488,7 +1452,7 @@ public final class Character implements Serializable, Comparable { * not belong to any defined block. * @throws IllegalArgumentException * if {@code codePoint} is not a valid Unicode code point. - * @since Android 1.0 + * @since 1.5 */ public static UnicodeBlock of(int codePoint) { if (!isValidCodePoint(codePoint)) { @@ -1505,9 +1469,9 @@ public final class Character implements Serializable, Comparable { return BLOCKS[block]; // END android-changed } - + // BEGIN android-changed - private UnicodeBlock(String blockName) { + private UnicodeBlock(String blockName, int start, int end) { super(blockName); } // END android-changed @@ -1516,10 +1480,9 @@ public final class Character implements Serializable, Comparable { /** * Constructs a new {@code Character} with the specified primitive char * value. - * + * * @param value * the primitive char value to store in the new instance. - * @since Android 1.0 */ public Character(char value) { this.value = value; @@ -1527,9 +1490,8 @@ public final class Character implements Serializable, Comparable { /** * Gets the primitive value of this character. - * + * * @return this object's primitive value. - * @since Android 1.0 */ public char charValue() { return value; @@ -1538,7 +1500,7 @@ public final class Character implements Serializable, Comparable { /** * Compares this object to the specified character object to determine their * relative order. - * + * * @param c * the character object to compare this object to. * @return {@code 0} if the value of this character and the value of @@ -1547,22 +1509,22 @@ public final class Character implements Serializable, Comparable { * value if the value of this character is less than the value of * {@code c}. * @see java.lang.Comparable - * @since Android 1.0 + * @since 1.2 */ public int compareTo(Character c) { return value - c.value; } - + /** * Returns a {@code Character} instance for the {@code char} value passed. * For ASCII/Latin-1 characters (and generally all characters with a Unicode * value up to 512), this method should be used instead of the constructor, * as it maintains a cache of corresponding {@code Character} instances. - * + * * @param c * the char value for which to get a {@code Character} instance. * @return the {@code Character} instance for {@code c}. - * @since Android 1.0 + * @since 1.5 */ public static Character valueOf(char c) { if (c >= CACHE_LEN ) { @@ -1589,12 +1551,12 @@ public final class Character implements Serializable, Comparable { } /** * Indicates whether {@code codePoint} is a valid Unicode code point. - * + * * @param codePoint * the code point to test. * @return {@code true} if {@code codePoint} is a valid Unicode code point; * {@code false} otherwise. - * @since Android 1.0 + * @since 1.5 */ public static boolean isValidCodePoint(int codePoint) { return (MIN_CODE_POINT <= codePoint && MAX_CODE_POINT >= codePoint); @@ -1603,12 +1565,12 @@ public final class Character implements Serializable, Comparable { /** * Indicates whether {@code codePoint} is within the supplementary code * point range. - * + * * @param codePoint * the code point to test. * @return {@code true} if {@code codePoint} is within the supplementary * code point range; {@code false} otherwise. - * @since Android 1.0 + * @since 1.5 */ public static boolean isSupplementaryCodePoint(int codePoint) { return (MIN_SUPPLEMENTARY_CODE_POINT <= codePoint && MAX_CODE_POINT >= codePoint); @@ -1618,13 +1580,13 @@ public final class Character implements Serializable, Comparable { * Indicates whether {@code ch} is a high- (or leading-) surrogate code unit * that is used for representing supplementary characters in UTF-16 * encoding. - * + * * @param ch * the character to test. * @return {@code true} if {@code ch} is a high-surrogate code unit; * {@code false} otherwise. * @see #isLowSurrogate(char) - * @since Android 1.0 + * @since 1.5 */ public static boolean isHighSurrogate(char ch) { return (MIN_HIGH_SURROGATE <= ch && MAX_HIGH_SURROGATE >= ch); @@ -1634,21 +1596,21 @@ public final class Character implements Serializable, Comparable { * Indicates whether {@code ch} is a low- (or trailing-) surrogate code unit * that is used for representing supplementary characters in UTF-16 * encoding. - * + * * @param ch * the character to test. * @return {@code true} if {@code ch} is a low-surrogate code unit; * {@code false} otherwise. * @see #isHighSurrogate(char) - * @since Android 1.0 - */ + * @since 1.5 + */ public static boolean isLowSurrogate(char ch) { return (MIN_LOW_SURROGATE <= ch && MAX_LOW_SURROGATE >= ch); } /** * Indicates whether the specified character pair is a valid surrogate pair. - * + * * @param high * the high surrogate unit to test. * @param low @@ -1658,7 +1620,7 @@ public final class Character implements Serializable, Comparable { * otherwise. * @see #isHighSurrogate(char) * @see #isLowSurrogate(char) - * @since Android 1.0 + * @since 1.5 */ public static boolean isSurrogatePair(char high, char low) { return (isHighSurrogate(high) && isLowSurrogate(low)); @@ -1670,12 +1632,14 @@ public final class Character implements Serializable, Comparable { * is greater than or equal to {@code 0x10000}, in which case {@code 2} is * returned, otherwise {@code 1}. To test if the code point is valid, use * the {@link #isValidCodePoint(int)} method. - * + * * @param codePoint * the code point for which to calculate the number of required * chars. * @return {@code 2} if {@code codePoint >= 0x10000}; {@code 1} otherwise. - * @since Android 1.0 + * @see #isValidCodePoint(int) + * @see #isSupplementaryCodePoint(int) + * @since 1.5 */ public static int charCount(int codePoint) { return (codePoint >= 0x10000 ? 2 : 1); @@ -1687,14 +1651,14 @@ public final class Character implements Serializable, Comparable { * surrogates, then the result is indeterminate. The * {@link #isSurrogatePair(char, char)} method should be used prior to this * method to validate the pair. - * + * * @param high * the high surrogate unit. * @param low * the low surrogate unit. * @return the Unicode code point corresponding to the surrogate unit pair. * @see #isSurrogatePair(char, char) - * @since Android 1.0 + * @since 1.5 */ public static int toCodePoint(char high, char low) { // See RFC 2781, Section 2.2 @@ -1711,7 +1675,7 @@ public final class Character implements Serializable, Comparable { * {@code index + 1} is a low-surrogate unit, then the supplementary code * point represented by the pair is returned; otherwise the {@code char} * value at {@code index} is returned. - * + * * @param seq * the source sequence of {@code char} units. * @param index @@ -1719,10 +1683,12 @@ public final class Character implements Serializable, Comparable { * point. * @return the Unicode code point or {@code char} value at {@code index} in * {@code seq}. + * @throws NullPointerException + * if {@code seq} is {@code null}. * @throws IndexOutOfBoundsException * if the {@code index} is negative or greater than or equal to * the length of {@code seq}. - * @since Android 1.0 + * @since 1.5 */ public static int codePointAt(CharSequence seq, int index) { if (seq == null) { @@ -1751,7 +1717,7 @@ public final class Character implements Serializable, Comparable { * {@code index + 1} is a low-surrogate unit, then the supplementary code * point represented by the pair is returned; otherwise the {@code char} * value at {@code index} is returned. - * + * * @param seq * the source array of {@code char} units. * @param index @@ -1759,10 +1725,12 @@ public final class Character implements Serializable, Comparable { * point. * @return the Unicode code point or {@code char} value at {@code index} in * {@code seq}. + * @throws NullPointerException + * if {@code seq} is {@code null}. * @throws IndexOutOfBoundsException * if the {@code index} is negative or greater than or equal to * the length of {@code seq}. - * @since Android 1.0 + * @since 1.5 */ public static int codePointAt(char[] seq, int index) { if (seq == null) { @@ -1792,7 +1760,7 @@ public final class Character implements Serializable, Comparable { * low-surrogate unit, then the supplementary code point represented by the * pair is returned; otherwise the {@code char} value at {@code index} is * returned. - * + * * @param seq * the source array of {@code char} units. * @param index @@ -1801,16 +1769,18 @@ public final class Character implements Serializable, Comparable { * the index after the last unit in {@code seq} that can be used. * @return the Unicode code point or {@code char} value at {@code index} in * {@code seq}. + * @throws NullPointerException + * if {@code seq} is {@code null}. * @throws IndexOutOfBoundsException * if {@code index < 0}, {@code index >= limit}, * {@code limit < 0} or if {@code limit} is greater than the * length of {@code seq}. - * @since Android 1.0 + * @since 1.5 */ public static int codePointAt(char[] seq, int index, int limit) { if (index < 0 || index >= limit || limit < 0 || limit > seq.length) { throw new IndexOutOfBoundsException(); - } + } char high = seq[index++]; if (index >= limit) { @@ -1830,7 +1800,7 @@ public final class Character implements Serializable, Comparable { * {@code index - 2} is a high-surrogate unit, then the supplementary code * point represented by the pair is returned; otherwise the {@code char} * value at {@code index - 1} is returned. - * + * * @param seq * the source sequence of {@code char} units. * @param index @@ -1838,10 +1808,12 @@ public final class Character implements Serializable, Comparable { * point that should be returned. * @return the Unicode code point or {@code char} value before {@code index} * in {@code seq}. + * @throws NullPointerException + * if {@code seq} is {@code null}. * @throws IndexOutOfBoundsException * if the {@code index} is less than 1 or greater than the * length of {@code seq}. - * @since Android 1.0 + * @since 1.5 */ public static int codePointBefore(CharSequence seq, int index) { if (seq == null) { @@ -1870,7 +1842,7 @@ public final class Character implements Serializable, Comparable { * {@code index - 2} is a high-surrogate unit, then the supplementary code * point represented by the pair is returned; otherwise the {@code char} * value at {@code index - 1} is returned. - * + * * @param seq * the source array of {@code char} units. * @param index @@ -1878,10 +1850,12 @@ public final class Character implements Serializable, Comparable { * point that should be returned. * @return the Unicode code point or {@code char} value before {@code index} * in {@code seq}. + * @throws NullPointerException + * if {@code seq} is {@code null}. * @throws IndexOutOfBoundsException * if the {@code index} is less than 1 or greater than the * length of {@code seq}. - * @since Android 1.0 + * @since 1.5 */ public static int codePointBefore(char[] seq, int index) { if (seq == null) { @@ -1911,7 +1885,7 @@ public final class Character implements Serializable, Comparable { * high-surrogate unit, then the supplementary code point represented by the * pair is returned; otherwise the {@code char} value at {@code index - 1} * is returned. - * + * * @param seq * the source array of {@code char} units. * @param index @@ -1921,12 +1895,14 @@ public final class Character implements Serializable, Comparable { * the index of the first element in {@code seq}. * @return the Unicode code point or {@code char} value before {@code index} * in {@code seq}. + * @throws NullPointerException + * if {@code seq} is {@code null}. * @throws IndexOutOfBoundsException * if the {@code index <= start}, {@code start < 0}, * {@code index} is greater than the length of {@code seq}, or * if {@code start} is equal or greater than the length of * {@code seq}. - * @since Android 1.0 + * @since 1.5 */ public static int codePointBefore(char[] seq, int index, int start) { if (seq == null) { @@ -1952,7 +1928,7 @@ public final class Character implements Serializable, Comparable { * Converts the specified Unicode code point into a UTF-16 encoded sequence * and copies the value(s) into the char array {@code dst}, starting at * index {@code dstIndex}. - * + * * @param codePoint * the Unicode code point to encode. * @param dst @@ -1962,12 +1938,14 @@ public final class Character implements Serializable, Comparable { * @return the number of {@code char} value units copied into {@code dst}. * @throws IllegalArgumentException * if {@code codePoint} is not a valid Unicode code point. + * @throws NullPointerException + * if {@code dst} is {@code null}. * @throws IndexOutOfBoundsException * if {@code dstIndex} is negative, greater than or equal to * {@code dst.length} or equals {@code dst.length - 1} when * {@code codePoint} is a * {@link #isSupplementaryCodePoint(int) supplementary code point}. - * @since Android 1.0 + * @since 1.5 */ public static int toChars(int codePoint, char[] dst, int dstIndex) { if (!isValidCodePoint(codePoint)) { @@ -2001,7 +1979,7 @@ public final class Character implements Serializable, Comparable { /** * Converts the specified Unicode code point into a UTF-16 encoded sequence * and returns it as a char array. - * + * * @param codePoint * the Unicode code point to encode. * @return the UTF-16 encoded char sequence. If {@code codePoint} is a @@ -2010,7 +1988,7 @@ public final class Character implements Serializable, Comparable { * contains just one character. * @throws IllegalArgumentException * if {@code codePoint} is not a valid Unicode code point. - * @since Android 1.0 + * @since 1.5 */ public static char[] toChars(int codePoint) { if (!isValidCodePoint(codePoint)) { @@ -2031,7 +2009,7 @@ public final class Character implements Serializable, Comparable { * specified character sequence, as delineated by {@code beginIndex} and * {@code endIndex}. Any surrogate values with missing pair values will be * counted as one code point. - * + * * @param seq * the {@code CharSequence} to look through. * @param beginIndex @@ -2039,10 +2017,12 @@ public final class Character implements Serializable, Comparable { * @param endIndex * the exclusive index to stop counting at. * @return the number of Unicode code points. + * @throws NullPointerException + * if {@code seq} is {@code null}. * @throws IndexOutOfBoundsException * if {@code beginIndex < 0}, {@code beginIndex > endIndex} or * if {@code endIndex} is greater than the length of {@code seq}. - * @since Android 1.0 + * @since 1.5 */ public static int codePointCount(CharSequence seq, int beginIndex, int endIndex) { @@ -2075,7 +2055,7 @@ public final class Character implements Serializable, Comparable { * specified char array, as delineated by {@code offset} and {@code count}. * Any surrogate values with missing pair values will be counted as one code * point. - * + * * @param seq * the char array to look through * @param offset @@ -2084,11 +2064,13 @@ public final class Character implements Serializable, Comparable { * the number of {@code char} values to look through in * {@code seq}. * @return the number of Unicode code points. + * @throws NullPointerException + * if {@code seq} is {@code null}. * @throws IndexOutOfBoundsException * if {@code offset < 0}, {@code count < 0} or if * {@code offset + count} is greater than the length of * {@code seq}. - * @since Android 1.0 + * @since 1.5 */ public static int codePointCount(char[] seq, int offset, int count) { if (seq == null) { @@ -2119,7 +2101,7 @@ public final class Character implements Serializable, Comparable { /** * Determines the index in the specified character sequence that is offset * {@code codePointOffset} code points from {@code index}. - * + * * @param seq * the character sequence to find the index in. * @param index @@ -2129,13 +2111,15 @@ public final class Character implements Serializable, Comparable { * be a negative or positive value. * @return the index in {@code seq} that is {@code codePointOffset} code * points away from {@code index}. + * @throws NullPointerException + * if {@code seq} is {@code null}. * @throws IndexOutOfBoundsException * if {@code index < 0}, {@code index} is greater than the * length of {@code seq}, or if there are not enough values in * {@code seq} to skip {@code codePointOffset} code points * forwards or backwards (if {@code codePointOffset} is * negative) from {@code index}. - * @since Android 1.0 + * @since 1.5 */ public static int offsetByCodePoints(CharSequence seq, int index, int codePointOffset) { @@ -2193,7 +2177,7 @@ public final class Character implements Serializable, Comparable { * Determines the index in a subsequence of the specified character array * that is offset {@code codePointOffset} code points from {@code index}. * The subsequence is delineated by {@code start} and {@code count}. - * + * * @param seq * the character array to find the index in. * @param start @@ -2209,6 +2193,8 @@ public final class Character implements Serializable, Comparable { * be a negative or positive value. * @return the index in {@code seq} that is {@code codePointOffset} code * points away from {@code index}. + * @throws NullPointerException + * if {@code seq} is {@code null}. * @throws IndexOutOfBoundsException * if {@code start < 0}, {@code count < 0}, * {@code index < start}, {@code index > start + count}, @@ -2217,7 +2203,7 @@ public final class Character implements Serializable, Comparable { * {@code seq} to skip {@code codePointOffset} code points * forward or backward (if {@code codePointOffset} is * negative) from {@code index}. - * @since Android 1.0 + * @since 1.5 */ public static int offsetByCodePoints(char[] seq, int start, int count, int index, int codePointOffset) { @@ -2276,14 +2262,13 @@ public final class Character implements Serializable, Comparable { * Convenience method to determine the value of the specified character * {@code c} in the supplied radix. The value of {@code radix} must be * between MIN_RADIX and MAX_RADIX. - * + * * @param c * the character to determine the value of. * @param radix * the radix. * @return the value of {@code c} in {@code radix} if {@code radix} lies * between {@link #MIN_RADIX} and {@link #MAX_RADIX}; -1 otherwise. - * @since Android 1.0 */ public static int digit(char c, int radix) { // BEGIN android-changed @@ -2313,12 +2298,12 @@ public final class Character implements Serializable, Comparable { return UCharacter.digit(c, radix); // ENd android-changed } - + /** * Convenience method to determine the value of the character * {@code codePoint} in the supplied radix. The value of {@code radix} must * be between MIN_RADIX and MAX_RADIX. - * + * * @param codePoint * the character, including supplementary characters. * @param radix @@ -2326,7 +2311,6 @@ public final class Character implements Serializable, Comparable { * @return if {@code radix} lies between {@link #MIN_RADIX} and * {@link #MAX_RADIX} then the value of the character in the radix; * -1 otherwise. - * @since Android 1.0 */ public static int digit(int codePoint, int radix) { return UCharacter.digit(codePoint, radix); @@ -2336,12 +2320,11 @@ public final class Character implements Serializable, Comparable { * Compares this object with the specified object and indicates if they are * equal. In order to be equal, {@code object} must be an instance of * {@code Character} and have the same char value as this object. - * + * * @param object * the object to compare this double with. * @return {@code true} if the specified object is equal to this * {@code Character}; {@code false} otherwise. - * @since Android 1.0 */ @Override public boolean equals(Object object) { @@ -2355,14 +2338,13 @@ public final class Character implements Serializable, Comparable { * {@code MAX_RADIX} inclusive; {@code digit} must not be negative and * smaller than {@code radix}. If any of these conditions does not hold, 0 * is returned. - * + * * @param digit * the integer value. * @param radix * the radix. * @return the character which represents the {@code digit} in the * {@code radix}. - * @since Android 1.0 */ public static char forDigit(int digit, int radix) { if (MIN_RADIX <= radix && radix <= MAX_RADIX) { @@ -2375,13 +2357,12 @@ public final class Character implements Serializable, Comparable { /** * Gets the numeric value of the specified Unicode character. - * + * * @param c * the Unicode character to get the numeric value of. * @return a non-negative numeric integer value if a numeric value for * {@code c} exists, -1 if there is no numeric value for {@code c}, * -2 if the numeric value can not be represented with an integer. - * @since Android 1.0 */ public static int getNumericValue(char c) { // BEGIN android-changed @@ -2414,19 +2395,18 @@ public final class Character implements Serializable, Comparable { return UCharacter.getNumericValue(c); // END android-changed } - + /** * Gets the numeric value of the specified Unicode code point. For example, * the code point '\u216B' stands for the Roman number XII, which has the * numeric value 12. - * + * * @param codePoint * the Unicode code point to get the numeric value of. * @return a non-negative numeric integer value if a numeric value for * {@code codePoint} exists, -1 if there is no numeric value for * {@code codePoint}, -2 if the numeric value can not be * represented with an integer. - * @since Android 1.0 */ public static int getNumericValue(int codePoint) { return UCharacter.getNumericValue(codePoint); @@ -2434,14 +2414,16 @@ public final class Character implements Serializable, Comparable { /** * Gets the general Unicode category of the specified character. - * + * * @param c * the character to get the category of. * @return the Unicode category of {@code c}. - * @since Android 1.0 */ public static int getType(char c) { // BEGIN android-changed + // if(c < 1000) { + // return typeValuesCache[(int)c]; + // } // int result = BinarySearch.binarySearchRange(typeKeys, c); // int high = typeValues[result * 2]; // if (c <= high) { @@ -2452,19 +2434,23 @@ public final class Character implements Serializable, Comparable { // return (c & 1) == 1 ? code >> 8 : code & 0xff; // } // return UNASSIGNED; - return getType((int)c); + return getType((int) c); // END android-changed } - + /** * Gets the general Unicode category of the specified code point. - * + * * @param codePoint * the Unicode code point to get the category of. * @return the Unicode category of {@code codePoint}. - * @since Android 1.0 */ public static int getType(int codePoint) { + // BEGIN android-changed + // if (codePoint < 1000 && codePoint > 0) { + // return typeValuesCache[codePoint]; + // } + // END android-changed int type = UCharacter.getType(codePoint); // the type values returned by UCharacter are not compatible with what @@ -2477,11 +2463,10 @@ public final class Character implements Serializable, Comparable { /** * Gets the Unicode directionality of the specified character. - * + * * @param c * the character to get the directionality of. * @return the Unicode directionality of {@code c}. - * @since Android 1.0 */ public static byte getDirectionality(char c) { // BEGIN android-changed @@ -2498,21 +2483,20 @@ public final class Character implements Serializable, Comparable { return getDirectionality((int)c); // END android-changed } - + /** * Gets the Unicode directionality of the specified character. - * + * * @param codePoint * the Unicode code point to get the directionality of. * @return the Unicode directionality of {@code codePoint}. - * @since Android 1.0 */ public static byte getDirectionality(int codePoint) { if (getType(codePoint) == Character.UNASSIGNED) { return Character.DIRECTIONALITY_UNDEFINED; } - - byte UCDirectionality = UCharacter.getDirectionality(codePoint); + + byte UCDirectionality = UCharacter.getDirectionality(codePoint); if (UCDirectionality == -1) { return -1; } @@ -2521,12 +2505,11 @@ public final class Character implements Serializable, Comparable { /** * Indicates whether the specified character is mirrored. - * + * * @param c * the character to check. * @return {@code true} if {@code c} is mirrored; {@code false} * otherwise. - * @since Android 1.0 */ public static boolean isMirrored(char c) { // BEGIN android-changed @@ -2539,15 +2522,14 @@ public final class Character implements Serializable, Comparable { return isMirrored((int)c); // ENd android-changed } - + /** * Indicates whether the specified code point is mirrored. - * + * * @param codePoint * the code point to check. * @return {@code true} if {@code codePoint} is mirrored, {@code false} * otherwise. - * @since Android 1.0 */ public static boolean isMirrored(int codePoint) { return UCharacter.isMirrored(codePoint); @@ -2561,12 +2543,11 @@ public final class Character implements Serializable, Comparable { /** * Indicates whether the specified character is defined in the Unicode * specification. - * + * * @param c * the character to check. * @return {@code true} if the general Unicode category of the character is * not {@code UNASSIGNED}; {@code false} otherwise. - * @since Android 1.0 */ public static boolean isDefined(char c) { // BEGIN android-changed @@ -2574,16 +2555,15 @@ public final class Character implements Serializable, Comparable { return UCharacter.isDefined(c); // END android-changed } - + /** * Indicates whether the specified code point is defined in the Unicode * specification. - * + * * @param codePoint * the code point to check. * @return {@code true} if the general Unicode category of the code point is * not {@code UNASSIGNED}; {@code false} otherwise. - * @since Android 1.0 */ public static boolean isDefined(int codePoint) { return UCharacter.isDefined(codePoint); @@ -2591,12 +2571,11 @@ public final class Character implements Serializable, Comparable { /** * Indicates whether the specified character is a digit. - * + * * @param c * the character to check. * @return {@code true} if {@code c} is a digit; {@code false} * otherwise. - * @since Android 1.0 */ public static boolean isDigit(char c) { // BEGIN android-changed @@ -2611,15 +2590,14 @@ public final class Character implements Serializable, Comparable { return UCharacter.isDigit(c); // END android-changed } - + /** * Indicates whether the specified code point is a digit. - * + * * @param codePoint * the code point to check. * @return {@code true} if {@code codePoint} is a digit; {@code false} * otherwise. - * @since Android 1.0 */ public static boolean isDigit(int codePoint) { return UCharacter.isDigit(codePoint); @@ -2628,11 +2606,10 @@ public final class Character implements Serializable, Comparable { /** * Indicates whether the specified character is ignorable in a Java or * Unicode identifier. - * + * * @param c * the character to check. * @return {@code true} if {@code c} is ignorable; {@code false} otherwise. - * @since Android 1.0 */ public static boolean isIdentifierIgnorable(char c) { // BEGIN android-changed @@ -2641,16 +2618,15 @@ public final class Character implements Serializable, Comparable { return UCharacter.isIdentifierIgnorable(c); // END android-changed } - + /** * Indicates whether the specified code point is ignorable in a Java or * Unicode identifier. - * + * * @param codePoint * the code point to check. * @return {@code true} if {@code codePoint} is ignorable; {@code false} * otherwise. - * @since Android 1.0 */ public static boolean isIdentifierIgnorable(int codePoint) { return UCharacter.isIdentifierIgnorable(codePoint); @@ -2658,12 +2634,11 @@ public final class Character implements Serializable, Comparable { /** * Indicates whether the specified character is an ISO control character. - * + * * @param c * the character to check. * @return {@code true} if {@code c} is an ISO control character; * {@code false} otherwise. - * @since Android 1.0 */ public static boolean isISOControl(char c) { return isISOControl((int)c); @@ -2671,12 +2646,11 @@ public final class Character implements Serializable, Comparable { /** * Indicates whether the specified code point is an ISO control character. - * + * * @param c * the code point to check. * @return {@code true} if {@code c} is an ISO control character; * {@code false} otherwise. - * @since Android 1.0 */ public static boolean isISOControl(int c) { return (c >= 0 && c <= 0x1f) || (c >= 0x7f && c <= 0x9f); @@ -2685,12 +2659,11 @@ public final class Character implements Serializable, Comparable { /** * Indicates whether the specified character is a valid part of a Java * identifier other than the first character. - * + * * @param c * the character to check. * @return {@code true} if {@code c} is valid as part of a Java identifier; * {@code false} otherwise. - * @since Android 1.0 */ public static boolean isJavaIdentifierPart(char c) { // Optimized case for ASCII @@ -2709,31 +2682,29 @@ public final class Character implements Serializable, Comparable { /** * Indicates whether the specified code point is a valid part of a Java * identifier other than the first character. - * + * * @param codePoint * the code point to check. * @return {@code true} if {@code c} is valid as part of a Java identifier; * {@code false} otherwise. - * @since Android 1.0 */ public static boolean isJavaIdentifierPart(int codePoint) { int type = getType(codePoint); return (type >= UPPERCASE_LETTER && type <= OTHER_LETTER) || type == CURRENCY_SYMBOL || type == CONNECTOR_PUNCTUATION || (type >= DECIMAL_DIGIT_NUMBER && type <= LETTER_NUMBER) - || type == COMBINING_SPACING_MARK || type == NON_SPACING_MARK + || type == COMBINING_SPACING_MARK || type == NON_SPACING_MARK || isIdentifierIgnorable(codePoint); } /** * Indicates whether the specified character is a valid first character for * a Java identifier. - * + * * @param c * the character to check. * @return {@code true} if {@code c} is a valid first character of a Java * identifier; {@code false} otherwise. - * @since Android 1.0 */ public static boolean isJavaIdentifierStart(char c) { // Optimized case for ASCII @@ -2746,16 +2717,15 @@ public final class Character implements Serializable, Comparable { || type == CURRENCY_SYMBOL || type == CONNECTOR_PUNCTUATION || type == LETTER_NUMBER; } - + /** * Indicates whether the specified code point is a valid start for a Java * identifier. - * + * * @param codePoint * the code point to check. * @return {@code true} if {@code codePoint} is a valid start of a Java * identifier; {@code false} otherwise. - * @since Android 1.0 */ public static boolean isJavaIdentifierStart(int codePoint) { int type = getType(codePoint); @@ -2765,13 +2735,12 @@ public final class Character implements Serializable, Comparable { /** * Indicates whether the specified character is a Java letter. - * + * * @param c * the character to check. * @return {@code true} if {@code c} is a Java letter; {@code false} * otherwise. * @deprecated Use {@link #isJavaIdentifierStart(char)} - * @since Android 1.0 */ @Deprecated public static boolean isJavaLetter(char c) { @@ -2781,13 +2750,12 @@ public final class Character implements Serializable, Comparable { /** * Indicates whether the specified character is a Java letter or digit * character. - * + * * @param c * the character to check. * @return {@code true} if {@code c} is a Java letter or digit; * {@code false} otherwise. * @deprecated Use {@link #isJavaIdentifierPart(char)} - * @since Android 1.0 */ @Deprecated public static boolean isJavaLetterOrDigit(char c) { @@ -2796,11 +2764,10 @@ public final class Character implements Serializable, Comparable { /** * Indicates whether the specified character is a letter. - * + * * @param c * the character to check. * @return {@code true} if {@code c} is a letter; {@code false} otherwise. - * @since Android 1.0 */ public static boolean isLetter(char c) { // BEGIN android-changed @@ -2815,15 +2782,14 @@ public final class Character implements Serializable, Comparable { return UCharacter.isLetter(c); // END android-changed } - + /** * Indicates whether the specified code point is a letter. - * + * * @param codePoint * the code point to check. * @return {@code true} if {@code codePoint} is a letter; {@code false} * otherwise. - * @since Android 1.0 */ public static boolean isLetter(int codePoint) { return UCharacter.isLetter(codePoint); @@ -2831,12 +2797,11 @@ public final class Character implements Serializable, Comparable { /** * Indicates whether the specified character is a letter or a digit. - * + * * @param c * the character to check. * @return {@code true} if {@code c} is a letter or a digit; {@code false} * otherwise. - * @since Android 1.0 */ public static boolean isLetterOrDigit(char c) { // BEGIN android-changed @@ -2846,15 +2811,14 @@ public final class Character implements Serializable, Comparable { return UCharacter.isLetterOrDigit(c); // END andorid-changed } - + /** * Indicates whether the specified code point is a letter or a digit. - * + * * @param codePoint * the code point to check. * @return {@code true} if {@code codePoint} is a letter or a digit; * {@code false} otherwise. - * @since Android 1.0 */ public static boolean isLetterOrDigit(int codePoint) { return UCharacter.isLetterOrDigit(codePoint); @@ -2862,12 +2826,11 @@ public final class Character implements Serializable, Comparable { /** * Indicates whether the specified character is a lower case letter. - * + * * @param c * the character to check. * @return {@code true} if {@code c} is a lower case letter; {@code false} * otherwise. - * @since Android 1.0 */ public static boolean isLowerCase(char c) { // BEGIN android-changed @@ -2878,20 +2841,19 @@ public final class Character implements Serializable, Comparable { // if (c < 128) { // return false; // } - // + // // return getType(c) == LOWERCASE_LETTER; return UCharacter.isLowerCase(c); // END android-changed } - + /** * Indicates whether the specified code point is a lower case letter. - * + * * @param codePoint * the code point to check. * @return {@code true} if {@code codePoint} is a lower case letter; * {@code false} otherwise. - * @since Android 1.0 */ public static boolean isLowerCase(int codePoint) { return UCharacter.isLowerCase(codePoint); @@ -2899,13 +2861,12 @@ public final class Character implements Serializable, Comparable { /** * Indicates whether the specified character is a Java space. - * + * * @param c * the character to check. * @return {@code true} if {@code c} is a Java space; {@code false} * otherwise. * @deprecated Use {@link #isWhitespace(char)} - * @since Android 1.0 */ @Deprecated public static boolean isSpace(char c) { @@ -2916,12 +2877,11 @@ public final class Character implements Serializable, Comparable { * Indicates whether the specified character is a Unicode space character. * That is, if it is a member of one of the Unicode categories Space * Separator, Line Separator, or Paragraph Separator. - * + * * @param c * the character to check. * @return {@code true} if {@code c} is a Unicode space character, * {@code false} otherwise. - * @since Android 1.0 */ public static boolean isSpaceChar(char c) { // BEGIN android-changed @@ -2936,17 +2896,16 @@ public final class Character implements Serializable, Comparable { return UCharacter.isSpaceChar(c); // END android-changed } - + /** * Indicates whether the specified code point is a Unicode space character. * That is, if it is a member of one of the Unicode categories Space * Separator, Line Separator, or Paragraph Separator. - * + * * @param codePoint * the code point to check. * @return {@code true} if {@code codePoint} is a Unicode space character, * {@code false} otherwise. - * @since Android 1.0 */ public static boolean isSpaceChar(int codePoint) { return UCharacter.isSpaceChar(codePoint); @@ -2954,12 +2913,11 @@ public final class Character implements Serializable, Comparable { /** * Indicates whether the specified character is a titlecase character. - * + * * @param c * the character to check. * @return {@code true} if {@code c} is a titlecase character, {@code false} * otherwise. - * @since Android 1.0 */ public static boolean isTitleCase(char c) { // BEGIN android-changed @@ -2978,15 +2936,14 @@ public final class Character implements Serializable, Comparable { return UCharacter.isTitleCase(c); // END android-changed } - + /** * Indicates whether the specified code point is a titlecase character. - * + * * @param codePoint * the code point to check. * @return {@code true} if {@code codePoint} is a titlecase character, * {@code false} otherwise. - * @since Android 1.0 */ public static boolean isTitleCase(int codePoint) { return UCharacter.isTitleCase(codePoint); @@ -2995,12 +2952,11 @@ public final class Character implements Serializable, Comparable { /** * Indicates whether the specified character is valid as part of a Unicode * identifier other than the first character. - * + * * @param c * the character to check. * @return {@code true} if {@code c} is valid as part of a Unicode * identifier; {@code false} otherwise. - * @since Android 1.0 */ public static boolean isUnicodeIdentifierPart(char c) { // BEGIN android-changed @@ -3013,16 +2969,15 @@ public final class Character implements Serializable, Comparable { return UCharacter.isUnicodeIdentifierPart(c); // END android-changed } - + /** * Indicates whether the specified code point is valid as part of a Unicode * identifier other than the first character. - * + * * @param codePoint * the code point to check. * @return {@code true} if {@code codePoint} is valid as part of a Unicode * identifier; {@code false} otherwise. - * @since Android 1.0 */ public static boolean isUnicodeIdentifierPart(int codePoint) { return UCharacter.isUnicodeIdentifierPart(codePoint); @@ -3031,12 +2986,11 @@ public final class Character implements Serializable, Comparable { /** * Indicates whether the specified character is a valid initial character * for a Unicode identifier. - * + * * @param c * the character to check. * @return {@code true} if {@code c} is a valid first character for a * Unicode identifier; {@code false} otherwise. - * @since Android 1.0 */ public static boolean isUnicodeIdentifierStart(char c) { // BEGIN android-changed @@ -3046,16 +3000,15 @@ public final class Character implements Serializable, Comparable { return UCharacter.isUnicodeIdentifierStart(c); // END android-changed } - + /** * Indicates whether the specified code point is a valid initial character * for a Unicode identifier. - * + * * @param codePoint * the code point to check. * @return {@code true} if {@code codePoint} is a valid first character for * a Unicode identifier; {@code false} otherwise. - * @since Android 1.0 */ public static boolean isUnicodeIdentifierStart(int codePoint) { return UCharacter.isUnicodeIdentifierStart(codePoint); @@ -3063,12 +3016,11 @@ public final class Character implements Serializable, Comparable { /** * Indicates whether the specified character is an upper case letter. - * + * * @param c * the character to check. * @return {@code true} if {@code c} is a upper case letter; {@code false} * otherwise. - * @since Android 1.0 */ public static boolean isUpperCase(char c) { // BEGIN android-changed @@ -3079,20 +3031,19 @@ public final class Character implements Serializable, Comparable { // if (c < 128) { // return false; // } - // + // // return getType(c) == UPPERCASE_LETTER; return UCharacter.isUpperCase(c); // END android-changed } - + /** * Indicates whether the specified code point is an upper case letter. - * + * * @param codePoint * the code point to check. * @return {@code true} if {@code codePoint} is a upper case letter; * {@code false} otherwise. - * @since Android 1.0 */ public static boolean isUpperCase(int codePoint) { return UCharacter.isUpperCase(codePoint); @@ -3101,12 +3052,11 @@ public final class Character implements Serializable, Comparable { /** * Indicates whether the specified character is a whitespace character in * Java. - * + * * @param c * the character to check. * @return {@code true} if the supplied {@code c} is a whitespace character * in Java; {@code false} otherwise. - * @since Android 1.0 */ public static boolean isWhitespace(char c) { // BEGIN android-changed @@ -3124,30 +3074,29 @@ public final class Character implements Serializable, Comparable { return UCharacter.isWhitespace(c); // END android-changed } - + /** * Indicates whether the specified code point is a whitespace character in * Java. - * + * * @param codePoint * the code point to check. * @return {@code true} if the supplied {@code c} is a whitespace character * in Java; {@code false} otherwise. - * @since Android 1.0 */ public static boolean isWhitespace(int codePoint) { //FIXME depends on ICU when the codePoint is '\u2007' return UCharacter.isWhitespace(codePoint); + } /** * Reverses the order of the first and second byte in the specified * character. - * + * * @param c * the character to reverse. * @return the character with reordered bytes. - * @since Android 1.0 */ public static char reverseBytes(char c) { return (char)((c<<8) | (c>>8)); @@ -3157,12 +3106,11 @@ public final class Character implements Serializable, Comparable { * Returns the lower case equivalent for the specified character if the * character is an upper case letter. Otherwise, the specified character is * returned unchanged. - * + * * @param c * the character * @return if {@code c} is an upper case character then its lower case * counterpart, otherwise just {@code c}. - * @since Android 1.0 */ public static char toLowerCase(char c) { // BEGIN android-changed @@ -3170,10 +3118,13 @@ public final class Character implements Serializable, Comparable { // if ('A' <= c && c <= 'Z') { // return (char) (c + ('a' - 'A')); // } - // if (c < 128) { + // if (c < 192) {// || c == 215 || (c > 222 && c < 256)) { // return c; // } - // + // if (c<1000) { + // return (char)lowercaseValuesCache[c-192]; + // } + // // int result = BinarySearch.binarySearchRange(lowercaseKeys, c); // if (result >= 0) { // boolean by2 = false; @@ -3195,17 +3146,16 @@ public final class Character implements Serializable, Comparable { return (char)UCharacter.toLowerCase(c); // END android-changed } - + /** * Returns the lower case equivalent for the specified code point if it is * an upper case letter. Otherwise, the specified code point is returned * unchanged. - * + * * @param codePoint * the code point to check. * @return if {@code codePoint} is an upper case character then its lower * case counterpart, otherwise just {@code codePoint}. - * @since Android 1.0 */ public static int toLowerCase(int codePoint) { return UCharacter.toLowerCase(codePoint); @@ -3218,11 +3168,10 @@ public final class Character implements Serializable, Comparable { /** * Converts the specified character to its string representation. - * + * * @param value * the character to convert. * @return the character converted to a string. - * @since Android 1.0 */ public static String toString(char value) { return String.valueOf(value); @@ -3231,12 +3180,11 @@ public final class Character implements Serializable, Comparable { /** * Returns the title case equivalent for the specified character if it * exists. Otherwise, the specified character is returned unchanged. - * + * * @param c * the character to convert. * @return the title case equivalent of {@code c} if it exists, otherwise * {@code c}. - * @since Android 1.0 */ public static char toTitleCase(char c) { // BEGIN android-changed @@ -3251,16 +3199,15 @@ public final class Character implements Serializable, Comparable { return (char)UCharacter.toTitleCase(c); // ENd android-changed } - + /** * Returns the title case equivalent for the specified code point if it * exists. Otherwise, the specified code point is returned unchanged. - * + * * @param codePoint * the code point to convert. * @return the title case equivalent of {@code codePoint} if it exists, * otherwise {@code codePoint}. - * @since Android 1.0 */ public static int toTitleCase(int codePoint) { return UCharacter.toTitleCase(codePoint); @@ -3270,12 +3217,11 @@ public final class Character implements Serializable, Comparable { * Returns the upper case equivalent for the specified character if the * character is a lower case letter. Otherwise, the specified character is * returned unchanged. - * + * * @param c * the character to convert. * @return if {@code c} is a lower case character then its upper case * counterpart, otherwise just {@code c}. - * @since Android 1.0 */ public static char toUpperCase(char c) { // BEGIN android-changed @@ -3283,10 +3229,12 @@ public final class Character implements Serializable, Comparable { // if ('a' <= c && c <= 'z') { // return (char) (c - ('a' - 'A')); // } - // if (c < 128) { + // if (c < 181) { // return c; // } - // + // if (c<1000) { + // return (char)uppercaseValuesCache[(int)c-181]; + // } // int result = BinarySearch.binarySearchRange(uppercaseKeys, c); // if (result >= 0) { // boolean by2 = false; @@ -3308,17 +3256,16 @@ public final class Character implements Serializable, Comparable { return (char)UCharacter.toUpperCase(c); // END android-changed } - + /** * Returns the upper case equivalent for the specified code point if the * code point is a lower case letter. Otherwise, the specified code point is * returned unchanged. - * + * * @param codePoint * the code point to convert. * @return if {@code codePoint} is a lower case character then its upper * case counterpart, otherwise just {@code codePoint}. - * @since Android 1.0 */ public static int toUpperCase(int codePoint) { return UCharacter.toUpperCase(codePoint); diff --git a/libcore/luni/src/main/java/java/lang/ClassCastException.java b/libcore/luni/src/main/java/java/lang/ClassCastException.java index 1482b2170..b33efcf03 100644 --- a/libcore/luni/src/main/java/java/lang/ClassCastException.java +++ b/libcore/luni/src/main/java/java/lang/ClassCastException.java @@ -22,8 +22,6 @@ import org.apache.harmony.luni.util.Msg; /** * Thrown when a program attempts to cast a an object to a type with which it is * not compatible. - * - * @since Android 1.0 */ public class ClassCastException extends RuntimeException { private static final long serialVersionUID = -9223365651070458532L; @@ -31,8 +29,6 @@ public class ClassCastException extends RuntimeException { /** * Constructs a new {@code ClassCastException} that includes the current * stack trace. - * - * @since Android 1.0 */ public ClassCastException() { super(); @@ -41,10 +37,9 @@ public class ClassCastException extends RuntimeException { /** * Constructs a new {@code ClassCastException} with the current stack trace * and the specified detail message. - * + * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public ClassCastException(String detailMessage) { super(detailMessage); @@ -53,12 +48,11 @@ public class ClassCastException extends RuntimeException { /** * Constructs a new {@code ClassCastException} with the current stack trace * and a detail message based on the source and target class. - * + * * @param instanceClass * the class being cast from. * @param castClass * the class being cast to. - * @since Android 1.0 */ ClassCastException(Class instanceClass, Class castClass) { super(Msg.getString("K0340", instanceClass.getName(), castClass //$NON-NLS-1$ diff --git a/libcore/luni/src/main/java/java/lang/ClassCircularityError.java b/libcore/luni/src/main/java/java/lang/ClassCircularityError.java index 6dd1c8e61..07bf339b1 100644 --- a/libcore/luni/src/main/java/java/lang/ClassCircularityError.java +++ b/libcore/luni/src/main/java/java/lang/ClassCircularityError.java @@ -24,9 +24,6 @@ package java.lang; *

          * Note that this error can only occur when inconsistent class files are loaded, * since it would normally be detected at compile time. - *

          - * - * @since Android 1.0 */ public class ClassCircularityError extends LinkageError { @@ -35,8 +32,6 @@ public class ClassCircularityError extends LinkageError { /** * Constructs a new {@code ClassCircularityError} that include the current * stack trace. - * - * @since Android 1.0 */ public ClassCircularityError() { super(); @@ -45,10 +40,9 @@ public class ClassCircularityError extends LinkageError { /** * Constructs a new {@code ClassCircularityError} with the current stack * trace and the specified detail message. - * + * * @param detailMessage * the detail message for this error. - * @since Android 1.0 */ public ClassCircularityError(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/lang/ClassFormatError.java b/libcore/luni/src/main/java/java/lang/ClassFormatError.java index 282ed7064..009f44a1c 100644 --- a/libcore/luni/src/main/java/java/lang/ClassFormatError.java +++ b/libcore/luni/src/main/java/java/lang/ClassFormatError.java @@ -20,8 +20,6 @@ package java.lang; /** * Thrown by a class loader when a class file has an illegal format or if the * data that it contains can not be interpreted as a class. - * - * @since Android 1.0 */ public class ClassFormatError extends LinkageError { @@ -30,8 +28,6 @@ public class ClassFormatError extends LinkageError { /** * Constructs a new {@code ClassFormatError} that includes the current stack * trace. - * - * @since Android 1.0 */ public ClassFormatError() { super(); @@ -40,10 +36,9 @@ public class ClassFormatError extends LinkageError { /** * Constructs a new {@code ClassFormatError} with the current stack trace * and the specified detail message. - * + * * @param detailMessage * the detail message for this error. - * @since Android 1.0 */ public ClassFormatError(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/lang/ClassNotFoundException.java b/libcore/luni/src/main/java/java/lang/ClassNotFoundException.java index 26bdbf781..2ac3b4940 100644 --- a/libcore/luni/src/main/java/java/lang/ClassNotFoundException.java +++ b/libcore/luni/src/main/java/java/lang/ClassNotFoundException.java @@ -19,8 +19,6 @@ package java.lang; /** * Thrown when a class loader is unable to find a class. - * - * @since Android 1.0 */ public class ClassNotFoundException extends Exception { @@ -31,8 +29,6 @@ public class ClassNotFoundException extends Exception { /** * Constructs a new {@code ClassNotFoundException} that includes the current * stack trace. - * - * @since Android 1.0 */ public ClassNotFoundException() { super((Throwable) null); @@ -41,10 +37,9 @@ public class ClassNotFoundException extends Exception { /** * Constructs a new {@code ClassNotFoundException} with the current stack * trace and the specified detail message. - * + * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public ClassNotFoundException(String detailMessage) { super(detailMessage, null); @@ -54,12 +49,11 @@ public class ClassNotFoundException extends Exception { * Constructs a new {@code ClassNotFoundException} with the current stack * trace, the specified detail message and the exception that occurred when * loading the class. - * + * * @param detailMessage * the detail message for this exception. * @param exception * the exception which occurred while loading the class. - * @since Android 1.0 */ public ClassNotFoundException(String detailMessage, Throwable exception) { super(detailMessage); @@ -68,7 +62,7 @@ public class ClassNotFoundException extends Exception { /** * Returns the exception which occurred when loading the class. - * + * * @return Throwable the exception which occurred while loading the class. */ public Throwable getException() { @@ -78,7 +72,7 @@ public class ClassNotFoundException extends Exception { /** * Returns the cause of this Throwable, or {@code null} if there is no * cause. - * + * * @return Throwable the receiver's cause. */ @Override diff --git a/libcore/luni/src/main/java/java/lang/CloneNotSupportedException.java b/libcore/luni/src/main/java/java/lang/CloneNotSupportedException.java index b484e08d0..2f4f11c3b 100644 --- a/libcore/luni/src/main/java/java/lang/CloneNotSupportedException.java +++ b/libcore/luni/src/main/java/java/lang/CloneNotSupportedException.java @@ -20,9 +20,8 @@ package java.lang; /** * Thrown when a program attempts to clone an object which does not support the * {@link Cloneable} interface. - * + * * @see Cloneable - * @since Android 1.0 */ public class CloneNotSupportedException extends Exception { @@ -31,8 +30,6 @@ public class CloneNotSupportedException extends Exception { /** * Constructs a new {@code CloneNotSupportedException} that includes the * current stack trace. - * - * @since Android 1.0 */ public CloneNotSupportedException() { super(); @@ -41,10 +38,9 @@ public class CloneNotSupportedException extends Exception { /** * Constructs a new {@code CloneNotSupportedException} with the current * stack trace and the specified detail message. - * + * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public CloneNotSupportedException(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/lang/Comparable.java b/libcore/luni/src/main/java/java/lang/Comparable.java index c50f11291..df1041678 100644 --- a/libcore/luni/src/main/java/java/lang/Comparable.java +++ b/libcore/luni/src/main/java/java/lang/Comparable.java @@ -28,21 +28,17 @@ package java.lang; * invertible (the sign of the result of x.compareTo(y) must be equal to the * negation of the sign of the result of y.compareTo(x) for all combinations of * x and y). - *

          *

          * In addition, it is recommended (but not required) that if and only if the * result of x.compareTo(y) is zero, then the result of x.equals(y) should be * {@code true}. - *

          - * - * @since Android 1.0 */ public interface Comparable { - + /** * Compares this object to the specified object to determine their relative * order. - * + * * @param another * the object to compare to this instance. * @return a negative integer if this instance is less than {@code another}; @@ -52,7 +48,6 @@ public interface Comparable { * @throws ClassCastException * if {@code another} cannot be converted into something * comparable to {@code this} instance. - * @since Android 1.0 */ int compareTo(T another); } diff --git a/libcore/luni/src/main/java/java/lang/Deprecated.java b/libcore/luni/src/main/java/java/lang/Deprecated.java index eb356c2b4..047a6be1d 100644 --- a/libcore/luni/src/main/java/java/lang/Deprecated.java +++ b/libcore/luni/src/main/java/java/lang/Deprecated.java @@ -1,11 +1,11 @@ -/* - * Copyright (C) 2007 The Android Open Source Project +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -18,17 +18,16 @@ package java.lang; import java.lang.annotation.Documented; import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; /** * Annotation type used to mark program elements that should no longer be used * by programmers. Compilers produce a warning if a deprecated program element * is used. - * - * @since Android 1.0 + * + * @since 1.5 */ @Documented -@Retention(value=java.lang.annotation.RetentionPolicy.RUNTIME) -public @interface Deprecated -{ - +@Retention(RetentionPolicy.RUNTIME) +public @interface Deprecated { } diff --git a/libcore/luni/src/main/java/java/lang/Double.java b/libcore/luni/src/main/java/java/lang/Double.java index c50b27dac..bc0c380ba 100644 --- a/libcore/luni/src/main/java/java/lang/Double.java +++ b/libcore/luni/src/main/java/java/lang/Double.java @@ -19,9 +19,9 @@ package java.lang; /** * The wrapper for the primitive type {@code double}. - * + * * @see java.lang.Number - * @since Android 1.0 + * @since 1.0 */ public final class Double extends Number implements Comparable { @@ -35,15 +35,11 @@ public final class Double extends Number implements Comparable { /** * Constant for the maximum {@code double} value, (2 - 2-52) * * 21023. - * - * @since Android 1.0 */ public static final double MAX_VALUE = 1.79769313486231570e+308; /** * Constant for the minimum {@code double} value, 2-1074. - * - * @since Android 1.0 */ public static final double MIN_VALUE = 5e-324; @@ -51,30 +47,24 @@ public final class Double extends Number implements Comparable { /** * Constant for the Not-a-Number (NaN) value of the {@code double} type. - * - * @since Android 1.0 */ public static final double NaN = 0.0 / 0.0; /** * Constant for the Positive Infinity value of the {@code double} type. - * - * @since Android 1.0 */ public static final double POSITIVE_INFINITY = 1.0 / 0.0; /** * Constant for the Negative Infinity value of the {@code double} type. - * - * @since Android 1.0 */ public static final double NEGATIVE_INFINITY = -1.0 / 0.0; /** * The {@link Class} object that represents the primitive type {@code * double}. - * - * @since Android 1.0 + * + * @since 1.1 */ @SuppressWarnings("unchecked") public static final Class TYPE = (Class) new double[0] @@ -86,18 +76,17 @@ public final class Double extends Number implements Comparable { /** * Constant for the number of bits needed to represent a {@code double} in * two's complement form. - * - * @since Android 1.0 + * + * @since 1.5 */ public static final int SIZE = 64; /** * Constructs a new {@code Double} with the specified primitive double * value. - * + * * @param value * the primitive double value to store in the new instance. - * @since Android 1.0 */ public Double(double value) { this.value = value; @@ -105,13 +94,12 @@ public final class Double extends Number implements Comparable { /** * Constructs a new {@code Double} from the specified string. - * + * * @param string * the string representation of a double value. * @throws NumberFormatException * if {@code string} can not be decoded into a double value. * @see #parseDouble(String) - * @since Android 1.0 */ public Double(String string) throws NumberFormatException { this(parseDouble(string)); @@ -125,36 +113,20 @@ public final class Double extends Number implements Comparable { * than any other double value, including {@code Double.POSITIVE_INFINITY}; *
        • +0.0d is greater than -0.0d
        • * - * + * * @param object * the double object to compare this object to. * @return a negative value if the value of this double is less than the * value of {@code object}; 0 if the value of this double and the * value of {@code object} are equal; a positive value if the value * of this double is greater than the value of {@code object}. + * @throws NullPointerException + * if {@code object} is {@code null}. * @see java.lang.Comparable - * @since Android 1.0 + * @since 1.2 */ public int compareTo(Double object) { - long d1, d2; - long NaNbits = Double.doubleToLongBits(Double.NaN); - if ((d1 = Double.doubleToLongBits(value)) == NaNbits) { - if (Double.doubleToLongBits(object.value) == NaNbits) { - return 0; - } - return 1; - } - if ((d2 = Double.doubleToLongBits(object.value)) == NaNbits) { - return -1; - } - if (value == object.value) { - if (d1 == d2) { - return 0; - } - // check for -0 - return d1 > d2 ? 1 : -1; - } - return value > object.value ? 1 : -1; + return compare(value, object.value); } @Override @@ -167,14 +139,13 @@ public final class Double extends Number implements Comparable { * to the IEEE 754 floating-point double precision bit layout. All * Not-a-Number (NaN) values are converted to a single NaN * representation ({@code 0x7ff8000000000000L}). - * + * * @param value * the double value to convert. * @return the IEEE 754 floating-point double precision representation of * {@code value}. * @see #doubleToRawLongBits(double) * @see #longBitsToDouble(long) - * @since Android 1.0 */ public static native long doubleToLongBits(double value); @@ -182,22 +153,20 @@ public final class Double extends Number implements Comparable { * Converts the specified double value to a binary representation conforming * to the IEEE 754 floating-point double precision bit layout. * Not-a-Number (NaN) values are preserved. - * + * * @param value * the double value to convert. * @return the IEEE 754 floating-point double precision representation of * {@code value}. * @see #doubleToLongBits(double) * @see #longBitsToDouble(long) - * @since Android 1.0 */ public static native long doubleToRawLongBits(double value); /** * Gets the primitive value of this double. - * + * * @return this object's primitive value. - * @since Android 1.0 */ @Override public double doubleValue() { @@ -209,12 +178,11 @@ public final class Double extends Number implements Comparable { * equal. In order to be equal, {@code object} must be an instance of * {@code Double} and the bit pattern of its double value is the same as * this object's. - * + * * @param object * the object to compare this double with. * @return {@code true} if the specified object is equal to this * {@code Double}; {@code false} otherwise. - * @since Android 1.0 */ @Override public boolean equals(Object object) { @@ -241,10 +209,9 @@ public final class Double extends Number implements Comparable { /** * Indicates whether this object represents an infinite value. - * + * * @return {@code true} if the value of this double is positive or negative * infinity; {@code false} otherwise. - * @since Android 1.0 */ public boolean isInfinite() { return isInfinite(value); @@ -252,12 +219,11 @@ public final class Double extends Number implements Comparable { /** * Indicates whether the specified double represents an infinite value. - * + * * @param d * the double to check. * @return {@code true} if the value of {@code d} is positive or negative * infinity; {@code false} otherwise. - * @since Android 1.0 */ public static boolean isInfinite(double d) { return (d == POSITIVE_INFINITY) || (d == NEGATIVE_INFINITY); @@ -265,10 +231,9 @@ public final class Double extends Number implements Comparable { /** * Indicates whether this object is a Not-a-Number (NaN) value. - * + * * @return {@code true} if this double is Not-a-Number; * {@code false} if it is a (potentially infinite) double number. - * @since Android 1.0 */ public boolean isNaN() { return isNaN(value); @@ -277,12 +242,11 @@ public final class Double extends Number implements Comparable { /** * Indicates whether the specified double is a Not-a-Number (NaN) * value. - * + * * @param d * the double value to check. * @return {@code true} if {@code d} is Not-a-Number; * {@code false} if it is a (potentially infinite) double number. - * @since Android 1.0 */ public static boolean isNaN(double d) { return d != d; @@ -291,14 +255,13 @@ public final class Double extends Number implements Comparable { /** * Converts the specified IEEE 754 floating-point double precision bit * pattern to a Java double value. - * + * * @param bits * the IEEE 754 floating-point double precision representation of * a double value. * @return the double value converted from {@code bits}. * @see #doubleToLongBits(double) * @see #doubleToRawLongBits(double) - * @since Android 1.0 */ public static native double longBitsToDouble(long bits); @@ -309,14 +272,13 @@ public final class Double extends Number implements Comparable { /** * Parses the specified string as a double value. - * + * * @param string * the string representation of a double value. * @return the primitive double value represented by {@code string}. * @throws NumberFormatException * if {@code string} is {@code null}, has a length of zero or * can not be parsed as a double value. - * @since Android 1.0 */ public static double parseDouble(String string) throws NumberFormatException { @@ -337,11 +299,10 @@ public final class Double extends Number implements Comparable { /** * Returns a string containing a concise, human-readable description of the * specified double value. - * + * * @param d * the double to convert to a string. * @return a printable representation of {@code d}. - * @since Android 1.0 */ public static String toString(double d) { return org.apache.harmony.luni.util.NumberConverter.convert(d); @@ -349,7 +310,7 @@ public final class Double extends Number implements Comparable { /** * Parses the specified string as a double value. - * + * * @param string * the string representation of a double value. * @return a {@code Double} instance containing the double value represented @@ -358,7 +319,6 @@ public final class Double extends Number implements Comparable { * if {@code string} is {@code null}, has a length of zero or * can not be parsed as a double value. * @see #parseDouble(String) - * @since Android 1.0 */ public static Double valueOf(String string) throws NumberFormatException { return new Double(parseDouble(string)); @@ -371,7 +331,7 @@ public final class Double extends Number implements Comparable { * than any other double value, including {@code Double.POSITIVE_INFINITY}; *
        • +0.0d is greater than -0.0d
        • * - * + * * @param double1 * the first value to compare. * @param double2 @@ -379,37 +339,44 @@ public final class Double extends Number implements Comparable { * @return a negative value if {@code double1} is less than {@code double2}; * 0 if {@code double1} and {@code double2} are equal; a positive * value if {@code double1} is greater than {@code double2}. - * @since Android 1.0 */ public static int compare(double double1, double double2) { - long d1, d2; - long NaNbits = Double.doubleToLongBits(Double.NaN); - if ((d1 = Double.doubleToLongBits(double1)) == NaNbits) { - if (Double.doubleToLongBits(double2) == NaNbits) { - return 0; - } + // Non-zero, non-NaN checking. + if (double1 > double2) { return 1; } - if ((d2 = Double.doubleToLongBits(double2)) == NaNbits) { + if (double2 > double1) { return -1; } - if (double1 == double2) { - if (d1 == d2) { + if (double1 == double2 && 0.0d != double1) { + return 0; + } + + // NaNs are equal to other NaNs and larger than any other double + if (isNaN(double1)) { + if (isNaN(double2)) { return 0; } - // check for -0 - return d1 > d2 ? 1 : -1; + return 1; + } else if (isNaN(double2)) { + return -1; } - return double1 > double2 ? 1 : -1; + + // Deal with +0.0 and -0.0 + long d1 = doubleToRawLongBits(double1); + long d2 = doubleToRawLongBits(double2); + // The below expression is equivalent to: + // (d1 == d2) ? 0 : (d1 < d2) ? -1 : 1 + return (int) ((d1 >> 63) - (d2 >> 63)); } /** * Returns a {@code Double} instance for the specified double value. - * + * * @param d * the double value to store in the instance. * @return a {@code Double} instance containing {@code d}. - * @since Android 1.0 + * @since 1.5 */ public static Double valueOf(double d) { return new Double(d); @@ -417,11 +384,11 @@ public final class Double extends Number implements Comparable { /** * Converts the specified double into its hexadecimal string representation. - * + * * @param d * the double to convert. * @return the hexadecimal string representation of {@code d}. - * @since Android 1.0 + * @since 1.5 */ public static String toHexString(double d) { /* diff --git a/libcore/luni/src/main/java/java/lang/Enum.java b/libcore/luni/src/main/java/java/lang/Enum.java index 0aa15ddac..395c82570 100644 --- a/libcore/luni/src/main/java/java/lang/Enum.java +++ b/libcore/luni/src/main/java/java/lang/Enum.java @@ -27,8 +27,6 @@ import org.apache.harmony.luni.util.Msg; * The superclass of all enumerated types. Actual enumeration types inherit from * this class, but extending this class does not make a class an enumration * type, since the compiler needs to generate special information for it. - * - * @since Android 1.0 */ public abstract class Enum> implements Serializable, Comparable { @@ -41,13 +39,12 @@ public abstract class Enum> implements Serializable, /** * Constructor for constants of enum subtypes. - * + * * @param name * the enum constant's declared name. * @param ordinal * the enum constant's ordinal, which corresponds to its position * in the enum declaration, starting at zero. - * @since Android 1.0 */ protected Enum(String name, int ordinal) { this.name = name; @@ -57,10 +54,9 @@ public abstract class Enum> implements Serializable, /** * Returns the name of this enum constant. The name is the field as it * appears in the {@code enum} declaration. - * + * * @return the name of this enum constant. * @see #toString() - * @since Android 1.0 */ public final String name() { return name; @@ -69,9 +65,8 @@ public abstract class Enum> implements Serializable, /** * Returns the position of the enum constant in the declaration. The first * constant has an ordinal value of zero. - * + * * @return the ordinal value of this enum constant. - * @since Android 1.0 */ public final int ordinal() { return ordinal; @@ -80,9 +75,8 @@ public abstract class Enum> implements Serializable, /** * Returns a string containing a concise, human-readable description of this * object. In this case, the enum constant's name is returned. - * + * * @return a printable representation of this object. - * @since Android 1.0 */ @Override public String toString() { @@ -93,12 +87,11 @@ public abstract class Enum> implements Serializable, * Compares this object with the specified object and indicates if they are * equal. In order to be equal, {@code object} must be identical to this * enum constant. - * + * * @param other * the object to compare this enum constant with. * @return {@code true} if the specified object is equal to this * {@code Enum}; {@code false} otherwise. - * @since Android 1.0 */ @Override public final boolean equals(Object other) { @@ -113,11 +106,10 @@ public abstract class Enum> implements Serializable, /** * {@code Enum} objects are singletons, they may not be cloned. This method * always throws a {@code CloneNotSupportedException}. - * + * * @return does not return. * @throws CloneNotSupportedException * is always thrown. - * @since Android 1.0 */ @Override protected final Object clone() throws CloneNotSupportedException { @@ -129,7 +121,7 @@ public abstract class Enum> implements Serializable, * Compares this object to the specified enum object to determine their * relative order. This method compares the object's ordinal values, that * is, their position in the enum declaration. - * + * * @param o * the enum object to compare this object to. * @return a negative value if the ordinal value of this enum constant is @@ -138,7 +130,6 @@ public abstract class Enum> implements Serializable, * value if the ordinal value of this enum constant is greater than * the ordinal value of {@code o}. * @see java.lang.Comparable - * @since Android 1.0 */ public final int compareTo(E o) { return ordinal - o.ordinal; @@ -146,9 +137,8 @@ public abstract class Enum> implements Serializable, /** * Returns the enum constant's declaring class. - * + * * @return the class object representing the constant's enum type. - * @since Android 1.0 */ @SuppressWarnings("unchecked") public final Class getDeclaringClass() { @@ -162,7 +152,7 @@ public abstract class Enum> implements Serializable, /** * Returns the constant with the specified name of the specified enum type. - * + * * @param enumType * the class of the enumerated type to search for the constant * value. @@ -174,7 +164,6 @@ public abstract class Enum> implements Serializable, * @throws IllegalArgumentException * if {@code enumType} is not an enumerated type or does not * have a constant value called {@code name}. - * @since Android 1.0 */ public static > T valueOf(Class enumType, String name) { if ((enumType == null) || (name == null)) { diff --git a/libcore/luni/src/main/java/java/lang/EnumConstantNotPresentException.java b/libcore/luni/src/main/java/java/lang/EnumConstantNotPresentException.java index 0416703fd..aec32d04c 100644 --- a/libcore/luni/src/main/java/java/lang/EnumConstantNotPresentException.java +++ b/libcore/luni/src/main/java/java/lang/EnumConstantNotPresentException.java @@ -20,8 +20,8 @@ import org.apache.harmony.luni.util.Msg; /** * Thrown if an {@code enum} constant does not exist for a particular name. - * - * @since Android 1.0 + * + * @since 1.5 */ public class EnumConstantNotPresentException extends RuntimeException { @@ -36,12 +36,11 @@ public class EnumConstantNotPresentException extends RuntimeException { * Constructs a new {@code EnumConstantNotPresentException} with the current * stack trace and a detail message based on the specified enum type and * missing constant name. - * + * * @param enumType * the enum type. * @param constantName * the missing constant name. - * @since Android 1.0 */ @SuppressWarnings("unchecked") public EnumConstantNotPresentException(Class enumType, @@ -54,9 +53,8 @@ public class EnumConstantNotPresentException extends RuntimeException { /** * Gets the enum type for which the constant name is missing. - * + * * @return the enum type for which a constant name has not been found. - * @since Android 1.0 */ @SuppressWarnings("unchecked") public Class enumType() { @@ -68,7 +66,6 @@ public class EnumConstantNotPresentException extends RuntimeException { * * @return the name of the constant that has not been found in the enum * type. - * @since Android 1.0 */ public String constantName() { return constantName; diff --git a/libcore/luni/src/main/java/java/lang/Error.java b/libcore/luni/src/main/java/java/lang/Error.java index 3bce72cc2..dd284ae52 100644 --- a/libcore/luni/src/main/java/java/lang/Error.java +++ b/libcore/luni/src/main/java/java/lang/Error.java @@ -26,7 +26,6 @@ package java.lang; * @see Throwable * @see Exception * @see RuntimeException - * @since Android 1.0 */ public class Error extends Throwable { @@ -34,8 +33,6 @@ public class Error extends Throwable { /** * Constructs a new {@code Error} that includes the current stack trace. - * - * @since Android 1.0 */ public Error() { super(); @@ -47,7 +44,6 @@ public class Error extends Throwable { * * @param detailMessage * the detail message for this error. - * @since Android 1.0 */ public Error(String detailMessage) { super(detailMessage); @@ -61,7 +57,6 @@ public class Error extends Throwable { * the detail message for this error. * @param throwable * the cause of this error. - * @since Android 1.0 */ public Error(String detailMessage, Throwable throwable) { super(detailMessage, throwable); @@ -73,7 +68,6 @@ public class Error extends Throwable { * * @param throwable * the cause of this error. - * @since Android 1.0 */ public Error(Throwable throwable) { super(throwable); diff --git a/libcore/luni/src/main/java/java/lang/Exception.java b/libcore/luni/src/main/java/java/lang/Exception.java index 010cea496..7921eddd5 100644 --- a/libcore/luni/src/main/java/java/lang/Exception.java +++ b/libcore/luni/src/main/java/java/lang/Exception.java @@ -26,15 +26,12 @@ package java.lang; * @see Throwable * @see Error * @see RuntimeException - * @since Android 1.0 */ public class Exception extends Throwable { private static final long serialVersionUID = -3387516993124229948L; /** * Constructs a new {@code Exception} that includes the current stack trace. - * - * @since Android 1.0 */ public Exception() { super(); @@ -46,7 +43,6 @@ public class Exception extends Throwable { * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public Exception(String detailMessage) { super(detailMessage); @@ -60,7 +56,6 @@ public class Exception extends Throwable { * the detail message for this exception. * @param throwable * the cause of this exception. - * @since Android 1.0 */ public Exception(String detailMessage, Throwable throwable) { super(detailMessage, throwable); @@ -72,7 +67,6 @@ public class Exception extends Throwable { * * @param throwable * the cause of this exception. - * @since Android 1.0 */ public Exception(Throwable throwable) { super(throwable); diff --git a/libcore/luni/src/main/java/java/lang/ExceptionInInitializerError.java b/libcore/luni/src/main/java/java/lang/ExceptionInInitializerError.java index 6096f0826..3767488fa 100644 --- a/libcore/luni/src/main/java/java/lang/ExceptionInInitializerError.java +++ b/libcore/luni/src/main/java/java/lang/ExceptionInInitializerError.java @@ -19,8 +19,6 @@ package java.lang; /** * Thrown when an exception occurs during class initialization. - * - * @since Android 1.0 */ public class ExceptionInInitializerError extends LinkageError { @@ -31,8 +29,6 @@ public class ExceptionInInitializerError extends LinkageError { /** * Constructs a new {@code ExceptionInInitializerError} that includes the * current stack trace. - * - * @since Android 1.0 */ public ExceptionInInitializerError() { super(); @@ -45,7 +41,6 @@ public class ExceptionInInitializerError extends LinkageError { * * @param detailMessage * the detail message for this error. - * @since Android 1.0 */ public ExceptionInInitializerError(String detailMessage) { super(detailMessage); @@ -70,7 +65,6 @@ public class ExceptionInInitializerError extends LinkageError { * Returns the exception that is the cause of this error. * * @return the exception that caused this error. - * @since Android 1.0 */ public Throwable getException() { return exception; @@ -80,7 +74,6 @@ public class ExceptionInInitializerError extends LinkageError { * Returns the cause of this error, or {@code null} if there is no cause. * * @return the exception that caused this error. - * @since Android 1.0 */ @Override public Throwable getCause() { diff --git a/libcore/luni/src/main/java/java/lang/Float.java b/libcore/luni/src/main/java/java/lang/Float.java index 69f91c563..067149dc4 100644 --- a/libcore/luni/src/main/java/java/lang/Float.java +++ b/libcore/luni/src/main/java/java/lang/Float.java @@ -19,9 +19,9 @@ package java.lang; /** * The wrapper for the primitive type {@code float}. - * + * * @see java.lang.Number - * @since Android 1.0 + * @since 1.0 */ public final class Float extends Number implements Comparable { @@ -34,44 +34,34 @@ public final class Float extends Number implements Comparable { /** * Constant for the maximum {@code float} value, (2 - 2-23) * 2127. - * - * @since Android 1.0 */ public static final float MAX_VALUE = 3.40282346638528860e+38f; /** * Constant for the minimum {@code float} value, 2-149. - * - * @since Android 1.0 */ public static final float MIN_VALUE = 1.40129846432481707e-45f; /** * Constant for the Not-a-Number (NaN) value of the {@code float} type. - * - * @since Android 1.0 */ public static final float NaN = 0.0f / 0.0f; /** * Constant for the Positive Infinity value of the {@code float} type. - * - * @since Android 1.0 */ public static final float POSITIVE_INFINITY = 1.0f / 0.0f; /** * Constant for the Negative Infinity value of the {@code float} type. - * - * @since Android 1.0 */ public static final float NEGATIVE_INFINITY = -1.0f / 0.0f; /** - * The {@link Class} object that represents the primitive type {@code + * The {@link Class} object that represents the primitive type {@code * float}. - * - * @since Android 1.0 + * + * @since 1.1 */ @SuppressWarnings("unchecked") public static final Class TYPE = (Class) new float[0] @@ -83,17 +73,16 @@ public final class Float extends Number implements Comparable { /** * Constant for the number of bits needed to represent a {@code float} in * two's complement form. - * - * @since Android 1.0 + * + * @since 1.5 */ public static final int SIZE = 32; /** * Constructs a new {@code Float} with the specified primitive float value. - * + * * @param value * the primitive float value to store in the new instance. - * @since Android 1.0 */ public Float(float value) { this.value = value; @@ -101,10 +90,9 @@ public final class Float extends Number implements Comparable { /** * Constructs a new {@code Float} with the specified primitive double value. - * + * * @param value * the primitive double value to store in the new instance. - * @since Android 1.0 */ public Float(double value) { this.value = (float) value; @@ -112,13 +100,12 @@ public final class Float extends Number implements Comparable { /** * Constructs a new {@code Float} from the specified string. - * + * * @param string * the string representation of a float value. * @throws NumberFormatException * if {@code string} can not be decoded into a float value. * @see #parseFloat(String) - * @since Android 1.0 */ public Float(String string) throws NumberFormatException { this(parseFloat(string)); @@ -132,7 +119,7 @@ public final class Float extends Number implements Comparable { * than any other float value, including {@code Float.POSITIVE_INFINITY}; *
        • +0.0f is greater than -0.0f
        • * - * + * * @param object * the float object to compare this object to. * @return a negative value if the value of this float is less than the @@ -140,28 +127,10 @@ public final class Float extends Number implements Comparable { * value of {@code object} are equal; a positive value if the value * of this float is greater than the value of {@code object}. * @see java.lang.Comparable - * @since Android 1.0 + * @since 1.2 */ public int compareTo(Float object) { - int f1, f2; - int NaNbits = Float.floatToIntBits(Float.NaN); - if ((f1 = Float.floatToIntBits(value)) == NaNbits) { - if (Float.floatToIntBits(object.value) == NaNbits) { - return 0; - } - return 1; - } - if ((f2 = Float.floatToIntBits(object.value)) == NaNbits) { - return -1; - } - if (value == object.value) { - if (f1 == f2) { - return 0; - } - // check for -0 - return f1 > f2 ? 1 : -1; - } - return value > object.value ? 1 : -1; + return compare(value, object.value); } @Override @@ -178,12 +147,11 @@ public final class Float extends Number implements Comparable { * Compares this instance with the specified object and indicates if they * are equal. In order to be equal, {@code object} must be an instance of * {@code Float} and have the same float value as this object. - * + * * @param object * the object to compare this float with. * @return {@code true} if the specified object is equal to this * {@code Float}; {@code false} otherwise. - * @since Android 1.0 */ @Override public boolean equals(Object object) { @@ -197,14 +165,13 @@ public final class Float extends Number implements Comparable { * to the IEEE 754 floating-point single precision bit layout. All * Not-a-Number (NaN) values are converted to a single NaN * representation ({@code 0x7ff8000000000000L}). - * + * * @param value * the float value to convert. * @return the IEEE 754 floating-point single precision representation of * {@code value}. * @see #floatToRawIntBits(float) * @see #intBitsToFloat(int) - * @since Android 1.0 */ public static native int floatToIntBits(float value); @@ -212,22 +179,20 @@ public final class Float extends Number implements Comparable { * Converts the specified float value to a binary representation conforming * to the IEEE 754 floating-point single precision bit layout. * Not-a-Number (NaN) values are preserved. - * + * * @param value * the float value to convert. * @return the IEEE 754 floating-point single precision representation of * {@code value}. * @see #floatToIntBits(float) * @see #intBitsToFloat(int) - * @since Android 1.0 */ public static native int floatToRawIntBits(float value); /** * Gets the primitive value of this float. - * + * * @return this object's primitive value. - * @since Android 1.0 */ @Override public float floatValue() { @@ -242,14 +207,13 @@ public final class Float extends Number implements Comparable { /** * Converts the specified IEEE 754 floating-point single precision bit * pattern to a Java float value. - * + * * @param bits * the IEEE 754 floating-point single precision representation of * a float value. * @return the float value converted from {@code bits}. * @see #floatToIntBits(float) * @see #floatToRawIntBits(float) - * @since Android 1.0 */ public static native float intBitsToFloat(int bits); @@ -260,10 +224,9 @@ public final class Float extends Number implements Comparable { /** * Indicates whether this object represents an infinite value. - * + * * @return {@code true} if the value of this float is positive or negative * infinity; {@code false} otherwise. - * @since Android 1.0 */ public boolean isInfinite() { return isInfinite(value); @@ -271,12 +234,11 @@ public final class Float extends Number implements Comparable { /** * Indicates whether the specified float represents an infinite value. - * + * * @param f * the float to check. * @return {@code true} if the value of {@code f} is positive or negative * infinity; {@code false} otherwise. - * @since Android 1.0 */ public static boolean isInfinite(float f) { return (f == POSITIVE_INFINITY) || (f == NEGATIVE_INFINITY); @@ -284,10 +246,9 @@ public final class Float extends Number implements Comparable { /** * Indicates whether this object is a Not-a-Number (NaN) value. - * + * * @return {@code true} if this float is Not-a-Number; * {@code false} if it is a (potentially infinite) float number. - * @since Android 1.0 */ public boolean isNaN() { return isNaN(value); @@ -296,12 +257,11 @@ public final class Float extends Number implements Comparable { /** * Indicates whether the specified float is a Not-a-Number (NaN) * value. - * + * * @param f * the float value to check. * @return {@code true} if {@code f} is Not-a-Number; * {@code false} if it is a (potentially infinite) float number. - * @since Android 1.0 */ public static boolean isNaN(float f) { return f != f; @@ -314,14 +274,15 @@ public final class Float extends Number implements Comparable { /** * Parses the specified string as a float value. - * + * * @param string * the string representation of a float value. * @return the primitive float value represented by {@code string}. * @throws NumberFormatException * if {@code string} is {@code null}, has a length of zero or * can not be parsed as a float value. - * @since Android 1.0 + * @see #valueOf(String) + * @since 1.2 */ public static float parseFloat(String string) throws NumberFormatException { return org.apache.harmony.luni.util.FloatingPointParser @@ -341,11 +302,10 @@ public final class Float extends Number implements Comparable { /** * Returns a string containing a concise, human-readable description of the * specified float value. - * + * * @param f * the float to convert to a string. * @return a printable representation of {@code f}. - * @since Android 1.0 */ public static String toString(float f) { return org.apache.harmony.luni.util.NumberConverter.convert(f); @@ -353,7 +313,7 @@ public final class Float extends Number implements Comparable { /** * Parses the specified string as a float value. - * + * * @param string * the string representation of a float value. * @return a {@code Float} instance containing the float value represented @@ -362,7 +322,6 @@ public final class Float extends Number implements Comparable { * if {@code string} is {@code null}, has a length of zero or * can not be parsed as a float value. * @see #parseFloat(String) - * @since Android 1.0 */ public static Float valueOf(String string) throws NumberFormatException { return valueOf(parseFloat(string)); @@ -375,7 +334,7 @@ public final class Float extends Number implements Comparable { * than any other float value, including {@code Float.POSITIVE_INFINITY}; *
        • +0.0f is greater than -0.0f
        • * - * + * * @param float1 * the first value to compare. * @param float2 @@ -383,37 +342,46 @@ public final class Float extends Number implements Comparable { * @return a negative value if {@code float1} is less than {@code float2}; * 0 if {@code float1} and {@code float2} are equal; a positive * value if {@code float1} is greater than {@code float2}. - * @since Android 1.0 + * @since 1.4 */ public static int compare(float float1, float float2) { - int f1, f2; - int NaNbits = Float.floatToIntBits(Float.NaN); - if ((f1 = Float.floatToIntBits(float1)) == NaNbits) { - if (Float.floatToIntBits(float2) == NaNbits) { - return 0; - } + // Non-zero, non-NaN checking. + if (float1 > float2) { return 1; } - if ((f2 = Float.floatToIntBits(float2)) == NaNbits) { + if (float2 > float1) { return -1; } - if (float1 == float2) { - if (f1 == f2) { + if (float1 == float2 && 0.0f != float1) { + return 0; + } + + // NaNs are equal to other NaNs and larger than any other float + if (isNaN(float1)) { + if (isNaN(float2)) { return 0; } - // check for -0 - return f1 > f2 ? 1 : -1; + return 1; + } else if (isNaN(float2)) { + return -1; } - return float1 > float2 ? 1 : -1; + + // Deal with +0.0 and -0.0 + int f1 = floatToRawIntBits(float1); + int f2 = floatToRawIntBits(float2); + // The below expression is equivalent to: + // (f1 == f2) ? 0 : (f1 < f2) ? -1 : 1 + // because f1 and f2 are either 0 or Integer.MIN_VALUE + return (f1 >> 31) - (f2 >> 31); } /** * Returns a {@code Float} instance for the specified float value. - * + * * @param f * the float value to store in the instance. * @return a {@code Float} instance containing {@code f}. - * @since Android 1.0 + * @since 1.5 */ public static Float valueOf(float f) { return new Float(f); @@ -421,11 +389,11 @@ public final class Float extends Number implements Comparable { /** * Converts the specified float into its hexadecimal string representation. - * + * * @param f * the float to convert. * @return the hexadecimal string representation of {@code f}. - * @since Android 1.0 + * @since 1.5 */ public static String toHexString(float f) { /* diff --git a/libcore/luni/src/main/java/java/lang/IllegalAccessError.java b/libcore/luni/src/main/java/java/lang/IllegalAccessError.java index 7f87afd50..7fcef8558 100644 --- a/libcore/luni/src/main/java/java/lang/IllegalAccessError.java +++ b/libcore/luni/src/main/java/java/lang/IllegalAccessError.java @@ -22,9 +22,6 @@ package java.lang; * which is not accessible from where it is referenced. *

          * Note that this can only occur when inconsistent class files have been loaded. - *

          - * - * @since Android 1.0 */ public class IllegalAccessError extends IncompatibleClassChangeError { @@ -33,8 +30,6 @@ public class IllegalAccessError extends IncompatibleClassChangeError { /** * Constructs a new {@code IllegalAccessError} that includes the current * stack trace. - * - * @since Android 1.0 */ public IllegalAccessError() { super(); @@ -46,7 +41,6 @@ public class IllegalAccessError extends IncompatibleClassChangeError { * * @param detailMessage * the detail message for this error. - * @since Android 1.0 */ public IllegalAccessError(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/lang/IllegalAccessException.java b/libcore/luni/src/main/java/java/lang/IllegalAccessException.java index b9d2d695a..d96fd56a6 100644 --- a/libcore/luni/src/main/java/java/lang/IllegalAccessException.java +++ b/libcore/luni/src/main/java/java/lang/IllegalAccessException.java @@ -20,8 +20,6 @@ package java.lang; /** * Thrown when a program attempts to access a field or method which is not * accessible from the location where the reference is made. - * - * @since Android 1.0 */ public class IllegalAccessException extends Exception { @@ -30,8 +28,6 @@ public class IllegalAccessException extends Exception { /** * Constructs a new {@code IllegalAccessException} that includes the current * stack trace. - * - * @since Android 1.0 */ public IllegalAccessException() { super(); @@ -43,7 +39,6 @@ public class IllegalAccessException extends Exception { * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public IllegalAccessException(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/lang/IllegalArgumentException.java b/libcore/luni/src/main/java/java/lang/IllegalArgumentException.java index 6f8dcf676..eb2ad912f 100644 --- a/libcore/luni/src/main/java/java/lang/IllegalArgumentException.java +++ b/libcore/luni/src/main/java/java/lang/IllegalArgumentException.java @@ -20,8 +20,6 @@ package java.lang; /** * Thrown when a method is invoked with an argument which it can not reasonably * deal with. - * - * @since Android 1.0 */ public class IllegalArgumentException extends RuntimeException { @@ -30,8 +28,6 @@ public class IllegalArgumentException extends RuntimeException { /** * Constructs a new {@code IllegalArgumentException} that includes the * current stack trace. - * - * @since Android 1.0 */ public IllegalArgumentException() { super(); @@ -43,7 +39,6 @@ public class IllegalArgumentException extends RuntimeException { * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public IllegalArgumentException(String detailMessage) { super(detailMessage); @@ -57,7 +52,7 @@ public class IllegalArgumentException extends RuntimeException { * the detail message for this exception. * @param cause * the cause of this exception, may be {@code null}. - * @since Android 1.0 + * @since 1.5 */ public IllegalArgumentException(String message, Throwable cause) { super(message, cause); @@ -69,8 +64,8 @@ public class IllegalArgumentException extends RuntimeException { * * @param cause * the cause of this exception, may be {@code null}. - * @since Android 1.0 - */ + * @since 1.5 + */ public IllegalArgumentException(Throwable cause) { super((cause == null ? null : cause.toString()), cause); } diff --git a/libcore/luni/src/main/java/java/lang/IllegalMonitorStateException.java b/libcore/luni/src/main/java/java/lang/IllegalMonitorStateException.java index e570bfdfd..a0b8a7547 100644 --- a/libcore/luni/src/main/java/java/lang/IllegalMonitorStateException.java +++ b/libcore/luni/src/main/java/java/lang/IllegalMonitorStateException.java @@ -21,8 +21,6 @@ package java.lang; * Thrown when a monitor operation is attempted when the monitor is not in the * correct state, for example when a thread attempts to exit a monitor which it * does not own. - * - * @since Android 1.0 */ public class IllegalMonitorStateException extends RuntimeException { @@ -31,8 +29,6 @@ public class IllegalMonitorStateException extends RuntimeException { /** * Constructs a new {@code IllegalMonitorStateException} that includes the * current stack trace. - * - * @since Android 1.0 */ public IllegalMonitorStateException() { super(); @@ -44,7 +40,6 @@ public class IllegalMonitorStateException extends RuntimeException { * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public IllegalMonitorStateException(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/lang/IllegalStateException.java b/libcore/luni/src/main/java/java/lang/IllegalStateException.java index 427aaf459..28db2de11 100644 --- a/libcore/luni/src/main/java/java/lang/IllegalStateException.java +++ b/libcore/luni/src/main/java/java/lang/IllegalStateException.java @@ -20,8 +20,6 @@ package java.lang; /** * Thrown when an action is attempted at a time when the virtual machine is not * in the correct state. - * - * @since Android 1.0 */ public class IllegalStateException extends RuntimeException { @@ -30,8 +28,6 @@ public class IllegalStateException extends RuntimeException { /** * Constructs a new {@code IllegalStateException} that includes the current * stack trace. - * - * @since Android 1.0 */ public IllegalStateException() { super(); @@ -43,7 +39,6 @@ public class IllegalStateException extends RuntimeException { * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public IllegalStateException(String detailMessage) { super(detailMessage); @@ -57,7 +52,7 @@ public class IllegalStateException extends RuntimeException { * the detail message for this exception. * @param cause * the cause of this exception. - * @since Android 1.0 + * @since 1.5 */ public IllegalStateException(String message, Throwable cause) { super(message, cause); @@ -69,7 +64,7 @@ public class IllegalStateException extends RuntimeException { * * @param cause * the cause of this exception, may be {@code null}. - * @since Android 1.0 + * @since 1.5 */ public IllegalStateException(Throwable cause) { super((cause == null ? null : cause.toString()), cause); diff --git a/libcore/luni/src/main/java/java/lang/IllegalThreadStateException.java b/libcore/luni/src/main/java/java/lang/IllegalThreadStateException.java index 82eccbb0b..f1e99a872 100644 --- a/libcore/luni/src/main/java/java/lang/IllegalThreadStateException.java +++ b/libcore/luni/src/main/java/java/lang/IllegalThreadStateException.java @@ -20,8 +20,6 @@ package java.lang; /** * Thrown when an operation is attempted which is not possible given the state * that the executing thread is in. - * - * @since Android 1.0 */ public class IllegalThreadStateException extends IllegalArgumentException { @@ -30,8 +28,6 @@ public class IllegalThreadStateException extends IllegalArgumentException { /** * Constructs a new {@code IllegalThreadStateException} that includes the * current stack trace. - * - * @since Android 1.0 */ public IllegalThreadStateException() { super(); @@ -43,7 +39,6 @@ public class IllegalThreadStateException extends IllegalArgumentException { * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public IllegalThreadStateException(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/lang/IncompatibleClassChangeError.java b/libcore/luni/src/main/java/java/lang/IncompatibleClassChangeError.java index 83950fd1a..2155a28af 100644 --- a/libcore/luni/src/main/java/java/lang/IncompatibleClassChangeError.java +++ b/libcore/luni/src/main/java/java/lang/IncompatibleClassChangeError.java @@ -24,7 +24,6 @@ package java.lang; * the same running image. * * @see Error - * @since Android 1.0 */ public class IncompatibleClassChangeError extends LinkageError { @@ -33,8 +32,6 @@ public class IncompatibleClassChangeError extends LinkageError { /** * Constructs a new {@code IncompatibleClassChangeError} that includes the * current stack trace. - * - * @since Android 1.0 */ public IncompatibleClassChangeError() { super(); @@ -46,7 +43,6 @@ public class IncompatibleClassChangeError extends LinkageError { * * @param detailMessage * the detail message for this error. - * @since Android 1.0 */ public IncompatibleClassChangeError(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/lang/IndexOutOfBoundsException.java b/libcore/luni/src/main/java/java/lang/IndexOutOfBoundsException.java index a4dc282aa..f26fcbfc3 100644 --- a/libcore/luni/src/main/java/java/lang/IndexOutOfBoundsException.java +++ b/libcore/luni/src/main/java/java/lang/IndexOutOfBoundsException.java @@ -20,8 +20,6 @@ package java.lang; /** * Thrown when a program attempts to access a value in an indexable collection * using a value which is outside of the range of valid indices. - * - * @since Android 1.0 */ public class IndexOutOfBoundsException extends RuntimeException { @@ -30,8 +28,6 @@ public class IndexOutOfBoundsException extends RuntimeException { /** * Constructs a new {@code IndexOutOfBoundsException} that includes the * current stack trace. - * - * @since Android 1.0 */ public IndexOutOfBoundsException() { super(); @@ -43,7 +39,6 @@ public class IndexOutOfBoundsException extends RuntimeException { * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public IndexOutOfBoundsException(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/lang/InheritableThreadLocal.java b/libcore/luni/src/main/java/java/lang/InheritableThreadLocal.java index 41e0d829f..7e12ac2aa 100644 --- a/libcore/luni/src/main/java/java/lang/InheritableThreadLocal.java +++ b/libcore/luni/src/main/java/java/lang/InheritableThreadLocal.java @@ -1,17 +1,18 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package java.lang; @@ -24,36 +25,33 @@ package java.lang; * creation time. However, subclasses may override {code #childValue(Object)} * to provide an arbitrary function for passing the value of a parent's * thread-local variable to the child's thread-local variable. - * + * * @see java.lang.Thread * @see java.lang.ThreadLocal - * - * @since Android 1.0 */ public class InheritableThreadLocal extends ThreadLocal { /** * Creates a new inheritable thread-local variable. - * - * @since Android 1.0 */ - public InheritableThreadLocal() {} + public InheritableThreadLocal() { + super(); + } /** * Computes the initial value of this thread-local variable for the child - * thread given the parent thread's value. Called from the parent thread - * when creating a child thread. The default implementation returns the - * parent thread's value. - * - * @param parentValue - * the value of the variable in the parent thread. + * thread given the parent thread's value. Called from the parent thread when + * creating a child thread. The default implementation returns the parent + * thread's value. + * + * @param parentValue the value of the variable in the parent thread. * @return the initial value of the variable for the child thread. - * @since Android 1.0 */ protected T childValue(T parentValue) { return parentValue; } + // BEGIN android-added @Override Values values(Thread current) { return current.inheritableValues; @@ -63,4 +61,5 @@ public class InheritableThreadLocal extends ThreadLocal { Values initializeValues(Thread current) { return current.inheritableValues = new Values(); } + // END android-added } diff --git a/libcore/luni/src/main/java/java/lang/InstantiationError.java b/libcore/luni/src/main/java/java/lang/InstantiationError.java index 0d387c62e..9ad1ed6b6 100644 --- a/libcore/luni/src/main/java/java/lang/InstantiationError.java +++ b/libcore/luni/src/main/java/java/lang/InstantiationError.java @@ -23,9 +23,6 @@ package java.lang; * {@code new} is invoked. *

          * Note that this can only occur when inconsistent class files are being loaded. - *

          - * - * @since Android 1.0 */ public class InstantiationError extends IncompatibleClassChangeError { private static final long serialVersionUID = -4885810657349421204L; @@ -33,8 +30,6 @@ public class InstantiationError extends IncompatibleClassChangeError { /** * Constructs a new {@code InstantiationError} that includes the current * stack trace. - * - * @since Android 1.0 */ public InstantiationError() { super(); @@ -46,7 +41,6 @@ public class InstantiationError extends IncompatibleClassChangeError { * * @param detailMessage * the detail message for this error. - * @since Android 1.0 */ public InstantiationError(String detailMessage) { super(detailMessage); @@ -58,7 +52,6 @@ public class InstantiationError extends IncompatibleClassChangeError { * * @param clazz * the class that can not be instantiated. - * @since Android 1.0 */ InstantiationError(Class clazz) { super(clazz.getName()); diff --git a/libcore/luni/src/main/java/java/lang/InstantiationException.java b/libcore/luni/src/main/java/java/lang/InstantiationException.java index cc8b55641..fe68345f9 100644 --- a/libcore/luni/src/main/java/java/lang/InstantiationException.java +++ b/libcore/luni/src/main/java/java/lang/InstantiationException.java @@ -20,8 +20,6 @@ package java.lang; /** * Thrown when a program attempts to access a constructor which is not * accessible from the location where the reference is made. - * - * @since Android 1.0 */ public class InstantiationException extends Exception { private static final long serialVersionUID = -8441929162975509110L; @@ -29,8 +27,6 @@ public class InstantiationException extends Exception { /** * Constructs a new {@code InstantiationException} that includes the current * stack trace. - * - * @since Android 1.0 */ public InstantiationException() { super(); @@ -42,7 +38,6 @@ public class InstantiationException extends Exception { * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public InstantiationException(String detailMessage) { super(detailMessage); @@ -54,7 +49,6 @@ public class InstantiationException extends Exception { * * @param clazz * the class that can not be instantiated. - * @since Android 1.0 */ InstantiationException(Class clazz) { super(clazz.getName()); diff --git a/libcore/luni/src/main/java/java/lang/Integer.java b/libcore/luni/src/main/java/java/lang/Integer.java index 3a98872eb..570120b96 100644 --- a/libcore/luni/src/main/java/java/lang/Integer.java +++ b/libcore/luni/src/main/java/java/lang/Integer.java @@ -24,10 +24,9 @@ package java.lang; * href="http://www.hackersdelight.org/">Henry S. Warren, Jr.'s Hacker's * Delight, (Addison Wesley, 2002) as well as The Aggregate's Magic Algorithms. - *

          - * + * * @see java.lang.Number - * @since Android 1.0 + * @since 1.1 */ public final class Integer extends Number implements Comparable { @@ -40,30 +39,32 @@ public final class Integer extends Number implements Comparable { /** * Constant for the maximum {@code int} value, 231-1. - * - * @since Android 1.0 */ public static final int MAX_VALUE = 0x7FFFFFFF; /** * Constant for the minimum {@code int} value, -231. - * - * @since Android 1.0 */ public static final int MIN_VALUE = 0x80000000; /** * Constant for the number of bits needed to represent an {@code int} in * two's complement form. - * - * @since Android 1.0 + * + * @since 1.5 */ public static final int SIZE = 32; + /* + * Progressively smaller decimal order of magnitude that can be represented + * by an instance of Integer. Used to help compute the String + * representation. + */ + private static final int[] decimalScale = new int[] { 1000000000, 100000000, + 10000000, 1000000, 100000, 10000, 1000, 100, 10, 1 }; + /** * The {@link Class} object that represents the primitive type {@code int}. - * - * @since Android 1.0 */ @SuppressWarnings("unchecked") public static final Class TYPE = (Class) new int[0] @@ -75,10 +76,9 @@ public final class Integer extends Number implements Comparable { /** * Constructs a new {@code Integer} with the specified primitive integer * value. - * + * * @param value * the primitive integer value to store in the new instance. - * @since Android 1.0 */ public Integer(int value) { this.value = value; @@ -86,13 +86,12 @@ public final class Integer extends Number implements Comparable { /** * Constructs a new {@code Integer} from the specified string. - * + * * @param string * the string representation of an integer value. * @throws NumberFormatException * if {@code string} can not be decoded into an integer value. * @see #parseInt(String) - * @since Android 1.0 */ public Integer(String string) throws NumberFormatException { this(parseInt(string)); @@ -106,7 +105,7 @@ public final class Integer extends Number implements Comparable { /** * Compares this object to the specified integer object to determine their * relative order. - * + * * @param object * the integer object to compare this object to. * @return a negative value if the value of this integer is less than the @@ -114,7 +113,7 @@ public final class Integer extends Number implements Comparable { * value of {@code object} are equal; a positive value if the value * of this integer is greater than the value of {@code object}. * @see java.lang.Comparable - * @since Android 1.0 + * @since 1.2 */ public int compareTo(Integer object) { return value > object.value ? 1 : (value < object.value ? -1 : 0); @@ -125,14 +124,13 @@ public final class Integer extends Number implements Comparable { * string can be decoded into an integer value. The string may be an * optional minus sign "-" followed by a hexadecimal ("0x..." or "#..."), * octal ("0..."), or decimal ("...") representation of an integer. - * + * * @param string * a string representation of an integer value. * @return an {@code Integer} containing the value represented by * {@code string}. * @throws NumberFormatException * if {@code string} can not be parsed as an integer value. - * @since Android 1.0 */ public static Integer decode(String string) throws NumberFormatException { int length = string.length(), i = 0; @@ -158,23 +156,21 @@ public final class Integer extends Number implements Comparable { return valueOf(0); } if ((firstDigit = string.charAt(i)) == 'x' || firstDigit == 'X') { - if (i == length) { + if (++i == length) { // BEGIN android-changed throw new NumberFormatException("unable to parse '"+string+"' as integer"); // END android-changed } - i++; base = 16; } else { base = 8; } } else if (firstDigit == '#') { - if (i == length) { + if (++i == length) { // BEGIN android-changed throw new NumberFormatException("unable to parse '"+string+"' as integer"); // END android-changed } - i++; base = 16; } @@ -191,12 +187,11 @@ public final class Integer extends Number implements Comparable { * Compares this instance with the specified object and indicates if they * are equal. In order to be equal, {@code o} must be an instance of * {@code Integer} and have the same integer value as this object. - * + * * @param o * the object to compare this integer with. * @return {@code true} if the specified object is equal to this * {@code Integer}; {@code false} otherwise. - * @since Android 1.0 */ @Override public boolean equals(Object o) { @@ -214,12 +209,11 @@ public final class Integer extends Number implements Comparable { * {@code string}. Returns {@code null} if {@code string} is {@code null} * or empty, if the property can not be found or if its value can not be * parsed as an integer. - * + * * @param string * the name of the requested system property. * @return the requested property's value as an {@code Integer} or * {@code null}. - * @since Android 1.0 */ public static Integer getInteger(String string) { if (string == null || string.length() == 0) { @@ -241,7 +235,7 @@ public final class Integer extends Number implements Comparable { * {@code string}. Returns the specified default value if {@code string} is * {@code null} or empty, if the property can not be found or if its value * can not be parsed as an integer. - * + * * @param string * the name of the requested system property. * @param defaultValue @@ -249,7 +243,6 @@ public final class Integer extends Number implements Comparable { * system property with the requested name. * @return the requested property's value as an {@code Integer} or the * default value. - * @since Android 1.0 */ public static Integer getInteger(String string, int defaultValue) { if (string == null || string.length() == 0) { @@ -271,7 +264,7 @@ public final class Integer extends Number implements Comparable { * {@code string}. Returns the specified default value if {@code string} is * {@code null} or empty, if the property can not be found or if its value * can not be parsed as an integer. - * + * * @param string * the name of the requested system property. * @param defaultValue @@ -279,7 +272,6 @@ public final class Integer extends Number implements Comparable { * system property with the requested name. * @return the requested property's value as an {@code Integer} or the * default value. - * @since Android 1.0 */ public static Integer getInteger(String string, Integer defaultValue) { if (string == null || string.length() == 0) { @@ -303,9 +295,8 @@ public final class Integer extends Number implements Comparable { /** * Gets the primitive value of this int. - * + * * @return this object's primitive value. - * @since Android 1.0 */ @Override public int intValue() { @@ -320,14 +311,13 @@ public final class Integer extends Number implements Comparable { /** * Parses the specified string as a signed decimal integer value. The ASCII * character \u002d ('-') is recognized as the minus sign. - * + * * @param string * the string representation of an integer value. * @return the primitive integer value represented by {@code string}. * @throws NumberFormatException * if {@code string} is {@code null}, has a length of zero or * can not be parsed as an integer value. - * @since Android 1.0 */ public static int parseInt(String string) throws NumberFormatException { return parseInt(string, 10); @@ -336,7 +326,7 @@ public final class Integer extends Number implements Comparable { /** * Parses the specified string as a signed integer value using the specified * radix. The ASCII character \u002d ('-') is recognized as the minus sign. - * + * * @param string * the string representation of an integer value. * @param radix @@ -348,7 +338,6 @@ public final class Integer extends Number implements Comparable { * {@code radix < Character.MIN_RADIX}, * {@code radix > Character.MAX_RADIX}, or if {@code string} * can not be parsed as an integer value. - * @since Android 1.0 */ public static int parseInt(String string, int radix) throws NumberFormatException { @@ -417,11 +406,10 @@ public final class Integer extends Number implements Comparable { /** * Converts the specified integer into its binary string representation. The * returned string is a concatenation of '0' and '1' characters. - * + * * @param i * the integer to convert. * @return the binary string representation of {@code i}. - * @since Android 1.0 */ public static String toBinaryString(int i) { int count = 1, j = i; @@ -446,12 +434,11 @@ public final class Integer extends Number implements Comparable { * Converts the specified integer into its hexadecimal string * representation. The returned string is a concatenation of characters from * '0' to '9' and 'a' to 'f'. - * + * * @param i * the integer to convert. * @return the hexadecimal string representation of {@code i}. - * @since Android 1.0 - */ + */ public static String toHexString(int i) { int count = 1, j = i; @@ -480,12 +467,11 @@ public final class Integer extends Number implements Comparable { /** * Converts the specified integer into its octal string representation. The * returned string is a concatenation of characters from '0' to '7'. - * + * * @param i * the integer to convert. * @return the octal string representation of {@code i}. - * @since Android 1.0 - */ + */ public static String toOctalString(int i) { int count = 1, j = i; @@ -514,14 +500,102 @@ public final class Integer extends Number implements Comparable { * Converts the specified integer into its decimal string representation. * The returned string is a concatenation of a minus sign if the number is * negative and characters from '0' to '9'. - * - * @param i + * + * @param value * the integer to convert. - * @return the decimal string representation of {@code i}. - * @since Android 1.0 - */ - public static String toString(int i) { - return toString(i, 10); + * @return the decimal string representation of {@code value}. + */ + public static String toString(int value) { + // BEGIN android-note + // cache the strings in the range 0..99 to save allocations? + // END android-note + if (value == 0) { + return "0"; //$NON-NLS-1$ + } + + // Faster algorithm for smaller Integers + if (value < 1000 && value > -1000) { + char[] buffer = new char[4]; + int positive_value = value < 0 ? -value : value; + int first_digit = 0; + if (value < 0) { + buffer[0] = '-'; + first_digit++; + } + int last_digit = first_digit; + int quot = positive_value; + do { + int res = quot / 10; + int digit_value = quot - (res * 10); + digit_value += '0'; + buffer[last_digit++] = (char) digit_value; + quot = res; + } while (quot != 0); + + int count = last_digit--; + do { + char tmp = buffer[last_digit]; + buffer[last_digit--] = buffer[first_digit]; + buffer[first_digit++] = tmp; + } while (first_digit < last_digit); + return new String(0, count, buffer); + } + if (value == MIN_VALUE) { + return "-2147483648";//$NON-NLS-1$ + } + + char[] buffer = new char[11]; + int positive_value = value < 0 ? -value : value; + byte first_digit = 0; + if (value < 0) { + buffer[0] = '-'; + first_digit++; + } + byte last_digit = first_digit; + byte count; + int number; + boolean start = false; + for (int i = 0; i < 9; i++) { + count = 0; + if (positive_value < (number = decimalScale[i])) { + if (start) { + buffer[last_digit++] = '0'; + } + continue; + } + + if (i > 0) { + number = (decimalScale[i] << 3); + if (positive_value >= number) { + positive_value -= number; + count += 8; + } + number = (decimalScale[i] << 2); + if (positive_value >= number) { + positive_value -= number; + count += 4; + } + } + number = (decimalScale[i] << 1); + if (positive_value >= number) { + positive_value -= number; + count += 2; + } + if (positive_value >= decimalScale[i]) { + positive_value -= decimalScale[i]; + count++; + } + if (count > 0 && !start) { + start = true; + } + if (start) { + buffer[last_digit++] = (char) (count + '0'); + } + } + + buffer[last_digit++] = (char) (positive_value + '0'); + count = last_digit--; + return new String(0, count, buffer); } /** @@ -531,15 +605,18 @@ public final class Integer extends Number implements Comparable { * depending on the radix. If {@code radix} is not in the interval defined * by {@code Character.MIN_RADIX} and {@code Character.MAX_RADIX} then 10 is * used as the base for the conversion. - * + * * @param i * the integer to convert. * @param radix * the base to use for the conversion. * @return the string representation of {@code i}. - * @since Android 1.0 - */ + */ public static String toString(int i, int radix) { + // BEGIN android-note + // if radix==10, call thru to faster Integer.toString(int) ? + // only worthwhile if 10 is a popular parameter + // END android-note if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) { radix = 10; } @@ -575,7 +652,7 @@ public final class Integer extends Number implements Comparable { /** * Parses the specified string as a signed decimal integer value. - * + * * @param string * the string representation of an integer value. * @return an {@code Integer} instance containing the integer value @@ -584,7 +661,6 @@ public final class Integer extends Number implements Comparable { * if {@code string} is {@code null}, has a length of zero or * can not be parsed as an integer value. * @see #parseInt(String) - * @since Android 1.0 */ public static Integer valueOf(String string) throws NumberFormatException { return valueOf(parseInt(string)); @@ -593,7 +669,7 @@ public final class Integer extends Number implements Comparable { /** * Parses the specified string as a signed integer value using the specified * radix. - * + * * @param string * the string representation of an integer value. * @param radix @@ -606,7 +682,6 @@ public final class Integer extends Number implements Comparable { * {@code radix > Character.MAX_RADIX}, or if {@code string} * can not be parsed as an integer value. * @see #parseInt(String, int) - * @since Android 1.0 */ public static Integer valueOf(String string, int radix) throws NumberFormatException { @@ -618,11 +693,11 @@ public final class Integer extends Number implements Comparable { * and returns the bit mask value for that bit. This is also referred to as * the Most Significant 1 Bit. Returns zero if the specified integer is * zero. - * + * * @param i * the integer to examine. * @return the bit mask indicating the highest 1 bit in {@code i}. - * @since Android 1.0 + * @since 1.5 */ public static int highestOneBit(int i) { i |= (i >> 1); @@ -638,11 +713,11 @@ public final class Integer extends Number implements Comparable { * and returns the bit mask value for that bit. This is also referred * to as the Least Significant 1 Bit. Returns zero if the specified integer * is zero. - * + * * @param i * the integer to examine. * @return the bit mask indicating the lowest 1 bit in {@code i}. - * @since Android 1.0 + * @since 1.5 */ public static int lowestOneBit(int i) { return (i & (-i)); @@ -651,11 +726,11 @@ public final class Integer extends Number implements Comparable { /** * Determines the number of leading zeros in the specified integer prior to * the {@link #highestOneBit(int) highest one bit}. - * + * * @param i * the integer to examine. * @return the number of leading zeros in {@code i}. - * @since Android 1.0 + * @since 1.5 */ public static int numberOfLeadingZeros(int i) { i |= i >> 1; @@ -669,11 +744,11 @@ public final class Integer extends Number implements Comparable { /** * Determines the number of trailing zeros in the specified integer after * the {@link #lowestOneBit(int) lowest one bit}. - * + * * @param i * the integer to examine. * @return the number of trailing zeros in {@code i}. - * @since Android 1.0 + * @since 1.5 */ public static int numberOfTrailingZeros(int i) { return bitCount((i & -i) - 1); @@ -682,11 +757,11 @@ public final class Integer extends Number implements Comparable { /** * Counts the number of 1 bits in the specified integer; this is also * referred to as population count. - * + * * @param i * the integer to examine. * @return the number of 1 bits in {@code i}. - * @since Android 1.0 + * @since 1.5 */ public static int bitCount(int i) { i -= ((i >> 1) & 0x55555555); @@ -700,13 +775,13 @@ public final class Integer extends Number implements Comparable { /** * Rotates the bits of the specified integer to the left by the specified * number of bits. - * + * * @param i * the integer value to rotate left. * @param distance * the number of bits to rotate. * @return the rotated value. - * @since Android 1.0 + * @since 1.5 */ public static int rotateLeft(int i, int distance) { if (distance == 0) { @@ -723,13 +798,13 @@ public final class Integer extends Number implements Comparable { /** * Rotates the bits of the specified integer to the right by the specified * number of bits. - * + * * @param i * the integer value to rotate right. * @param distance * the number of bits to rotate. * @return the rotated value. - * @since Android 1.0 + * @since 1.5 */ public static int rotateRight(int i, int distance) { if (distance == 0) { @@ -745,11 +820,11 @@ public final class Integer extends Number implements Comparable { /** * Reverses the order of the bytes of the specified integer. - * + * * @param i * the integer value for which to reverse the byte order. * @return the reversed value. - * @since Android 1.0 + * @since 1.5 */ public static int reverseBytes(int i) { int b3 = i >>> 24; @@ -761,11 +836,11 @@ public final class Integer extends Number implements Comparable { /** * Reverses the order of the bits of the specified integer. - * + * * @param i * the integer value for which to reverse the bit order. * @return the reversed value. - * @since Android 1.0 + * @since 1.5 */ public static int reverse(int i) { // From Hacker's Delight, 7-1, Figure 7-1 @@ -778,12 +853,12 @@ public final class Integer extends Number implements Comparable { /** * Returns the value of the {@code signum} function for the specified * integer. - * + * * @param i * the integer value to check. * @return -1 if {@code i} is negative, 1 if {@code i} is positive, 0 if * {@code i} is zero. - * @since Android 1.0 + * @since 1.5 */ public static int signum(int i) { return (i == 0 ? 0 : (i < 0 ? -1 : 1)); @@ -795,12 +870,11 @@ public final class Integer extends Number implements Comparable { * If it is not necessary to get a new {@code Integer} instance, it is * recommended to use this method instead of the constructor, since it * maintains a cache of instances which may result in better performance. - *

          - * + * * @param i * the integer value to store in the instance. * @return a {@code Integer} instance containing {@code i}. - * @since Android 1.0 + * @since 1.5 */ public static Integer valueOf(int i) { if (i < -128 || i > 127) { @@ -814,7 +888,6 @@ public final class Integer extends Number implements Comparable { /** *

          * A cache of instances used by {@link Integer#valueOf(int)} and auto-boxing. - *

          */ static final Integer[] CACHE = new Integer[256]; diff --git a/libcore/luni/src/main/java/java/lang/InternalError.java b/libcore/luni/src/main/java/java/lang/InternalError.java index 9082cd8b8..d4748af95 100644 --- a/libcore/luni/src/main/java/java/lang/InternalError.java +++ b/libcore/luni/src/main/java/java/lang/InternalError.java @@ -20,8 +20,6 @@ package java.lang; /** * Thrown when the virtual machine notices that it has gotten into an undefined * state. - * - * @since Android 1.0 */ public class InternalError extends VirtualMachineError { @@ -30,8 +28,6 @@ public class InternalError extends VirtualMachineError { /** * Constructs a new {@code InternalError} that includes the current stack * trace. - * - * @since Android 1.0 */ public InternalError() { super(); @@ -43,7 +39,6 @@ public class InternalError extends VirtualMachineError { * * @param detailMessage * the detail message for this error. - * @since Android 1.0 */ public InternalError(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/lang/InterruptedException.java b/libcore/luni/src/main/java/java/lang/InterruptedException.java index b01455929..0e099c335 100644 --- a/libcore/luni/src/main/java/java/lang/InterruptedException.java +++ b/libcore/luni/src/main/java/java/lang/InterruptedException.java @@ -20,8 +20,6 @@ package java.lang; /** * Thrown when a waiting thread is activated before the condition it was waiting * for has been satisfied. - * - * @since Android 1.0 */ public class InterruptedException extends Exception { @@ -30,8 +28,6 @@ public class InterruptedException extends Exception { /** * Constructs a new {@code InterruptedException} that includes the current * stack trace. - * - * @since Android 1.0 */ public InterruptedException() { super(); @@ -43,7 +39,6 @@ public class InterruptedException extends Exception { * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public InterruptedException(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/lang/Iterable.java b/libcore/luni/src/main/java/java/lang/Iterable.java index d12f0a691..31883fb0b 100644 --- a/libcore/luni/src/main/java/java/lang/Iterable.java +++ b/libcore/luni/src/main/java/java/lang/Iterable.java @@ -21,8 +21,8 @@ import java.util.Iterator; /** * Objects of classes that implement this interface can be used within a * {@code foreach} statement. - * - * @since Android 1.0 + * + * @since 1.5 */ public interface Iterable { @@ -30,7 +30,6 @@ public interface Iterable { * Returns an {@link Iterator} for the elements in this object. * * @return An {@code Iterator} instance. - * @since Android 1.0 */ Iterator iterator(); } diff --git a/libcore/luni/src/main/java/java/lang/LinkageError.java b/libcore/luni/src/main/java/java/lang/LinkageError.java index 9bba54bbb..061f3dc64 100644 --- a/libcore/luni/src/main/java/java/lang/LinkageError.java +++ b/libcore/luni/src/main/java/java/lang/LinkageError.java @@ -23,7 +23,6 @@ package java.lang; * loading and linking class files. * * @see Error - * @since Android 1.0 */ public class LinkageError extends Error { @@ -32,8 +31,6 @@ public class LinkageError extends Error { /** * Constructs a new {@code LinkageError} that includes the current stack * trace. - * - * @since Android 1.0 */ public LinkageError() { super(); @@ -45,7 +42,6 @@ public class LinkageError extends Error { * * @param detailMessage * the detail message for this error. - * @since Android 1.0 */ public LinkageError(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/lang/Long.java b/libcore/luni/src/main/java/java/lang/Long.java index 00b56ae7d..2746424e6 100644 --- a/libcore/luni/src/main/java/java/lang/Long.java +++ b/libcore/luni/src/main/java/java/lang/Long.java @@ -24,10 +24,9 @@ package java.lang; * href="http://www.hackersdelight.org/">Henry S. Warren, Jr.'s Hacker's * Delight, (Addison Wesley, 2002) as well as The Aggregate's Magic Algorithms. - *

          - * + * * @see java.lang.Number - * @since Android 1.0 + * @since 1.0 */ public final class Long extends Number implements Comparable { @@ -40,22 +39,16 @@ public final class Long extends Number implements Comparable { /** * Constant for the maximum {@code long} value, 263-1. - * - * @since Android 1.0 */ public static final long MAX_VALUE = 0x7FFFFFFFFFFFFFFFL; /** * Constant for the minimum {@code long} value, -263. - * - * @since Android 1.0 */ public static final long MIN_VALUE = 0x8000000000000000L; /** * The {@link Class} object that represents the primitive type {@code long}. - * - * @since Android 1.0 */ @SuppressWarnings("unchecked") public static final Class TYPE = (Class) new long[0].getClass() @@ -67,8 +60,8 @@ public final class Long extends Number implements Comparable { /** * Constant for the number of bits needed to represent a {@code long} in * two's complement form. - * - * @since Android 1.0 + * + * @since 1.5 */ public static final int SIZE = 64; @@ -78,7 +71,6 @@ public final class Long extends Number implements Comparable { * * @param value * the primitive long value to store in the new instance. - * @since Android 1.0 */ public Long(long value) { this.value = value; @@ -92,7 +84,6 @@ public final class Long extends Number implements Comparable { * @throws NumberFormatException * if {@code string} can not be decoded into a long value. * @see #parseLong(String) - * @since Android 1.0 */ public Long(String string) throws NumberFormatException { this(parseLong(string)); @@ -114,7 +105,7 @@ public final class Long extends Number implements Comparable { * {@code object} are equal; a positive value if the value of this * long is greater than the value of {@code object}. * @see java.lang.Comparable - * @since Android 1.0 + * @since 1.2 */ public int compareTo(Long object) { return value > object.value ? 1 : (value < object.value ? -1 : 0); @@ -131,7 +122,6 @@ public final class Long extends Number implements Comparable { * @return a {@code Long} containing the value represented by {@code string}. * @throws NumberFormatException * if {@code string} can not be parsed as a long value. - * @since Android 1.0 */ public static Long decode(String string) throws NumberFormatException { int length = string.length(), i = 0; @@ -187,7 +177,6 @@ public final class Long extends Number implements Comparable { * the object to compare this long with. * @return {@code true} if the specified object is equal to this * {@code Long}; {@code false} otherwise. - * @since Android 1.0 */ @Override public boolean equals(Object o) { @@ -209,7 +198,6 @@ public final class Long extends Number implements Comparable { * @param string * the name of the requested system property. * @return the requested property's value as a {@code Long} or {@code null}. - * @since Android 1.0 */ public static Long getLong(String string) { if (string == null || string.length() == 0) { @@ -239,7 +227,6 @@ public final class Long extends Number implements Comparable { * property with the requested name. * @return the requested property's value as a {@code Long} or the default * value. - * @since Android 1.0 */ public static Long getLong(String string, long defaultValue) { if (string == null || string.length() == 0) { @@ -269,7 +256,6 @@ public final class Long extends Number implements Comparable { * property with the requested name. * @return the requested property's value as a {@code Long} or the default * value. - * @since Android 1.0 */ public static Long getLong(String string, Long defaultValue) { if (string == null || string.length() == 0) { @@ -300,7 +286,6 @@ public final class Long extends Number implements Comparable { * Gets the primitive value of this long. * * @return this object's primitive value. - * @since Android 1.0 */ @Override public long longValue() { @@ -317,7 +302,6 @@ public final class Long extends Number implements Comparable { * @throws NumberFormatException * if {@code string} is {@code null}, has a length of zero or * can not be parsed as a long value. - * @since Android 1.0 */ public static long parseLong(String string) throws NumberFormatException { return parseLong(string, 10); @@ -338,7 +322,6 @@ public final class Long extends Number implements Comparable { * {@code radix < Character.MIN_RADIX}, * {@code radix > Character.MAX_RADIX}, or if {@code string} * can not be parsed as a long value. - * @since Android 1.0 */ public static long parseLong(String string, int radix) throws NumberFormatException { @@ -397,7 +380,6 @@ public final class Long extends Number implements Comparable { * @param l * the long value to convert. * @return the binary string representation of {@code l}. - * @since Android 1.0 */ public static String toBinaryString(long l) { int count = 1; @@ -427,7 +409,6 @@ public final class Long extends Number implements Comparable { * @param l * the long value to convert. * @return the hexadecimal string representation of {@code l}. - * @since Android 1.0 */ public static String toHexString(long l) { int count = 1; @@ -462,7 +443,6 @@ public final class Long extends Number implements Comparable { * @param l * the long value to convert. * @return the octal string representation of {@code l}. - * @since Android 1.0 */ public static String toOctalString(long l) { int count = 1; @@ -497,8 +477,7 @@ public final class Long extends Number implements Comparable { * @param l * the long to convert. * @return the decimal string representation of {@code l}. - * @since Android 1.0 - */ + */ public static String toString(long l) { return toString(l, 10); } @@ -516,7 +495,6 @@ public final class Long extends Number implements Comparable { * @param radix * the base to use for the conversion. * @return the string representation of {@code l}. - * @since Android 1.0 */ public static String toString(long l, int radix) { if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) { @@ -564,7 +542,6 @@ public final class Long extends Number implements Comparable { * if {@code string} is {@code null}, has a length of zero or * can not be parsed as a long value. * @see #parseLong(String) - * @since Android 1.0 */ public static Long valueOf(String string) throws NumberFormatException { return valueOf(parseLong(string)); @@ -586,7 +563,6 @@ public final class Long extends Number implements Comparable { * {@code radix > Character.MAX_RADIX}, or if {@code string} * can not be parsed as a long value. * @see #parseLong(String, int) - * @since Android 1.0 */ public static Long valueOf(String string, int radix) throws NumberFormatException { @@ -602,7 +578,7 @@ public final class Long extends Number implements Comparable { * @param lng * the long to examine. * @return the bit mask indicating the highest 1 bit in {@code lng}. - * @since Android 1.0 + * @since 1.5 */ public static long highestOneBit(long lng) { lng |= (lng >> 1); @@ -623,7 +599,7 @@ public final class Long extends Number implements Comparable { * @param lng * the long to examine. * @return the bit mask indicating the lowest 1 bit in {@code lng}. - * @since Android 1.0 + * @since 1.5 */ public static long lowestOneBit(long lng) { return (lng & (-lng)); @@ -632,11 +608,11 @@ public final class Long extends Number implements Comparable { /** * Determines the number of leading zeros in the specified long value prior * to the {@link #highestOneBit(long) highest one bit}. - * + * * @param lng * the long to examine. * @return the number of leading zeros in {@code lng}. - * @since Android 1.0 + * @since 1.5 */ public static int numberOfLeadingZeros(long lng) { lng |= lng >> 1; @@ -651,11 +627,11 @@ public final class Long extends Number implements Comparable { /** * Determines the number of trailing zeros in the specified long value after * the {@link #lowestOneBit(long) lowest one bit}. - * + * * @param lng * the long to examine. * @return the number of trailing zeros in {@code lng}. - * @since Android 1.0 + * @since 1.5 */ public static int numberOfTrailingZeros(long lng) { return bitCount((lng & -lng) - 1); @@ -664,11 +640,11 @@ public final class Long extends Number implements Comparable { /** * Counts the number of 1 bits in the specified long value; this is also * referred to as population count. - * + * * @param lng * the long to examine. * @return the number of 1 bits in {@code lng}. - * @since Android 1.0 + * @since 1.5 */ public static int bitCount(long lng) { lng = (lng & 0x5555555555555555L) + ((lng >> 1) & 0x5555555555555555L); @@ -684,13 +660,13 @@ public final class Long extends Number implements Comparable { /** * Rotates the bits of the specified long value to the left by the specified * number of bits. - * + * * @param lng * the long value to rotate left. * @param distance * the number of bits to rotate. * @return the rotated value. - * @since Android 1.0 + * @since 1.5 */ public static long rotateLeft(long lng, int distance) { if (distance == 0) { @@ -705,15 +681,16 @@ public final class Long extends Number implements Comparable { } /** + *

          * Rotates the bits of the specified long value to the right by the * specified number of bits. - * + * * @param lng * the long value to rotate right. * @param distance * the number of bits to rotate. * @return the rotated value. - * @since Android 1.0 + * @since 1.5 */ public static long rotateRight(long lng, int distance) { if (distance == 0) { @@ -733,7 +710,7 @@ public final class Long extends Number implements Comparable { * @param lng * the long value for which to reverse the byte order. * @return the reversed value. - * @since Android 1.0 + * @since 1.5 */ public static long reverseBytes(long lng) { long b7 = lng >>> 56; @@ -753,7 +730,7 @@ public final class Long extends Number implements Comparable { * @param lng * the long value for which to reverse the bit order. * @return the reversed value. - * @since Android 1.0 + * @since 1.5 */ public static long reverse(long lng) { // From Hacker's Delight, 7-1, Figure 7-1 @@ -774,7 +751,7 @@ public final class Long extends Number implements Comparable { * the long value to check. * @return -1 if {@code lng} is negative, 1 if {@code lng} is positive, 0 if * {@code lng} is zero. - * @since Android 1.0 + * @since 1.5 */ public static int signum(long lng) { return (lng == 0 ? 0 : (lng < 0 ? -1 : 1)); @@ -786,12 +763,11 @@ public final class Long extends Number implements Comparable { * If it is not necessary to get a new {@code Long} instance, it is * recommended to use this method instead of the constructor, since it * maintains a cache of instances which may result in better performance. - *

          - * + * * @param lng * the long value to store in the instance. * @return a {@code Long} instance containing {@code lng}. - * @since Android 1.0 + * @since 1.5 */ public static Long valueOf(long lng) { if (lng < -128 || lng > 127) { @@ -804,7 +780,6 @@ public final class Long extends Number implements Comparable { /** *

          * A cache of instances used by {@link Long#valueOf(long)} and auto-boxing. - *

          */ static final Long[] CACHE = new Long[256]; diff --git a/libcore/luni/src/main/java/java/lang/Math.java b/libcore/luni/src/main/java/java/lang/Math.java index f18f40f0c..d06e6e4e8 100644 --- a/libcore/luni/src/main/java/java/lang/Math.java +++ b/libcore/luni/src/main/java/java/lang/Math.java @@ -20,23 +20,17 @@ package java.lang; /** * Class Math provides basic math constants and operations such as trigonometric * functions, hyperbolic functions, exponential, logarithms, etc. - * - * @since Android 1.0 */ public final class Math { /** * The double value closest to e, the base of the natural logarithm. - * - * @since Android 1.0 */ public static final double E = 2.718281828459045; /** * The double value closest to pi, the ratio of a circle's circumference to * its diameter. - * - * @since Android 1.0 */ public static final double PI = 3.141592653589793; @@ -58,12 +52,10 @@ public final class Math { *
        • {@code abs(-infinity) = +infinity}
        • *
        • {@code abs(NaN) = NaN}
        • * - *

          * * @param d * the value whose absolute value has to be computed. * @return the absolute value of the argument. - * @since Android 1.0 */ public static double abs(double d) { long bits = Double.doubleToLongBits(d); @@ -81,13 +73,11 @@ public final class Math { *
        • {@code abs(-infinity) = +infinity}
        • *
        • {@code abs(NaN) = NaN}
        • * - *

          * * @param f * the value whose absolute value has to be computed. * @return the argument if it is positive, otherwise the negation of the * argument. - * @since Android 1.0 */ public static float abs(float f) { int bits = Float.floatToIntBits(f); @@ -100,13 +90,11 @@ public final class Math { *

          * If the argument is {@code Integer.MIN_VALUE}, {@code Integer.MIN_VALUE} * is returned. - *

          * * @param i * the value whose absolute value has to be computed. * @return the argument if it is positive, otherwise the negation of the * argument. - * @since Android 1.0 */ public static int abs(int i) { return i >= 0 ? i : -i; @@ -120,7 +108,6 @@ public final class Math { * the value whose absolute value has to be computed. * @return the argument if it is positive, otherwise the negation of the * argument. - * @since Android 1.0 */ public static long abs(long l) { return l >= 0 ? l : -l; @@ -137,12 +124,10 @@ public final class Math { *
        • {@code acos((anything < -1) = NaN}
        • *
        • {@code acos(NaN) = NaN}
        • * - *

          * * @param d * the value to compute arc cosine of. * @return the arc cosine of the argument. - * @since Android 1.0 */ public static native double acos(double d); @@ -157,12 +142,10 @@ public final class Math { *
        • {@code asin((anything < -1)) = NaN}
        • *
        • {@code asin(NaN) = NaN}
        • * - *

          * * @param d * the value whose arc sine has to be computed. * @return the arc sine of the argument. - * @since Android 1.0 */ public static native double asin(double d); @@ -179,20 +162,18 @@ public final class Math { *
        • {@code atan(-infinity) = -pi/2}
        • *
        • {@code atan(NaN) = NaN}
        • * - *

          * * @param d * the value whose arc tangent has to be computed. * @return the arc tangent of the argument. - * @since Android 1.0 */ public static native double atan(double d); /** - * Returns the closest double approximation of the arc tangent of - * {@code y/x} within the range {@code [-pi..pi]}. This is the angle of the - * polar representation of the rectangular coordinates (x,y). The returned - * result is within 2 ulps (units in the last place) of the real result. + * Returns the closest double approximation of the arc tangent of {@code + * y/x} within the range {@code [-pi..pi]}. This is the angle of the polar + * representation of the rectangular coordinates (x,y). The returned result + * is within 2 ulps (units in the last place) of the real result. *

          * Special cases: *

            @@ -214,24 +195,19 @@ public final class Math { *
          • {@code atan2(-infinity, +infinity ) = -pi/4}
          • *
          • {@code atan2(+infinity, -infinity ) = +3pi/4}
          • *
          • {@code atan2(-infinity, -infinity ) = -3pi/4}
          • - *
          • {@code atan2(+infinity, (anything but,0, NaN, and infinity))} - * {@code =} {@code +pi/2}
          • - *
          • {@code atan2(-infinity, (anything but,0, NaN, and infinity))} - * {@code =} {@code -pi/2}
          • + *
          • {@code atan2(+infinity, (anything but,0, NaN, and infinity))} {@code + * =} {@code +pi/2}
          • + *
          • {@code atan2(-infinity, (anything but,0, NaN, and infinity))} {@code + * =} {@code -pi/2}
          • *
          - *

          * * @param y * the numerator of the value whose atan has to be computed. * @param x * the denominator of the value whose atan has to be computed. * @return the arc tangent of {@code y/x}. - * @since Android 1.0 */ - public static native double atan2(double y, double x); - // BEGIN android-note - // parameter names changed from d1 / d2 to x / y - // END android-note + public static native double atan2(double x, double y); /** * Returns the closest double approximation of the cube root of the @@ -245,18 +221,16 @@ public final class Math { *
        • {@code cbrt(-infinity) = -infinity}
        • *
        • {@code cbrt(NaN) = NaN}
        • * - *

          * * @param d * the value whose cube root has to be computed. * @return the cube root of the argument. - * @since Android 1.0 */ public static native double cbrt(double d); /** - * Returns the double conversion of the most negative (closest to - * negative infinity) integer value which is greater than the argument. + * Returns the double conversion of the most negative (closest to negative + * infinity) integer value which is greater than the argument. *

          * Special cases: *

            @@ -267,14 +241,14 @@ public final class Math { *
          • {@code ceil(-infinity) = -infinity}
          • *
          • {@code ceil(NaN) = NaN}
          • *
          - *

          * * @param d * the value whose closest integer value has to be computed. * @return the ceiling of the argument. - * @since Android 1.0 */ + // BEGIN android-changed public static native double ceil(double d); + // END android-changed /** * Returns the closest double approximation of the cosine of the argument. @@ -287,15 +261,13 @@ public final class Math { *
        • {@code cos(-infinity) = NaN}
        • *
        • {@code cos(NaN) = NaN}
        • * - *

          * * @param d * the angle whose cosine has to be computed, in radians. * @return the cosine of the argument. - * @since Android 1.0 */ public static native double cos(double d); - + /** * Returns the closest double approximation of the hyperbolic cosine of the * argument. The returned result is within 2.5 ulps (units in the last @@ -307,12 +279,10 @@ public final class Math { *
        • {@code cosh(-infinity) = +infinity}
        • *
        • {@code cosh(NaN) = NaN}
        • * - *

          * * @param d * the value whose hyperbolic cosine has to be computed. * @return the hyperbolic cosine of the argument. - * @since Android 1.0 */ public static native double cosh(double d); @@ -327,19 +297,17 @@ public final class Math { *
        • {@code exp(-infinity) = +0.0}
        • *
        • {@code exp(NaN) = NaN}
        • * - *

          * * @param d * the value whose exponential has to be computed. * @return the exponential of the argument. - * @since Android 1.0 */ public static native double exp(double d); - + /** - * Returns the closest double approximation of {@code e} - * {@code d}{@code - 1}. If the argument is very close to 0, it is - * much more accurate to use {@code expm1(d)+1} than {@code exp(d)} (due to + * Returns the closest double approximation of {@code e} {@code + * d}{@code - 1}. If the argument is very close to 0, it is much more + * accurate to use {@code expm1(d)+1} than {@code exp(d)} (due to * cancellation of significant digits). The returned result is within 1 ulp * (unit in the last place) of the real result. *

          @@ -354,14 +322,12 @@ public final class Math { *

        • {@code expm1(-infinity) = -1.0}
        • *
        • {@code expm1(NaN) = NaN}
        • * - *

          * * @param d - * the value to compute the {@code e}{@code d} - * {@code - 1} of. - * @return the {@code e}{@code d}{@code - 1} value - * of the argument. - * @since Android 1.0 + * the value to compute the {@code e}{@code d} + * {@code - 1} of. + * @return the {@code e}{@code d}{@code - 1} value of the + * argument. */ public static native double expm1(double d); @@ -377,20 +343,21 @@ public final class Math { *
        • {@code floor(-infinity) = -infinity}
        • *
        • {@code floor(NaN) = NaN}
        • * - *

          * * @param d * the value whose closest integer value has to be computed. * @return the floor of the argument. - * @since Android 1.0 */ + // BEGIN android-changed public static native double floor(double d); - + // END android-changed + /** - * Returns {@code sqrt(}{@code x}{@code 2}{@code +} - * {@code y}{@code 2}{@code )}. The final result is - * without medium underflow or overflow. The returned result is within 1 ulp - * (unit in the last place) of the real result. + * Returns {@code sqrt(}{@code x}{@code 2}{@code +} + * {@code y}{@code 2}{@code )}. The final result is without + * medium underflow or overflow. The returned result is within 1 ulp (unit + * in the last place) of the real result. If one parameter remains constant, + * the result should be semi-monotonic. *

          * Special cases: *

            @@ -400,7 +367,6 @@ public final class Math { *
          • {@code hypot((anything including NaN), -infinity) = +infinity}
          • *
          • {@code hypot(NaN, NaN) = NaN}
          • *
          - *

          * * @param x * a double number. @@ -409,7 +375,6 @@ public final class Math { * @return the {@code sqrt(}{@code x}{@code 2}{@code +} * {@code y}{@code 2}{@code )} value of the * arguments. - * @since Android 1.0 */ public static native double hypot(double x, double y); @@ -431,19 +396,14 @@ public final class Math { *
        • {@code IEEEremainder(x, -infinity) = x } where x is anything but * +/-infinity
        • * - *

          * * @param x * the numerator of the operation. * @param y * the denominator of the operation. * @return the IEEE754 floating point reminder of of {@code x/y}. - * @since Android 1.0 */ public static native double IEEEremainder(double x, double y); - // BEGIN android-note - // parameter names changed from d1 / d2 to x / y - // END android-note /** * Returns the closest double approximation of the natural logarithm of the @@ -459,12 +419,10 @@ public final class Math { *
        • {@code log(-infinity) = NaN}
        • *
        • {@code log(NaN) = NaN}
        • * - *

          * * @param d * the value whose log has to be computed. * @return the natural logarithm of the argument. - * @since Android 1.0 */ public static native double log(double d); @@ -482,21 +440,19 @@ public final class Math { *
        • {@code log10(-infinity) = NaN}
        • *
        • {@code log10(NaN) = NaN}
        • * - *

          * * @param d * the value whose base 10 log has to be computed. * @return the natural logarithm of the argument. - * @since Android 1.0 */ public static native double log10(double d); - + /** * Returns the closest double approximation of the natural logarithm of the * sum of the argument and 1. If the argument is very close to 0, it is much * more accurate to use {@code log1p(d)} than {@code log(1.0+d)} (due to * numerical cancellation). The returned result is within 1 ulp (unit in the - * last place) of the real result. + * last place) of the real result and is semi-monotonic. *

          * Special cases: *

            @@ -508,12 +464,10 @@ public final class Math { *
          • {@code log1p(-infinity) = NaN}
          • *
          • {@code log1p(NaN) = NaN}
          • *
          - *

          * * @param d * the value to compute the {@code ln(1+d)} of. * @return the natural logarithm of the sum of the argument and 1. - * @since Android 1.0 */ public static native double log1p(double d); @@ -528,28 +482,30 @@ public final class Math { *
        • {@code max(+0.0, -0.0) = +0.0}
        • *
        • {@code max(-0.0, +0.0) = +0.0}
        • * - *

          * * @param d1 * the first argument. * @param d2 * the second argument. * @return the larger of {@code d1} and {@code d2}. - * @since Android 1.0 */ public static double max(double d1, double d2) { - if (d1 > d2) + if (d1 > d2) { return d1; - if (d1 < d2) + } + if (d1 < d2) { return d2; + } /* if either arg is NaN, return NaN */ - if (d1 != d2) + if (d1 != d2) { return Double.NaN; - /* max( +0.0,-0.0) == +0.0 */ - if (d1 == 0.0 - && ((Double.doubleToLongBits(d1) & Double.doubleToLongBits(d2)) & 0x8000000000000000L) == 0) - return 0.0; - return d1; + } + /* max(+0.0,-0.0) == +0.0 */ + /* 0 == Double.doubleToRawLongBits(0.0d) */ + if (Double.doubleToRawLongBits(d1) != 0) { + return d2; + } + return 0.0d; } /** @@ -563,28 +519,30 @@ public final class Math { *
        • {@code max(+0.0, -0.0) = +0.0}
        • *
        • {@code max(-0.0, +0.0) = +0.0}
        • * - *

          * * @param f1 * the first argument. * @param f2 * the second argument. * @return the larger of {@code f1} and {@code f2}. - * @since Android 1.0 */ public static float max(float f1, float f2) { - if (f1 > f2) + if (f1 > f2) { return f1; - if (f1 < f2) + } + if (f1 < f2) { return f2; + } /* if either arg is NaN, return NaN */ - if (f1 != f2) + if (f1 != f2) { return Float.NaN; - /* max( +0.0,-0.0) == +0.0 */ - if (f1 == 0.0f - && ((Float.floatToIntBits(f1) & Float.floatToIntBits(f2)) & 0x80000000) == 0) - return 0.0f; - return f1; + } + /* max(+0.0,-0.0) == +0.0 */ + /* 0 == Float.floatToRawIntBits(0.0f) */ + if (Float.floatToRawIntBits(f1) != 0) { + return f2; + } + return 0.0f; } /** @@ -596,7 +554,6 @@ public final class Math { * @param i2 * the second argument. * @return the larger of {@code i1} and {@code i2}. - * @since Android 1.0 */ public static int max(int i1, int i2) { return i1 > i2 ? i1 : i2; @@ -611,7 +568,6 @@ public final class Math { * @param l2 * the second argument. * @return the larger of {@code l1} and {@code l2}. - * @since Android 1.0 */ public static long max(long l1, long l2) { return l1 > l2 ? l1 : l2; @@ -628,28 +584,30 @@ public final class Math { *
        • {@code min(+0.0, -0.0) = -0.0}
        • *
        • {@code min(-0.0, +0.0) = -0.0}
        • * - *

          * * @param d1 * the first argument. * @param d2 * the second argument. * @return the smaller of {@code d1} and {@code d2}. - * @since Android 1.0 */ public static double min(double d1, double d2) { - if (d1 > d2) + if (d1 > d2) { return d2; - if (d1 < d2) + } + if (d1 < d2) { return d1; + } /* if either arg is NaN, return NaN */ - if (d1 != d2) + if (d1 != d2) { return Double.NaN; - /* min( +0.0,-0.0) == -0.0 */ - if (d1 == 0.0 - && ((Double.doubleToLongBits(d1) | Double.doubleToLongBits(d2)) & 0x8000000000000000l) != 0) - return 0.0 * (-1.0); - return d1; + } + /* min(+0.0,-0.0) == -0.0 */ + /* 0x8000000000000000L == Double.doubleToRawLongBits(-0.0d) */ + if (Double.doubleToRawLongBits(d1) == 0x8000000000000000L) { + return -0.0d; + } + return d2; } /** @@ -663,28 +621,30 @@ public final class Math { *
        • {@code min(+0.0, -0.0) = -0.0}
        • *
        • {@code min(-0.0, +0.0) = -0.0}
        • * - *

          * * @param f1 * the first argument. * @param f2 * the second argument. * @return the smaller of {@code f1} and {@code f2}. - * @since Android 1.0 */ public static float min(float f1, float f2) { - if (f1 > f2) + if (f1 > f2) { return f2; - if (f1 < f2) + } + if (f1 < f2) { return f1; + } /* if either arg is NaN, return NaN */ - if (f1 != f2) + if (f1 != f2) { return Float.NaN; - /* min( +0.0,-0.0) == -0.0 */ - if (f1 == 0.0f - && ((Float.floatToIntBits(f1) | Float.floatToIntBits(f2)) & 0x80000000) != 0) - return 0.0f * (-1.0f); - return f1; + } + /* min(+0.0,-0.0) == -0.0 */ + /* 0x80000000 == Float.floatToRawIntBits(-0.0f) */ + if (Float.floatToRawIntBits(f1) == 0x80000000) { + return -0.0f; + } + return f2; } /** @@ -696,7 +656,6 @@ public final class Math { * @param i2 * the second argument. * @return the smaller of {@code i1} and {@code i2}. - * @since Android 1.0 */ public static int min(int i1, int i2) { return i1 < i2 ? i1 : i2; @@ -711,15 +670,14 @@ public final class Math { * @param l2 * the second argument. * @return the smaller of {@code l1} and {@code l2}. - * @since Android 1.0 */ public static long min(long l1, long l2) { return l1 < l2 ? l1 : l2; } /** - * Returns the closest double approximation of the result of raising - * {@code x} to the power of {@code y}. + * Returns the closest double approximation of the result of raising {@code + * x} to the power of {@code y}. *

          * Special cases: *

            @@ -743,23 +701,18 @@ public final class Math { *
          • {@code pow(+infinity, (+anything except 0, NaN)) = +infinity}
          • *
          • {@code pow(+infinity, (-anything except 0, NaN)) = +0.0}
          • *
          • {@code pow(-infinity, (anything)) = -pow(0, (-anything))}
          • - *
          • {@code pow((-anything), (integer))} {@code =} - * {@code pow(-1,(integer))*pow(+anything,integer) }
          • + *
          • {@code pow((-anything), (integer))} {@code =} {@code + * pow(-1,(integer))*pow(+anything,integer) }
          • *
          • {@code pow((-anything except 0 and inf), (non-integer)) = NAN}
          • *
          - *

          * * @param x * the base of the operation. * @param y * the exponent of the operation. * @return {@code x} to the power of {@code y}. - * @since Android 1.0 */ public static native double pow(double x, double y); - // BEGIN android-note - // parameter names changed from d1 / d2 to x / y - // END android-note /** * Returns the double conversion of the result of rounding the argument to @@ -773,14 +726,14 @@ public final class Math { *
        • {@code rint(-infinity) = -infinity}
        • *
        • {@code rint(NaN) = NaN}
        • * - *

          * * @param d * the value to be rounded. * @return the closest integer to the argument (as a double). - * @since Android 1.0 */ + // BEGIN android-changed public static native double rint(double d); + // END android-changed /** * Returns the result of rounding the argument to an integer. The result is @@ -796,17 +749,16 @@ public final class Math { *
        • {@code round(-infintiy) = Long.MIN_VALUE}
        • *
        • {@code round(NaN) = +0.0}
        • * - *

          * * @param d * the value to be rounded. * @return the closest integer to the argument. - * @since Android 1.0 */ public static long round(double d) { // check for NaN - if (d != d) + if (d != d) { return 0L; + } return (long) floor(d + 0.5d); } @@ -824,17 +776,16 @@ public final class Math { *
        • {@code round(-infintiy) = Integer.MIN_VALUE}
        • *
        • {@code round(NaN) = +0.0}
        • * - *

          * * @param f * the value to be rounded. * @return the closest integer to the argument. - * @since Android 1.0 */ public static int round(float f) { // check for NaN - if (f != f) + if (f != f) { return 0; + } return (int) floor(f + 0.5f); } @@ -852,17 +803,15 @@ public final class Math { *
        • {@code signum(-infinity) = -1.0}
        • *
        • {@code signum(NaN) = NaN}
        • * - *

          * * @param d * the value whose signum has to be computed. * @return the value of the signum function. - * @since Android 1.0 */ public static double signum(double d) { return StrictMath.signum(d); } - + /** * Returns the signum function of the argument. If the argument is less than * zero, it returns -1.0. If the argument is greater than zero, 1.0 is @@ -877,12 +826,10 @@ public final class Math { *
        • {@code signum(-infinity) = -1.0}
        • *
        • {@code signum(NaN) = NaN}
        • * - *

          * * @param f * the value whose signum has to be computed. * @return the value of the signum function. - * @since Android 1.0 */ public static float signum(float f) { return StrictMath.signum(f); @@ -901,15 +848,13 @@ public final class Math { *
        • {@code sin(-infinity) = NaN}
        • *
        • {@code sin(NaN) = NaN}
        • * - *

          * * @param d * the angle whose sin has to be computed, in radians. * @return the sine of the argument. - * @since Android 1.0 */ public static native double sin(double d); - + /** * Returns the closest double approximation of the hyperbolic sine of the * argument. The returned result is within 2.5 ulps (units in the last @@ -923,12 +868,10 @@ public final class Math { *
        • {@code sinh(-infinity) = -infinity}
        • *
        • {@code sinh(NaN) = NaN}
        • * - *

          * * @param d * the value whose hyperbolic sine has to be computed. * @return the hyperbolic sine of the argument. - * @since Android 1.0 */ public static native double sinh(double d); @@ -944,12 +887,10 @@ public final class Math { *
        • {@code sqrt(+infinity) = +infinity}
        • *
        • {@code sqrt(NaN) = NaN}
        • * - *

          * * @param d * the value whose square root has to be computed. * @return the square root of the argument. - * @since Android 1.0 */ public static native double sqrt(double d); @@ -966,15 +907,13 @@ public final class Math { *
        • {@code tan(-infinity) = NaN}
        • *
        • {@code tan(NaN) = NaN}
        • * - *

          * * @param d * the angle whose tangens has to be computed, in radians. * @return the tangent of the argument. - * @since Android 1.0 */ public static native double tan(double d); - + /** * Returns the closest double approximation of the hyperbolic tangent of the * argument. The absolute value is always less than 1. The returned result @@ -990,12 +929,10 @@ public final class Math { *
        • {@code tanh(-infinity) = -1.0}
        • *
        • {@code tanh(NaN) = NaN}
        • * - *

          * * @param d * the value whose hyperbolic tangent has to be computed. * @return the hyperbolic tangent of the argument. - * @since Android 1.0 */ public static native double tanh(double d); @@ -1004,7 +941,6 @@ public final class Math { * (exclusive). * * @return a pseudo-random number. - * @since Android 1.0 */ public static double random() { if (random == null) { @@ -1025,12 +961,10 @@ public final class Math { *
        • {@code toRadians(-infinity) = -infinity}
        • *
        • {@code toRadians(NaN) = NaN}
        • * - *

          * * @param angdeg * an angle in degrees. * @return the radian measure of the angle. - * @since Android 1.0 */ public static double toRadians(double angdeg) { return angdeg / 180d * PI; @@ -1048,22 +982,20 @@ public final class Math { *
        • {@code toDegrees(-infinity) = -infinity}
        • *
        • {@code toDegrees(NaN) = NaN}
        • * - *

          * * @param angrad * an angle in radians. * @return the degree measure of the angle. - * @since Android 1.0 */ public static double toDegrees(double angrad) { return angrad * 180d / PI; } - + /** * Returns the argument's ulp (unit in the last place). The size of a ulp of * a double value is the positive distance between this value and the double - * value next larger in magnitude. For non-NaN {@code x}, - * {@code ulp(-x) == ulp(x)}. + * value next larger in magnitude. For non-NaN {@code x}, {@code ulp(-x) == + * ulp(x)}. *

          * Special cases: *

            @@ -1073,12 +1005,10 @@ public final class Math { *
          • {@code ulp(-infintiy) = infinity}
          • *
          • {@code ulp(NaN) = NaN}
          • *
          - *

          * * @param d * the floating-point value to compute ulp of. * @return the size of a ulp of the argument. - * @since Android 1.0 */ public static double ulp(double d) { // special cases @@ -1094,8 +1024,8 @@ public final class Math { /** * Returns the argument's ulp (unit in the last place). The size of a ulp of * a float value is the positive distance between this value and the float - * value next larger in magnitude. For non-NaN {@code x}, - * {@code ulp(-x) == ulp(x)}. + * value next larger in magnitude. For non-NaN {@code x}, {@code ulp(-x) == + * ulp(x)}. *

          * Special cases: *

            @@ -1105,12 +1035,10 @@ public final class Math { *
          • {@code ulp(-infintiy) = infinity}
          • *
          • {@code ulp(NaN) = NaN}
          • *
          - *

          * * @param f * the floating-point value to compute ulp of. * @return the size of a ulp of the argument. - * @since Android 1.0 */ public static float ulp(float f) { // special cases @@ -1127,5 +1055,5 @@ public final class Math { private native static double nextafter(double x, double y); - private native static float nextafterf(float x, float y); + private native static float nextafterf(float x, float y); } diff --git a/libcore/luni/src/main/java/java/lang/NegativeArraySizeException.java b/libcore/luni/src/main/java/java/lang/NegativeArraySizeException.java index 1ca78551b..50b692945 100644 --- a/libcore/luni/src/main/java/java/lang/NegativeArraySizeException.java +++ b/libcore/luni/src/main/java/java/lang/NegativeArraySizeException.java @@ -20,8 +20,6 @@ package java.lang; /** * Thrown when an attempt is made to create an array with a size of less than * zero. - * - * @since Android 1.0 */ public class NegativeArraySizeException extends RuntimeException { @@ -30,8 +28,6 @@ public class NegativeArraySizeException extends RuntimeException { /** * Constructs a new {@code NegativeArraySizeException} that includes the * current stack trace. - * - * @since Android 1.0 */ public NegativeArraySizeException() { super(); @@ -43,7 +39,6 @@ public class NegativeArraySizeException extends RuntimeException { * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public NegativeArraySizeException(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/lang/NoClassDefFoundError.java b/libcore/luni/src/main/java/java/lang/NoClassDefFoundError.java index e23ecc686..9fae3606b 100644 --- a/libcore/luni/src/main/java/java/lang/NoClassDefFoundError.java +++ b/libcore/luni/src/main/java/java/lang/NoClassDefFoundError.java @@ -20,8 +20,6 @@ package java.lang; /** * Thrown when the virtual machine is unable to locate a class which it has been * asked to load. - * - * @since Android 1.0 */ public class NoClassDefFoundError extends LinkageError { @@ -30,8 +28,6 @@ public class NoClassDefFoundError extends LinkageError { /** * Constructs a new {@code NoClassDefFoundError} that includes the current * stack trace. - * - * @since Android 1.0 */ public NoClassDefFoundError() { super(); @@ -43,7 +39,6 @@ public class NoClassDefFoundError extends LinkageError { * * @param detailMessage * the detail message for this error. - * @since Android 1.0 */ public NoClassDefFoundError(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/lang/NoSuchFieldError.java b/libcore/luni/src/main/java/java/lang/NoSuchFieldError.java index 667d6a599..9b479f644 100644 --- a/libcore/luni/src/main/java/java/lang/NoSuchFieldError.java +++ b/libcore/luni/src/main/java/java/lang/NoSuchFieldError.java @@ -22,9 +22,6 @@ package java.lang; * on a class or object, a field that does not exist. *

          * Note that this can only occur when inconsistent class files are being loaded. - *

          - * - * @since Android 1.0 */ public class NoSuchFieldError extends IncompatibleClassChangeError { @@ -33,8 +30,6 @@ public class NoSuchFieldError extends IncompatibleClassChangeError { /** * Constructs a new {@code NoSuchFieldError} that includes the current stack * trace. - * - * @since Android 1.0 */ public NoSuchFieldError() { super(); @@ -46,7 +41,6 @@ public class NoSuchFieldError extends IncompatibleClassChangeError { * * @param detailMessage * the detail message for this error. - * @since Android 1.0 */ public NoSuchFieldError(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/lang/NoSuchFieldException.java b/libcore/luni/src/main/java/java/lang/NoSuchFieldException.java index 31a566d6d..283bfab0a 100644 --- a/libcore/luni/src/main/java/java/lang/NoSuchFieldException.java +++ b/libcore/luni/src/main/java/java/lang/NoSuchFieldException.java @@ -20,8 +20,6 @@ package java.lang; /** * Thrown when the virtual machine notices that a program tries to reference, * on a class or object, a field that does not exist. - * - * @since Android 1.0 */ public class NoSuchFieldException extends java.lang.Exception { @@ -30,8 +28,6 @@ public class NoSuchFieldException extends java.lang.Exception { /** * Constructs a new {@code NoSuchFieldException} that includes the current * stack trace. - * - * @since Android 1.0 */ public NoSuchFieldException() { super(); @@ -43,7 +39,6 @@ public class NoSuchFieldException extends java.lang.Exception { * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public NoSuchFieldException(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/lang/NoSuchMethodError.java b/libcore/luni/src/main/java/java/lang/NoSuchMethodError.java index a72f76b83..91ed06c01 100644 --- a/libcore/luni/src/main/java/java/lang/NoSuchMethodError.java +++ b/libcore/luni/src/main/java/java/lang/NoSuchMethodError.java @@ -20,8 +20,6 @@ package java.lang; /** * Thrown when the virtual machine notices that a program tries to reference, * on a class or object, a method that does not exist. - * - * @since Android 1.0 */ public class NoSuchMethodError extends IncompatibleClassChangeError { @@ -30,8 +28,6 @@ public class NoSuchMethodError extends IncompatibleClassChangeError { /** * Constructs a new {@code NoSuchMethodError} that includes the current * stack trace. - * - * @since Android 1.0 */ public NoSuchMethodError() { super(); @@ -43,7 +39,6 @@ public class NoSuchMethodError extends IncompatibleClassChangeError { * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public NoSuchMethodError(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/lang/NoSuchMethodException.java b/libcore/luni/src/main/java/java/lang/NoSuchMethodException.java index bd3cf0140..cf4dbe02c 100644 --- a/libcore/luni/src/main/java/java/lang/NoSuchMethodException.java +++ b/libcore/luni/src/main/java/java/lang/NoSuchMethodException.java @@ -20,8 +20,6 @@ package java.lang; /** * Thrown when the virtual machine notices that a program tries to reference, * on a class or object, a method that does not exist. - * - * @since Android 1.0 */ public class NoSuchMethodException extends java.lang.Exception { @@ -30,8 +28,6 @@ public class NoSuchMethodException extends java.lang.Exception { /** * Constructs a new {@code NoSuchMethodException} that includes the current * stack trace. - * - * @since Android 1.0 */ public NoSuchMethodException() { super(); @@ -43,7 +39,6 @@ public class NoSuchMethodException extends java.lang.Exception { * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public NoSuchMethodException(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/lang/NullPointerException.java b/libcore/luni/src/main/java/java/lang/NullPointerException.java index d11b2828c..90e9e5057 100644 --- a/libcore/luni/src/main/java/java/lang/NullPointerException.java +++ b/libcore/luni/src/main/java/java/lang/NullPointerException.java @@ -23,8 +23,6 @@ package java.lang; * object or array points to {@code null}. It also occurs in some other, less * obvious circumstances, like a {@code throw e} statement where the {@link * Throwable} reference is {@code null}. - * - * @since Android 1.0 */ public class NullPointerException extends RuntimeException { @@ -33,8 +31,6 @@ public class NullPointerException extends RuntimeException { /** * Constructs a new {@code NullPointerException} that includes the current * stack trace. - * - * @since Android 1.0 */ public NullPointerException() { super(); @@ -46,7 +42,6 @@ public class NullPointerException extends RuntimeException { * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public NullPointerException(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/lang/Number.java b/libcore/luni/src/main/java/java/lang/Number.java index dc868cb10..4fb559652 100644 --- a/libcore/luni/src/main/java/java/lang/Number.java +++ b/libcore/luni/src/main/java/java/lang/Number.java @@ -22,8 +22,6 @@ package java.lang; * The abstract superclass of the classes which represent numeric base types * (that is {@link Byte}, {@link Short}, {@link Integer}, {@link Long}, * {@link Float}, and {@link Double}. - * - * @since Android 1.0 */ public abstract class Number implements java.io.Serializable { @@ -31,8 +29,6 @@ public abstract class Number implements java.io.Serializable { /** * Empty default constructor. - * - * @since Android 1.0 */ public Number() { } @@ -42,7 +38,6 @@ public abstract class Number implements java.io.Serializable { * truncating the value, so it fits into a byte. * * @return the primitive byte value of this object. - * @since Android 1.0 */ public byte byteValue() { return (byte) intValue(); @@ -52,7 +47,6 @@ public abstract class Number implements java.io.Serializable { * Returns this object's value as a double. Might involve rounding. * * @return the primitive double value of this object. - * @since Android 1.0 */ public abstract double doubleValue(); @@ -60,7 +54,6 @@ public abstract class Number implements java.io.Serializable { * Returns this object's value as a float. Might involve rounding. * * @return the primitive float value of this object. - * @since Android 1.0 */ public abstract float floatValue(); @@ -69,7 +62,6 @@ public abstract class Number implements java.io.Serializable { * truncating the value, so it fits into an int. * * @return the primitive int value of this object. - * @since Android 1.0 */ public abstract int intValue(); @@ -78,7 +70,6 @@ public abstract class Number implements java.io.Serializable { * truncating the value, so it fits into a long. * * @return the primitive long value of this object. - * @since Android 1.0 */ public abstract long longValue(); @@ -87,7 +78,6 @@ public abstract class Number implements java.io.Serializable { * truncating the value, so it fits into a short. * * @return the primitive short value of this object. - * @since Android 1.0 */ public short shortValue() { return (short) intValue(); diff --git a/libcore/luni/src/main/java/java/lang/NumberFormatException.java b/libcore/luni/src/main/java/java/lang/NumberFormatException.java index ed5433d9d..9d2f0c7e6 100644 --- a/libcore/luni/src/main/java/java/lang/NumberFormatException.java +++ b/libcore/luni/src/main/java/java/lang/NumberFormatException.java @@ -20,8 +20,6 @@ package java.lang; /** * Thrown when an invalid value is passed to a string-to-number conversion * method. - * - * @since Android 1.0 */ public class NumberFormatException extends java.lang.IllegalArgumentException { @@ -30,8 +28,6 @@ public class NumberFormatException extends java.lang.IllegalArgumentException { /** * Constructs a new {@code NumberFormatException} that includes the current * stack trace. - * - * @since Android 1.0 */ public NumberFormatException() { super(); @@ -43,7 +39,6 @@ public class NumberFormatException extends java.lang.IllegalArgumentException { * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public NumberFormatException(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/lang/OutOfMemoryError.java b/libcore/luni/src/main/java/java/lang/OutOfMemoryError.java index 2c54ad176..a8f8a2cff 100644 --- a/libcore/luni/src/main/java/java/lang/OutOfMemoryError.java +++ b/libcore/luni/src/main/java/java/lang/OutOfMemoryError.java @@ -21,8 +21,6 @@ package java.lang; * Thrown when a request for memory is made that can not be satisfied using the * available platform resources. Such a request may be made by both the running * application or by an internal function of the virtual machine. - * - * @since Android 1.0 */ public class OutOfMemoryError extends java.lang.VirtualMachineError { @@ -31,8 +29,6 @@ public class OutOfMemoryError extends java.lang.VirtualMachineError { /** * Constructs a new {@code OutOfMemoryError} that includes the current stack * trace. - * - * @since Android 1.0 */ public OutOfMemoryError() { super(); @@ -44,7 +40,6 @@ public class OutOfMemoryError extends java.lang.VirtualMachineError { * * @param detailMessage * the detail message for this error. - * @since Android 1.0 */ public OutOfMemoryError(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/lang/Override.java b/libcore/luni/src/main/java/java/lang/Override.java index 864357d78..c4049cb55 100644 --- a/libcore/luni/src/main/java/java/lang/Override.java +++ b/libcore/luni/src/main/java/java/lang/Override.java @@ -1,12 +1,12 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,19 +16,19 @@ package java.lang; -import java.lang.annotation.Target; +import java.lang.annotation.ElementType; import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** - * Annotation type used to mark methods that override a method declaration in a superclass. - * Compilers produce an error if a method annotated with @Override does not actually override - * a method in a superclass. - * - * @since Android 1.0 + * Annotation type used to mark methods that override a method declaration in a + * superclass. Compilers produce an error if a method annotated with @Override + * does not actually override a method in a superclass. + * + * @since 1.5 */ -@Retention(value=java.lang.annotation.RetentionPolicy.SOURCE) -@Target(value=java.lang.annotation.ElementType.METHOD) -public @interface Override -{ - +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.SOURCE) +public @interface Override { } diff --git a/libcore/luni/src/main/java/java/lang/Process.java b/libcore/luni/src/main/java/java/lang/Process.java index 5fe8be7fb..c030fcc97 100644 --- a/libcore/luni/src/main/java/java/lang/Process.java +++ b/libcore/luni/src/main/java/java/lang/Process.java @@ -17,25 +17,20 @@ package java.lang; - import java.io.InputStream; import java.io.OutputStream; /** * Represents an external process. Enables writing to, reading from, destroying, * and waiting for the external process, as well as querying its exit value. - * + * * @see Runtime#exec * @see ProcessBuilder#start() - * - * @since Android 1.0 */ public abstract class Process { /** * Terminates this process and closes any associated streams. - * - * @since Android 1.0 */ abstract public void destroy(); @@ -46,7 +41,6 @@ public abstract class Process { * @return the exit value of this process. * @throws IllegalThreadStateException * if this process has not terminated. - * @since Android 1.0 */ abstract public int exitValue(); @@ -56,7 +50,6 @@ public abstract class Process { * * @return the input stream to read from the error stream associated with * the native process. - * @since Android 1.0 */ abstract public InputStream getErrorStream(); @@ -66,7 +59,6 @@ public abstract class Process { * * @return the input stream to read from the output stream associated with * the native process. - * @since Android 1.0 */ abstract public InputStream getInputStream(); @@ -76,7 +68,6 @@ public abstract class Process { * * @return the output stream to write to the input stream associated with * the native process. - * @since Android 1.0 */ abstract public OutputStream getOutputStream(); @@ -87,7 +78,6 @@ public abstract class Process { * @return the exit value of the native process being waited on. * @throws InterruptedException * if the calling thread is interrupted. - * @since Android 1.0 */ abstract public int waitFor() throws InterruptedException; } diff --git a/libcore/luni/src/main/java/java/lang/ProcessBuilder.java b/libcore/luni/src/main/java/java/lang/ProcessBuilder.java index 9d6b90bc7..f649fec9d 100644 --- a/libcore/luni/src/main/java/java/lang/ProcessBuilder.java +++ b/libcore/luni/src/main/java/java/lang/ProcessBuilder.java @@ -24,8 +24,8 @@ import java.util.Map; /** * Creates operating system processes. - * - * @since Android 1.0 + * + * @since 1.5 */ public final class ProcessBuilder { @@ -40,10 +40,9 @@ public final class ProcessBuilder { /** * Constructs a new {@code ProcessBuilder} instance with the specified * operating system program and its arguments. - * + * * @param command * the requested operating system program and its arguments. - * @since Android 1.0 */ public ProcessBuilder(String... command) { this(toList(command)); @@ -54,12 +53,11 @@ public final class ProcessBuilder { * operating system program and its arguments. Note that the list passed to * this constructor is not copied, so any subsequent updates to it are * reflected in this instance's state. - * + * * @param command * the requested operating system program and its arguments. * @throws NullPointerException * if {@code command} is {@code null}. - * @since Android 1.0 */ public ProcessBuilder(List command) { super(); @@ -76,9 +74,8 @@ public final class ProcessBuilder { * Returns this process builder's current program and arguments. Note that * the returned list is not a copy and modifications to it will change the * state of this instance. - * + * * @return this process builder's program and arguments. - * @since Android 1.0 */ public List command() { return command; @@ -86,11 +83,10 @@ public final class ProcessBuilder { /** * Changes the program and arguments of this process builder. - * + * * @param command * the new operating system program and its arguments. * @return this process builder instance. - * @since Android 1.0 */ public ProcessBuilder command(String... command) { return command(toList(command)); @@ -100,13 +96,12 @@ public final class ProcessBuilder { * Changes the program and arguments of this process builder. Note that the * list passed to this method is not copied, so any subsequent updates to it * are reflected in this instance's state. - * + * * @param command * the new operating system program and its arguments. * @return this process builder instance. * @throws NullPointerException * if {@code command} is {@code null}. - * @since Android 1.0 */ public ProcessBuilder command(List command) { if (command == null) { @@ -120,9 +115,8 @@ public final class ProcessBuilder { * Returns the working directory of this process builder. If {@code null} is * returned, then the working directory of the Java process is used when a * process is started. - * + * * @return the current working directory, may be {@code null}. - * @since Android 1.0 */ public File directory() { return directory; @@ -132,11 +126,10 @@ public final class ProcessBuilder { * Changes the working directory of this process builder. If the specified * directory is {@code null}, then the working directory of the Java * process is used when a process is started. - * + * * @param directory * the new working directory for this process builder. * @return this process builder instance. - * @since Android 1.0 */ public ProcessBuilder directory(File directory) { this.directory = directory; @@ -149,9 +142,8 @@ public final class ProcessBuilder { * the environment, as returned by {@link System#getenv()}. Note that the * map returned by this method is not a copy and any changes made to it are * reflected in this instance's state. - * + * * @return the map containing this process builder's environment variables. - * @since Android 1.0 */ public Map environment() { return environment; @@ -162,10 +154,9 @@ public final class ProcessBuilder { * output. If redirected, the {@link Process#getErrorStream()} will always * return end of stream and standard error is written to * {@link Process#getInputStream()}. - * + * * @return {@code true} if the standard error is redirected; {@code false} * otherwise. - * @since Android 1.0 */ public boolean redirectErrorStream() { return redirectErrorStream; @@ -174,12 +165,11 @@ public final class ProcessBuilder { /** * Changes the state of whether or not standard error is redirected to * standard output. - * + * * @param redirectErrorStream * {@code true} to redirect standard error, {@code false} * otherwise. * @return this process builder instance. - * @since Android 1.0 */ public ProcessBuilder redirectErrorStream(boolean redirectErrorStream) { this.redirectErrorStream = redirectErrorStream; @@ -188,7 +178,7 @@ public final class ProcessBuilder { /** * Starts a new process based on the current state of this process builder. - * + * * @return the new {@code Process} instance. * @throws NullPointerException * if any of the elements of {@link #command()} is {@code null}. @@ -199,7 +189,6 @@ public final class ProcessBuilder { * process creation. * @throws IOException * if an I/O error happens. - * @since Android 1.0 */ public Process start() throws IOException { if (command.isEmpty()) { diff --git a/libcore/luni/src/main/java/java/lang/Readable.java b/libcore/luni/src/main/java/java/lang/Readable.java index 1eca6cc0a..b536f5985 100644 --- a/libcore/luni/src/main/java/java/lang/Readable.java +++ b/libcore/luni/src/main/java/java/lang/Readable.java @@ -22,8 +22,6 @@ import java.nio.CharBuffer; /** * Represents a sequence of characters that can be incrementally read (copied) * into a {@link CharBuffer}. - * - * @since Android 1.0 */ public interface Readable { @@ -37,7 +35,6 @@ public interface Readable { * {@code Readable} reaches its end * @throws IOException * if an I/O error occurs. - * @since Android 1.0 */ int read(CharBuffer cb) throws IOException; } diff --git a/libcore/luni/src/main/java/java/lang/Runnable.java b/libcore/luni/src/main/java/java/lang/Runnable.java index 2cca717c2..b09d91442 100644 --- a/libcore/luni/src/main/java/java/lang/Runnable.java +++ b/libcore/luni/src/main/java/java/lang/Runnable.java @@ -21,8 +21,6 @@ package java.lang; /** * Represents a command that can be executed. Often used to run code in a * different {@link Thread}. - * - * @since Android 1.0 */ public interface Runnable { @@ -30,8 +28,6 @@ public interface Runnable { * Starts executing the active part of the class' code. This method is * called when a thread is started that has been created with a class which * implements {@code Runnable}. - * - * @since Android 1.0 */ public void run(); } diff --git a/libcore/luni/src/main/java/java/lang/RuntimeException.java b/libcore/luni/src/main/java/java/lang/RuntimeException.java index e519bf17d..9229b0c64 100644 --- a/libcore/luni/src/main/java/java/lang/RuntimeException.java +++ b/libcore/luni/src/main/java/java/lang/RuntimeException.java @@ -23,8 +23,6 @@ package java.lang; * the virtual machine. Unlike checked exceptions (exceptions where the type * doesn't extend {@code RuntimeException} or {@link Error}), the compiler does * not require code to handle runtime exceptions. - * - * @since Android 1.0 */ public class RuntimeException extends Exception { @@ -33,8 +31,6 @@ public class RuntimeException extends Exception { /** * Constructs a new {@code RuntimeException} that includes the current stack * trace. - * - * @since Android 1.0 */ public RuntimeException() { super(); @@ -46,7 +42,6 @@ public class RuntimeException extends Exception { * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public RuntimeException(String detailMessage) { super(detailMessage); @@ -60,7 +55,6 @@ public class RuntimeException extends Exception { * the detail message for this exception. * @param throwable * the cause of this exception. - * @since Android 1.0 */ public RuntimeException(String detailMessage, Throwable throwable) { super(detailMessage, throwable); @@ -72,7 +66,6 @@ public class RuntimeException extends Exception { * * @param throwable * the cause of this exception. - * @since Android 1.0 */ public RuntimeException(Throwable throwable) { super(throwable); diff --git a/libcore/luni/src/main/java/java/lang/RuntimePermission.java b/libcore/luni/src/main/java/java/lang/RuntimePermission.java index 0f856a0f0..ba7e66554 100644 --- a/libcore/luni/src/main/java/java/lang/RuntimePermission.java +++ b/libcore/luni/src/main/java/java/lang/RuntimePermission.java @@ -23,8 +23,6 @@ import java.security.BasicPermission; * Represents the permission to execute a runtime-related function. There is no * action list associated with a {@code RuntimePermission}; the user either has * the permission or he doesn't. - * - * @since Android 1.0 */ public final class RuntimePermission extends BasicPermission { @@ -83,7 +81,6 @@ public final class RuntimePermission extends BasicPermission { * * @param permissionName * the name of the new permission. - * @since Android 1.0 */ public RuntimePermission(String permissionName) { super(permissionName); @@ -97,7 +94,6 @@ public final class RuntimePermission extends BasicPermission { * the name of the new permission. * @param actions * ignored. - * @since Android 1.0 */ public RuntimePermission(String name, String actions) { super(name, actions); diff --git a/libcore/luni/src/main/java/java/lang/SecurityException.java b/libcore/luni/src/main/java/java/lang/SecurityException.java index 738708804..c977be05e 100644 --- a/libcore/luni/src/main/java/java/lang/SecurityException.java +++ b/libcore/luni/src/main/java/java/lang/SecurityException.java @@ -19,8 +19,6 @@ package java.lang; /** * Thrown when a security manager check fails. - * - * @since Android 1.0 */ public class SecurityException extends java.lang.RuntimeException { @@ -29,8 +27,6 @@ public class SecurityException extends java.lang.RuntimeException { /** * Constructs a new {@code SecurityException} that includes the current * stack trace. - * - * @since Android 1.0 */ public SecurityException() { super(); @@ -42,7 +38,6 @@ public class SecurityException extends java.lang.RuntimeException { * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public SecurityException(String detailMessage) { super(detailMessage); @@ -56,7 +51,7 @@ public class SecurityException extends java.lang.RuntimeException { * the detail message for this exception. * @param cause * the optional cause of this exception, may be {@code null}. - * @since Android 1.0 + * @since 1.5 */ public SecurityException(String message, Throwable cause) { super(message, cause); @@ -68,7 +63,7 @@ public class SecurityException extends java.lang.RuntimeException { * * @param cause * the optional cause of this exception, may be {@code null}. - * @since Android 1.0 + * @since 1.5 */ public SecurityException(Throwable cause) { super((cause == null ? null : cause.toString()), cause); diff --git a/libcore/luni/src/main/java/java/lang/SecurityManager.java b/libcore/luni/src/main/java/java/lang/SecurityManager.java index dc624e37c..9125850f7 100644 --- a/libcore/luni/src/main/java/java/lang/SecurityManager.java +++ b/libcore/luni/src/main/java/java/lang/SecurityManager.java @@ -49,8 +49,6 @@ import org.apache.harmony.luni.util.PriviAction; * operation is not allowed, then they throw a {@link SecurityException}. The * only exception is {@link #checkTopLevelWindow(Object)}, which returns a * boolean to indicate permission. - * - * @since Android 1.0 */ public class SecurityManager { @@ -63,9 +61,8 @@ public class SecurityManager { /** * Flag to indicate whether a security check is in progress. - * + * * @deprecated Use {@link #checkPermission} - * @since Android 1.0 */ @Deprecated protected boolean inCheck; @@ -75,9 +72,6 @@ public class SecurityManager { *

          * The {@code RuntimePermission("createSecurityManager")} is checked if a * security manager is installed. - *

          - * - * @since Android 1.0 */ public SecurityManager() { SecurityManager security = System.getSecurityManager(); @@ -94,7 +88,7 @@ public class SecurityManager { /** * Checks whether the calling thread is allowed to accept socket * connections. - * + * * @param host * the address of the host that attempts to connect. * @param port @@ -104,7 +98,6 @@ public class SecurityManager { * @throws SecurityException * if the calling thread is not allowed to accept socket * connections from {@code host} through {@code port}. - * @since Android 1.0 */ public void checkAccept(String host, int port) { if (host == null) { @@ -116,12 +109,11 @@ public class SecurityManager { /** * Checks whether the calling thread is allowed to modify the specified * thread. - * + * * @param thread * the thread to access. * @throws SecurityException * if the calling thread is not allowed to access {@code thread}. - * @since Android 1.0 */ public void checkAccess(Thread thread) { // Only worry about system threads. Dead threads have a null group. @@ -134,14 +126,13 @@ public class SecurityManager { /** * Checks whether the calling thread is allowed to modify the specified * thread group. - * + * * @param group * the thread group to access. * @throws NullPointerException * if {@code group} is {@code null}. * @throws SecurityException * if the calling thread is not allowed to access {@code group}. - * @since Android 1.0 */ public void checkAccess(ThreadGroup group) { // Only worry about system threads. @@ -157,7 +148,7 @@ public class SecurityManager { * Checks whether the calling thread is allowed to establish socket * connections. A -1 port indicates the caller is trying to resolve the * hostname. - * + * * @param host * the address of the host to connect to. * @param port @@ -167,7 +158,6 @@ public class SecurityManager { * @throws SecurityException * if the calling thread is not allowed to connect to {@code * host} through {@code port}. - * @since Android 1.0 */ public void checkConnect(String host, int port) { if (host == null) { @@ -184,7 +174,7 @@ public class SecurityManager { * Checks whether the specified security context is allowed to establish * socket connections. A -1 port indicates the caller is trying to resolve * the hostname. - * + * * @param host * the address of the host to connect to. * @param port @@ -196,7 +186,6 @@ public class SecurityManager { * @throws SecurityException * if {@code context} is not allowed to connect to {@code host} * through {@code port}. - * @since Android 1.0 */ public void checkConnect(String host, int port, Object context) { // BEGIN android-added @@ -214,11 +203,10 @@ public class SecurityManager { /** * Checks whether the calling thread is allowed to create a class loader. - * + * * @throws SecurityException * if the calling thread is not allowed to create a class * loader. - * @since Android 1.0 */ public void checkCreateClassLoader() { checkPermission(RuntimePermission.permissionToCreateClassLoader); @@ -227,12 +215,11 @@ public class SecurityManager { /** * Checks whether the calling thread is allowed to delete the file with the * specified name, which should be passed in canonical form. - * + * * @param file * the name of the file to delete. * @throws SecurityException * if the calling thread is not allowed to delete {@code file}. - * @since Android 1.0 */ public void checkDelete(String file) { checkPermission(new FilePermission(file, "delete")); //$NON-NLS-1$ @@ -241,12 +228,11 @@ public class SecurityManager { /** * Checks whether the calling thread is allowed to execute the specified * platform specific command. - * + * * @param cmd * the command line to execute. * @throws SecurityException * if the calling thread is not allowed to execute {@code cmd}. - * @since Android 1.0 */ public void checkExec(String cmd) { checkPermission(new FilePermission(new File(cmd).isAbsolute() ? cmd @@ -256,14 +242,13 @@ public class SecurityManager { /** * Checks whether the calling thread is allowed to terminate the virtual * machine. - * + * * @param status * the status that the virtual machine returns when it is * terminated. * @throws SecurityException * if the calling thread is not allowed to terminate the virtual * machine with {@code status}. - * @since Android 1.0 */ public void checkExit(int status) { checkPermission(RuntimePermission.permissionToExitVM); @@ -272,12 +257,11 @@ public class SecurityManager { /** * Checks whether the calling thread is allowed to load the specified native * library. - * + * * @param libName * the name of the library to load. * @throws SecurityException * if the calling thread is not allowed to load {@code libName}. - * @since Android 1.0 */ public void checkLink(String libName) { if (libName == null) { @@ -289,12 +273,11 @@ public class SecurityManager { /** * Checks whether the calling thread is allowed to listen on the specified * port. - * + * * @param port * the port number to check. * @throws SecurityException * if the calling thread is not allowed listen on {@code port}. - * @since Android 1.0 */ public void checkListen(int port) { if (port == 0) { @@ -312,7 +295,7 @@ public class SecurityManager { * reflect API). Due to the nature of the check, overriding implementations * cannot call {@code super.checkMemberAccess()} since the stack would no * longer be of the expected shape. - * + * * @param cls * the class of which members are accessed. * @param type @@ -322,7 +305,6 @@ public class SecurityManager { * @throws SecurityException * if the calling thread is not allowed to access members of * {@code cls}. - * @since Android 1.0 */ public void checkMemberAccess(Class cls, int type) { if (cls == null) { @@ -352,12 +334,11 @@ public class SecurityManager { /** * Checks whether the calling thread is allowed to use the specified IP * multicast group address. - * + * * @param maddr * the internet group address to use. * @throws SecurityException * if the calling thread is not allowed to use {@code maddr}. - * @since Android 1.0 */ public void checkMulticast(InetAddress maddr) { checkPermission(new SocketPermission(maddr.getHostAddress(), @@ -367,7 +348,7 @@ public class SecurityManager { /** * Checks whether the calling thread is allowed to use the specified IP * multicast group address. - * + * * @param maddr * the internet group address to use. * @param ttl @@ -376,7 +357,6 @@ public class SecurityManager { * @throws SecurityException * if the calling thread is not allowed to use {@code maddr}. * @deprecated use {@link #checkMulticast(java.net.InetAddress)} - * @since Android 1.0 */ @Deprecated public void checkMulticast(InetAddress maddr, byte ttl) { @@ -387,13 +367,12 @@ public class SecurityManager { /** * Checks whether the calling thread is allowed to access the specified * package. - * + * * @param packageName * the name of the package to access. * @throws SecurityException * if the calling thread is not allowed to access {@code * packageName}. - * @since Android 1.0 */ public void checkPackageAccess(String packageName) { if (packageName == null) { @@ -408,13 +387,12 @@ public class SecurityManager { /** * Checks whether the calling thread is allowed to define new classes in the * specified package. - * + * * @param packageName * the name of the package to add a class to. * @throws SecurityException * if the calling thread is not allowed to add classes to * {@code packageName}. - * @since Android 1.0 */ public void checkPackageDefinition(String packageName) { if (packageName == null) { @@ -459,11 +437,10 @@ public class SecurityManager { /** * Checks whether the calling thread is allowed to access the system * properties. - * + * * @throws SecurityException * if the calling thread is not allowed to access system * properties. - * @since Android 1.0 */ public void checkPropertiesAccess() { checkPermission(READ_WRITE_ALL_PROPERTIES_PERMISSION); @@ -472,13 +449,12 @@ public class SecurityManager { /** * Checks whether the calling thread is allowed to access a particular * system property. - * + * * @param key * the name of the property to access. * @throws SecurityException * if the calling thread is not allowed to access the {@code * key} system property. - * @since Android 1.0 */ public void checkPropertyAccess(String key) { checkPermission(new PropertyPermission(key, "read")); //$NON-NLS-1$ @@ -487,12 +463,11 @@ public class SecurityManager { /** * Checks whether the calling thread is allowed to read from the file with * the specified file descriptor. - * + * * @param fd * the file descriptor of the file to read from. * @throws SecurityException * if the calling thread is not allowed to read from {@code fd}. - * @since Android 1.0 */ public void checkRead(FileDescriptor fd) { if (fd == null) { @@ -504,13 +479,12 @@ public class SecurityManager { /** * Checks whether the calling thread is allowed to read from the file with * the specified name, which should be passed in canonical form. - * + * * @param file * the name of the file or directory to read from. * @throws SecurityException * if the calling thread is not allowed to read from {@code * file}. - * @since Android 1.0 */ public void checkRead(String file) { checkPermission(new FilePermission(file, "read")); //$NON-NLS-1$ @@ -519,14 +493,13 @@ public class SecurityManager { /** * Checks whether the given security context is allowed to read from the * file named by the argument, which should be passed in canonical form. - * + * * @param file * the name of the file or directory to check. * @param context * the security context to use for the check. * @throws SecurityException * if {@code context} is not allowed to read from {@code file}. - * @since Android 1.0 */ public void checkRead(String file, Object context) { checkPermission(new FilePermission(file, "read"), context); //$NON-NLS-1$ @@ -535,13 +508,12 @@ public class SecurityManager { /** * Checks whether the calling thread is allowed to perform the security * operation named by the target. - * + * * @param target * the name of the operation to perform. * @throws SecurityException * if the calling thread is not allowed to perform * {@code target}. - * @since Android 1.0 */ public void checkSecurityAccess(String target) { checkPermission(new SecurityPermission(target)); @@ -550,11 +522,10 @@ public class SecurityManager { /** * Checks whether the calling thread is allowed to set the net object * factories. - * + * * @throws SecurityException * if the calling thread is not allowed to set the net object * factories. - * @since Android 1.0 */ public void checkSetFactory() { checkPermission(RuntimePermission.permissionToSetFactory); @@ -563,14 +534,13 @@ public class SecurityManager { /** * Checks whether the calling thread is trusted to show the specified top * level window. - * + * * @param window * the window to show. * @return {@code true} if the calling thread is allowed to show {@code * window}; {@code false} otherwise. * @throws NullPointerException * if {@code window} is {@code null}. - * @since Android 1.0 */ public boolean checkTopLevelWindow(Object window) { if (window == null) { @@ -597,11 +567,10 @@ public class SecurityManager { /** * Checks whether the calling thread is allowed to access the system * clipboard. - * + * * @throws SecurityException * if the calling thread is not allowed to access the system * clipboard. - * @since Android 1.0 */ public void checkSystemClipboardAccess() { try { @@ -623,11 +592,10 @@ public class SecurityManager { /** * Checks whether the calling thread is allowed to access the AWT event * queue. - * + * * @throws SecurityException * if the calling thread is not allowed to access the AWT event * queue. - * @since Android 1.0 */ public void checkAwtEventQueueAccess() { try { @@ -648,11 +616,10 @@ public class SecurityManager { /** * Checks whether the calling thread is allowed to start a new print job. - * + * * @throws SecurityException * if the calling thread is not allowed to start a new print * job. - * @since Android 1.0 */ public void checkPrintJobAccess() { checkPermission(RuntimePermission.permissionToQueuePrintJob); @@ -661,12 +628,11 @@ public class SecurityManager { /** * Checks whether the calling thread is allowed to write to the file with * the specified file descriptor. - * + * * @param fd * the file descriptor of the file to write to. * @throws SecurityException * if the calling thread is not allowed to write to {@code fd}. - * @since Android 1.0 */ public void checkWrite(FileDescriptor fd) { if (fd == null) { @@ -678,13 +644,12 @@ public class SecurityManager { /** * Checks whether the calling thread is allowed to write to the file with * the specified name, which should be passed in canonical form. - * + * * @param file * the name of the file or directory to write to. * @throws SecurityException - * if the calling thread is not allowed to write to + * if the calling thread is not allowed to write to * {@code file}. - * @since Android 1.0 */ public void checkWrite(String file) { checkPermission(new FilePermission(file, "write")); //$NON-NLS-1$ @@ -692,11 +657,10 @@ public class SecurityManager { /** * Indicates if this security manager is currently checking something. - * + * * @return {@code true} if this security manager is executing a security * check method; {@code false} otherwise. * @deprecated Use {@link #checkPermission}. - * @since Android 1.0 */ @Deprecated public boolean getInCheck() { @@ -707,9 +671,8 @@ public class SecurityManager { * Returns an array containing one entry for each method in the current * execution stack. Each entry is the {@code java.lang.Class} which * represents the class in which the method is defined. - * + * * @return all classes in the execution stack. - * @since Android 1.0 */ @SuppressWarnings("unchecked") protected Class[] getClassContext() { @@ -719,10 +682,9 @@ public class SecurityManager { /** * Returns the class loader of the first class in the execution stack whose * class loader is not a system class loader. - * + * * @return the most recent non-system class loader. * @deprecated Use {@link #checkPermission}. - * @since Android 1.0 */ @Deprecated protected ClassLoader currentClassLoader() { @@ -755,11 +717,10 @@ public class SecurityManager { /** * Returns the index in the call stack of the first class whose class loader * is not a system class loader. - * + * * @return the frame index of the first method whose class was loaded by a * non-system class loader. * @deprecated Use {@link #checkPermission}. - * @since Android 1.0 */ @Deprecated protected int classLoaderDepth() { @@ -791,10 +752,9 @@ public class SecurityManager { /** * Returns the first class in the call stack that was loaded by a class * loader which is not a system class loader. - * + * * @return the most recent class loaded by a non-system class loader. * @deprecated Use {@link #checkPermission}. - * @since Android 1.0 */ @Deprecated protected Class currentLoadedClass() { @@ -827,13 +787,12 @@ public class SecurityManager { * Returns the index in the call stack of the first method which is * contained in the class with the specified name. Returns -1 if no methods * from this class are in the stack. - * + * * @param name * the name of the class to look for. * @return the frame index of the first method found is contained in the * class identified by {@code name}. * @deprecated Use {@link #checkPermission}. - * @since Android 1.0 */ @Deprecated protected int classDepth(String name) { @@ -849,13 +808,12 @@ public class SecurityManager { /** * Indicates whether there is a method in the call stack from the class with * the specified name. - * + * * @param name * the name of the class to look for. * @return {@code true} if a method from the class identified by {@code * name} is executing; {@code false} otherwise. * @deprecated Use {@link #checkPermission}. - * @since Android 1.0 */ @Deprecated protected boolean inClass(String name) { @@ -865,11 +823,10 @@ public class SecurityManager { /** * Indicates whether there is a method in the call stack from a class which * was defined by a non-system class loader. - * + * * @return {@code true} if a method from a class that was defined by a * non-system class loader is executing; {@code false} otherwise. * @deprecated Use {@link #checkPermission} - * @since Android 1.0 */ @Deprecated protected boolean inClassLoader() { @@ -880,9 +837,8 @@ public class SecurityManager { * Returns the thread group which should be used to instantiate new threads. * By default, this is the same as the thread group of the thread running * this method. - * + * * @return ThreadGroup the thread group to create new threads in. - * @since Android 1.0 */ public ThreadGroup getThreadGroup() { return Thread.currentThread().getThreadGroup(); @@ -890,12 +846,11 @@ public class SecurityManager { /** * Returns an object which encapsulates the security state of the current - * point in the execution. In the Android reference implementation, this is - * an {@link java.security.AccessControlContext}. - * + * point in the execution. In our case, this is an {@link + * java.security.AccessControlContext}. + * * @return an object that encapsulates information about the current * execution environment. - * @since Android 1.0 */ public Object getSecurityContext() { return AccessController.getContext(); @@ -904,13 +859,12 @@ public class SecurityManager { /** * Checks whether the calling thread is allowed to access the resource being * guarded by the specified permission object. - * + * * @param permission * the permission to check. * @throws SecurityException * if the requested {@code permission} is denied according to * the current security policy. - * @since Android 1.0 */ public void checkPermission(Permission permission) { try { @@ -924,7 +878,7 @@ public class SecurityManager { /** * Checks whether the specified security context is allowed to access the * resource being guarded by the specified permission object. - * + * * @param permission * the permission to check. * @param context @@ -934,7 +888,6 @@ public class SecurityManager { * AccessControlContext} or if the requested {@code permission} * is denied for {@code context} according to the current * security policy. - * @since Android 1.0 */ public void checkPermission(Permission permission, Object context) { try { diff --git a/libcore/luni/src/main/java/java/lang/Short.java b/libcore/luni/src/main/java/java/lang/Short.java index cf95d2219..9baf3a8cd 100644 --- a/libcore/luni/src/main/java/java/lang/Short.java +++ b/libcore/luni/src/main/java/java/lang/Short.java @@ -21,11 +21,11 @@ package java.lang; * The wrapper for the primitive type {@code short}. * * @see java.lang.Number - * @since Android 1.0 + * @since 1.1 */ public final class Short extends Number implements Comparable { - private static final long serialVersionUID = 7515723908773894738L; + private static final long serialVersionUID = 7515723908773894738L; /** * The value which the receiver represents. @@ -34,69 +34,61 @@ public final class Short extends Number implements Comparable { /** * Constant for the maximum {@code short} value, 215-1. - * - * @since Android 1.0 */ public static final short MAX_VALUE = (short) 0x7FFF; /** * Constant for the minimum {@code short} value, -215. - * - * @since Android 1.0 */ public static final short MIN_VALUE = (short) 0x8000; /** * Constant for the number of bits needed to represent a {@code short} in * two's complement form. - * - * @since Android 1.0 + * + * @since 1.5 */ public static final int SIZE = 16; /** - * The {@link Class} object that represents the primitive type {@code + * The {@link Class} object that represents the primitive type {@code * short}. - * - * @since Android 1.0 */ @SuppressWarnings("unchecked") public static final Class TYPE = (Class) new short[0] .getClass().getComponentType(); - // Note: This can't be set to "short.class", since *that* is - // defined to be "java.lang.Short.TYPE"; + // Note: This can't be set to "short.class", since *that* is + // defined to be "java.lang.Short.TYPE"; /** * Constructs a new {@code Short} from the specified string. - * + * * @param string * the string representation of a short value. * @throws NumberFormatException * if {@code string} can not be decoded into a short value. * @see #parseShort(String) - * @since Android 1.0 */ - public Short(String string) throws NumberFormatException { - this(parseShort(string)); - } + public Short(String string) throws NumberFormatException { + this(parseShort(string)); + } /** * Constructs a new {@code Short} with the specified primitive short value. - * + * * @param value * the primitive short value to store in the new instance. - * @since Android 1.0 */ - public Short(short value) { - this.value = value; - } + public Short(short value) { + this.value = value; + } - @Override + @Override public byte byteValue() { - return (byte) value; - } + return (byte) value; + } /** * Compares this object to the specified short object to determine their @@ -108,98 +100,97 @@ public final class Short extends Number implements Comparable { * value of {@code object}; 0 if the value of this short and the * value of {@code object} are equal; a positive value if the value * of this short is greater than the value of {@code object}. + * @throws NullPointerException + * if {@code object} is null. * @see java.lang.Comparable - * @since Android 1.0 + * @since 1.2 */ - public int compareTo(Short object) { - return value > object.value ? 1 : (value < object.value ? -1 : 0); - } + public int compareTo(Short object) { + return value > object.value ? 1 : (value < object.value ? -1 : 0); + } /** * Parses the specified string and returns a {@code Short} instance if the * string can be decoded into a short value. The string may be an optional * minus sign "-" followed by a hexadecimal ("0x..." or "#..."), octal * ("0..."), or decimal ("...") representation of a short. - * + * * @param string * a string representation of a short value. * @return a {@code Short} containing the value represented by * {@code string}. * @throws NumberFormatException * if {@code string} can not be parsed as a short value. - * @since Android 1.0 */ - public static Short decode(String string) throws NumberFormatException { - int intValue = Integer.decode(string).intValue(); - short result = (short) intValue; - if (result == intValue) { + public static Short decode(String string) throws NumberFormatException { + int intValue = Integer.decode(string).intValue(); + short result = (short) intValue; + if (result == intValue) { return valueOf(result); } - throw new NumberFormatException(); - } + throw new NumberFormatException(); + } - @Override + @Override public double doubleValue() { - return value; - } + return value; + } /** * Compares this instance with the specified object and indicates if they * are equal. In order to be equal, {@code object} must be an instance of * {@code Short} and have the same short value as this object. - * + * * @param object * the object to compare this short with. * @return {@code true} if the specified object is equal to this * {@code Short}; {@code false} otherwise. - * @since Android 1.0 */ - @Override + @Override public boolean equals(Object object) { - return (object instanceof Short) - && (value == ((Short) object).value); - } + return (object instanceof Short) + && (value == ((Short) object).value); + } - @Override + @Override public float floatValue() { - return value; - } + return value; + } - @Override + @Override public int hashCode() { - return value; - } + return value; + } - @Override + @Override public int intValue() { - return value; - } + return value; + } - @Override + @Override public long longValue() { - return value; - } + return value; + } /** * Parses the specified string as a signed decimal short value. The ASCII * character \u002d ('-') is recognized as the minus sign. - * + * * @param string * the string representation of a short value. * @return the primitive short value represented by {@code string}. * @throws NumberFormatException * if {@code string} is {@code null}, has a length of zero or * can not be parsed as a short value. - * @since Android 1.0 */ - public static short parseShort(String string) throws NumberFormatException { - return parseShort(string, 10); - } + public static short parseShort(String string) throws NumberFormatException { + return parseShort(string, 10); + } /** * Parses the specified string as a signed short value using the specified * radix. The ASCII character \u002d ('-') is recognized as the minus sign. - * + * * @param string * the string representation of a short value. * @param radix @@ -211,50 +202,47 @@ public final class Short extends Number implements Comparable { * {@code radix < Character.MIN_RADIX}, * {@code radix > Character.MAX_RADIX}, or if {@code string} * can not be parsed as a short value. - * @since Android 1.0 */ - public static short parseShort(String string, int radix) - throws NumberFormatException { - int intValue = Integer.parseInt(string, radix); - short result = (short) intValue; - if (result == intValue) { + public static short parseShort(String string, int radix) + throws NumberFormatException { + int intValue = Integer.parseInt(string, radix); + short result = (short) intValue; + if (result == intValue) { return result; } - throw new NumberFormatException(); - } + throw new NumberFormatException(); + } /** * Gets the primitive value of this short. - * + * * @return this object's primitive value. - * @since Android 1.0 */ @Override public short shortValue() { - return value; - } + return value; + } - @Override + @Override public String toString() { - return Integer.toString(value); - } + return Integer.toString(value); + } /** * Returns a string containing a concise, human-readable description of the * specified short value with radix 10. - * + * * @param value * the short to convert to a string. * @return a printable representation of {@code value}. - * @since Android 1.0 */ - public static String toString(short value) { - return Integer.toString(value); - } + public static String toString(short value) { + return Integer.toString(value); + } /** * Parses the specified string as a signed decimal short value. - * + * * @param string * the string representation of a short value. * @return a {@code Short} instance containing the short value represented @@ -263,16 +251,15 @@ public final class Short extends Number implements Comparable { * if {@code string} is {@code null}, has a length of zero or * can not be parsed as a short value. * @see #parseShort(String) - * @since Android 1.0 */ - public static Short valueOf(String string) throws NumberFormatException { - return valueOf(parseShort(string)); - } + public static Short valueOf(String string) throws NumberFormatException { + return valueOf(parseShort(string)); + } /** * Parses the specified string as a signed short value using the specified * radix. - * + * * @param string * the string representation of a short value. * @param radix @@ -285,12 +272,11 @@ public final class Short extends Number implements Comparable { * {@code radix > Character.MAX_RADIX}, or if {@code string} * can not be parsed as a short value. * @see #parseShort(String, int) - * @since Android 1.0 */ - public static Short valueOf(String string, int radix) - throws NumberFormatException { - return valueOf(parseShort(string, radix)); - } + public static Short valueOf(String string, int radix) + throws NumberFormatException { + return valueOf(parseShort(string, radix)); + } /** * Reverses the bytes of the specified short. @@ -298,7 +284,7 @@ public final class Short extends Number implements Comparable { * @param s * the short value for which to reverse bytes. * @return the reversed value. - * @since Android 1.0 + * @since 1.5 */ public static short reverseBytes(short s) { int high = (s >> 8) & 0xFF; @@ -312,12 +298,11 @@ public final class Short extends Number implements Comparable { * If it is not necessary to get a new {@code Short} instance, it is * recommended to use this method instead of the constructor, since it * maintains a cache of instances which may result in better performance. - *

          - * + * * @param s * the short value to store in the instance. * @return a {@code Short} instance containing {@code s}. - * @since Android 1.0 + * @since 1.5 */ public static Short valueOf(short s) { if (s < -128 || s > 127) { @@ -328,9 +313,7 @@ public final class Short extends Number implements Comparable { static class valueOfCache { /** - *

          * A cache of instances used by {@link Short#valueOf(short)} and auto-boxing. - *

          */ private static final Short[] CACHE = new Short[256]; diff --git a/libcore/luni/src/main/java/java/lang/StackOverflowError.java b/libcore/luni/src/main/java/java/lang/StackOverflowError.java index dd63b6b53..3cbc0c70b 100644 --- a/libcore/luni/src/main/java/java/lang/StackOverflowError.java +++ b/libcore/luni/src/main/java/java/lang/StackOverflowError.java @@ -22,8 +22,6 @@ package java.lang; * platform or virtual machine specific limit. Typically, this will occur only * when a program becomes infinitely recursive, but it can also occur in * correctly written (but deeply recursive) programs. - * - * @since Android 1.0 */ public class StackOverflowError extends java.lang.VirtualMachineError { @@ -32,8 +30,6 @@ public class StackOverflowError extends java.lang.VirtualMachineError { /** * Constructs a new {@code StackOverflowError} that includes the current * stack trace. - * - * @since Android 1.0 */ public StackOverflowError() { super(); @@ -45,7 +41,6 @@ public class StackOverflowError extends java.lang.VirtualMachineError { * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public StackOverflowError(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/lang/StrictMath.java b/libcore/luni/src/main/java/java/lang/StrictMath.java index 66e4771f4..7a2da8eae 100644 --- a/libcore/luni/src/main/java/java/lang/StrictMath.java +++ b/libcore/luni/src/main/java/java/lang/StrictMath.java @@ -37,25 +37,19 @@ package java.lang; * Library" (fdlibm), version 5.3. *

          * http://www.netlib.org/fdlibm/ - * - * @since Android 1.0 */ public final class StrictMath { - /** + /** * The double value closest to e, the base of the natural logarithm. - * - * @since Android 1.0 - */ - public final static double E = Math.E; + */ + public final static double E = Math.E; - /** + /** * The double value closest to pi, the ratio of a circle's circumference to * its diameter. - * - * @since Android 1.0 - */ - public final static double PI = Math.PI; + */ + public final static double PI = Math.PI; private static java.util.Random random; @@ -65,7 +59,7 @@ public final class StrictMath { private StrictMath() { } - /** + /** * Returns the absolute value of the argument. *

          * Special cases: @@ -75,18 +69,16 @@ public final class StrictMath { *

        • {@code abs(-infinity) = +infinity}
        • *
        • {@code abs(NaN) = NaN}
        • * - *

          - * + * * @param d * the value whose absolute value has to be computed. * @return the absolute value of the argument. - * @since Android 1.0 */ - public static double abs(double d) { - long bits = Double.doubleToLongBits(d); - bits &= 0x7fffffffffffffffL; - return Double.longBitsToDouble(bits); - } + public static double abs(double d) { + long bits = Double.doubleToLongBits(d); + bits &= 0x7fffffffffffffffL; + return Double.longBitsToDouble(bits); + } /** * Returns the absolute value of the argument. @@ -98,52 +90,47 @@ public final class StrictMath { *
        • {@code abs(-infinity) = +infinity}
        • *
        • {@code abs(NaN) = NaN}
        • * - *

          - * + * * @param f * the value whose absolute value has to be computed. * @return the argument if it is positive, otherwise the negation of the * argument. - * @since Android 1.0 */ - public static float abs(float f) { - int bits = Float.floatToIntBits(f); - bits &= 0x7fffffff; - return Float.intBitsToFloat(bits); - } + public static float abs(float f) { + int bits = Float.floatToIntBits(f); + bits &= 0x7fffffff; + return Float.intBitsToFloat(bits); + } /** * Returns the absolute value of the argument. *

          * If the argument is {@code Integer.MIN_VALUE}, {@code Integer.MIN_VALUE} * is returned. - * + * * @param i * the value whose absolute value has to be computed. * @return the argument if it is positive, otherwise the negation of the * argument. - * @since Android 1.0 */ - public static int abs(int i) { - return i >= 0 ? i : -i; - } + public static int abs(int i) { + return i >= 0 ? i : -i; + } /** * Returns the absolute value of the argument. *

          * If the argument is {@code Long.MIN_VALUE}, {@code Long.MIN_VALUE} is * returned. - *

          - * + * * @param l * the value whose absolute value has to be computed. * @return the argument if it is positive, otherwise the negation of the * argument. - * @since Android 1.0 */ - public static long abs(long l) { - return l >= 0 ? l : -l; - } + public static long abs(long l) { + return l >= 0 ? l : -l; + } /** * Returns the closest double approximation of the arc cosine of the @@ -155,14 +142,12 @@ public final class StrictMath { *
        • {@code acos((anything < -1) = NaN}
        • *
        • {@code acos(NaN) = NaN}
        • * - *

          - * + * * @param d * the value to compute arc cosine of. * @return the arc cosine of the argument. - * @since Android 1.0 */ - public static native double acos(double d); + public static native double acos(double d); /** * Returns the closest double approximation of the arc sine of the argument @@ -174,14 +159,12 @@ public final class StrictMath { *
        • {@code asin((anything < -1)) = NaN}
        • *
        • {@code asin(NaN) = NaN}
        • * - *

          - * + * * @param d * the value whose arc sine has to be computed. * @return the arc sine of the argument. - * @since Android 1.0 */ - public static native double asin(double d); + public static native double asin(double d); /** * Returns the closest double approximation of the arc tangent of the @@ -195,14 +178,12 @@ public final class StrictMath { *
        • {@code atan(-infinity) = -pi/2}
        • *
        • {@code atan(NaN) = NaN}
        • * - *

          - * + * * @param d * the value whose arc tangent has to be computed. * @return the arc tangent of the argument. - * @since Android 1.0 */ - public static native double atan(double d); + public static native double atan(double d); /** * Returns the closest double approximation of the arc tangent of @@ -234,20 +215,15 @@ public final class StrictMath { *
        • {@code atan2(-infinity, (anything but,0, NaN, and infinity))} * {@code =} {@code -pi/2}
        • * - *

          - * + * * @param y * the numerator of the value whose atan has to be computed. * @param x * the denominator of the value whose atan has to be computed. * @return the arc tangent of {@code y/x}. - * @since Android 1.0 */ - public static native double atan2(double y, double x); - // BEGIN android-note - // parameter names changed from d1 / d2 to x / y - // END android-note - + public static native double atan2(double y, double x); + /** * Returns the closest double approximation of the cube root of the * argument. @@ -260,12 +236,10 @@ public final class StrictMath { *
        • {@code cbrt(-infinity) = -infinity}
        • *
        • {@code cbrt(NaN) = NaN}
        • * - *

          - * + * * @param d * the value whose cube root has to be computed. * @return the cube root of the argument. - * @since Android 1.0 */ public static native double cbrt(double d); @@ -282,15 +256,14 @@ public final class StrictMath { *
        • {@code ceil(-infinity) = -infinity}
        • *
        • {@code ceil(NaN) = NaN}
        • * - *

          - * + * * @param d * the value whose closest integer value has to be computed. * @return the ceiling of the argument. - * @since Android 1.0 */ - public static native double ceil(double d); - + public static native double ceil(double d); + + /** * Returns the closest double approximation of the hyperbolic cosine of the * argument. @@ -301,12 +274,10 @@ public final class StrictMath { *
        • {@code cosh(-infinity) = +infinity}
        • *
        • {@code cosh(NaN) = NaN}
        • * - *

          - * + * * @param d * the value whose hyperbolic cosine has to be computed. * @return the hyperbolic cosine of the argument. - * @since Android 1.0 */ public static native double cosh(double d); @@ -319,12 +290,10 @@ public final class StrictMath { *
        • {@code cos(-infinity) = NaN}
        • *
        • {@code cos(NaN) = NaN}
        • * - *

          - * + * * @param d * the angle whose cosine has to be computed, in radians. * @return the cosine of the argument. - * @since Android 1.0 */ public static native double cos(double d); @@ -338,15 +307,13 @@ public final class StrictMath { *
        • {@code exp(-infinity) = +0.0}
        • *
        • {@code exp(NaN) = NaN}
        • * - *

          - * + * * @param d * the value whose exponential has to be computed. * @return the exponential of the argument. - * @since Android 1.0 */ public static native double exp(double d); - + /** * Returns the closest double approximation of {@code e} * {@code d}{@code - 1}. If the argument is very close to 0, it is @@ -361,14 +328,12 @@ public final class StrictMath { *
        • {@code expm1(-infinity) = -1.0}
        • *
        • {@code expm1(NaN) = NaN}
        • * - *

          - * + * * @param d * the value to compute the {@code e}{@code d} * {@code - 1} of. * @return the {@code e}{@code d}{@code - 1} value * of the argument. - * @since Android 1.0 */ public static native double expm1(double d); @@ -384,14 +349,12 @@ public final class StrictMath { *
        • {@code floor(-infinity) = -infinity}
        • *
        • {@code floor(NaN) = NaN}
        • * - *

          - * + * * @param d the value whose closest integer value has to be computed. * @return the floor of the argument. - * @since Android 1.0 */ public static native double floor(double d); - + /** * Returns {@code sqrt(}{@code x}{@code 2}{@code +} * {@code y}{@code 2}{@code )}. The final result is @@ -405,8 +368,7 @@ public final class StrictMath { *
        • {@code hypot((anything including NaN), -infinity) = +infinity}
        • *
        • {@code hypot(NaN, NaN) = NaN}
        • * - *

          - * + * * @param x * a double number. * @param y @@ -414,7 +376,6 @@ public final class StrictMath { * @return the {@code sqrt(}{@code x}{@code 2}{@code +} * {@code y}{@code 2}{@code )} value of the * arguments. - * @since Android 1.0 */ public static native double hypot(double x, double y); @@ -436,19 +397,14 @@ public final class StrictMath { *
        • {@code IEEEremainder(x, -infinity) = x } where x is anything but * +/-infinity
        • * - *

          - * + * * @param x * the numerator of the operation. * @param y * the denominator of the operation. * @return the IEEE754 floating point reminder of of {@code x/y}. - * @since Android 1.0 */ - public static native double IEEEremainder(double x, double y); - // BEGIN android-note - // parameter names changed from d1 / d2 to x / y - // END android-note + public static native double IEEEremainder(double x, double y); /** * Returns the closest double approximation of the natural logarithm of the @@ -463,15 +419,13 @@ public final class StrictMath { *
        • {@code log(-infinity) = NaN}
        • *
        • {@code log(NaN) = NaN}
        • * - *

          - * + * * @param d * the value whose log has to be computed. * @return the natural logarithm of the argument. - * @since Android 1.0 */ public static native double log(double d); - + /** * Returns the closest double approximation of the base 10 logarithm of the * argument. @@ -485,15 +439,13 @@ public final class StrictMath { *
        • {@code log10(-infinity) = NaN}
        • *
        • {@code log10(NaN) = NaN}
        • * - *

          - * + * * @param d * the value whose base 10 log has to be computed. * @return the natural logarithm of the argument. - * @since Android 1.0 */ public static native double log10(double d); - + /** * Returns the closest double approximation of the natural logarithm of the * sum of the argument and 1. If the argument is very close to 0, it is much @@ -510,12 +462,10 @@ public final class StrictMath { *
        • {@code log1p(-infinity) = NaN}
        • *
        • {@code log1p(NaN) = NaN}
        • * - *

          - * + * * @param d * the value to compute the {@code ln(1+d)} of. * @return the natural logarithm of the sum of the argument and 1. - * @since Android 1.0 */ public static native double log1p(double d); @@ -530,29 +480,27 @@ public final class StrictMath { *
        • {@code max(+0.0, -0.0) = +0.0}
        • *
        • {@code max(-0.0, +0.0) = +0.0}
        • * - *

          - * + * * @param d1 * the first argument. * @param d2 * the second argument. * @return the larger of {@code d1} and {@code d2}. - * @since Android 1.0 */ - public static double max(double d1, double d2) { - if (d1 > d2) - return d1; - if (d1 < d2) - return d2; - /* if either arg is NaN, return NaN */ - if (d1 != d2) - return Double.NaN; - /* max( +0.0,-0.0) == +0.0 */ - if (d1 == 0.0 - && ((Double.doubleToLongBits(d1) & Double.doubleToLongBits(d2)) & 0x8000000000000000L) == 0) - return 0.0; - return d1; - } + public static double max(double d1, double d2) { + if (d1 > d2) + return d1; + if (d1 < d2) + return d2; + /* if either arg is NaN, return NaN */ + if (d1 != d2) + return Double.NaN; + /* max( +0.0,-0.0) == +0.0 */ + if (d1 == 0.0 + && ((Double.doubleToLongBits(d1) & Double.doubleToLongBits(d2)) & 0x8000000000000000L) == 0) + return 0.0; + return d1; + } /** * Returns the most positive (closest to positive infinity) of the two @@ -565,59 +513,55 @@ public final class StrictMath { *
        • {@code max(+0.0, -0.0) = +0.0}
        • *
        • {@code max(-0.0, +0.0) = +0.0}
        • * - *

          - * + * * @param f1 * the first argument. * @param f2 * the second argument. * @return the larger of {@code f1} and {@code f2}. - * @since Android 1.0 */ - public static float max(float f1, float f2) { - if (f1 > f2) - return f1; - if (f1 < f2) - return f2; - /* if either arg is NaN, return NaN */ - if (f1 != f2) - return Float.NaN; - /* max( +0.0,-0.0) == +0.0 */ - if (f1 == 0.0f - && ((Float.floatToIntBits(f1) & Float.floatToIntBits(f2)) & 0x80000000) == 0) - return 0.0f; - return f1; - } + public static float max(float f1, float f2) { + if (f1 > f2) + return f1; + if (f1 < f2) + return f2; + /* if either arg is NaN, return NaN */ + if (f1 != f2) + return Float.NaN; + /* max( +0.0,-0.0) == +0.0 */ + if (f1 == 0.0f + && ((Float.floatToIntBits(f1) & Float.floatToIntBits(f2)) & 0x80000000) == 0) + return 0.0f; + return f1; + } /** * Returns the most positive (closest to positive infinity) of the two * arguments. - * + * * @param i1 * the first argument. * @param i2 * the second argument. * @return the larger of {@code i1} and {@code i2}. - * @since Android 1.0 */ - public static int max(int i1, int i2) { - return i1 > i2 ? i1 : i2; - } + public static int max(int i1, int i2) { + return i1 > i2 ? i1 : i2; + } /** * Returns the most positive (closest to positive infinity) of the two * arguments. - * + * * @param l1 * the first argument. * @param l2 * the second argument. * @return the larger of {@code l1} and {@code l2}. - * @since Android 1.0 */ - public static long max(long l1, long l2) { - return l1 > l2 ? l1 : l2; - } + public static long max(long l1, long l2) { + return l1 > l2 ? l1 : l2; + } /** * Returns the most negative (closest to negative infinity) of the two @@ -630,29 +574,27 @@ public final class StrictMath { *
        • {@code min(+0.0, -0.0) = -0.0}
        • *
        • {@code min(-0.0, +0.0) = -0.0}
        • * - *

          - * + * * @param d1 * the first argument. * @param d2 * the second argument. * @return the smaller of {@code d1} and {@code d2}. - * @since Android 1.0 */ - public static double min(double d1, double d2) { - if (d1 > d2) - return d2; - if (d1 < d2) - return d1; - /* if either arg is NaN, return NaN */ - if (d1 != d2) - return Double.NaN; - /* min( +0.0,-0.0) == -0.0 */ - if (d1 == 0.0 - && ((Double.doubleToLongBits(d1) | Double.doubleToLongBits(d2)) & 0x8000000000000000l) != 0) - return 0.0 * (-1.0); - return d1; - } + public static double min(double d1, double d2) { + if (d1 > d2) + return d2; + if (d1 < d2) + return d1; + /* if either arg is NaN, return NaN */ + if (d1 != d2) + return Double.NaN; + /* min( +0.0,-0.0) == -0.0 */ + if (d1 == 0.0 + && ((Double.doubleToLongBits(d1) | Double.doubleToLongBits(d2)) & 0x8000000000000000l) != 0) + return 0.0 * (-1.0); + return d1; + } /** * Returns the most negative (closest to negative infinity) of the two @@ -665,59 +607,55 @@ public final class StrictMath { *
        • {@code min(+0.0, -0.0) = -0.0}
        • *
        • {@code min(-0.0, +0.0) = -0.0}
        • * - *

          - * + * * @param f1 * the first argument. * @param f2 * the second argument. * @return the smaller of {@code f1} and {@code f2}. - * @since Android 1.0 */ - public static float min(float f1, float f2) { - if (f1 > f2) - return f2; - if (f1 < f2) - return f1; - /* if either arg is NaN, return NaN */ - if (f1 != f2) - return Float.NaN; - /* min( +0.0,-0.0) == -0.0 */ - if (f1 == 0.0f - && ((Float.floatToIntBits(f1) | Float.floatToIntBits(f2)) & 0x80000000) != 0) - return 0.0f * (-1.0f); - return f1; - } + public static float min(float f1, float f2) { + if (f1 > f2) + return f2; + if (f1 < f2) + return f1; + /* if either arg is NaN, return NaN */ + if (f1 != f2) + return Float.NaN; + /* min( +0.0,-0.0) == -0.0 */ + if (f1 == 0.0f + && ((Float.floatToIntBits(f1) | Float.floatToIntBits(f2)) & 0x80000000) != 0) + return 0.0f * (-1.0f); + return f1; + } /** * Returns the most negative (closest to negative infinity) of the two * arguments. - * + * * @param i1 * the first argument. * @param i2 * the second argument. * @return the smaller of {@code i1} and {@code i2}. - * @since Android 1.0 */ - public static int min(int i1, int i2) { - return i1 < i2 ? i1 : i2; - } + public static int min(int i1, int i2) { + return i1 < i2 ? i1 : i2; + } /** * Returns the most negative (closest to negative infinity) of the two * arguments. - * + * * @param l1 * the first argument. * @param l2 * the second argument. * @return the smaller of {@code l1} and {@code l2}. - * @since Android 1.0 */ - public static long min(long l1, long l2) { - return l1 < l2 ? l1 : l2; - } + public static long min(long l1, long l2) { + return l1 < l2 ? l1 : l2; + } /** * Returns the closest double approximation of the result of raising @@ -750,26 +688,20 @@ public final class StrictMath { *
        • {@code pow((-anything except 0 and infinity), (non-integer))} * {@code =} {@code NAN}
        • * - *

          - * + * * @param x * the base of the operation. * @param y * the exponent of the operation. * @return {@code x} to the power of {@code y}. - * @since Android 1.0 */ - public static native double pow(double x, double y); - // BEGIN android-note - // parameter names changed from d1 / d2 to x / y - // END android-note + public static native double pow(double x, double y); /** * Returns a pseudo-random number between 0.0 (inclusive) and 1.0 * (exclusive). - * + * * @return a pseudo-random number. - * @since Android 1.0 */ public static double random() { // BEGIN android-changed @@ -777,8 +709,8 @@ public final class StrictMath { random = new java.util.Random(); } // END android-changed - return random.nextDouble(); - } + return random.nextDouble(); + } /** * Returns the double conversion of the result of rounding the argument to @@ -792,12 +724,10 @@ public final class StrictMath { *
        • {@code rint(-infinity) = -infinity}
        • *
        • {@code rint(NaN) = NaN}
        • * - *

          - * + * * @param d * the value to be rounded. * @return the closest integer to the argument (as a double). - * @since Android 1.0 */ public static native double rint(double d); @@ -815,19 +745,17 @@ public final class StrictMath { *
        • {@code round(-infinity) = Long.MIN_VALUE}
        • *
        • {@code round(NaN) = +0.0}
        • * - *

          - * + * * @param d * the value to be rounded. * @return the closest integer to the argument. - * @since Android 1.0 */ - public static long round(double d) { - // check for NaN - if (d != d) - return 0L; - return (long) Math.floor(d + 0.5d); - } + public static long round(double d) { + // check for NaN + if (d != d) + return 0L; + return (long) Math.floor(d + 0.5d); + } /** * Returns the result of rounding the argument to an integer. The result is @@ -843,19 +771,17 @@ public final class StrictMath { *
        • {@code round(-infinity) = Integer.MIN_VALUE}
        • *
        • {@code round(NaN) = +0.0}
        • * - *

          - * + * * @param f * the value to be rounded. * @return the closest integer to the argument. - * @since Android 1.0 */ - public static int round(float f) { - // check for NaN - if (f != f) - return 0; - return (int) Math.floor(f + 0.5f); - } + public static int round(float f) { + // check for NaN + if (f != f) + return 0; + return (int) Math.floor(f + 0.5f); + } /** * Returns the signum function of the argument. If the argument is less than @@ -871,12 +797,10 @@ public final class StrictMath { *
        • {@code signum(-infinity) = -1.0}
        • *
        • {@code signum(NaN) = NaN}
        • * - *

          - * + * * @param d * the value whose signum has to be computed. * @return the value of the signum function. - * @since Android 1.0 */ public static double signum(double d){ if(Double.isNaN(d)){ @@ -905,12 +829,10 @@ public final class StrictMath { *
        • {@code signum(-infinity) = -1.0}
        • *
        • {@code signum(NaN) = NaN}
        • * - *

          - * + * * @param f * the value whose signum has to be computed. * @return the value of the signum function. - * @since Android 1.0 */ public static float signum(float f){ if(Float.isNaN(f)){ @@ -937,15 +859,13 @@ public final class StrictMath { *
        • {@code sinh(-infinity) = -infinity}
        • *
        • {@code sinh(NaN) = NaN}
        • * - *

          - * + * * @param d * the value whose hyperbolic sine has to be computed. * @return the hyperbolic sine of the argument. - * @since Android 1.0 */ public static native double sinh(double d); - + /** * Returns the closest double approximation of the sine of the argument. *

          @@ -957,12 +877,10 @@ public final class StrictMath { *

        • {@code sin(-infinity) = NaN}
        • *
        • {@code sin(NaN) = NaN}
        • * - *

          - * + * * @param d * the angle whose sin has to be computed, in radians. * @return the sine of the argument. - * @since Android 1.0 */ public static native double sin(double d); @@ -978,12 +896,10 @@ public final class StrictMath { *
        • {@code sqrt(+infinity) = +infinity}
        • *
        • {@code sqrt(NaN) = NaN}
        • * - *

          - * + * * @param d * the value whose square root has to be computed. * @return the square root of the argument. - * @since Android 1.0 */ public static native double sqrt(double d); @@ -998,12 +914,10 @@ public final class StrictMath { *
        • {@code tan(-infinity) = NaN}
        • *
        • {@code tan(NaN) = NaN}
        • * - *

          - * + * * @param d * the angle whose tangens has to be computed, in radians. * @return the tangent of the argument. - * @since Android 1.0 */ public static native double tan(double d); @@ -1019,15 +933,13 @@ public final class StrictMath { *
        • {@code tanh(-infinity) = -1.0}
        • *
        • {@code tanh(NaN) = NaN}
        • * - *

          - * + * * @param d * the value whose hyperbolic tangent has to be computed. * @return the hyperbolic tangent of the argument - * @since Android 1.0 */ public static native double tanh(double d); - + /** * Returns the measure in degrees of the supplied radian angle. The result * is {@code angrad * 180 / pi}. @@ -1040,16 +952,14 @@ public final class StrictMath { *
        • {@code toDegrees(-infinity) = -infinity}
        • *
        • {@code toDegrees(NaN) = NaN}
        • * - *

          - * + * * @param angrad * an angle in radians. * @return the degree measure of the angle. - * @since Android 1.0 */ - public static double toDegrees(double angrad) { - return angrad * 180d / PI; - } + public static double toDegrees(double angrad) { + return angrad * 180d / PI; + } /** * Returns the measure in radians of the supplied degree angle. The result @@ -1063,18 +973,16 @@ public final class StrictMath { *
        • {@code toRadians(-infinity) = -infinity}
        • *
        • {@code toRadians(NaN) = NaN}
        • * - *

          - * + * * @param angdeg * an angle in degrees. * @return the radian measure of the angle. - * @since Android 1.0 */ - public static double toRadians(double angdeg) { - return angdeg / 180d * PI; - } - - /** + public static double toRadians(double angdeg) { + return angdeg / 180d * PI; + } + + /** * Returns the argument's ulp (unit in the last place). The size of a ulp of * a double value is the positive distance between this value and the double * value next larger in magnitude. For non-NaN {@code x}, @@ -1088,12 +996,10 @@ public final class StrictMath { *
        • {@code ulp(-infintiy) = infinity}
        • *
        • {@code ulp(NaN) = NaN}
        • * - *

          - * + * * @param d * the floating-point value to compute ulp of. * @return the size of a ulp of the argument. - * @since Android 1.0 */ public static double ulp(double d) { // special cases @@ -1120,12 +1026,10 @@ public final class StrictMath { *
        • {@code ulp(-infintiy) = infinity}
        • *
        • {@code ulp(NaN) = NaN}
        • * - *

          - * + * * @param f * the floating-point value to compute ulp of. * @return the size of a ulp of the argument. - * @since Android 1.0 */ public static float ulp(float f) { // special cases diff --git a/libcore/luni/src/main/java/java/lang/String.java b/libcore/luni/src/main/java/java/lang/String.java index b257a367a..6d533571d 100644 --- a/libcore/luni/src/main/java/java/lang/String.java +++ b/libcore/luni/src/main/java/java/lang/String.java @@ -21,9 +21,6 @@ import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.util.Comparator; import java.util.Formatter; -// BEGIN android-added -import java.util.IllegalFormatException; -// END android-added import java.util.Locale; import java.util.regex.Pattern; @@ -50,7 +47,7 @@ import org.apache.harmony.luni.util.PriviAction; * @see StringBuffer * @see StringBuilder * @see Charset - * @since Android 1.0 + * @since 1.0 */ public final class String implements Serializable, Comparable, CharSequence { @@ -138,8 +135,6 @@ public final class String implements Serializable, Comparable, /** * A comparator ignoring the case of the characters. - * - * @since Android 1.0 */ public static final Comparator CASE_INSENSITIVE_ORDER = new CaseInsensitiveComparator(); @@ -166,8 +161,6 @@ public final class String implements Serializable, Comparable, /** * Creates an empty string. - * - * @since Android 1.0 */ public String() { value = new char[0]; @@ -175,6 +168,9 @@ public final class String implements Serializable, Comparable, count = 0; } + /* + * Private constructor used for JIT optimization. + */ @SuppressWarnings("unused") private String(String s, char c) { offset = 0; @@ -192,7 +188,6 @@ public final class String implements Serializable, Comparable, * * @param data * the byte array to convert to a string. - * @since Android 1.0 */ public String(byte[] data) { this(data, 0, data.length); @@ -206,9 +201,10 @@ public final class String implements Serializable, Comparable, * the byte array to convert to a string. * @param high * the high byte to use. + * @throws NullPointerException + * when {@code data} is {@code null}. * @deprecated Use {@link #String(byte[])} or * {@link #String(byte[], String)} instead. - * @since Android 1.0 */ @Deprecated public String(byte[] data, int high) { @@ -227,10 +223,11 @@ public final class String implements Serializable, Comparable, * the starting offset in the byte array. * @param length * the number of bytes to convert. + * @throws NullPointerException + * when {@code data} is {@code null}. * @throws IndexOutOfBoundsException * if {@code length < 0, start < 0} or {@code start + length > * data.length}. - * @since Android 1.0 */ public String(byte[] data, int start, int length) { // start + length could overflow, start/length maybe MaxInt @@ -264,13 +261,13 @@ public final class String implements Serializable, Comparable, * the starting offset in the byte array. * @param length * the number of bytes to convert. - * + * @throws NullPointerException + * when {@code data} is {@code null}. * @throws IndexOutOfBoundsException * if {@code length < 0, start < 0} or * {@code start + length > data.length} * * @deprecated Use {@link #String(byte[], int, int)} instead. - * @since Android 1.0 */ @Deprecated public String(byte[] data, int high, int start, int length) { @@ -303,12 +300,13 @@ public final class String implements Serializable, Comparable, * the number of bytes to convert. * @param encoding * the encoding. + * @throws NullPointerException + * when {@code data} is {@code null}. * @throws IndexOutOfBoundsException * if {@code length < 0, start < 0} or {@code start + length > * data.length}. * @throws UnsupportedEncodingException * if {@code encoding} is not supported. - * @since Android 1.0 */ public String(byte[] data, int start, int length, final String encoding) throws UnsupportedEncodingException { @@ -444,7 +442,7 @@ public final class String implements Serializable, Comparable, try { cb = charset.decode(ByteBuffer.wrap(data, start, length)); } catch (Exception e) { - // do nothing. according to spec: + // do nothing. according to spec: // behavior is unspecified for invalid array cb = CharBuffer.wrap("\u003f".toCharArray()); //$NON-NLS-1$ } @@ -467,9 +465,10 @@ public final class String implements Serializable, Comparable, * the byte array to convert to a string. * @param encoding * the encoding. + * @throws NullPointerException + * when {@code data} is {@code null}. * @throws UnsupportedEncodingException * if {@code encoding} is not supported. - * @since Android 1.0 */ public String(byte[] data, String encoding) throws UnsupportedEncodingException { this(data, 0, data.length, encoding); @@ -482,7 +481,8 @@ public final class String implements Serializable, Comparable, * * @param data * the array of characters. - * @since Android 1.0 + * @throws NullPointerException + * when {@code data} is {@code null}. */ public String(char[] data) { this(data, 0, data.length); @@ -499,10 +499,11 @@ public final class String implements Serializable, Comparable, * the starting offset in the character array. * @param length * the number of characters to use. + * @throws NullPointerException + * when {@code data} is {@code null}. * @throws IndexOutOfBoundsException * if {@code length < 0, start < 0} or {@code start + length > * data.length} - * @since Android 1.0 */ public String(char[] data, int start, int length) { // range check everything so a new char[] is not created @@ -532,7 +533,6 @@ public final class String implements Serializable, Comparable, * * @param string * the string to copy. - * @since Android 1.0 */ public String(String string) { value = string.value; @@ -540,13 +540,53 @@ public final class String implements Serializable, Comparable, count = string.count; } + /* + * Private constructor useful for JIT optimization. + */ + @SuppressWarnings( { "unused", "nls" }) + private String(String s1, String s2) { + if (s1 == null) { + s1 = "null"; + } + if (s2 == null) { + s2 = "null"; + } + count = s1.count + s2.count; + value = new char[count]; + offset = 0; + System.arraycopy(s1.value, s1.offset, value, 0, s1.count); + System.arraycopy(s2.value, s2.offset, value, s1.count, s2.count); + } + + /* + * Private constructor useful for JIT optimization. + */ + @SuppressWarnings( { "unused", "nls" }) + private String(String s1, String s2, String s3) { + if (s1 == null) { + s1 = "null"; + } + if (s2 == null) { + s2 = "null"; + } + if (s3 == null) { + s3 = "null"; + } + count = s1.count + s2.count + s3.count; + value = new char[count]; + offset = 0; + System.arraycopy(s1.value, s1.offset, value, 0, s1.count); + System.arraycopy(s2.value, s2.offset, value, s1.count, s2.count); + System.arraycopy(s3.value, s3.offset, value, s1.count + s2.count, + s3.count); + } + /** * Creates a {@code String} from the contents of the specified * {@code StringBuffer}. * * @param stringbuffer * the buffer to get the contents from. - * @since Android 1.0 */ public String(StringBuffer stringbuffer) { offset = 0; @@ -558,7 +598,7 @@ public final class String implements Serializable, Comparable, /** * Creates a {@code String} from the sub-array of Unicode code points. - * + * * @param codePoints * the array of Unicode code points to convert. * @param offset @@ -566,13 +606,15 @@ public final class String implements Serializable, Comparable, * converting from. * @param count * the number of elements in {@code codePoints} to copy. + * @throws NullPointerException + * if {@code codePoints} is {@code null}. * @throws IllegalArgumentException * if any of the elements of {@code codePoints} are not valid * Unicode code points. * @throws IndexOutOfBoundsException * if {@code offset} or {@code count} are not within the bounds * of {@code codePoints}. - * @since Android 1.0 + * @since 1.5 */ public String(int[] codePoints, int offset, int count) { super(); @@ -599,7 +641,9 @@ public final class String implements Serializable, Comparable, * * @param sb * the {@code StringBuilder} to copy the contents from. - * @since Android 1.0 + * @throws NullPointerException + * if {@code sb} is {@code null}. + * @since 1.5 */ public String(StringBuilder sb) { if (sb == null) { @@ -636,7 +680,6 @@ public final class String implements Serializable, Comparable, * @return the character at the index. * @throws IndexOutOfBoundsException * if {@code index < 0} or {@code index >= length()}. - * @since Android 1.0 */ public char charAt(int index) { if (0 <= index && index < count) { @@ -645,6 +688,39 @@ public final class String implements Serializable, Comparable, throw new StringIndexOutOfBoundsException(); } + // Optimized for ASCII + private char compareValue(char ch) { + if (ch < 128) { + if ('A' <= ch && ch <= 'Z') { + return (char) (ch + ('a' - 'A')); + } + return ch; + } + return Character.toLowerCase(Character.toUpperCase(ch)); + } + + // Optimized for ASCII + private char toLowerCase(char ch) { + if (ch < 128) { + if ('A' <= ch && ch <= 'Z') { + return (char) (ch + ('a' - 'A')); + } + return ch; + } + return Character.toLowerCase(ch); + } + + // Optimized for ASCII + private char toUpperCase(char ch) { + if (ch < 128) { + if ('a' <= ch && ch <= 'z') { + return (char) (ch - ('a' - 'A')); + } + return ch; + } + return Character.toUpperCase(ch); + } + /** * Compares the specified string to this string using the Unicode values of * the characters. Returns 0 if the strings contain the same characters in @@ -662,7 +738,8 @@ public final class String implements Serializable, Comparable, * @return 0 if the strings are equal, a negative integer if this string is * before the specified string, or a positive integer if this string * is after the specified string. - * @since Android 1.0 + * @throws NullPointerException + * if {@code string} is {@code null}. */ public int compareTo(String string) { // Code adapted from K&R, pg 101 @@ -694,7 +771,8 @@ public final class String implements Serializable, Comparable, * @return 0 if the strings are equal, a negative integer if this string is * before the specified string, or a positive integer if this string * is after the specified string. - * @since Android 1.0 + * @throws NullPointerException + * if {@code string} is {@code null}. */ public int compareToIgnoreCase(String string) { int o1 = offset, o2 = string.offset, result; @@ -705,8 +783,8 @@ public final class String implements Serializable, Comparable, if ((c1 = value[o1++]) == (c2 = target[o2++])) { continue; } - c1 = Character.toLowerCase(Character.toUpperCase(c1)); - c2 = Character.toLowerCase(Character.toUpperCase(c2)); + c1 = compareValue(c1); + c2 = compareValue(c2); if ((result = c1 - c2) != 0) { return result; } @@ -721,7 +799,6 @@ public final class String implements Serializable, Comparable, * the string to concatenate * @return a new string which is the concatenation of this string and the * specified string. - * @since Android 1.0 */ public String concat(String string) { if (string.count > 0 && count > 0) { @@ -742,7 +819,8 @@ public final class String implements Serializable, Comparable, * @param data * the array of characters. * @return the new string. - * @since Android 1.0 + * @throws NullPointerException + * if {@code data} is {@code null}. */ public static String copyValueOf(char[] data) { return new String(data, 0, data.length); @@ -760,10 +838,11 @@ public final class String implements Serializable, Comparable, * @param length * the number of characters to use. * @return the new string. + * @throws NullPointerException + * if {@code data} is {@code null}. * @throws IndexOutOfBoundsException * if {@code length < 0, start < 0} or {@code start + length > * data.length}. - * @since Android 1.0 */ public static String copyValueOf(char[] data, int start, int length) { return new String(data, start, length); @@ -799,7 +878,8 @@ public final class String implements Serializable, Comparable, * the suffix to look for. * @return {@code true} if the specified string is a suffix of this string, * {@code false} otherwise. - * @since Android 1.0 + * @throws NullPointerException + * if {@code suffix} is {@code null}. */ public boolean endsWith(String suffix) { return regionMatches(count - suffix.count, suffix, 0, suffix.count); @@ -815,7 +895,6 @@ public final class String implements Serializable, Comparable, * @return {@code true} if the specified object is equal to this string, * {@code false} otherwise. * @see #hashCode - * @since Android 1.0 */ @Override public boolean equals(Object object) { @@ -855,7 +934,6 @@ public final class String implements Serializable, Comparable, * the string to compare. * @return {@code true} if the specified string is equal to this string, * {@code false} otherwise. - * @since Android 1.0 */ public boolean equalsIgnoreCase(String string) { if (string == this) { @@ -871,9 +949,9 @@ public final class String implements Serializable, Comparable, char[] target = string.value; while (o1 < end) { if ((c1 = value[o1++]) != (c2 = target[o2++]) - && Character.toUpperCase(c1) != Character.toUpperCase(c2) + && toUpperCase(c1) != toUpperCase(c2) // Required for unicode that we test both cases - && Character.toLowerCase(c1) != Character.toLowerCase(c2)) { + && toLowerCase(c1) != toLowerCase(c2)) { return false; } } @@ -887,7 +965,6 @@ public final class String implements Serializable, Comparable, * is not available, an ASCII encoding is used. * * @return the byte array encoding of this string. - * @since Android 1.0 */ public byte[] getBytes() { ByteBuffer buffer = defaultCharset().encode( @@ -909,11 +986,12 @@ public final class String implements Serializable, Comparable, * the destination byte array. * @param index * the starting offset in the destination byte array. + * @throws NullPointerException + * if {@code data} is {@code null}. * @throws IndexOutOfBoundsException * if {@code start < 0}, {@code end > length()}, {@code index < * 0} or {@code end - start > data.length - index}. * @deprecated Use {@link #getBytes()} or {@link #getBytes(String)} - * @since Android 1.0 */ @Deprecated public void getBytes(int start, int end, byte[] data, int index) { @@ -939,7 +1017,6 @@ public final class String implements Serializable, Comparable, * @return the encoded byte array of this string. * @throws UnsupportedEncodingException * if the encoding is not supported. - * @since Android 1.0 */ public byte[] getBytes(String encoding) throws UnsupportedEncodingException { ByteBuffer buffer = getCharset(encoding).encode( @@ -979,11 +1056,12 @@ public final class String implements Serializable, Comparable, * the destination character array. * @param index * the starting offset in the character array. + * @throws NullPointerException + * if {@code buffer} is {@code null}. * @throws IndexOutOfBoundsException * if {@code start < 0}, {@code end > length()}, {@code start > * end}, {@code index < 0}, {@code end - start > buffer.length - * index} - * @since Android 1.0 */ public void getChars(int start, int end, char[] buffer, int index) { // NOTE last character not copied! @@ -1036,7 +1114,6 @@ public final class String implements Serializable, Comparable, * the character to find. * @return the index in this string of the specified character, -1 if the * character isn't found. - * @since Android 1.0 */ public int indexOf(int c) { // BEGIN android-changed @@ -1066,7 +1143,6 @@ public final class String implements Serializable, Comparable, * the starting offset. * @return the index in this string of the specified character, -1 if the * character isn't found. - * @since Android 1.0 */ public int indexOf(int c, int start) { // BEGIN android-changed @@ -1097,7 +1173,8 @@ public final class String implements Serializable, Comparable, * the string to find. * @return the index of the first character of the specified string in this * string, -1 if the specified string is not a substring. - * @since Android 1.0 + * @throws NullPointerException + * if {@code string} is {@code null}. */ public int indexOf(String string) { // BEGIN android-changed @@ -1143,7 +1220,8 @@ public final class String implements Serializable, Comparable, * the starting offset. * @return the index of the first character of the specified string in this * string, -1 if the specified string is not a substring. - * @since Android 1.0 + * @throws NullPointerException + * if {@code subString} is {@code null}. */ public int indexOf(String subString, int start) { // BEGIN android-changed @@ -1187,11 +1265,9 @@ public final class String implements Serializable, Comparable, * object is always returned for strings which are equal. * * @return the interned string equal to this string. - * @since Android 1.0 */ native public String intern(); - /** * Searches in this string for the last index of the specified character. * The search for the character starts at the end and moves towards the @@ -1201,7 +1277,6 @@ public final class String implements Serializable, Comparable, * the character to find. * @return the index in this string of the specified character, -1 if the * character isn't found. - * @since Android 1.0 */ public int lastIndexOf(int c) { // BEGIN android-changed @@ -1228,7 +1303,6 @@ public final class String implements Serializable, Comparable, * the starting offset. * @return the index in this string of the specified character, -1 if the * character isn't found. - * @since Android 1.0 */ public int lastIndexOf(int c, int start) { // BEGIN android-changed @@ -1258,7 +1332,8 @@ public final class String implements Serializable, Comparable, * the string to find. * @return the index of the first character of the specified string in this * string, -1 if the specified string is not a substring. - * @since Android 1.0 + * @throws NullPointerException + * if {@code string} is {@code null}. */ public int lastIndexOf(String string) { // Use count instead of count - 1 so lastIndexOf("") returns count @@ -1276,7 +1351,8 @@ public final class String implements Serializable, Comparable, * the starting offset. * @return the index of the first character of the specified string in this * string , -1 if the specified string is not a substring. - * @since Android 1.0 + * @throws NullPointerException + * if {@code subString} is {@code null}. */ public int lastIndexOf(String subString, int start) { int subCount = subString.count; @@ -1314,7 +1390,6 @@ public final class String implements Serializable, Comparable, * Returns the size of this string. * * @return the number of characters in this string. - * @since Android 1.0 */ public int length() { return count; @@ -1334,7 +1409,8 @@ public final class String implements Serializable, Comparable, * the number of characters to compare. * @return {@code true} if the ranges of characters are equal, {@code false} * otherwise - * @since Android 1.0 + * @throws NullPointerException + * if {@code string} is {@code null}. */ public boolean regionMatches(int thisStart, String string, int start, int length) { @@ -1380,7 +1456,8 @@ public final class String implements Serializable, Comparable, * the number of characters to compare. * @return {@code true} if the ranges of characters are equal, {@code false} * otherwise. - * @since Android 1.0 + * @throws NullPointerException + * if {@code string} is {@code null}. */ public boolean regionMatches(boolean ignoreCase, int thisStart, String string, int start, int length) { @@ -1403,11 +1480,9 @@ public final class String implements Serializable, Comparable, char[] target = string.value; while (thisStart < end) { if ((c1 = value[thisStart++]) != (c2 = target[start++]) - && Character.toUpperCase(c1) != Character - .toUpperCase(c2) + && toUpperCase(c1) != toUpperCase(c2) // Required for unicode that we test both cases - && Character.toLowerCase(c1) != Character - .toLowerCase(c2)) { + && toLowerCase(c1) != toLowerCase(c2)) { return false; } } @@ -1425,7 +1500,6 @@ public final class String implements Serializable, Comparable, * @param newChar * the replacement character. * @return a new string with occurrences of oldChar replaced by newChar. - * @since Android 1.0 */ public String replace(char oldChar, char newChar) { // BEGIN endroid-changed @@ -1465,7 +1539,8 @@ public final class String implements Serializable, Comparable, * @param replacement * the replacement sequence. * @return the resulting string. - * @since Android 1.0 + * @throws NullPointerException + * if {@code target} or {@code replacement} is {@code null}. */ public String replace(CharSequence target, CharSequence replacement) { if (target == null) { @@ -1489,7 +1564,7 @@ public final class String implements Serializable, Comparable, buffer.append(rs); tail = index + tl; } while ((index = indexOf(ts, tail)) != -1); - //append trailing chars + //append trailing chars buffer.append(value, offset + tail, count - tail); return buffer.toString(); @@ -1503,7 +1578,8 @@ public final class String implements Serializable, Comparable, * the string to look for. * @return {@code true} if the specified string is a prefix of this string, * {@code false} otherwise - * @since Android 1.0 + * @throws NullPointerException + * if {@code prefix} is {@code null}. */ public boolean startsWith(String prefix) { return startsWith(prefix, 0); @@ -1519,7 +1595,8 @@ public final class String implements Serializable, Comparable, * the starting offset. * @return {@code true} if the specified string occurs in this string at the * specified offset, {@code false} otherwise. - * @since Android 1.0 + * @throws NullPointerException + * if {@code prefix} is {@code null}. */ public boolean startsWith(String prefix, int start) { return regionMatches(start, prefix, 0, prefix.count); @@ -1534,7 +1611,6 @@ public final class String implements Serializable, Comparable, * the string. * @throws IndexOutOfBoundsException * if {@code start < 0} or {@code start > length()}. - * @since Android 1.0 */ public String substring(int start) { if (start == 0) { @@ -1557,7 +1633,6 @@ public final class String implements Serializable, Comparable, * @throws IndexOutOfBoundsException * if {@code start < 0}, {@code start > end} or {@code end > * length()}. - * @since Android 1.0 */ public String substring(int start, int end) { if (start == 0 && end == count) { @@ -1575,7 +1650,6 @@ public final class String implements Serializable, Comparable, * Copies the characters in this string to a character array. * * @return a character array containing the characters of this string. - * @since Android 1.0 */ public char[] toCharArray() { char[] buffer = new char[count]; @@ -1589,7 +1663,6 @@ public final class String implements Serializable, Comparable, * * @return a new string containing the lowercase characters equivalent to * the characters in this string. - * @since Android 1.0 */ public String toLowerCase() { return toLowerCase(Locale.getDefault()); @@ -1603,12 +1676,11 @@ public final class String implements Serializable, Comparable, * the Locale to use. * @return a new string containing the lowercase characters equivalent to * the characters in this string. - * @since Android 1.0 */ public String toLowerCase(Locale locale) { for (int o = offset, end = offset + count; o < end; o++) { char ch = value[o]; - if (ch != Character.toLowerCase(ch)) { + if (ch != toLowerCase(ch)) { char[] buffer = new char[count]; int i = o - offset; // Not worth checking for i == 0 case @@ -1616,12 +1688,12 @@ public final class String implements Serializable, Comparable, // Turkish if (!"tr".equals(locale.getLanguage())) { //$NON-NLS-1$ while (i < count) { - buffer[i++] = Character.toLowerCase(value[o++]); + buffer[i++] = toLowerCase(value[o++]); } } else { while (i < count) { - buffer[i++] = (ch = value[o++]) != 0x49 ? Character - .toLowerCase(ch) : (char) 0x131; + buffer[i++] = (ch = value[o++]) != 0x49 ? toLowerCase(ch) + : (char) 0x131; } } return new String(0, count, buffer); @@ -1634,7 +1706,6 @@ public final class String implements Serializable, Comparable, * Returns this string. * * @return this string. - * @since Android 1.0 */ @Override public String toString() { @@ -1647,12 +1718,14 @@ public final class String implements Serializable, Comparable, * * @return a new string containing the uppercase characters equivalent to * the characters in this string. - * @since Android 1.0 */ public String toUpperCase() { return toUpperCase(Locale.getDefault()); } + // BEGIN android-note + // put this in a helper class so that it's only initialized on demand? + // END android-note private static final char[] upperValues = "SS\u0000\u02bcN\u0000J\u030c\u0000\u0399\u0308\u0301\u03a5\u0308\u0301\u0535\u0552\u0000H\u0331\u0000T\u0308\u0000W\u030a\u0000Y\u030a\u0000A\u02be\u0000\u03a5\u0313\u0000\u03a5\u0313\u0300\u03a5\u0313\u0301\u03a5\u0313\u0342\u1f08\u0399\u0000\u1f09\u0399\u0000\u1f0a\u0399\u0000\u1f0b\u0399\u0000\u1f0c\u0399\u0000\u1f0d\u0399\u0000\u1f0e\u0399\u0000\u1f0f\u0399\u0000\u1f08\u0399\u0000\u1f09\u0399\u0000\u1f0a\u0399\u0000\u1f0b\u0399\u0000\u1f0c\u0399\u0000\u1f0d\u0399\u0000\u1f0e\u0399\u0000\u1f0f\u0399\u0000\u1f28\u0399\u0000\u1f29\u0399\u0000\u1f2a\u0399\u0000\u1f2b\u0399\u0000\u1f2c\u0399\u0000\u1f2d\u0399\u0000\u1f2e\u0399\u0000\u1f2f\u0399\u0000\u1f28\u0399\u0000\u1f29\u0399\u0000\u1f2a\u0399\u0000\u1f2b\u0399\u0000\u1f2c\u0399\u0000\u1f2d\u0399\u0000\u1f2e\u0399\u0000\u1f2f\u0399\u0000\u1f68\u0399\u0000\u1f69\u0399\u0000\u1f6a\u0399\u0000\u1f6b\u0399\u0000\u1f6c\u0399\u0000\u1f6d\u0399\u0000\u1f6e\u0399\u0000\u1f6f\u0399\u0000\u1f68\u0399\u0000\u1f69\u0399\u0000\u1f6a\u0399\u0000\u1f6b\u0399\u0000\u1f6c\u0399\u0000\u1f6d\u0399\u0000\u1f6e\u0399\u0000\u1f6f\u0399\u0000\u1fba\u0399\u0000\u0391\u0399\u0000\u0386\u0399\u0000\u0391\u0342\u0000\u0391\u0342\u0399\u0391\u0399\u0000\u1fca\u0399\u0000\u0397\u0399\u0000\u0389\u0399\u0000\u0397\u0342\u0000\u0397\u0342\u0399\u0397\u0399\u0000\u0399\u0308\u0300\u0399\u0308\u0301\u0399\u0342\u0000\u0399\u0308\u0342\u03a5\u0308\u0300\u03a5\u0308\u0301\u03a1\u0313\u0000\u03a5\u0342\u0000\u03a5\u0308\u0342\u1ffa\u0399\u0000\u03a9\u0399\u0000\u038f\u0399\u0000\u03a9\u0342\u0000\u03a9\u0342\u0399\u03a9\u0399\u0000FF\u0000FI\u0000FL\u0000FFIFFLST\u0000ST\u0000\u0544\u0546\u0000\u0544\u0535\u0000\u0544\u053b\u0000\u054e\u0546\u0000\u0544\u053d\u0000".value; //$NON-NLS-1$ /** @@ -1721,7 +1794,6 @@ public final class String implements Serializable, Comparable, * the Locale to use. * @return a new string containing the uppercase characters equivalent to * the characters in this string. - * @since Android 1.0 */ public String toUpperCase(Locale locale) { boolean turkish = "tr".equals(locale.getLanguage()); //$NON-NLS-1$ @@ -1736,8 +1808,8 @@ public final class String implements Serializable, Comparable, System.arraycopy(output, 0, newoutput, 0, output.length); output = newoutput; } - char upch = !turkish ? Character.toUpperCase(ch) - : (ch != 0x69 ? Character.toUpperCase(ch) + char upch = !turkish ? toUpperCase(ch) + : (ch != 0x69 ? toUpperCase(ch) : (char) 0x130); if (ch != upch) { if (output == null) { @@ -1785,7 +1857,6 @@ public final class String implements Serializable, Comparable, * * @return a new string with characters <= \\u0020 removed from * the beginning and the end. - * @since Android 1.0 */ public String trim() { int start = offset, last = offset + count - 1; @@ -1810,7 +1881,8 @@ public final class String implements Serializable, Comparable, * @param data * the array of characters. * @return the new string. - * @since Android 1.0 + * @throws NullPointerException + * if {@code data} is {@code null}. */ public static String valueOf(char[] data) { return new String(data, 0, data.length); @@ -1831,7 +1903,8 @@ public final class String implements Serializable, Comparable, * @throws IndexOutOfBoundsException * if {@code length < 0}, {@code start < 0} or {@code start + * length > data.length} - * @since Android 1.0 + * @throws NullPointerException + * if {@code data} is {@code null}. */ public static String valueOf(char[] data, int start, int length) { return new String(data, start, length); @@ -1843,7 +1916,6 @@ public final class String implements Serializable, Comparable, * @param value * the character. * @return the character converted to a string. - * @since Android 1.0 */ public static String valueOf(char value) { String s; @@ -1862,7 +1934,6 @@ public final class String implements Serializable, Comparable, * @param value * the double. * @return the double converted to a string. - * @since Android 1.0 */ public static String valueOf(double value) { return Double.toString(value); @@ -1874,7 +1945,6 @@ public final class String implements Serializable, Comparable, * @param value * the float. * @return the float converted to a string. - * @since Android 1.0 */ public static String valueOf(float value) { return Float.toString(value); @@ -1886,7 +1956,6 @@ public final class String implements Serializable, Comparable, * @param value * the integer. * @return the integer converted to a string. - * @since Android 1.0 */ public static String valueOf(int value) { return Integer.toString(value); @@ -1898,7 +1967,6 @@ public final class String implements Serializable, Comparable, * @param value * the long. * @return the long converted to a string. - * @since Android 1.0 */ public static String valueOf(long value) { return Long.toString(value); @@ -1912,7 +1980,6 @@ public final class String implements Serializable, Comparable, * @param value * the object. * @return the object converted to a string, or the string {@code "null"}. - * @since Android 1.0 */ public static String valueOf(Object value) { return value != null ? value.toString() : "null"; //$NON-NLS-1$ @@ -1926,7 +1993,6 @@ public final class String implements Serializable, Comparable, * @param value * the boolean. * @return the boolean converted to a string. - * @since Android 1.0 */ public static String valueOf(boolean value) { return value ? "true" : "false"; //$NON-NLS-1$ //$NON-NLS-2$ @@ -1941,7 +2007,9 @@ public final class String implements Serializable, Comparable, * @return {@code true} if the characters in {@code strbuf} are identical to * those in this string. If they are not, {@code false} will be * returned. - * @since Android 1.0 + * @throws NullPointerException + * if {@code strbuf} is {@code null}. + * @since 1.4 */ public boolean contentEquals(StringBuffer strbuf) { synchronized (strbuf) { @@ -1957,11 +2025,11 @@ public final class String implements Serializable, Comparable, /** * Compares a {@code CharSequence} to this {@code String} to determine if * their contents are equal. - * + * * @param cs * the character sequence to compare to. * @return {@code true} if equal, otherwise {@code false} - * @since Android 1.0 + * @since 1.5 */ public boolean contentEquals(CharSequence cs) { if (cs == null) { @@ -1990,7 +2058,9 @@ public final class String implements Serializable, Comparable, * @throws PatternSyntaxException * if the syntax of the supplied regular expression is not * valid. - * @since Android 1.0 + * @throws NullPointerException + * if {@code expr} is {@code null}. + * @since 1.4 */ public boolean matches(String expr) { return Pattern.matches(expr, this); @@ -2009,7 +2079,7 @@ public final class String implements Serializable, Comparable, * if the syntax of the supplied regular expression is not * valid. * @see Pattern - * @since Android 1.0 + * @since 1.4 */ public String replaceAll(String expr, String substitute) { return Pattern.compile(expr).matcher(this).replaceAll(substitute); @@ -2027,8 +2097,10 @@ public final class String implements Serializable, Comparable, * @throws PatternSyntaxException * if the syntax of the supplied regular expression is not * valid. + * @throws NullPointerException + * if {@code strbuf} is {@code null}. * @see Pattern - * @since Android 1.0 + * @since 1.4 */ public String replaceFirst(String expr, String substitute) { return Pattern.compile(expr).matcher(this).replaceFirst(substitute); @@ -2041,11 +2113,13 @@ public final class String implements Serializable, Comparable, * the regular expression used to divide the string. * @return an array of Strings created by separating the string along * matches of the regular expression. + * @throws NullPointerException + * if {@code expr} is {@code null}. * @throws PatternSyntaxException * if the syntax of the supplied regular expression is not * valid. * @see Pattern - * @since Android 1.0 + * @since 1.4 */ public String[] split(String expr) { return Pattern.compile(expr).split(this); @@ -2062,11 +2136,13 @@ public final class String implements Serializable, Comparable, * the number of entries in the resulting array. * @return an array of Strings created by separating the string along * matches of the regular expression. + * @throws NullPointerException + * if {@code expr} is {@code null}. * @throws PatternSyntaxException * if the syntax of the supplied regular expression is not * valid. * @see Pattern#split(CharSequence, int) - * @since Android 1.0 + * @since 1.4 */ public String[] split(String expr, int max) { return Pattern.compile(expr).split(this, max); @@ -2085,7 +2161,7 @@ public final class String implements Serializable, Comparable, * if {@code start < 0}, {@code end < 0}, {@code start > end} or * {@code end > length()}. * @see java.lang.CharSequence#subSequence(int, int) - * @since Android 1.0 + * @since 1.4 */ public CharSequence subSequence(int start, int end) { return substring(start, end); @@ -2102,7 +2178,7 @@ public final class String implements Serializable, Comparable, * if {@code index} is negative or greater than or equal to * {@code length()}. * @see Character#codePointAt(char[], int, int) - * @since Android 1.0 + * @since 1.5 */ public int codePointAt(int index) { if (index < 0 || index >= count) { @@ -2123,7 +2199,7 @@ public final class String implements Serializable, Comparable, * if {@code index} is less than 1 or greater than * {@code length()}. * @see Character#codePointBefore(char[], int, int) - * @since Android 1.0 + * @since 1.5 */ public int codePointBefore(int index) { if (index < 1 || index > count) { @@ -2147,7 +2223,7 @@ public final class String implements Serializable, Comparable, * endIndex} or {@code endIndex} is greater than {@code * length()}. * @see Character#codePointCount(CharSequence, int, int) - * @since Android 1.0 + * @since 1.5 */ public int codePointCount(int beginIndex, int endIndex) { if (beginIndex < 0 || endIndex > count || beginIndex > endIndex) { @@ -2160,12 +2236,12 @@ public final class String implements Serializable, Comparable, /** * Determines if this {@code String} contains the sequence of characters in * the {@code CharSequence} passed. - * + * * @param cs * the character sequence to search for. * @return {@code true} if the sequence of characters are contained in this * string, otherwise {@code false}. - * @since Android 1.0 + * @since 1.5 */ public boolean contains(CharSequence cs) { if (cs == null) { @@ -2177,7 +2253,7 @@ public final class String implements Serializable, Comparable, /** * Returns the index within this object that is offset from {@code index} by * {@code codePointOffset} code points. - * + * * @param index * the index within this object to calculate the offset from. * @param codePointOffset @@ -2187,7 +2263,7 @@ public final class String implements Serializable, Comparable, * if {@code index} is negative or greater than {@code length()} * or if there aren't enough code points before or after {@code * index} to match {@code codePointOffset}. - * @since Android 1.0 + * @since 1.5 */ public int offsetByCodePoints(int index, int codePointOffset) { int s = index + offset; @@ -2205,10 +2281,12 @@ public final class String implements Serializable, Comparable, * @param args * arguments to replace format specifiers (may be none). * @return the formatted string. - * @throws IllegalFormatException + * @throws NullPointerException + * if {@code format} is {@code null}. + * @throws java.util.IllegalFormatException * if the format is invalid. * @see java.util.Formatter - * @since Android 1.0 + * @since 1.5 */ public static String format(String format, Object... args) { return format(Locale.getDefault(), format, args); @@ -2224,7 +2302,7 @@ public final class String implements Serializable, Comparable, * if you use the method only rarely, but if you rely on it for formatting a * large number of strings, consider creating and reusing your own * {@link java.util.Formatter} instance instead. - * + * * @param loc * the locale to apply; {@code null} value means no localization. * @param format @@ -2232,10 +2310,12 @@ public final class String implements Serializable, Comparable, * @param args * arguments to replace format specifiers (may be none). * @return the formatted string. - * @throws IllegalFormatException + * @throws NullPointerException + * if {@code format} is {@code null}. + * @throws java.util.IllegalFormatException * if the format is invalid. * @see java.util.Formatter - * @since Android 1.0 + * @since 1.5 */ public static String format(Locale loc, String format, Object... args) { if (format == null) { diff --git a/libcore/luni/src/main/java/java/lang/StringBuffer.java b/libcore/luni/src/main/java/java/lang/StringBuffer.java index 5cb5c24b8..3ec43dca5 100644 --- a/libcore/luni/src/main/java/java/lang/StringBuffer.java +++ b/libcore/luni/src/main/java/java/lang/StringBuffer.java @@ -39,7 +39,7 @@ import java.io.Serializable; * * @see String * @see StringBuilder - * @since Android 1.0 + * @since 1.0 */ public final class StringBuffer extends AbstractStringBuilder implements Appendable, Serializable, CharSequence { @@ -53,8 +53,6 @@ public final class StringBuffer extends AbstractStringBuilder implements /** * Constructs a new StringBuffer using the default capacity which is 16. - * - * @since Android 1.0 */ public StringBuffer() { super(); @@ -65,7 +63,6 @@ public final class StringBuffer extends AbstractStringBuilder implements * * @param capacity * the initial capacity. - * @since Android 1.0 */ public StringBuffer(int capacity) { super(capacity); @@ -73,12 +70,13 @@ public final class StringBuffer extends AbstractStringBuilder implements /** * Constructs a new StringBuffer containing the characters in the specified - * stringy. The capacity of the new buffer will be the length of the + * string. The capacity of the new buffer will be the length of the * {@code String} plus the default capacity. * * @param string * the string content with which to initialize the new instance. - * @since Android 1.0 + * @throws NullPointerException + * if {@code string} is {@code null}. */ public StringBuffer(String string) { super(string); @@ -91,7 +89,9 @@ public final class StringBuffer extends AbstractStringBuilder implements * * @param cs * the content to initialize the instance. - * @since Android 1.0 + * @throws NullPointerException + * if {@code cs} is {@code null}. + * @since 1.5 */ public StringBuffer(CharSequence cs) { super(cs.toString()); @@ -103,13 +103,11 @@ public final class StringBuffer extends AbstractStringBuilder implements *

          * If the argument is {@code true} the string {@code "true"} is appended, * otherwise the string {@code "false"} is appended. - *

          - * + * * @param b * the boolean to append. * @return this StringBuffer. * @see String#valueOf(boolean) - * @since Android 1.0 */ public StringBuffer append(boolean b) { return append(b ? "true" : "false"); //$NON-NLS-1$//$NON-NLS-2$ @@ -122,7 +120,6 @@ public final class StringBuffer extends AbstractStringBuilder implements * the character to append. * @return this StringBuffer. * @see String#valueOf(char) - * @since Android 1.0 */ public synchronized StringBuffer append(char ch) { append0(ch); @@ -137,7 +134,6 @@ public final class StringBuffer extends AbstractStringBuilder implements * the double to append. * @return this StringBuffer. * @see String#valueOf(double) - * @since Android 1.0 */ public StringBuffer append(double d) { return append(Double.toString(d)); @@ -151,7 +147,6 @@ public final class StringBuffer extends AbstractStringBuilder implements * the float to append. * @return this StringBuffer. * @see String#valueOf(float) - * @since Android 1.0 */ public StringBuffer append(float f) { return append(Float.toString(f)); @@ -165,7 +160,6 @@ public final class StringBuffer extends AbstractStringBuilder implements * the integer to append. * @return this StringBuffer. * @see String#valueOf(int) - * @since Android 1.0 */ public StringBuffer append(int i) { return append(Integer.toString(i)); @@ -179,7 +173,6 @@ public final class StringBuffer extends AbstractStringBuilder implements * the long to append. * @return this StringBuffer. * @see String#valueOf(long) - * @since Android 1.0 */ public StringBuffer append(long l) { return append(Long.toString(l)); @@ -192,13 +185,11 @@ public final class StringBuffer extends AbstractStringBuilder implements * If the specified object is {@code null} the string {@code "null"} is * appended, otherwise the objects {@code toString} is used to get its * string representation. - *

          - * + * * @param obj * the object to append (may be null). * @return this StringBuffer. * @see String#valueOf(Object) - * @since Android 1.0 */ public synchronized StringBuffer append(Object obj) { if (obj == null) { @@ -214,12 +205,10 @@ public final class StringBuffer extends AbstractStringBuilder implements *

          * If the specified string is {@code null} the string {@code "null"} is * appended, otherwise the contents of the specified string is appended. - *

          - * + * * @param string * the string to append (may be null). * @return this StringBuffer. - * @since Android 1.0 */ public synchronized StringBuffer append(String string) { append0(string); @@ -232,12 +221,12 @@ public final class StringBuffer extends AbstractStringBuilder implements * If the specified StringBuffer is {@code null} the string {@code "null"} * is appended, otherwise the contents of the specified StringBuffer is * appended. - *

          - * + * * @param sb * the StringBuffer to append (may be null). * @return this StringBuffer. - * @since Android 1.0 + * + * @since 1.4 */ public synchronized StringBuffer append(StringBuffer sb) { if (sb == null) { @@ -256,7 +245,8 @@ public final class StringBuffer extends AbstractStringBuilder implements * @param chars * the character array to append. * @return this StringBuffer. - * @since Android 1.0 + * @throws NullPointerException + * if {@code chars} is {@code null}. */ public synchronized StringBuffer append(char chars[]) { append0(chars); @@ -276,7 +266,8 @@ public final class StringBuffer extends AbstractStringBuilder implements * @throws ArrayIndexOutOfBoundsException * if {@code length < 0} , {@code start < 0} or {@code start + * length > chars.length}. - * @since Android 1.0 + * @throws NullPointerException + * if {@code chars} is {@code null}. */ public synchronized StringBuffer append(char chars[], int start, int length) { append0(chars, start, length); @@ -289,12 +280,11 @@ public final class StringBuffer extends AbstractStringBuilder implements * If the specified CharSequence is {@code null} the string {@code "null"} * is appended, otherwise the contents of the specified CharSequence is * appended. - *

          - * + * * @param s * the CharSequence to append. * @return this StringBuffer. - * @since Android 1.0 + * @since 1.5 */ public synchronized StringBuffer append(CharSequence s) { if (s == null) { @@ -310,8 +300,7 @@ public final class StringBuffer extends AbstractStringBuilder implements *

          * If the specified CharSequence is {@code null}, then the string {@code * "null"} is used to extract a subsequence. - *

          - * + * * @param s * the CharSequence to append. * @param start @@ -323,7 +312,7 @@ public final class StringBuffer extends AbstractStringBuilder implements * if {@code start} or {@code end} are negative, {@code start} * is greater than {@code end} or {@code end} is greater than * the length of {@code s}. - * @since Android 1.0 + * @since 1.5 */ public synchronized StringBuffer append(CharSequence s, int start, int end) { append0(s, start, end); @@ -336,13 +325,12 @@ public final class StringBuffer extends AbstractStringBuilder implements *

          * The code point is converted to a {@code char[]} as defined by * {@link Character#toChars(int)}. - *

          - * + * * @param codePoint * the Unicode code point to encode and append. * @return this StringBuffer. * @see Character#toChars(int) - * @since Android 1.0 + * @since 1.5 */ public StringBuffer appendCodePoint(int codePoint) { return append(Character.toChars(codePoint)); @@ -379,7 +367,6 @@ public final class StringBuffer extends AbstractStringBuilder implements * @throws StringIndexOutOfBoundsException * if {@code start < 0}, {@code start > end} or {@code end > * length()}. - * @since Android 1.0 */ public synchronized StringBuffer delete(int start, int end) { delete0(start, end); @@ -394,7 +381,6 @@ public final class StringBuffer extends AbstractStringBuilder implements * @return this StringBuffer. * @throws StringIndexOutOfBoundsException * if {@code location < 0} or {@code location >= length()} - * @since Android 1.0 */ public synchronized StringBuffer deleteCharAt(int location) { deleteCharAt0(location); @@ -405,6 +391,7 @@ public final class StringBuffer extends AbstractStringBuilder implements public synchronized void ensureCapacity(int min) { super.ensureCapacity(min); } + /** * Copies the requested sequence of characters to the {@code char[]} passed * starting at {@code idx}. @@ -421,7 +408,6 @@ public final class StringBuffer extends AbstractStringBuilder implements * if {@code start < 0}, {@code end > length()}, {@code start > * end}, {@code index < 0}, {@code end - start > buffer.length - * index} - * @since Android 1.0 */ @Override public synchronized void getChars(int start, int end, char[] buffer, int idx) { @@ -443,7 +429,6 @@ public final class StringBuffer extends AbstractStringBuilder implements * @return this buffer. * @throws ArrayIndexOutOfBoundsException * if {@code index < 0} or {@code index > length()}. - * @since Android 1.0 */ public synchronized StringBuffer insert(int index, char ch) { insert0(index, ch); @@ -461,7 +446,6 @@ public final class StringBuffer extends AbstractStringBuilder implements * @return this buffer. * @throws StringIndexOutOfBoundsException * if {@code index < 0} or {@code index > length()}. - * @since Android 1.0 */ public StringBuffer insert(int index, boolean b) { return insert(index, b ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$ @@ -478,7 +462,6 @@ public final class StringBuffer extends AbstractStringBuilder implements * @return this buffer. * @throws StringIndexOutOfBoundsException * if {@code index < 0} or {@code index > length()}. - * @since Android 1.0 */ public StringBuffer insert(int index, int i) { return insert(index, Integer.toString(i)); @@ -495,7 +478,6 @@ public final class StringBuffer extends AbstractStringBuilder implements * @return this buffer. * @throws StringIndexOutOfBoundsException * if {@code index < 0} or {@code index > length()}. - * @since Android 1.0 */ public StringBuffer insert(int index, long l) { return insert(index, Long.toString(l)); @@ -512,7 +494,6 @@ public final class StringBuffer extends AbstractStringBuilder implements * @return this buffer. * @throws StringIndexOutOfBoundsException * if {@code index < 0} or {@code index > length()}. - * @since Android 1.0 */ public StringBuffer insert(int index, double d) { return insert(index, Double.toString(d)); @@ -529,7 +510,6 @@ public final class StringBuffer extends AbstractStringBuilder implements * @return this buffer. * @throws StringIndexOutOfBoundsException * if {@code index < 0} or {@code index > length()}. - * @since Android 1.0 */ public StringBuffer insert(int index, float f) { return insert(index, Float.toString(f)); @@ -542,8 +522,7 @@ public final class StringBuffer extends AbstractStringBuilder implements * If the specified object is {@code null}, the string {@code "null"} is * inserted, otherwise the objects {@code toString} method is used to get * its string representation. - *

          - * + * * @param index * the index at which to insert. * @param obj @@ -551,7 +530,6 @@ public final class StringBuffer extends AbstractStringBuilder implements * @return this buffer. * @throws StringIndexOutOfBoundsException * if {@code index < 0} or {@code index > length()}. - * @since Android 1.0 */ public StringBuffer insert(int index, Object obj) { return insert(index, obj == null ? "null" : obj.toString()); //$NON-NLS-1$ @@ -562,8 +540,7 @@ public final class StringBuffer extends AbstractStringBuilder implements *

          * If the specified string is {@code null}, the string {@code "null"} is * inserted, otherwise the contents of the string is inserted. - *

          - * + * * @param index * the index at which to insert. * @param string @@ -571,7 +548,6 @@ public final class StringBuffer extends AbstractStringBuilder implements * @return this buffer. * @throws StringIndexOutOfBoundsException * if {@code index < 0} or {@code index > length()}. - * @since Android 1.0 */ public synchronized StringBuffer insert(int index, String string) { insert0(index, string); @@ -588,7 +564,8 @@ public final class StringBuffer extends AbstractStringBuilder implements * @return this buffer. * @throws StringIndexOutOfBoundsException * if {@code index < 0} or {@code index > length()}. - * @since Android 1.0 + * @throws NullPointerException + * if {@code chars} is {@code null}. */ public synchronized StringBuffer insert(int index, char[] chars) { insert0(index, chars); @@ -608,11 +585,12 @@ public final class StringBuffer extends AbstractStringBuilder implements * @param length * the number of characters. * @return this buffer. + * @throws NullPointerException + * if {@code chars} is {@code null}. * @throws StringIndexOutOfBoundsException * if {@code length < 0}, {@code start < 0}, {@code start + * length > chars.length}, {@code index < 0} or {@code index > * length()} - * @since Android 1.0 */ public synchronized StringBuffer insert(int index, char chars[], int start, int length) { @@ -626,8 +604,7 @@ public final class StringBuffer extends AbstractStringBuilder implements *

          * If the specified CharSequence is {@code null}, the string {@code "null"} * is inserted, otherwise the contents of the CharSequence. - *

          - * + * * @param index * The index at which to insert. * @param s @@ -635,7 +612,7 @@ public final class StringBuffer extends AbstractStringBuilder implements * @return this buffer. * @throws IndexOutOfBoundsException * if {@code index < 0} or {@code index > length()}. - * @since Android 1.0 + * @since 1.5 */ public synchronized StringBuffer insert(int index, CharSequence s) { insert0(index, s == null ? "null" : s.toString()); //$NON-NLS-1$ @@ -648,7 +625,6 @@ public final class StringBuffer extends AbstractStringBuilder implements *

          * If the specified CharSequence is {@code null}, the string {@code "null"} * is inserted, otherwise the contents of the CharSequence. - *

          * * @param index * The index at which to insert. @@ -664,7 +640,7 @@ public final class StringBuffer extends AbstractStringBuilder implements * length, {@code start} or {@code end} are negative, {@code * start} is greater than {@code end} or {@code end} is greater * than the length of {@code s}. - * @since Android 1.0 + * @since 1.5 */ public synchronized StringBuffer insert(int index, CharSequence s, int start, int end) { @@ -697,7 +673,6 @@ public final class StringBuffer extends AbstractStringBuilder implements * if {@code start} or {@code end} are negative, {@code start} * is greater than {@code end} or {@code end} is greater than * the length of {@code s}. - * @since Android 1.0 */ public synchronized StringBuffer replace(int start, int end, String string) { replace0(start, end, string); @@ -708,7 +683,6 @@ public final class StringBuffer extends AbstractStringBuilder implements * Reverses the order of characters in this buffer. * * @return this buffer. - * @since Android 1.0 */ public synchronized StringBuffer reverse() { reverse0(); diff --git a/libcore/luni/src/main/java/java/lang/StringBuilder.java b/libcore/luni/src/main/java/java/lang/StringBuilder.java index d62f9008e..12d401f48 100644 --- a/libcore/luni/src/main/java/java/lang/StringBuilder.java +++ b/libcore/luni/src/main/java/java/lang/StringBuilder.java @@ -32,13 +32,13 @@ import java.io.Serializable; * StringBuilder}, so that, like {@code StringBuffer}s, they can be used in * chaining method calls together. For example, {@code new StringBuilder("One * should ").append("always strive ").append("to achieve Harmony")}. - *

          * * @see CharSequence * @see Appendable * @see StringBuffer * @see String - * @since Android 1.0 + * + * @since 1.5 */ public final class StringBuilder extends AbstractStringBuilder implements Appendable, CharSequence, Serializable { @@ -49,7 +49,6 @@ public final class StringBuilder extends AbstractStringBuilder implements * Constructs an instance with an initial capacity of {@code 16}. * * @see #capacity() - * @since Android 1.0 */ public StringBuilder() { super(); @@ -57,13 +56,12 @@ public final class StringBuilder extends AbstractStringBuilder implements /** * Constructs an instance with the specified capacity. - * + * * @param capacity * the initial capacity to use. * @throws NegativeArraySizeException * if the specified {@code capacity} is negative. * @see #capacity() - * @since Android 1.0 */ public StringBuilder(int capacity) { super(capacity); @@ -73,10 +71,11 @@ public final class StringBuilder extends AbstractStringBuilder implements * Constructs an instance that's initialized with the contents of the * specified {@code CharSequence}. The capacity of the new builder will be * the length of the {@code CharSequence} plus 16. - * + * * @param seq * the {@code CharSequence} to copy into the builder. - * @since Android 1.0 + * @throws NullPointerException + * if {@code seq} is {@code null}. */ public StringBuilder(CharSequence seq) { super(seq.toString()); @@ -86,10 +85,11 @@ public final class StringBuilder extends AbstractStringBuilder implements * Constructs an instance that's initialized with the contents of the * specified {@code String}. The capacity of the new builder will be the * length of the {@code String} plus 16. - * + * * @param str * the {@code String} to copy into the builder. - * @since Android 1.0 + * @throws NullPointerException + * if {@code str} is {@code null}. */ public StringBuilder(String str) { super(str); @@ -99,12 +99,11 @@ public final class StringBuilder extends AbstractStringBuilder implements * Appends the string representation of the specified {@code boolean} value. * The {@code boolean} value is converted to a String according to the rule * defined by {@link String#valueOf(boolean)}. - * + * * @param b * the {@code boolean} value to append. * @return this builder. * @see String#valueOf(boolean) - * @since Android 1.0 */ public StringBuilder append(boolean b) { append0(b ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$ @@ -115,12 +114,11 @@ public final class StringBuilder extends AbstractStringBuilder implements * Appends the string representation of the specified {@code char} value. * The {@code char} value is converted to a string according to the rule * defined by {@link String#valueOf(char)}. - * + * * @param c * the {@code char} value to append. * @return this builder. * @see String#valueOf(char) - * @since Android 1.0 */ public StringBuilder append(char c) { append0(c); @@ -131,12 +129,11 @@ public final class StringBuilder extends AbstractStringBuilder implements * Appends the string representation of the specified {@code int} value. The * {@code int} value is converted to a string according to the rule defined * by {@link String#valueOf(int)}. - * + * * @param i * the {@code int} value to append. * @return this builder. * @see String#valueOf(int) - * @since Android 1.0 */ public StringBuilder append(int i) { append0(Integer.toString(i)); @@ -147,12 +144,11 @@ public final class StringBuilder extends AbstractStringBuilder implements * Appends the string representation of the specified {@code long} value. * The {@code long} value is converted to a string according to the rule * defined by {@link String#valueOf(long)}. - * + * * @param lng * the {@code long} value. * @return this builder. * @see String#valueOf(long) - * @since Android 1.0 */ public StringBuilder append(long lng) { append0(Long.toString(lng)); @@ -163,12 +159,11 @@ public final class StringBuilder extends AbstractStringBuilder implements * Appends the string representation of the specified {@code float} value. * The {@code float} value is converted to a string according to the rule * defined by {@link String#valueOf(float)}. - * + * * @param f * the {@code float} value to append. * @return this builder. * @see String#valueOf(float) - * @since Android 1.0 */ public StringBuilder append(float f) { append0(Float.toString(f)); @@ -179,12 +174,11 @@ public final class StringBuilder extends AbstractStringBuilder implements * Appends the string representation of the specified {@code double} value. * The {@code double} value is converted to a string according to the rule * defined by {@link String#valueOf(double)}. - * + * * @param d * the {@code double} value to append. * @return this builder. * @see String#valueOf(double) - * @since Android 1.0 */ public StringBuilder append(double d) { append0(Double.toString(d)); @@ -195,12 +189,11 @@ public final class StringBuilder extends AbstractStringBuilder implements * Appends the string representation of the specified {@code Object}. * The {@code Object} value is converted to a string according to the rule * defined by {@link String#valueOf(Object)}. - * + * * @param obj * the {@code Object} to append. * @return this builder. * @see String#valueOf(Object) - * @since Android 1.0 */ public StringBuilder append(Object obj) { if (obj == null) { @@ -214,11 +207,10 @@ public final class StringBuilder extends AbstractStringBuilder implements /** * Appends the contents of the specified string. If the string is {@code * null}, then the string {@code "null"} is appended. - * + * * @param str * the string to append. * @return this builder. - * @since Android 1.0 */ public StringBuilder append(String str) { append0(str); @@ -229,11 +221,10 @@ public final class StringBuilder extends AbstractStringBuilder implements * Appends the contents of the specified {@code StringBuffer}. If the * StringBuffer is {@code null}, then the string {@code "null"} is * appended. - * + * * @param sb * the {@code StringBuffer} to append. * @return this builder. - * @since Android 1.0 */ public StringBuilder append(StringBuffer sb) { if (sb == null) { @@ -248,12 +239,11 @@ public final class StringBuilder extends AbstractStringBuilder implements * Appends the string representation of the specified {@code char[]}. * The {@code char[]} is converted to a string according to the rule * defined by {@link String#valueOf(char[])}. - * + * * @param ch * the {@code char[]} to append.. * @return this builder. * @see String#valueOf(char[]) - * @since Android 1.0 */ public StringBuilder append(char[] ch) { append0(ch); @@ -264,7 +254,7 @@ public final class StringBuilder extends AbstractStringBuilder implements * Appends the string representation of the specified subset of the {@code * char[]}. The {@code char[]} value is converted to a String according to * the rule defined by {@link String#valueOf(char[],int,int)}. - * + * * @param str * the {@code char[]} to append. * @param offset @@ -276,7 +266,6 @@ public final class StringBuilder extends AbstractStringBuilder implements * if {@code offset} and {@code len} do not specify a valid * subsequence. * @see String#valueOf(char[],int,int) - * @since Android 1.0 */ public StringBuilder append(char[] str, int offset, int len) { append0(str, offset, len); @@ -287,11 +276,10 @@ public final class StringBuilder extends AbstractStringBuilder implements * Appends the string representation of the specified {@code CharSequence}. * If the {@code CharSequence} is {@code null}, then the string {@code * "null"} is appended. - * + * * @param csq * the {@code CharSequence} to append. * @return this builder. - * @since Android 1.0 */ public StringBuilder append(CharSequence csq) { if (csq == null) { @@ -306,7 +294,7 @@ public final class StringBuilder extends AbstractStringBuilder implements * Appends the string representation of the specified subsequence of the * {@code CharSequence}. If the {@code CharSequence} is {@code null}, then * the string {@code "null"} is used to extract the subsequence from. - * + * * @param csq * the {@code CharSequence} to append. * @param start @@ -318,7 +306,6 @@ public final class StringBuilder extends AbstractStringBuilder implements * if {@code start} or {@code end} are negative, {@code start} * is greater than {@code end} or {@code end} is greater than * the length of {@code csq}. - * @since Android 1.0 */ public StringBuilder append(CharSequence csq, int start, int end) { append0(csq, start, end); @@ -328,12 +315,11 @@ public final class StringBuilder extends AbstractStringBuilder implements /** * Appends the encoded Unicode code point. The code point is converted to a * {@code char[]} as defined by {@link Character#toChars(int)}. - * + * * @param codePoint * the Unicode code point to encode and append. * @return this builder. * @see Character#toChars(int) - * @since Android 1.0 */ public StringBuilder appendCodePoint(int codePoint) { append0(Character.toChars(codePoint)); @@ -352,7 +338,6 @@ public final class StringBuilder extends AbstractStringBuilder implements * @throws StringIndexOutOfBoundsException * if {@code start} is less than zero, greater than the current * length or greater than {@code end}. - * @since Android 1.0 */ public StringBuilder delete(int start, int end) { delete0(start, end); @@ -369,7 +354,6 @@ public final class StringBuilder extends AbstractStringBuilder implements * @throws StringIndexOutOfBoundsException * if {@code index} is less than zero or is greater than or * equal to the current length. - * @since Android 1.0 */ public StringBuilder deleteCharAt(int index) { deleteCharAt0(index); @@ -381,7 +365,7 @@ public final class StringBuilder extends AbstractStringBuilder implements * at the specified {@code offset}. The {@code boolean} value is converted * to a string according to the rule defined by * {@link String#valueOf(boolean)}. - * + * * @param offset * the index to insert at. * @param b @@ -391,7 +375,6 @@ public final class StringBuilder extends AbstractStringBuilder implements * if {@code offset} is negative or greater than the current * {@code length}. * @see String#valueOf(boolean) - * @since Android 1.0 */ public StringBuilder insert(int offset, boolean b) { insert0(offset, b ? "true" : "false"); //$NON-NLS-1$ //$NON-NLS-2$ @@ -402,7 +385,7 @@ public final class StringBuilder extends AbstractStringBuilder implements * Inserts the string representation of the specified {@code char} value at * the specified {@code offset}. The {@code char} value is converted to a * string according to the rule defined by {@link String#valueOf(char)}. - * + * * @param offset * the index to insert at. * @param c @@ -412,7 +395,6 @@ public final class StringBuilder extends AbstractStringBuilder implements * if {@code offset} is negative or greater than the current * {@code length()}. * @see String#valueOf(char) - * @since Android 1.0 */ public StringBuilder insert(int offset, char c) { insert0(offset, c); @@ -423,7 +405,7 @@ public final class StringBuilder extends AbstractStringBuilder implements * Inserts the string representation of the specified {@code int} value at * the specified {@code offset}. The {@code int} value is converted to a * String according to the rule defined by {@link String#valueOf(int)}. - * + * * @param offset * the index to insert at. * @param i @@ -433,7 +415,6 @@ public final class StringBuilder extends AbstractStringBuilder implements * if {@code offset} is negative or greater than the current * {@code length()}. * @see String#valueOf(int) - * @since Android 1.0 */ public StringBuilder insert(int offset, int i) { insert0(offset, Integer.toString(i)); @@ -444,7 +425,7 @@ public final class StringBuilder extends AbstractStringBuilder implements * Inserts the string representation of the specified {@code long} value at * the specified {@code offset}. The {@code long} value is converted to a * String according to the rule defined by {@link String#valueOf(long)}. - * + * * @param offset * the index to insert at. * @param l @@ -454,7 +435,6 @@ public final class StringBuilder extends AbstractStringBuilder implements * if {@code offset} is negative or greater than the current * {code length()}. * @see String#valueOf(long) - * @since Android 1.0 */ public StringBuilder insert(int offset, long l) { insert0(offset, Long.toString(l)); @@ -465,7 +445,7 @@ public final class StringBuilder extends AbstractStringBuilder implements * Inserts the string representation of the specified {@code float} value at * the specified {@code offset}. The {@code float} value is converted to a * string according to the rule defined by {@link String#valueOf(float)}. - * + * * @param offset * the index to insert at. * @param f @@ -475,7 +455,6 @@ public final class StringBuilder extends AbstractStringBuilder implements * if {@code offset} is negative or greater than the current * {@code length()}. * @see String#valueOf(float) - * @since Android 1.0 */ public StringBuilder insert(int offset, float f) { insert0(offset, Float.toString(f)); @@ -487,7 +466,7 @@ public final class StringBuilder extends AbstractStringBuilder implements * at the specified {@code offset}. The {@code double} value is converted * to a String according to the rule defined by * {@link String#valueOf(double)}. - * + * * @param offset * the index to insert at. * @param d @@ -497,7 +476,6 @@ public final class StringBuilder extends AbstractStringBuilder implements * if {@code offset} is negative or greater than the current * {@code length()}. * @see String#valueOf(double) - * @since Android 1.0 */ public StringBuilder insert(int offset, double d) { insert0(offset, Double.toString(d)); @@ -508,7 +486,7 @@ public final class StringBuilder extends AbstractStringBuilder implements * Inserts the string representation of the specified {@code Object} at the * specified {@code offset}. The {@code Object} value is converted to a * String according to the rule defined by {@link String#valueOf(Object)}. - * + * * @param offset * the index to insert at. * @param obj @@ -518,7 +496,6 @@ public final class StringBuilder extends AbstractStringBuilder implements * if {@code offset} is negative or greater than the current * {@code length()}. * @see String#valueOf(Object) - * @since Android 1.0 */ public StringBuilder insert(int offset, Object obj) { insert0(offset, obj == null ? "null" : obj.toString()); //$NON-NLS-1$ @@ -528,7 +505,7 @@ public final class StringBuilder extends AbstractStringBuilder implements /** * Inserts the specified string at the specified {@code offset}. If the * specified string is null, then the String {@code "null"} is inserted. - * + * * @param offset * the index to insert at. * @param str @@ -537,7 +514,6 @@ public final class StringBuilder extends AbstractStringBuilder implements * @throws StringIndexOutOfBoundsException * if {@code offset} is negative or greater than the current * {@code length()}. - * @since Android 1.0 */ public StringBuilder insert(int offset, String str) { insert0(offset, str); @@ -548,7 +524,7 @@ public final class StringBuilder extends AbstractStringBuilder implements * Inserts the string representation of the specified {@code char[]} at the * specified {@code offset}. The {@code char[]} value is converted to a * String according to the rule defined by {@link String#valueOf(char[])}. - * + * * @param offset * the index to insert at. * @param ch @@ -558,7 +534,6 @@ public final class StringBuilder extends AbstractStringBuilder implements * if {@code offset} is negative or greater than the current * {@code length()}. * @see String#valueOf(char[]) - * @since Android 1.0 */ public StringBuilder insert(int offset, char[] ch) { insert0(offset, ch); @@ -570,7 +545,7 @@ public final class StringBuilder extends AbstractStringBuilder implements * {@code char[]} at the specified {@code offset}. The {@code char[]} value * is converted to a String according to the rule defined by * {@link String#valueOf(char[],int,int)}. - * + * * @param offset * the index to insert at. * @param str @@ -585,7 +560,6 @@ public final class StringBuilder extends AbstractStringBuilder implements * {@code length()}, or {@code strOffset} and {@code strLen} do * not specify a valid subsequence. * @see String#valueOf(char[],int,int) - * @since Android 1.0 */ public StringBuilder insert(int offset, char[] str, int strOffset, int strLen) { @@ -598,7 +572,7 @@ public final class StringBuilder extends AbstractStringBuilder implements * at the specified {@code offset}. The {@code CharSequence} is converted * to a String as defined by {@link CharSequence#toString()}. If {@code s} * is {@code null}, then the String {@code "null"} is inserted. - * + * * @param offset * the index to insert at. * @param s @@ -608,7 +582,6 @@ public final class StringBuilder extends AbstractStringBuilder implements * if {@code offset} is negative or greater than the current * {@code length()}. * @see CharSequence#toString() - * @since Android 1.0 */ public StringBuilder insert(int offset, CharSequence s) { insert0(offset, s == null ? "null" : s.toString()); //$NON-NLS-1$ @@ -622,7 +595,7 @@ public final class StringBuilder extends AbstractStringBuilder implements * {@link CharSequence#subSequence(int, int)}. If the {@code CharSequence} * is {@code null}, then the string {@code "null"} is used to determine the * subsequence. - * + * * @param offset * the index to insert at. * @param s @@ -637,7 +610,6 @@ public final class StringBuilder extends AbstractStringBuilder implements * {@code length()}, or {@code start} and {@code end} do not * specify a valid subsequence. * @see CharSequence#subSequence(int, int) - * @since Android 1.0 */ public StringBuilder insert(int offset, CharSequence s, int start, int end) { insert0(offset, s, start, end); @@ -658,7 +630,8 @@ public final class StringBuilder extends AbstractStringBuilder implements * @throws StringIndexOutOfBoundsException * if {@code start} is negative, greater than the current * {@code length()} or greater than {@code end}. - * @since Android 1.0 + * @throws NullPointerException + * if {@code str} is {@code null}. */ public StringBuilder replace(int start, int end, String str) { replace0(start, end, str); @@ -669,7 +642,6 @@ public final class StringBuilder extends AbstractStringBuilder implements * Reverses the order of characters in this builder. * * @return this buffer. - * @since Android 1.0 */ public StringBuilder reverse() { reverse0(); @@ -680,7 +652,6 @@ public final class StringBuilder extends AbstractStringBuilder implements * Returns the contents of this builder. * * @return the string representation of the data in this builder. - * @since Android 1.0 */ @Override public String toString() { @@ -695,7 +666,7 @@ public final class StringBuilder extends AbstractStringBuilder implements /** * Reads the state of a {@code StringBuilder} from the passed stream and * restores it to this instance. - * + * * @param in * the stream to read the state from. * @throws IOException @@ -713,7 +684,7 @@ public final class StringBuilder extends AbstractStringBuilder implements /** * Writes the state of this object to the stream passed. - * + * * @param out * the stream to write the state to. * @throws IOException diff --git a/libcore/luni/src/main/java/java/lang/StringIndexOutOfBoundsException.java b/libcore/luni/src/main/java/java/lang/StringIndexOutOfBoundsException.java index a2fca9a66..b3fbf7ea4 100644 --- a/libcore/luni/src/main/java/java/lang/StringIndexOutOfBoundsException.java +++ b/libcore/luni/src/main/java/java/lang/StringIndexOutOfBoundsException.java @@ -23,8 +23,6 @@ import org.apache.harmony.luni.util.Msg; /** * Thrown when the a string is indexed with a value less than zero, or greater * than or equal to the size of the array. - * - * @since Android 1.0 */ public class StringIndexOutOfBoundsException extends IndexOutOfBoundsException { @@ -33,8 +31,6 @@ public class StringIndexOutOfBoundsException extends IndexOutOfBoundsException { /** * Constructs a new {@code StringIndexOutOfBoundsException} that includes * the current stack trace. - * - * @since Android 1.0 */ public StringIndexOutOfBoundsException() { super(); @@ -47,7 +43,6 @@ public class StringIndexOutOfBoundsException extends IndexOutOfBoundsException { * * @param index * the index which is out of bounds. - * @since Android 1.0 */ public StringIndexOutOfBoundsException(int index) { super(Msg.getString("K0055", index)); //$NON-NLS-1$ @@ -59,7 +54,6 @@ public class StringIndexOutOfBoundsException extends IndexOutOfBoundsException { * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public StringIndexOutOfBoundsException(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/lang/SuppressWarnings.java b/libcore/luni/src/main/java/java/lang/SuppressWarnings.java index d3b428962..1ff0e42cb 100644 --- a/libcore/luni/src/main/java/java/lang/SuppressWarnings.java +++ b/libcore/luni/src/main/java/java/lang/SuppressWarnings.java @@ -1,12 +1,12 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -16,15 +16,9 @@ package java.lang; -import static java.lang.annotation.ElementType.CONSTRUCTOR; -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.ElementType.LOCAL_VARIABLE; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.SOURCE; - +import java.lang.annotation.ElementType; import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** @@ -35,18 +29,17 @@ import java.lang.annotation.Target; *

          * It is recommended that programmers always use this annotation on the most * deeply nested element where it is actually needed. - *

          - * - * @since Android 1.0 + * + * @since 1.5 */ -@Target(value={TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE}) -@Retention(value=SOURCE) -public @interface SuppressWarnings -{ +@Target( { ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, + ElementType.PARAMETER, ElementType.CONSTRUCTOR, + ElementType.LOCAL_VARIABLE }) +@Retention(RetentionPolicy.SOURCE) +public @interface SuppressWarnings { + /** * The list of warnings a compiler should not issue. - * - * @since Android 1.0 */ - String[] value(); + public String[] value(); } diff --git a/libcore/luni/src/main/java/java/lang/ThreadDeath.java b/libcore/luni/src/main/java/java/lang/ThreadDeath.java index e4f2cc2ae..300686505 100644 --- a/libcore/luni/src/main/java/java/lang/ThreadDeath.java +++ b/libcore/luni/src/main/java/java/lang/ThreadDeath.java @@ -20,8 +20,6 @@ package java.lang; /** * ThreadDeath is thrown when a thread stops executing. It is used to aid in the * orderly unrolling of the thread's stack (eg. cleanup of monitors). - * - * @since Android 1.0 */ public class ThreadDeath extends Error { @@ -31,8 +29,6 @@ public class ThreadDeath extends Error { * Constructs a new instance of this class. Note that in the case of * ThreadDeath, the stacktrace may not be filled in a way which * allows a stack trace to be printed. - * - * @since Android 1.0 */ public ThreadDeath() { } diff --git a/libcore/luni/src/main/java/java/lang/ThreadLocal.java b/libcore/luni/src/main/java/java/lang/ThreadLocal.java index 58e85be05..90250b3a7 100644 --- a/libcore/luni/src/main/java/java/lang/ThreadLocal.java +++ b/libcore/luni/src/main/java/java/lang/ThreadLocal.java @@ -16,6 +16,12 @@ package java.lang; +/* + * Android's thread local is not derived from Harmony's classlib. It is used in + * Harmony's DRLVM, however, whose source is here: + * http://svn.apache.org/viewvc/harmony/enhanced/drlvm/trunk/vm/vmcore/src/kernel_classes/ + */ + import java.lang.ref.WeakReference; import java.lang.ref.Reference; import java.util.concurrent.atomic.AtomicInteger; @@ -29,7 +35,6 @@ import java.util.concurrent.atomic.AtomicInteger; * * @see java.lang.Thread * @author Bob Lee - * @since Android 1.0 */ public class ThreadLocal { @@ -97,6 +102,8 @@ public class ThreadLocal { * is followed by a {@link #get()} before a {@link #set}, * {@code #get()} will call {@link #initialValue()} and create a new * entry with the resulting value. + * + * @since 1.5 */ public void remove() { Thread currentThread = Thread.currentThread(); diff --git a/libcore/luni/src/main/java/java/lang/TypeNotPresentException.java b/libcore/luni/src/main/java/java/lang/TypeNotPresentException.java index 11ef4c19f..d96f766ce 100644 --- a/libcore/luni/src/main/java/java/lang/TypeNotPresentException.java +++ b/libcore/luni/src/main/java/java/lang/TypeNotPresentException.java @@ -21,8 +21,8 @@ package java.lang; * type through a string that contains the type's name and the type cannot be * found. This exception is an unchecked alternative to * {@link java.lang.ClassNotFoundException}. - * - * @since Android 1.0 + * + * @since 1.5 */ public class TypeNotPresentException extends RuntimeException { private static final long serialVersionUID = -5101214195716534496L; @@ -38,7 +38,6 @@ public class TypeNotPresentException extends RuntimeException { * the fully qualified name of the type that could not be found. * @param cause * the optional cause of this exception, may be {@code null}. - * @since Android 1.0 */ public TypeNotPresentException(String typeName, Throwable cause) { super("Type " + typeName + " not present", cause); diff --git a/libcore/luni/src/main/java/java/lang/UnknownError.java b/libcore/luni/src/main/java/java/lang/UnknownError.java index a98a5d4d7..ecdb0087d 100644 --- a/libcore/luni/src/main/java/java/lang/UnknownError.java +++ b/libcore/luni/src/main/java/java/lang/UnknownError.java @@ -20,8 +20,6 @@ package java.lang; /** * Thrown when the virtual machine must throw an error which does not match any * known exceptional condition. - * - * @since Android 1.0 */ public class UnknownError extends VirtualMachineError { @@ -30,8 +28,6 @@ public class UnknownError extends VirtualMachineError { /** * Constructs a new {@code UnknownError} that includes the current stack * trace. - * - * @since Android 1.0 */ public UnknownError() { super(); @@ -43,7 +39,6 @@ public class UnknownError extends VirtualMachineError { * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public UnknownError(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/lang/UnsatisfiedLinkError.java b/libcore/luni/src/main/java/java/lang/UnsatisfiedLinkError.java index 489601ff3..12429a036 100644 --- a/libcore/luni/src/main/java/java/lang/UnsatisfiedLinkError.java +++ b/libcore/luni/src/main/java/java/lang/UnsatisfiedLinkError.java @@ -20,8 +20,6 @@ package java.lang; /** * Thrown when an attempt is made to invoke a native for which an implementation * could not be found. - * - * @since Android 1.0 */ public class UnsatisfiedLinkError extends LinkageError { @@ -30,8 +28,6 @@ public class UnsatisfiedLinkError extends LinkageError { /** * Constructs a new {@code UnsatisfiedLinkError} that includes the current * stack trace. - * - * @since Android 1.0 */ public UnsatisfiedLinkError() { super(); @@ -43,7 +39,6 @@ public class UnsatisfiedLinkError extends LinkageError { * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public UnsatisfiedLinkError(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/lang/UnsupportedClassVersionError.java b/libcore/luni/src/main/java/java/lang/UnsupportedClassVersionError.java index 3ee8f8cb2..7670f704d 100644 --- a/libcore/luni/src/main/java/java/lang/UnsupportedClassVersionError.java +++ b/libcore/luni/src/main/java/java/lang/UnsupportedClassVersionError.java @@ -20,8 +20,6 @@ package java.lang; /** * Thrown when an attempt is made to load a class with a format version that is * not supported by the virtual machine. - * - * @since Android 1.0 */ public class UnsupportedClassVersionError extends ClassFormatError { @@ -30,8 +28,6 @@ public class UnsupportedClassVersionError extends ClassFormatError { /** * Constructs a new {@code UnsupportedClassVersionError} that includes the * current stack trace. - * - * @since Android 1.0 */ public UnsupportedClassVersionError() { super(); @@ -43,7 +39,6 @@ public class UnsupportedClassVersionError extends ClassFormatError { * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public UnsupportedClassVersionError(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/lang/UnsupportedOperationException.java b/libcore/luni/src/main/java/java/lang/UnsupportedOperationException.java index 8b782267f..ac27518b0 100644 --- a/libcore/luni/src/main/java/java/lang/UnsupportedOperationException.java +++ b/libcore/luni/src/main/java/java/lang/UnsupportedOperationException.java @@ -19,8 +19,6 @@ package java.lang; /** * Thrown when an unsupported operation is attempted. - * - * @since Android 1.0 */ public class UnsupportedOperationException extends RuntimeException { @@ -29,13 +27,8 @@ public class UnsupportedOperationException extends RuntimeException { /** * Constructs a new {@code UnsupportedOperationException} that includes the * current stack trace. - * - * @since Android 1.0 */ public UnsupportedOperationException() { - // BEGIN android-added - super(); - // END android-added } /** @@ -44,7 +37,6 @@ public class UnsupportedOperationException extends RuntimeException { * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public UnsupportedOperationException(String detailMessage) { super(detailMessage); @@ -58,7 +50,7 @@ public class UnsupportedOperationException extends RuntimeException { * the detail message for this exception. * @param cause * the optional cause of this exception, may be {@code null}. - * @since Android 1.0 + * @since 1.5 */ public UnsupportedOperationException(String message, Throwable cause) { super(message, cause); @@ -70,7 +62,7 @@ public class UnsupportedOperationException extends RuntimeException { * * @param cause * the optional cause of this exception, may be {@code null}. - * @since Android 1.0 + * @since 1.5 */ public UnsupportedOperationException(Throwable cause) { super((cause == null ? null : cause.toString()), cause); diff --git a/libcore/luni/src/main/java/java/lang/VerifyError.java b/libcore/luni/src/main/java/java/lang/VerifyError.java index ddf4de789..35cf34b90 100644 --- a/libcore/luni/src/main/java/java/lang/VerifyError.java +++ b/libcore/luni/src/main/java/java/lang/VerifyError.java @@ -20,8 +20,6 @@ package java.lang; /** * Thrown when the virtual machine notices that an attempt is made to load a * class which does not pass the class verification phase. - * - * @since Android 1.0 */ public class VerifyError extends LinkageError { @@ -30,8 +28,6 @@ public class VerifyError extends LinkageError { /** * Constructs a new {@code VerifyError} that includes the current stack * trace. - * - * @since Android 1.0 */ public VerifyError() { super(); @@ -43,7 +39,6 @@ public class VerifyError extends LinkageError { * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public VerifyError(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/lang/VirtualMachineError.java b/libcore/luni/src/main/java/java/lang/VirtualMachineError.java index 107f56289..87a634226 100644 --- a/libcore/luni/src/main/java/java/lang/VirtualMachineError.java +++ b/libcore/luni/src/main/java/java/lang/VirtualMachineError.java @@ -23,7 +23,6 @@ package java.lang; * during the operation of the virtual machine. * * @see Error - * @since Android 1.0 */ public abstract class VirtualMachineError extends Error { @@ -32,8 +31,6 @@ public abstract class VirtualMachineError extends Error { /** * Constructs a new {@code VirtualMachineError} that includes the current * stack trace. - * - * @since Android 1.0 */ public VirtualMachineError() { super(); @@ -45,7 +42,6 @@ public abstract class VirtualMachineError extends Error { * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public VirtualMachineError(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/lang/Void.java b/libcore/luni/src/main/java/java/lang/Void.java index 8d06b642a..4132e4156 100644 --- a/libcore/luni/src/main/java/java/lang/Void.java +++ b/libcore/luni/src/main/java/java/lang/Void.java @@ -21,15 +21,13 @@ import java.lang.reflect.Method; /** * Placeholder class for the Java keyword {@code void}. - * - * @since Android 1.0 + * + * @since 1.1 */ public final class Void extends Object { /** * The {@link Class} object that represents the primitive type {@code void}. - * - * @since Android 1.0 */ public static final Class TYPE = lookupType(); @@ -48,6 +46,6 @@ public final class Void extends Object { return (Class) voidType; } - private Void() { - } + private Void() { + } } diff --git a/libcore/luni/src/main/java/java/lang/ref/ReferenceQueue.java b/libcore/luni/src/main/java/java/lang/ref/ReferenceQueue.java index eb6280b1e..ac6d69509 100644 --- a/libcore/luni/src/main/java/java/lang/ref/ReferenceQueue.java +++ b/libcore/luni/src/main/java/java/lang/ref/ReferenceQueue.java @@ -16,25 +16,24 @@ */ // BEGIN android-note -// This implementation was changed significantly. Changes where not marked. +// This implementation is quite different from Harmony. Changes are not marked. // END android-note + package java.lang.ref; /** * The {@code ReferenceQueue} is the container on which reference objects are * enqueued when the garbage collector detects the reachability type specified * for the referent. - * - * @since Android 1.0 + * + * @since 1.2 */ public class ReferenceQueue { - + private Reference head; /** * Constructs a new instance of this class. - * - * @since Android 1.0 */ public ReferenceQueue() { super(); @@ -43,11 +42,9 @@ public class ReferenceQueue { /** * Returns the next available reference from the queue, removing it in the * process. Does not wait for a reference to become available. - * + * * @return the next available reference, or {@code null} if no reference is * immediately available - * - * @since Android 1.0 */ @SuppressWarnings("unchecked") public synchronized Reference poll() { @@ -73,37 +70,31 @@ public class ReferenceQueue { /** * Returns the next available reference from the queue, removing it in the * process. Waits indefinitely for a reference to become available. - * + * * @return the next available reference - * + * * @throws InterruptedException - * if the blocking call was interrupted for some reason - * - * @since Android 1.0 + * if the blocking call was interrupted for some reason */ public Reference remove() throws InterruptedException { return remove(0L); } - + /** * Returns the next available reference from the queue, removing it in the * process. Waits for a reference to become available or the given timeout * period to elapse, whichever happens first. - * + * * @param timeout * maximum time (in ms) to spend waiting for a reference object * to become available. A value of zero results in the method * waiting indefinitely. - * * @return the next available reference, or {@code null} if no reference * becomes available within the timeout period - * * @throws IllegalArgumentException * if the wait period is negative. * @throws InterruptedException - * if the blocking call was interrupted for some reason - * - * @since Android 1.0 + * if the blocking call was interrupted for some reason */ public synchronized Reference remove(long timeout) throws IllegalArgumentException, InterruptedException { @@ -128,20 +119,20 @@ public class ReferenceQueue { } /** - * Enqueues the reference object on the receiver. - * - * @param toQueue + * Enqueue the reference object on the receiver. + * + * @param reference * reference object to be enqueued. - * - * @since Android 1.0 + * @return boolean true if reference is enqueued. false if reference failed + * to enqueue. */ - synchronized void enqueue(Reference toQueue) { + synchronized void enqueue(Reference reference) { if (head == null) { - toQueue.queueNext = toQueue; + reference.queueNext = reference; } else { - toQueue.queueNext = head; + reference.queueNext = head; } - head = toQueue; + head = reference; notify(); } } diff --git a/libcore/luni/src/main/java/java/lang/reflect/AnnotatedElement.java b/libcore/luni/src/main/java/java/lang/reflect/AnnotatedElement.java index 1f081b902..426daba84 100644 --- a/libcore/luni/src/main/java/java/lang/reflect/AnnotatedElement.java +++ b/libcore/luni/src/main/java/java/lang/reflect/AnnotatedElement.java @@ -21,8 +21,8 @@ import java.lang.annotation.Annotation; /** * This interface provides reflective access to annotation information. - * - * @since Android 1.0 + * + * @since 1.5 */ public interface AnnotatedElement { @@ -33,13 +33,9 @@ public interface AnnotatedElement { * * @param annotationType * the type of the annotation to search for - * * @return the annotation with the specified type or {@code null} - * * @throws NullPointerException * if {@code annotationType} is {@code null} - * - * @since Android 1.0 */ T getAnnotation(Class annotationType); @@ -49,8 +45,6 @@ public interface AnnotatedElement { * returns a zero length array. * * @return an array of all annotations for this element - * - * @since Android 1.0 */ Annotation[] getAnnotations(); @@ -60,8 +54,6 @@ public interface AnnotatedElement { * method returns a zero length array. * * @return an array of annotations declared for this element - * - * @since Android 1.0 */ Annotation[] getDeclaredAnnotations(); @@ -71,13 +63,9 @@ public interface AnnotatedElement { * * @param annotationType * the type of the annotation to search for - * * @return {@code true} if the annotation exists, {@code false} otherwise - * * @throws NullPointerException * if {@code annotationType} is {@code null} - * - * @since Android 1.0 */ boolean isAnnotationPresent(Class annotationType); } diff --git a/libcore/luni/src/main/java/java/lang/reflect/GenericArrayType.java b/libcore/luni/src/main/java/java/lang/reflect/GenericArrayType.java index 81fb39f21..56f873d58 100644 --- a/libcore/luni/src/main/java/java/lang/reflect/GenericArrayType.java +++ b/libcore/luni/src/main/java/java/lang/reflect/GenericArrayType.java @@ -21,22 +21,19 @@ package java.lang.reflect; * This interface represents an array type with a component type that is either * a parameterized type or a type variable. * - * @since Android 1.0 + * @since 1.5 */ public interface GenericArrayType extends Type { - /** * Returns the component type of this array. * * @return the component type of this array - * + * * @throws TypeNotPresentException * if the component type points to a missing type * @throws MalformedParameterizedTypeException * if the component type points to a type that cannot be * instantiated for some reason - * - * @since Android 1.0 */ Type getGenericComponentType(); -} +} \ No newline at end of file diff --git a/libcore/luni/src/main/java/java/lang/reflect/GenericDeclaration.java b/libcore/luni/src/main/java/java/lang/reflect/GenericDeclaration.java index c7cedcf91..880350bdc 100644 --- a/libcore/luni/src/main/java/java/lang/reflect/GenericDeclaration.java +++ b/libcore/luni/src/main/java/java/lang/reflect/GenericDeclaration.java @@ -18,8 +18,8 @@ package java.lang.reflect; /** * Common interface for language constructs that declare type parameters. - * - * @since Android 1.0 + * + * @since 1.5 */ public interface GenericDeclaration { @@ -28,11 +28,8 @@ public interface GenericDeclaration { * no type parameters, this method returns a zero length array. * * @return the declared type parameters in declaration order - * * @throws GenericSignatureFormatError * if the signature is malformed - * - * @since Android 1.0 */ TypeVariable[] getTypeParameters(); } diff --git a/libcore/luni/src/main/java/java/lang/reflect/GenericSignatureFormatError.java b/libcore/luni/src/main/java/java/lang/reflect/GenericSignatureFormatError.java index bb901fd73..c1130069f 100644 --- a/libcore/luni/src/main/java/java/lang/reflect/GenericSignatureFormatError.java +++ b/libcore/luni/src/main/java/java/lang/reflect/GenericSignatureFormatError.java @@ -20,8 +20,8 @@ package java.lang.reflect; /** * Indicates that a malformed signature has been encountered via a reflective * method. - * - * @since Android 1.0 + * + * @since 1.5 */ public class GenericSignatureFormatError extends ClassFormatError { @@ -29,8 +29,6 @@ public class GenericSignatureFormatError extends ClassFormatError { /** * Constructs a new {@code GenericSignatureFormatError} instance. - * - * @since Android 1.0 */ public GenericSignatureFormatError() { super(); diff --git a/libcore/luni/src/main/java/java/lang/reflect/InvocationHandler.java b/libcore/luni/src/main/java/java/lang/reflect/InvocationHandler.java index b28d53518..fe3eabaf0 100644 --- a/libcore/luni/src/main/java/java/lang/reflect/InvocationHandler.java +++ b/libcore/luni/src/main/java/java/lang/reflect/InvocationHandler.java @@ -21,8 +21,6 @@ package java.lang.reflect; * Implementors of this interface dispatch methods invoked on proxy instances. * * @see Proxy - * - * @since Android 1.0 */ public interface InvocationHandler { @@ -58,8 +56,6 @@ public interface InvocationHandler { * The exception must match one of the declared exception types * of the invoked method or any unchecked exception type. If not * then an {@code UndeclaredThrowableException} is thrown - * - * @since Android 1.0 */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; diff --git a/libcore/luni/src/main/java/java/lang/reflect/InvocationTargetException.java b/libcore/luni/src/main/java/java/lang/reflect/InvocationTargetException.java index 2d69135d3..6dc44e726 100644 --- a/libcore/luni/src/main/java/java/lang/reflect/InvocationTargetException.java +++ b/libcore/luni/src/main/java/java/lang/reflect/InvocationTargetException.java @@ -23,8 +23,6 @@ package java.lang.reflect; * * @see Method#invoke * @see Constructor#newInstance - * - * @since Android 1.0 */ public class InvocationTargetException extends Exception { @@ -35,8 +33,6 @@ public class InvocationTargetException extends Exception { /** * Constructs a new {@code InvocationTargetException} instance with a * {@code null} cause / target exception. - * - * @since Android 1.0 */ protected InvocationTargetException() { super((Throwable) null); @@ -49,8 +45,6 @@ public class InvocationTargetException extends Exception { * @param exception * the exception which occurred while running the Method or * Constructor - * - * @since Android 1.0 */ public InvocationTargetException(Throwable exception) { super(null, exception); @@ -66,8 +60,6 @@ public class InvocationTargetException extends Exception { * @param exception * the exception which occurred while running the Method or * Constructor - * - * @since Android 1.0 */ public InvocationTargetException(Throwable exception, String detailMessage) { super(detailMessage, exception); @@ -78,8 +70,6 @@ public class InvocationTargetException extends Exception { * Returns the target exception, which may be {@code null}. * * @return the target exception - * - * @since Android 1.0 */ public Throwable getTargetException() { return target; @@ -89,8 +79,6 @@ public class InvocationTargetException extends Exception { * Returns the cause of this exception, which may be {@code null}. * * @return the cause of this exception - * - * @since Android 1.0 */ @Override public Throwable getCause() { diff --git a/libcore/luni/src/main/java/java/lang/reflect/MalformedParameterizedTypeException.java b/libcore/luni/src/main/java/java/lang/reflect/MalformedParameterizedTypeException.java index 7dfbc6fe1..71f28195b 100644 --- a/libcore/luni/src/main/java/java/lang/reflect/MalformedParameterizedTypeException.java +++ b/libcore/luni/src/main/java/java/lang/reflect/MalformedParameterizedTypeException.java @@ -21,7 +21,7 @@ package java.lang.reflect; * Indicates that a malformed parameterized type has been encountered by a * reflective method. * - * @since Android 1.0 + * @since 1.5 */ public class MalformedParameterizedTypeException extends RuntimeException { @@ -29,8 +29,6 @@ public class MalformedParameterizedTypeException extends RuntimeException { /** * Constructs a new {@code MalformedParameterizedTypeException} instance. - * - * @since Android 1.0 */ public MalformedParameterizedTypeException() { super(); diff --git a/libcore/luni/src/main/java/java/lang/reflect/Member.java b/libcore/luni/src/main/java/java/lang/reflect/Member.java index fb0739f0e..a91e108e8 100644 --- a/libcore/luni/src/main/java/java/lang/reflect/Member.java +++ b/libcore/luni/src/main/java/java/lang/reflect/Member.java @@ -8,11 +8,11 @@ * * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package java.lang.reflect; @@ -23,28 +23,22 @@ package java.lang.reflect; * @see Field * @see Constructor * @see Method - * - * @since Android 1.0 */ public interface Member { /** * Designates all public members of a class or interface (including * inherited members). - * + * * @see java.lang.SecurityManager#checkMemberAccess - * - * @since Android 1.0 */ public static final int PUBLIC = 0; /** * Designates all declared members of a class or interface (without * inherited members). - * + * * @see java.lang.SecurityManager#checkMemberAccess - * - * @since Android 1.0 */ public static final int DECLARED = 1; @@ -52,8 +46,6 @@ public interface Member { * Returns the class that declares this member. * * @return the declaring class - * - * @since Android 1.0 */ @SuppressWarnings("unchecked") Class getDeclaringClass(); @@ -63,10 +55,8 @@ public interface Member { * be used to decode the result. * * @return the modifiers for this member - * + * * @see Modifier - * - * @since Android 1.0 */ int getModifiers(); @@ -74,8 +64,6 @@ public interface Member { * Returns the name of this member. * * @return the name of this member - * - * @since Android 1.0 */ String getName(); @@ -84,8 +72,6 @@ public interface Member { * introduced by the compiler). * * @return {@code true} if this member is synthetic, {@code false} otherwise - * - * @since Android 1.0 */ boolean isSynthetic(); } diff --git a/libcore/luni/src/main/java/java/lang/reflect/Modifier.java b/libcore/luni/src/main/java/java/lang/reflect/Modifier.java index ed201aa1f..900564772 100644 --- a/libcore/luni/src/main/java/java/lang/reflect/Modifier.java +++ b/libcore/luni/src/main/java/java/lang/reflect/Modifier.java @@ -22,100 +22,74 @@ package java.lang.reflect; * * @see Class#getModifiers() * @see Member#getModifiers() - * - * @since Android 1.0 */ public class Modifier { /** * The {@code int} value representing the {@code public} * modifier. - * - * @since Android 1.0 */ public static final int PUBLIC = 0x1; /** * The {@code int} value representing the {@code private} * modifier. - * - * @since Android 1.0 */ public static final int PRIVATE = 0x2; /** * The {@code int} value representing the {@code protected} * modifier. - * - * @since Android 1.0 */ public static final int PROTECTED = 0x4; /** * The {@code int} value representing the {@code static} modifier. - * - * @since Android 1.0 */ public static final int STATIC = 0x8; /** * The {@code int} value representing the {@code final} modifier. - * - * @since Android 1.0 */ public static final int FINAL = 0x10; /** * The {@code int} value representing the {@code synchronized} * modifier. - * - * @since Android 1.0 */ public static final int SYNCHRONIZED = 0x20; /** * The {@code int} value representing the {@code volatile} * modifier. - * - * @since Android 1.0 */ public static final int VOLATILE = 0x40; /** * The {@code int} value representing the {@code transient} * modifier. - * - * @since Android 1.0 */ public static final int TRANSIENT = 0x80; /** * The {@code int} value representing the {@code native} modifier. - * - * @since Android 1.0 */ public static final int NATIVE = 0x100; /** * The {@code int} value representing the {@code interface} * modifier. - * - * @since Android 1.0 */ public static final int INTERFACE = 0x200; /** * The {@code int} value representing the {@code abstract} * modifier. - * - * @since Android 1.0 */ public static final int ABSTRACT = 0x400; /** * The {@code int} value representing the {@code strict} modifier. - * - * @since Android 1.0 */ public static final int STRICT = 0x800; @@ -132,8 +106,6 @@ public class Modifier { /** * Constructs a new {@code Modifier} instance. - * - * @since Android 1.0 */ public Modifier() { } @@ -144,11 +116,8 @@ public class Modifier { * * @param modifiers * the modifiers to test - * * @return {@code true} if the specified modifiers contain the {@code * abstract} modifier, {@code false} otherwise - * - * @since Android 1.0 */ public static boolean isAbstract(int modifiers) { return ((modifiers & ABSTRACT) != 0); @@ -160,11 +129,8 @@ public class Modifier { * * @param modifiers * the modifiers to test - * * @return {@code true} if the specified modifiers contain the {@code * final} modifier, {@code false} otherwise - * - * @since Android 1.0 */ public static boolean isFinal(int modifiers) { return ((modifiers & FINAL) != 0); @@ -176,11 +142,8 @@ public class Modifier { * * @param modifiers * the modifiers to test - * * @return {@code true} if the specified modifiers contain the {@code * interface} modifier, {@code false} otherwise - * - * @since Android 1.0 */ public static boolean isInterface(int modifiers) { return ((modifiers & INTERFACE) != 0); @@ -192,11 +155,8 @@ public class Modifier { * * @param modifiers * the modifiers to test - * * @return {@code true} if the specified modifiers contain the {@code * native} modifier, {@code false} otherwise - * - * @since Android 1.0 */ public static boolean isNative(int modifiers) { return ((modifiers & NATIVE) != 0); @@ -208,11 +168,8 @@ public class Modifier { * * @param modifiers * the modifiers to test - * * @return {@code true} if the specified modifiers contain the {@code * private} modifier, {@code false} otherwise - * - * @since Android 1.0 */ public static boolean isPrivate(int modifiers) { return ((modifiers & PRIVATE) != 0); @@ -224,11 +181,8 @@ public class Modifier { * * @param modifiers * the modifiers to test - * * @return {@code true} if the specified modifiers contain the {@code * protected} modifier, {@code false} otherwise - * - * @since Android 1.0 */ public static boolean isProtected(int modifiers) { return ((modifiers & PROTECTED) != 0); @@ -240,11 +194,8 @@ public class Modifier { * * @param modifiers * the modifiers to test - * * @return {@code true} if the specified modifiers contain the {@code * public} modifier, {@code false} otherwise - * - * @since Android 1.0 */ public static boolean isPublic(int modifiers) { return ((modifiers & PUBLIC) != 0); @@ -256,11 +207,8 @@ public class Modifier { * * @param modifiers * the modifiers to test - * * @return {@code true} if the specified modifiers contain the {@code * static} modifier, {@code false} otherwise - * - * @since Android 1.0 */ public static boolean isStatic(int modifiers) { return ((modifiers & STATIC) != 0); @@ -272,11 +220,8 @@ public class Modifier { * * @param modifiers * the modifiers to test - * * @return {@code true} if the specified modifiers contain the {@code * strict} modifier, {@code false} otherwise - * - * @since Android 1.0 */ public static boolean isStrict(int modifiers) { return ((modifiers & STRICT) != 0); @@ -288,11 +233,8 @@ public class Modifier { * * @param modifiers * the modifiers to test - * * @return {@code true} if the specified modifiers contain the {@code * synchronized} modifier, {@code false} otherwise - * - * @since Android 1.0 */ public static boolean isSynchronized(int modifiers) { return ((modifiers & SYNCHRONIZED) != 0); @@ -304,11 +246,8 @@ public class Modifier { * * @param modifiers * the modifiers to test - * * @return {@code true} if the specified modifiers contain the {@code * transient} modifier, {@code false} otherwise - * - * @since Android 1.0 */ public static boolean isTransient(int modifiers) { return ((modifiers & TRANSIENT) != 0); @@ -320,11 +259,8 @@ public class Modifier { * * @param modifiers * the modifiers to test - * * @return {@code true} if the specified modifiers contain the {@code * volatile} modifier, {@code false} otherwise - * - * @since Android 1.0 */ public static boolean isVolatile(int modifiers) { return ((modifiers & VOLATILE) != 0); @@ -339,16 +275,11 @@ public class Modifier { * * @param modifiers * the modifiers to print - * * @return a printable representation of the modifiers - * - * @since Android 1.0 */ @SuppressWarnings("nls") public static java.lang.String toString(int modifiers) { - StringBuffer buf; - - buf = new StringBuffer(); + StringBuilder buf = new StringBuilder(); if (isPublic(modifiers)) { buf.append("public "); diff --git a/libcore/luni/src/main/java/java/lang/reflect/ParameterizedType.java b/libcore/luni/src/main/java/java/lang/reflect/ParameterizedType.java index 3d03921ea..91f2520b5 100644 --- a/libcore/luni/src/main/java/java/lang/reflect/ParameterizedType.java +++ b/libcore/luni/src/main/java/java/lang/reflect/ParameterizedType.java @@ -18,10 +18,10 @@ package java.lang.reflect; /** - * This interface represents a parameterized type such as {@code + * This interface represents a parameterized type such as {@code * 'Set<String>'}. - * - * @since Android 1.0 + * + * @since 1.5 */ public interface ParameterizedType extends Type { @@ -32,25 +32,23 @@ public interface ParameterizedType extends Type { * parameterized type, this method returns a zero length array. The generic * type of the following {@code field} declaration is an example for a * parameterized type without type arguments. - * + * *
                * A<String>.B field;
          -     * 
          +     *
                * class A<T> {
                *     class B {
                *     }
                * }
          - * - * + * + * * @return the actual type arguments - * + * * @throws TypeNotPresentException * if one of the type arguments cannot be found * @throws MalformedParameterizedTypeException * if one of the type arguments cannot be instantiated for some * reason - * - * @since Android 1.0 */ Type[] getActualTypeArguments(); @@ -59,13 +57,11 @@ public interface ParameterizedType extends Type { * {@code null} is returned if this is a top-level type. * * @return the owner type or {@code null} if this is a top-level type - * + * * @throws TypeNotPresentException * if one of the type arguments cannot be found * @throws MalformedParameterizedTypeException * if the owner type cannot be instantiated for some reason - * - * @since Android 1.0 */ Type getOwnerType(); @@ -73,10 +69,8 @@ public interface ParameterizedType extends Type { * Returns the declaring type of this parameterized type. *

          * The raw type of {@code Set<String> field;} is {@code Set}. - * + * * @return the raw type of this parameterized type - * - * @since Android 1.0 */ Type getRawType(); } diff --git a/libcore/luni/src/main/java/java/lang/reflect/Proxy.java b/libcore/luni/src/main/java/java/lang/reflect/Proxy.java index 29d15c562..cc0c956ce 100644 --- a/libcore/luni/src/main/java/java/lang/reflect/Proxy.java +++ b/libcore/luni/src/main/java/java/lang/reflect/Proxy.java @@ -35,8 +35,7 @@ import org.apache.harmony.luni.util.Msg; * invocations to an {@code InvocationHandler}. * * @see InvocationHandler - * - * @since Android 1.0 + * @since 1.3 */ public class Proxy implements Serializable { @@ -52,8 +51,6 @@ public class Proxy implements Serializable { /** * The invocation handler on which the method calls are dispatched. - * - * @since Android 1.0 */ protected InvocationHandler h; @@ -64,11 +61,9 @@ public class Proxy implements Serializable { /** * Constructs a new {@code Proxy} instance with the specified invocation * handler. - * + * * @param h * the invocation handler for the newly created proxy - * - * @since Android 1.0 */ protected Proxy(InvocationHandler h) { this.h = h; @@ -81,24 +76,20 @@ public class Proxy implements Serializable { * different order result in different generated classes. The interfaces * must be visible from the supplied class loader; no duplicates are * permitted. All non-public interfaces must be defined in the same package. - * + * * @param loader * the class loader that will define the proxy class * @param interfaces * an array of {@code Class} objects, each one identifying an * interface that will be implemented by the returned proxy * class - * * @return a proxy class that implements all of the interfaces referred to * in the contents of {@code interfaces} - * * @throws IllegalArgumentException * if any of the interface restrictions are violated * @throws NullPointerException * if either {@code interfaces} or any of its elements are * {@code null} - * - * @since Android 1.0 */ public static Class getProxyClass(ClassLoader loader, Class... interfaces) throws IllegalArgumentException { @@ -174,19 +165,17 @@ public class Proxy implements Serializable { WeakReference> ref = interfaceCache.get(interfaceKey); if (ref == null) { String nextClassName = "$Proxy" + NextClassNameIndex++; //$NON-NLS-1$ - if (commonPackageName != null) { + if (commonPackageName != null && commonPackageName.length() > 0) { nextClassName = commonPackageName + "." + nextClassName; //$NON-NLS-1$ } // BEGIN android-changed - //byte[] classFileBytes = ProxyClassFile.generateBytes( - // nextClassName, interfaces); - // END android-changed + // byte[] classFileBytes = ProxyClassFile.generateBytes( + // nextClassName, interfaces); + // newClass = defineClassImpl(loader, nextClassName.replace('.', + // '/'), classFileBytes); if (loader == null) { loader = ClassLoader.getSystemClassLoader(); } - // BEGIN android-changed - //newClass = defineClassImpl(loader, nextClassName.replace('.', - // '/'), classFileBytes); newClass = generateProxy(nextClassName.replace('.', '/'), interfaces, loader); // END android-changed @@ -200,6 +189,10 @@ public class Proxy implements Serializable { } } else { newClass = ref.get(); + assert newClass != null : "\ninterfaceKey=\"" + interfaceKey + "\"" + + "\nloaderCache=\"" + loaderCache + "\"" + + "\nintfCache=\"" + interfaceCache + "\"" + + "\nproxyCache=\"" + proxyCache + "\""; } return newClass; } @@ -211,7 +204,7 @@ public class Proxy implements Serializable { * the specified invocation handler. The interfaces must be visible from the * supplied class loader; no duplicates are permitted. All non-public * interfaces must be defined in the same package. - * + * * @param loader * the class loader that will define the proxy class * @param interfaces @@ -221,15 +214,11 @@ public class Proxy implements Serializable { * @param h * the invocation handler that handles the dispatched method * invocations - * * @return a new proxy object that delegates to the handler {@code h} - * * @throws IllegalArgumentException * if any of the interface restrictions are violated * @throws NullPointerException * if the interfaces or any of its elements are null - * - * @since Android 1.0 */ public static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) @@ -260,17 +249,13 @@ public class Proxy implements Serializable { /** * Indicates whether or not the specified class is a dynamically generated * proxy class. - * + * * @param cl * the class - * * @return {@code true} if the class is a proxy class, {@code false} * otherwise - * * @throws NullPointerException * if the class is {@code null} - * - * @since Android 1.0 */ public static boolean isProxyClass(Class cl) { if (cl == null) { @@ -283,16 +268,12 @@ public class Proxy implements Serializable { /** * Returns the invocation handler of the specified proxy instance. - * + * * @param proxy * the proxy instance - * * @return the invocation handler of the specified proxy instance - * * @throws IllegalArgumentException * if the supplied {@code proxy} is not a proxy object - * - * @since Android 1.0 */ public static InvocationHandler getInvocationHandler(Object proxy) throws IllegalArgumentException { diff --git a/libcore/luni/src/main/java/java/lang/reflect/ReflectPermission.java b/libcore/luni/src/main/java/java/lang/reflect/ReflectPermission.java index b56866331..93eef3996 100644 --- a/libcore/luni/src/main/java/java/lang/reflect/ReflectPermission.java +++ b/libcore/luni/src/main/java/java/lang/reflect/ReflectPermission.java @@ -22,8 +22,6 @@ import java.security.BasicPermission; /** * A {@code ReflectPermission} object represents a permission to access * operations in the reflection layer. - * - * @since Android 1.0 */ public final class ReflectPermission extends BasicPermission { @@ -35,13 +33,10 @@ public final class ReflectPermission extends BasicPermission { * * @param permissionName * the name of the new permission - * * @throws IllegalArgumentException * if {@code name} is empty * @throws NullPointerException * if {@code name} is {@code null} - * - * @since Android 1.0 */ public ReflectPermission(String permissionName) { super(permissionName); @@ -55,13 +50,10 @@ public final class ReflectPermission extends BasicPermission { * the name of the new permission * @param actions * this parameter will be ignored - * * @throws IllegalArgumentException * if {@code name} is empty * @throws NullPointerException * if {@code name} is {@code null} - * - * @since Android 1.0 */ public ReflectPermission(String name, String actions) { super(name, actions); diff --git a/libcore/luni/src/main/java/java/lang/reflect/Type.java b/libcore/luni/src/main/java/java/lang/reflect/Type.java index abd497814..a057088b0 100644 --- a/libcore/luni/src/main/java/java/lang/reflect/Type.java +++ b/libcore/luni/src/main/java/java/lang/reflect/Type.java @@ -18,8 +18,8 @@ package java.lang.reflect; /** * Common interface implemented by all Java types. - * - * @since Android 1.0 + * + * @since 1.5 */ public interface Type { // Empty diff --git a/libcore/luni/src/main/java/java/lang/reflect/TypeVariable.java b/libcore/luni/src/main/java/java/lang/reflect/TypeVariable.java index 99aca77ba..e3f8c1d09 100644 --- a/libcore/luni/src/main/java/java/lang/reflect/TypeVariable.java +++ b/libcore/luni/src/main/java/java/lang/reflect/TypeVariable.java @@ -17,15 +17,14 @@ package java.lang.reflect; /** - * This interface represents a type variables such as {@code 'T'} in {@code - * 'public interface Comparable<T>'}, the bounded {@code 'T'} in {@code + * This interface represents a type variables such as {@code 'T'} in {@code + * 'public interface Comparable<T>'}, the bounded {@code 'T'} in {@code * 'public interface A<T extends Number>'} or the multiple bounded {@code * 'T'} in {@code 'public interface B<T extends Number & Cloneable>'}. - * + * * @param * the generic declaration that declares this type variable - * - * @since Android 1.0 + * @since 1.5 */ public interface TypeVariable extends Type { @@ -33,15 +32,13 @@ public interface TypeVariable extends Type { * Returns the upper bounds of this type variable. {@code Object} is the * implicit upper bound if no other bounds are declared. * - * @return the upper bounds of this type variable - * + * @return the upper bounds of this type variable + * * @throws TypeNotPresentException * if any of the bounds points to a missing type * @throws MalformedParameterizedTypeException * if any of the bounds points to a type that cannot be * instantiated for some reason - * - * @since Android 1.0 */ Type[] getBounds(); @@ -49,8 +46,6 @@ public interface TypeVariable extends Type { * Returns the language construct that declares this type variable. * * @return the generic declaration - * - * @since Android 1.0 */ D getGenericDeclaration(); @@ -59,8 +54,6 @@ public interface TypeVariable extends Type { * code. * * @return the name of this type variable - * - * @since Android 1.0 */ String getName(); } diff --git a/libcore/luni/src/main/java/java/lang/reflect/UndeclaredThrowableException.java b/libcore/luni/src/main/java/java/lang/reflect/UndeclaredThrowableException.java index 108f98af1..bd5adfae4 100644 --- a/libcore/luni/src/main/java/java/lang/reflect/UndeclaredThrowableException.java +++ b/libcore/luni/src/main/java/java/lang/reflect/UndeclaredThrowableException.java @@ -22,8 +22,6 @@ package java.lang.reflect; * an InvocationHandler. * * @see java.lang.reflect.InvocationHandler#invoke - * - * @since Android 1.0 */ public class UndeclaredThrowableException extends RuntimeException { @@ -37,8 +35,6 @@ public class UndeclaredThrowableException extends RuntimeException { * * @param exception * the undeclared, checked exception that occurred - * - * @since Android 1.0 */ public UndeclaredThrowableException(Throwable exception) { super(); @@ -54,8 +50,6 @@ public class UndeclaredThrowableException extends RuntimeException { * the detail message for the exception * @param exception * the undeclared, checked exception that occurred - * - * @since Android 1.0 */ public UndeclaredThrowableException(Throwable exception, String detailMessage) { @@ -69,8 +63,6 @@ public class UndeclaredThrowableException extends RuntimeException { * {@code null}. * * @return the undeclared, checked exception that occurred - * - * @since Android 1.0 */ public Throwable getUndeclaredThrowable() { return undeclaredThrowable; @@ -81,8 +73,6 @@ public class UndeclaredThrowableException extends RuntimeException { * {@code null}. * * @return the undeclared, checked exception that occurred - * - * @since Android 1.0 */ @Override public Throwable getCause() { diff --git a/libcore/luni/src/main/java/java/lang/reflect/WildcardType.java b/libcore/luni/src/main/java/java/lang/reflect/WildcardType.java index 72a022df3..affd5264d 100644 --- a/libcore/luni/src/main/java/java/lang/reflect/WildcardType.java +++ b/libcore/luni/src/main/java/java/lang/reflect/WildcardType.java @@ -22,24 +22,21 @@ package java.lang.reflect; * {@code '?'}, the upper bounded wildcard {@code '? extends Closeable'}, the * multiple upper bounded wildcard {@code '? extends Closeable & Flushable'} or * the lower bounded wildcard {@code '? super OutputStream'}. - * - * @since Android 1.0 + * + * @since 1.5 */ public interface WildcardType extends Type { - /** * Returns the array of types that represent the upper bounds of this type. * The default upper bound is {@code Object}. * * @return an array containing the upper bounds types - * + * * @throws TypeNotPresentException * if any of the bounds points to a missing type * @throws MalformedParameterizedTypeException * if any bound points to a type that cannot be instantiated for * some reason - * - * @since Android 1.0 */ Type[] getUpperBounds(); @@ -48,16 +45,14 @@ public interface WildcardType extends Type { * The default lower bound is {@code null}, in which case an empty array is * returned. Since only one lower bound is allowed, the returned array's * length will never exceed one. - * + * * @return an array containing the lower bounds types - * + * * @throws TypeNotPresentException * if any of the bounds points to a missing type * @throws MalformedParameterizedTypeException * if any of the bounds points to a type that cannot be * instantiated for some reason - * - * @since Android 1.0 */ Type[] getLowerBounds(); } diff --git a/libcore/luni/src/main/java/java/net/Authenticator.java b/libcore/luni/src/main/java/java/net/Authenticator.java index 1eea8d805..75d68fa92 100644 --- a/libcore/luni/src/main/java/java/net/Authenticator.java +++ b/libcore/luni/src/main/java/java/net/Authenticator.java @@ -27,7 +27,6 @@ package java.net; * * @see #setDefault * @see #getPasswordAuthentication - * @since Android 1.0 */ public abstract class Authenticator { @@ -63,10 +62,8 @@ public abstract class Authenticator { * default which is {@code null}. *

          * Returns {@code null} by default. - *

          - * + * * @return collected password authentication data. - * @since Android 1.0 */ protected PasswordAuthentication getPasswordAuthentication() { return null; @@ -76,7 +73,6 @@ public abstract class Authenticator { * Returns the port of the connection that requests authorization. * * @return port of the connection. - * @since Android 1.0 */ protected final int getRequestingPort() { return this.port; @@ -87,7 +83,6 @@ public abstract class Authenticator { * {@code null} if unknown. * * @return address of the connection. - * @since Android 1.0 */ protected final InetAddress getRequestingSite() { return this.addr; @@ -98,7 +93,6 @@ public abstract class Authenticator { * authorization. * * @return prompt string of the connection. - * @since Android 1.0 */ protected final String getRequestingPrompt() { return this.prompt; @@ -108,7 +102,6 @@ public abstract class Authenticator { * Returns the protocol of the connection that requests authorization. * * @return protocol of the connection. - * @since Android 1.0 */ protected final String getRequestingProtocol() { return this.protocol; @@ -119,7 +112,6 @@ public abstract class Authenticator { * example HTTP Basic Authentication. * * @return scheme of the connection. - * @since Android 1.0 */ protected final String getRequestingScheme() { return this.scheme; @@ -145,7 +137,6 @@ public abstract class Authenticator { * @throws SecurityException * if a security manager denies the password authentication * permission. - * @since Android 1.0 */ public static synchronized PasswordAuthentication requestPasswordAuthentication( InetAddress rAddr, int rPort, String rProtocol, String rPrompt, @@ -182,7 +173,6 @@ public abstract class Authenticator { * @throws SecurityException * if a security manager denies the password authentication * permission. - * @since Android 1.0 */ public static void setDefault(Authenticator a) { SecurityManager sm = System.getSecurityManager(); @@ -214,7 +204,6 @@ public abstract class Authenticator { * @throws SecurityException * if a security manager denies the password authentication * permission. - * @since Android 1.0 */ public static synchronized PasswordAuthentication requestPasswordAuthentication( String rHost, InetAddress rAddr, int rPort, String rProtocol, @@ -244,9 +233,8 @@ public abstract class Authenticator { /** * Returns the host name of the connection that requests authentication or * {@code null} if unknown. - * + * * @return name of the requesting host or {@code null}. - * @since Android 1.0 */ protected final String getRequestingHost() { return host; @@ -278,7 +266,6 @@ public abstract class Authenticator { * @throws SecurityException * if a security manager denies the password authentication * permission. - * @since Android 1.0 */ public static PasswordAuthentication requestPasswordAuthentication( String rHost, InetAddress rAddr, int rPort, String rProtocol, @@ -312,7 +299,6 @@ public abstract class Authenticator { * Returns the URL of the authentication request. * * @return authentication request url. - * @since Android 1.0 */ protected URL getRequestingURL() { return url; @@ -322,7 +308,6 @@ public abstract class Authenticator { * Returns the type of this request, it can be {@code PROXY} or {@code SERVER}. * * @return RequestorType of the authentication request. - * @since Android 1.0 */ protected Authenticator.RequestorType getRequestorType() { return rt; @@ -330,8 +315,6 @@ public abstract class Authenticator { /** * Enumeration class for the origin of the authentication request. - * - * @since Android 1.0 */ public enum RequestorType { diff --git a/libcore/luni/src/main/java/java/net/BindException.java b/libcore/luni/src/main/java/java/net/BindException.java index 69be315df..48f1cf6af 100644 --- a/libcore/luni/src/main/java/java/net/BindException.java +++ b/libcore/luni/src/main/java/java/net/BindException.java @@ -20,8 +20,6 @@ package java.net; /** * A {@code BindException} is thrown when a process cannot bind a local * address/port, either because it is already bound or reserved by the OS. - * - * @since Android 1.0 */ public class BindException extends SocketException { @@ -29,8 +27,6 @@ public class BindException extends SocketException { /** * Constructs a new instance with its walkback filled in. - * - * @since Android 1.0 */ public BindException() { super(); @@ -41,7 +37,6 @@ public class BindException extends SocketException { * * @param detailMessage * detail message of the exception. - * @since Android 1.0 */ public BindException(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/net/CacheRequest.java b/libcore/luni/src/main/java/java/net/CacheRequest.java index 8c67a4443..778cb13d6 100644 --- a/libcore/luni/src/main/java/java/net/CacheRequest.java +++ b/libcore/luni/src/main/java/java/net/CacheRequest.java @@ -29,14 +29,11 @@ import java.io.OutputStream; * the current cache store operation is abandoned. * * @see ResponseCache - * @since Android 1.0 */ public abstract class CacheRequest { /** * This implementation does nothing. - * - * @since Android 1.0 */ public CacheRequest() { super(); @@ -46,8 +43,6 @@ public abstract class CacheRequest { * Aborts the current cache operation. If an {@code IOException} occurs * while reading the response or writing resource data to the cache, the * current cache store operation is aborted. - * - * @since Android 1.0 */ public abstract void abort(); @@ -58,7 +53,6 @@ public abstract class CacheRequest { * @throws IOException * if an I/O error is encountered during writing response body * operation. - * @since Android 1.0 */ public abstract OutputStream getBody() throws IOException; } diff --git a/libcore/luni/src/main/java/java/net/CacheResponse.java b/libcore/luni/src/main/java/java/net/CacheResponse.java index ae5be440c..b0ad09424 100644 --- a/libcore/luni/src/main/java/java/net/CacheResponse.java +++ b/libcore/luni/src/main/java/java/net/CacheResponse.java @@ -28,13 +28,10 @@ import java.util.Map; * getHeaders()} to fetch the response headers. * * @see ResponseCache - * @since Android 1.0 */ public abstract class CacheResponse { /** * This implementation does nothing. - * - * @since Android 1.0 */ public CacheResponse() { super(); @@ -48,7 +45,6 @@ public abstract class CacheResponse { * @throws IOException * if an I/O error is encountered while retrieving the response * body. - * @since Android 1.0 */ public abstract InputStream getBody() throws IOException; @@ -62,7 +58,6 @@ public abstract class CacheResponse { * @throws IOException * if an I/O error is encountered while retrieving the response * headers. - * @since Android 1.0 */ public abstract Map> getHeaders() throws IOException; } diff --git a/libcore/luni/src/main/java/java/net/ConnectException.java b/libcore/luni/src/main/java/java/net/ConnectException.java index 42389c768..84cd2bb4f 100644 --- a/libcore/luni/src/main/java/java/net/ConnectException.java +++ b/libcore/luni/src/main/java/java/net/ConnectException.java @@ -20,8 +20,6 @@ package java.net; /** * A {@code ConnectException} is thrown if a connection cannot be established to * a remote host on a specific port. - * - * @since Android 1.0 */ public class ConnectException extends SocketException { @@ -29,8 +27,6 @@ public class ConnectException extends SocketException { /** * This implementation does nothing. - * - * @since Android 1.0 */ public ConnectException() { super(); @@ -38,10 +34,9 @@ public class ConnectException extends SocketException { /** * This implementation does nothing. - * + * * @param detailMessage * detail message of the exception. - * @since Android 1.0 */ public ConnectException(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/net/ContentHandler.java b/libcore/luni/src/main/java/java/net/ContentHandler.java index 05c6acf0e..8e0bbb786 100644 --- a/libcore/luni/src/main/java/java/net/ContentHandler.java +++ b/libcore/luni/src/main/java/java/net/ContentHandler.java @@ -23,28 +23,26 @@ import java.io.IOException; * This class converts the content of a certain format (i.e. a MIME type) into a * Java type object. It is created by {@code ContentHandlerFactory}. The data * values should be accessed via {@code URL} or {@code URLConnection}. - * + * * @see ContentHandlerFactory * @see URL#getContent() * @see URLConnection#getContent() - * @since Android 1.0 */ public abstract class ContentHandler { /** * Returns the object pointed by the specified URL connection {@code uConn}. - * + * * @param uConn * URL connection that points to the desired object. * @return object referred by {@code uConn}. * @throws IOException * if an IO error occurs during the retrieval of the object - * @since Android 1.0 */ public abstract Object getContent(URLConnection uConn) throws IOException; /** * Returns the object pointed by the specified URL connection {@code uConn}. - * + * * @param uConn * URL connection that points to the desired object. * @param types @@ -53,7 +51,6 @@ public abstract class ContentHandler { * content doesn't match one of the specified content types. * @throws IOException * if an error occurred while obtaining the content. - * @since Android 1.0 */ // Class arg not generified in the spec. @SuppressWarnings("unchecked") diff --git a/libcore/luni/src/main/java/java/net/ContentHandlerFactory.java b/libcore/luni/src/main/java/java/net/ContentHandlerFactory.java index 1a564ee8d..3089c04ce 100644 --- a/libcore/luni/src/main/java/java/net/ContentHandlerFactory.java +++ b/libcore/luni/src/main/java/java/net/ContentHandlerFactory.java @@ -21,7 +21,6 @@ package java.net; * Defines a factory which is responsible for creating a {@code ContentHandler}. * * @see ContentHandler - * @since Android 1.0 */ public interface ContentHandlerFactory { /** @@ -31,7 +30,6 @@ public interface ContentHandlerFactory { * specifies the content type which is handled by the returned * {@code ContentHandler}. * @return content handler object for a specific content type. - * @since Android 1.0 */ ContentHandler createContentHandler(String contentType); } diff --git a/libcore/luni/src/main/java/java/net/CookieHandler.java b/libcore/luni/src/main/java/java/net/CookieHandler.java index 29428ec20..05eddde7a 100644 --- a/libcore/luni/src/main/java/java/net/CookieHandler.java +++ b/libcore/luni/src/main/java/java/net/CookieHandler.java @@ -22,8 +22,6 @@ import java.util.Map; /** * This class provides a way to manage cookies with a HTTP protocol handler. - * - * @since Android 1.0 */ public abstract class CookieHandler { @@ -37,9 +35,8 @@ public abstract class CookieHandler { /** * Returns the system-wide cookie handler or {@code null} if not set. - * + * * @return the system-wide cookie handler. - * @since Android 1.0 */ public static CookieHandler getDefault() { SecurityManager sm = System.getSecurityManager(); @@ -51,10 +48,9 @@ public abstract class CookieHandler { /** * Sets the system-wide cookie handler. - * + * * @param cHandler * a cookie handler to set as the system-wide default handler. - * @since Android 1.0 */ public static void setDefault(CookieHandler cHandler) { SecurityManager sm = System.getSecurityManager(); @@ -66,7 +62,7 @@ public abstract class CookieHandler { /** * Gets all cookies for a specific URI from the cookie cache. - * + * * @param uri * a URI to search for applicable cookies. * @param requestHeaders @@ -74,7 +70,6 @@ public abstract class CookieHandler { * @return an unchangeable map of all appropriate cookies. * @throws IOException * if an error occurs during the I/O operation. - * @since Android 1.0 */ public abstract Map> get(URI uri, Map> requestHeaders) throws IOException; @@ -82,14 +77,13 @@ public abstract class CookieHandler { /** * Sets all cookies of a specific URI in the {@code responseHeaders} into * the cookie cache. - * + * * @param uri * the origin URI of the cookies. * @param responseHeaders * a list of request headers. * @throws IOException * if an error occurs during the I/O operation. - * @since Android 1.0 */ public abstract void put(URI uri, Map> responseHeaders) throws IOException; diff --git a/libcore/luni/src/main/java/java/net/DatagramPacket.java b/libcore/luni/src/main/java/java/net/DatagramPacket.java index bcc531ea3..7bbf55368 100644 --- a/libcore/luni/src/main/java/java/net/DatagramPacket.java +++ b/libcore/luni/src/main/java/java/net/DatagramPacket.java @@ -25,20 +25,30 @@ import org.apache.harmony.luni.util.Msg; * such as its source or destination host. * * @see DatagramSocket - * @since Android 1.0 */ public final class DatagramPacket { byte[] data; + /** + * Length of the data to be sent or size of data that was received via + * DatagramSocket#receive() method call. + */ int length; + /** + * Size of internal buffer that is used to store received data. Should be + * greater or equal to "length" field. + */ + int capacity; + InetAddress address; int port = -1; // The default port number is -1 int offset = 0; + /** * Constructs a new {@code DatagramPacket} object to receive data up to * {@code length} bytes. @@ -47,7 +57,6 @@ public final class DatagramPacket { * a byte array to store the read characters. * @param length * the length of the data buffer. - * @since Android 1.0 */ public DatagramPacket(byte[] data, int length) { this(data, 0, length); @@ -63,7 +72,6 @@ public final class DatagramPacket { * the offset of the byte array where the bytes is written. * @param length * the length of the data. - * @since Android 1.0 */ public DatagramPacket(byte[] data, int offset, int length) { super(); @@ -75,7 +83,7 @@ public final class DatagramPacket { * {@code aPort} of the address {@code host}. The {@code length} must be * lesser than or equal to the size of {@code data}. The first {@code * length} bytes from the byte array position {@code offset} are sent. - * + * * @param data * a byte array which stores the characters to be sent. * @param offset @@ -86,7 +94,6 @@ public final class DatagramPacket { * the address of the target host. * @param aPort * the port of the target host. - * @since Android 1.0 */ public DatagramPacket(byte[] data, int offset, int length, InetAddress host, int aPort) { @@ -100,7 +107,7 @@ public final class DatagramPacket { * {@code aPort} of the address {@code host}. The {@code length} must be * lesser than or equal to the size of {@code data}. The first {@code * length} bytes are sent. - * + * * @param data * a byte array which stores the characters to be sent. * @param length @@ -109,7 +116,6 @@ public final class DatagramPacket { * the address of the target host. * @param port * the port of the target host. - * @since Android 1.0 */ public DatagramPacket(byte[] data, int length, InetAddress host, int port) { this(data, 0, length, host, port); @@ -120,7 +126,6 @@ public final class DatagramPacket { * * @return the address from where the datagram was received or to which it * is sent. - * @since Android 1.0 */ public synchronized InetAddress getAddress() { return address; @@ -130,7 +135,6 @@ public final class DatagramPacket { * Gets the data of this datagram packet. * * @return the received data or the data to be sent. - * @since Android 1.0 */ public synchronized byte[] getData() { return data; @@ -140,7 +144,6 @@ public final class DatagramPacket { * Gets the length of the data stored in this datagram packet. * * @return the length of the received data or the data to be sent. - * @since Android 1.0 */ public synchronized int getLength() { return length; @@ -150,7 +153,6 @@ public final class DatagramPacket { * Gets the offset of the data stored in this datagram packet. * * @return the position of the received data or the data to be sent. - * @since Android 1.0 */ public synchronized int getOffset() { return offset; @@ -159,9 +161,8 @@ public final class DatagramPacket { /** * Gets the port number of the target or sender host of this datagram * packet. - * + * * @return the port number of the origin or target host. - * @since Android 1.0 */ public synchronized int getPort() { return port; @@ -172,7 +173,6 @@ public final class DatagramPacket { * * @param addr * the target host address. - * @since Android 1.0 */ public synchronized void setAddress(InetAddress addr) { address = addr; @@ -188,7 +188,6 @@ public final class DatagramPacket { * @param aLength * the length of the data to be sent or the length of buffer to * store the received data. - * @since Android 1.0 */ public synchronized void setData(byte[] buf, int anOffset, int aLength) { if (0 > anOffset || anOffset > buf.length || 0 > aLength @@ -198,6 +197,7 @@ public final class DatagramPacket { data = buf; offset = anOffset; length = aLength; + capacity = aLength; } /** @@ -206,10 +206,10 @@ public final class DatagramPacket { * * @param buf * the buffer to store the data. - * @since Android 1.0 */ public synchronized void setData(byte[] buf) { length = buf.length; // This will check for null + capacity = buf.length; data = buf; offset = 0; } @@ -220,13 +220,13 @@ public final class DatagramPacket { * * @param len * the length of this datagram packet. - * @since Android 1.0 */ public synchronized void setLength(int len) { if (0 > len || offset + len > data.length) { throw new IllegalArgumentException(Msg.getString("K002f")); //$NON-NLS-1$ } length = len; + capacity = len; } /** @@ -234,7 +234,6 @@ public final class DatagramPacket { * * @param aPort * the target host port number. - * @since Android 1.0 */ public synchronized void setPort(int aPort) { if (aPort < 0 || aPort > 65535) { @@ -257,9 +256,9 @@ public final class DatagramPacket { * the target host address and port. * @throws SocketException * if an error in the underlying protocol occurs. - * @since Android 1.0 */ - public DatagramPacket(byte[] data, int length, SocketAddress sockAddr) throws SocketException { + public DatagramPacket(byte[] data, int length, SocketAddress sockAddr) + throws SocketException { this(data, 0, length); setSocketAddress(sockAddr); } @@ -280,7 +279,6 @@ public final class DatagramPacket { * the target host address and port. * @throws SocketException * if an error in the underlying protocol occurs. - * @since Android 1.0 */ public DatagramPacket(byte[] data, int offset, int length, SocketAddress sockAddr) throws SocketException { @@ -291,9 +289,8 @@ public final class DatagramPacket { /** * Gets the host address and the port to which this datagram packet is sent * as a {@code SocketAddress} object. - * + * * @return the SocketAddress of the target host. - * @since Android 1.0 */ public synchronized SocketAddress getSocketAddress() { return new InetSocketAddress(getAddress(), getPort()); @@ -304,7 +301,6 @@ public final class DatagramPacket { * * @param sockAddr * the SocketAddress of the target host. - * @since Android 1.0 */ public synchronized void setSocketAddress(SocketAddress sockAddr) { if (!(sockAddr instanceof InetSocketAddress)) { diff --git a/libcore/luni/src/main/java/java/net/DatagramSocket.java b/libcore/luni/src/main/java/java/net/DatagramSocket.java index 0c7745f1a..ba5e207a6 100644 --- a/libcore/luni/src/main/java/java/net/DatagramSocket.java +++ b/libcore/luni/src/main/java/java/net/DatagramSocket.java @@ -20,19 +20,17 @@ package java.net; import java.io.IOException; import java.nio.channels.DatagramChannel; -import org.apache.harmony.luni.net.SocketImplProvider; +import org.apache.harmony.luni.net.PlainDatagramSocketImpl; import org.apache.harmony.luni.platform.Platform; - import org.apache.harmony.luni.util.Msg; /** * This class implements a UDP socket for sending and receiving {@code * DatagramPacket}. A {@code DatagramSocket} object can be used for both * endpoints of a connection for a packet delivery service. - * + * * @see DatagramPacket * @see DatagramSocketImplFactory - * @since Android 1.0 */ public class DatagramSocket { @@ -56,16 +54,15 @@ public class DatagramSocket { static { Platform.getNetworkSystem().oneTimeInitialization(true); } - + private Object lock = new Lock(); /** * Constructs a UDP datagram socket which is bound to any available port on * the localhost. - * + * * @throws SocketException * if an error occurs while creating or binding the socket. - * @since Android 1.0 */ public DatagramSocket() throws SocketException { this(0); @@ -75,46 +72,43 @@ public class DatagramSocket { * Constructs a UDP datagram socket which is bound to the specific port * {@code aPort} on the localhost. Valid values for {@code aPort} are * between 0 and 65535 inclusive. - * + * * @param aPort * the port to bind on the localhost. * @throws SocketException * if an error occurs while creating or binding the socket. - * @since Android 1.0 */ public DatagramSocket(int aPort) throws SocketException { super(); checkListen(aPort); - createSocket(aPort, InetAddress.ANY); + createSocket(aPort, Inet4Address.ANY); } /** * Constructs a UDP datagram socket which is bound to the specific local * address {@code addr} on port {@code aPort}. Valid values for {@code * aPort} are between 0 and 65535 inclusive. - * + * * @param aPort * the port to bind on the localhost. * @param addr * the address to bind on the localhost. * @throws SocketException * if an error occurs while creating or binding the socket. - * @since Android 1.0 */ public DatagramSocket(int aPort, InetAddress addr) throws SocketException { super(); checkListen(aPort); - createSocket(aPort, null == addr ? InetAddress.ANY : addr); + createSocket(aPort, null == addr ? Inet4Address.ANY : addr); } /** * Sends prior to attempting to bind the socket, checks whether the port is * within the valid port range and verifies with the security manager that * the port may be bound by the current context. - * + * * @param aPort * the port on the localhost that is to be bound. - * @since Android 1.0 */ void checkListen(int aPort) { if (aPort < 0 || aPort > 65535) { @@ -128,8 +122,6 @@ public class DatagramSocket { /** * Closes this UDP datagram socket and all possibly associated channels. - * - * @since Android 1.0 */ // In the documentation jdk1.1.7a/guide/net/miscNet.html, this method is // noted as not being synchronized. @@ -144,12 +136,11 @@ public class DatagramSocket { * validated, thereafter the only validation on {@code send()} and {@code * receive()} is to check whether the packet address/port matches the * connected target. - * + * * @param anAddress * the target address of this socket. * @param aPort * the target port of this socket. - * @since Android 1.0 */ public void connect(InetAddress anAddress, int aPort) { if (anAddress == null || aPort < 0 || aPort > 65535) { @@ -189,8 +180,6 @@ public class DatagramSocket { /** * Disconnects this UDP datagram socket from the remote host. This method * called on an unconnected socket does nothing. - * - * @since Android 1.0 */ public void disconnect() { if (isClosed() || !isConnected()) { @@ -205,7 +194,7 @@ public class DatagramSocket { synchronized void createSocket(int aPort, InetAddress addr) throws SocketException { impl = factory != null ? factory.createDatagramSocketImpl() - : SocketImplProvider.getDatagramSocketImpl(); + : new PlainDatagramSocketImpl(); impl.create(); try { impl.bind(aPort, addr); @@ -219,10 +208,9 @@ public class DatagramSocket { /** * Gets the {@code InetAddress} instance representing the remote address to * which this UDP datagram socket is connected. - * + * * @return the remote address this socket is connected to or {@code null} if * this socket is not connected. - * @since Android 1.0 */ public InetAddress getInetAddress() { return address; @@ -231,17 +219,16 @@ public class DatagramSocket { /** * Gets the {@code InetAddress} instance representing the bound local * address of this UDP datagram socket. - * + * * @return the local address to which this socket is bound to or {@code * null} if this socket is closed. - * @since Android 1.0 */ public InetAddress getLocalAddress() { if (isClosed()) { return null; } if (!isBound()) { - return InetAddress.ANY; + return Inet4Address.ANY; } InetAddress anAddr = impl.getLocalAddress(); try { @@ -250,17 +237,16 @@ public class DatagramSocket { security.checkConnect(anAddr.getHostName(), -1); } } catch (SecurityException e) { - return InetAddress.ANY; + return Inet4Address.ANY; } return anAddr; } /** * Gets the local port which this socket is bound to. - * + * * @return the local port of this socket or {@code -1} if this socket is * closed and {@code 0} if it is unbound. - * @since Android 1.0 */ public int getLocalPort() { if (isClosed()) { @@ -274,10 +260,9 @@ public class DatagramSocket { /** * Gets the remote port which this socket is connected to. - * + * * @return the remote port of this socket. The return value {@code -1} * indicates that this socket is not connected. - * @since Android 1.0 */ public int getPort() { return port; @@ -285,9 +270,8 @@ public class DatagramSocket { /** * Indicates whether this socket is multicast or not. - * + * * @return the return value is always {@code false}. - * @since Android 1.0 */ boolean isMulticastSocket() { return false; @@ -295,11 +279,10 @@ public class DatagramSocket { /** * Gets the socket receive buffer size. ( {@code SocketOptions.SO_RCVBUF} ) - * + * * @return the input buffer size. - * @exception SocketException + * @throws SocketException * if an error occurs while getting the option value. - * @since Android 1.0 */ public synchronized int getReceiveBufferSize() throws SocketException { checkClosedAndBind(false); @@ -308,11 +291,10 @@ public class DatagramSocket { /** * Gets the socket send buffer size. ( {@code SocketOptions.SO_SNDBUF} ) - * + * * @return the output buffer size. - * @exception SocketException + * @throws SocketException * if an error occurs while getting the option value. - * @since Android 1.0 */ public synchronized int getSendBufferSize() throws SocketException { checkClosedAndBind(false); @@ -323,11 +305,10 @@ public class DatagramSocket { * Gets the socket receive timeout in milliseconds. The return value {@code * 0} implies the timeout is disabled/infinitive. ( {@code * SocketOptions.SO_TIMEOUT} ) - * + * * @return the socket receive timeout. - * @exception SocketException + * @throws SocketException * if an error occurs while getting the option value. - * @since Android 1.0 */ public synchronized int getSoTimeout() throws SocketException { checkClosedAndBind(false); @@ -342,42 +323,49 @@ public class DatagramSocket { * has expired. If a security manager exists, its {@code checkAccept} method * determines whether or not a packet is discarded. Any packets from * unacceptable origins are silently discarded. - * + * * @param pack * the {@code DatagramPacket} to store the received data. - * @exception IOException + * @throws IOException * if an error occurs while receiving the packet. - * @since Android 1.0 */ public synchronized void receive(DatagramPacket pack) throws IOException { checkClosedAndBind(true); - boolean secure = true; - - InetAddress senderAddr = null; - - int senderPort = 0; + InetAddress senderAddr; + int senderPort; DatagramPacket tempPack = new DatagramPacket(new byte[1], 1); + + // means that we have received the packet into the temporary buffer boolean copy = false; SecurityManager security = System.getSecurityManager(); - if (address != null || security != null) { // The socket is connected + + if (address != null || security != null) { + // The socket is connected or we need to check security permissions + // Check pack before peeking if (pack == null) { throw new NullPointerException(); } - secure = false; - while (!secure) { + + // iterate over incoming packets + while (true) { copy = false; + + // let's get sender's address and port try { senderPort = impl.peekData(tempPack); senderAddr = tempPack.getAddress(); } catch (SocketException e) { if (e.getMessage().equals( "The socket does not support the operation")) { //$NON-NLS-1$ - tempPack = new DatagramPacket(new byte[pack.length], - pack.getLength()); + // receive packet to temporary buffer + tempPack = new DatagramPacket(new byte[pack.capacity], + pack.capacity); impl.receive(tempPack); + // tempPack's length field is now updated, capacity is unchanged + // let's extract address & port senderAddr = tempPack.getAddress(); senderPort = tempPack.getPort(); copy = true; @@ -385,46 +373,45 @@ public class DatagramSocket { throw e; } } + if (address == null) { + // if we are not connected let's check if we are allowed to + // receive packets from sender's address and port try { security.checkAccept(senderAddr.getHostName(), senderPort); - if (!copy) { - secure = true; - } + // address & port are valid break; } catch (SecurityException e) { if (!copy) { - if (tempPack == null) { - tempPack = new DatagramPacket( - new byte[pack.length], pack.length); - } + // drop this packet and continue impl.receive(tempPack); } } } else if (port == senderPort && address.equals(senderAddr)) { - if (!copy) { - secure = true; - } + // we are connected and the packet came + // from the address & port we are connected to break; } else if (!copy) { - if (tempPack == null) { - tempPack = new DatagramPacket(new byte[pack.length], - pack.length); - } + // drop packet and continue impl.receive(tempPack); } } } + if (copy) { System.arraycopy(tempPack.getData(), 0, pack.getData(), pack .getOffset(), tempPack.getLength()); - pack.setLength(tempPack.getLength()); + // we shouldn't update the pack's capacity field in order to be + // compatible with RI + pack.length = tempPack.length; pack.setAddress(tempPack.getAddress()); pack.setPort(tempPack.getPort()); - } - if (secure) { + } else { + pack.setLength(pack.capacity); impl.receive(pack); + // pack's length field is now updated by native code call; + // pack's capacity field is unchanged } } @@ -433,12 +420,11 @@ public class DatagramSocket { * policy before it may be sent. If a security manager is installed, this * method checks whether it is allowed to send this packet to the specified * address. - * + * * @param pack * the {@code DatagramPacket} which has to be sent. - * @exception IOException + * @throws IOException * if an error occurs while sending the packet. - * @since Android 1.0 */ public void send(DatagramPacket pack) throws IOException { checkClosedAndBind(true); @@ -480,12 +466,11 @@ public class DatagramSocket { * maximum packet size is that can be sent over this socket. It depends on * the network implementation what will happen if the packet is bigger than * the buffer size. ( {@code SocketOptions.SO_SNDBUF} ) - * + * * @param size * the buffer size in bytes. The size must be at least one byte. - * @exception SocketException + * @throws SocketException * if an error occurs while setting the option. - * @since Android 1.0 */ public synchronized void setSendBufferSize(int size) throws SocketException { if (size < 1) { @@ -500,12 +485,11 @@ public class DatagramSocket { * the maximum packet size is that can be received over this socket. It * depends on the network implementation what will happen if the packet is * bigger than the buffer size. ( {@code SocketOptions.SO_RCVBUF} ) - * + * * @param size * the buffer size in bytes. The size must be at least one byte. - * @exception SocketException + * @throws SocketException * if an error occurs while setting the option. - * @since Android 1.0 */ public synchronized void setReceiveBufferSize(int size) throws SocketException { @@ -523,12 +507,11 @@ public class DatagramSocket { * {@code 0} (default) is used to set an infinite timeout. To have effect * this option must be set before the blocking method was called. ( {@code * SocketOptions.SO_TIMEOUT} ) - * + * * @param timeout * the timeout period in milliseconds or {@code 0} for infinite. - * @exception SocketException + * @throws SocketException * if an error occurs while setting the option. - * @since Android 1.0 */ public synchronized void setSoTimeout(int timeout) throws SocketException { if (timeout < 0) { @@ -545,13 +528,12 @@ public class DatagramSocket { * method {@code checkSetFactory()} is called to check if the operation is * allowed. A {@code SecurityException} is thrown if the operation is not * allowed. - * + * * @param fac * the socket factory to use. - * @exception IOException + * @throws IOException * if the factory has already been set. * @see DatagramSocketImplFactory - * @since Android 1.0 */ public static synchronized void setDatagramSocketImplFactory( DatagramSocketImplFactory fac) throws IOException { @@ -569,10 +551,9 @@ public class DatagramSocket { * Constructs a new {@code DatagramSocket} using the specific datagram * socket implementation {@code socketImpl}. The created {@code * DatagramSocket} will not be bound. - * + * * @param socketImpl * the DatagramSocketImpl to use. - * @since Android 1.0 */ protected DatagramSocket(DatagramSocketImpl socketImpl) { if (socketImpl == null) { @@ -585,12 +566,13 @@ public class DatagramSocket { * Constructs a new {@code DatagramSocket} bound to the host/port specified * by the {@code SocketAddress} {@code localAddr} or an unbound {@code * DatagramSocket} if the {@code SocketAddress} is {@code null}. - * + * * @param localAddr * the local machine address and port to bind to. + * @throws IllegalArgumentException + * if the SocketAddress is not supported * @throws SocketException * if a problem occurs creating or binding the socket. - * @since Android 1.0 */ public DatagramSocket(SocketAddress localAddr) throws SocketException { if (localAddr != null) { @@ -601,7 +583,7 @@ public class DatagramSocket { checkListen(((InetSocketAddress) localAddr).getPort()); } impl = factory != null ? factory.createDatagramSocketImpl() - : SocketImplProvider.getDatagramSocketImpl(); + : new PlainDatagramSocketImpl(); impl.create(); if (localAddr != null) { try { @@ -621,7 +603,7 @@ public class DatagramSocket { } if (bind && !isBound()) { checkListen(0); - impl.bind(0, InetAddress.ANY); + impl.bind(0, Inet4Address.ANY); isBound = true; } } @@ -630,18 +612,19 @@ public class DatagramSocket { * Binds this socket to the local address and port specified by {@code * localAddr}. If this value is {@code null} any free port on a valid local * address is used. - * + * * @param localAddr * the local machine address and port to bind on. + * @throws IllegalArgumentException + * if the SocketAddress is not supported * @throws SocketException * if the socket is already bound or a problem occurs during * binding. - * @since Android 1.0 */ public void bind(SocketAddress localAddr) throws SocketException { checkClosedAndBind(false); int localPort = 0; - InetAddress addr = InetAddress.ANY; + InetAddress addr = Inet4Address.ANY; if (localAddr != null) { if (!(localAddr instanceof InetSocketAddress)) { throw new IllegalArgumentException(Msg.getString( @@ -665,12 +648,11 @@ public class DatagramSocket { * {@code remoteAddr}. The host and port are validated, thereafter the only * validation on {@code send()} and {@code receive()} is that the packet * address/port matches the connected target. - * + * * @param remoteAddr * the address and port of the target host. - * @exception SocketException + * @throws SocketException * if an error occurs during connecting. - * @since Android 1.0 */ public void connect(SocketAddress remoteAddr) throws SocketException { if (remoteAddr == null) { @@ -720,9 +702,8 @@ public class DatagramSocket { /** * Determines whether the socket is bound to an address or not. - * + * * @return {@code true} if the socket is bound, {@code false} otherwise. - * @since Android 1.0 */ public boolean isBound() { return isBound; @@ -730,9 +711,8 @@ public class DatagramSocket { /** * Determines whether the socket is connected to a target host. - * + * * @return {@code true} if the socket is connected, {@code false} otherwise. - * @since Android 1.0 */ public boolean isConnected() { return isConnected; @@ -741,9 +721,8 @@ public class DatagramSocket { /** * Gets the address and port of the connected remote host. If this socket is * not connected yet, {@code null} is returned. - * + * * @return the remote socket address. - * @since Android 1.0 */ public SocketAddress getRemoteSocketAddress() { if (!isConnected()) { @@ -755,9 +734,8 @@ public class DatagramSocket { /** * Gets the bound local address and port of this socket. If the socket is * unbound, {@code null} is returned. - * + * * @return the local socket address. - * @since Android 1.0 */ public SocketAddress getLocalSocketAddress() { if (!isBound()) { @@ -773,13 +751,11 @@ public class DatagramSocket { *

          * There is an undefined behavior if this option is set after the socket is * already bound. - *

          - * + * * @param reuse * the socket option value to enable or disable this option. * @throws SocketException * if the socket is closed or the option could not be set. - * @since Android 1.0 */ public void setReuseAddress(boolean reuse) throws SocketException { checkClosedAndBind(false); @@ -789,11 +765,10 @@ public class DatagramSocket { /** * Gets the state of the socket option {@code SocketOptions.SO_REUSEADDR}. - * + * * @return {@code true} if the option is enabled, {@code false} otherwise. * @throws SocketException * if the socket is closed or the option is invalid. - * @since Android 1.0 */ public boolean getReuseAddress() throws SocketException { checkClosedAndBind(false); @@ -804,12 +779,11 @@ public class DatagramSocket { /** * Sets the socket option {@code SocketOptions.SO_BROADCAST}. This option * must be enabled to send broadcast messages. - * + * * @param broadcast * the socket option value to enable or disable this option. * @throws SocketException * if the socket is closed or the option could not be set. - * @since Android 1.0 */ public void setBroadcast(boolean broadcast) throws SocketException { checkClosedAndBind(false); @@ -819,11 +793,10 @@ public class DatagramSocket { /** * Gets the state of the socket option {@code SocketOptions.SO_BROADCAST}. - * + * * @return {@code true} if the option is enabled, {@code false} otherwise. * @throws SocketException * if the socket is closed or the option is invalid. - * @since Android 1.0 */ public boolean getBroadcast() throws SocketException { checkClosedAndBind(false); @@ -839,13 +812,11 @@ public class DatagramSocket { *

          * Values between {@code 0} and {@code 255} inclusive are valid for this * option. - *

          - * + * * @param value * the socket option value to be set as type-of-service. * @throws SocketException * if the socket is closed or the option could not be set. - * @since Android 1.0 */ public void setTrafficClass(int value) throws SocketException { checkClosedAndBind(false); @@ -858,11 +829,10 @@ public class DatagramSocket { /** * Gets the value of the type-of-service socket option {@code * SocketOptions.IP_TOS}. - * + * * @return the type-of-service socket option value. * @throws SocketException * if the socket is closed or the option is invalid. - * @since Android 1.0 */ public int getTrafficClass() throws SocketException { checkClosedAndBind(false); @@ -871,9 +841,8 @@ public class DatagramSocket { /** * Gets the state of this socket. - * + * * @return {@code true} if the socket is closed, {@code false} otherwise. - * @since Android 1.0 */ public boolean isClosed() { return isClosed; @@ -882,10 +851,9 @@ public class DatagramSocket { /** * Gets the related DatagramChannel of this socket. This implementation * returns always {@code null}. - * + * * @return the related DatagramChannel or {@code null} if this socket was * not created by a {@code DatagramChannel} object. - * @since Android 1.0 */ public DatagramChannel getChannel() { return null; diff --git a/libcore/luni/src/main/java/java/net/DatagramSocketImpl.java b/libcore/luni/src/main/java/java/net/DatagramSocketImpl.java index 79151560b..db3b9ec82 100644 --- a/libcore/luni/src/main/java/java/net/DatagramSocketImpl.java +++ b/libcore/luni/src/main/java/java/net/DatagramSocketImpl.java @@ -25,29 +25,21 @@ import org.apache.harmony.luni.platform.Platform; /** * The abstract superclass for datagram and multicast socket implementations. - * - * @since Android 1.0 */ public abstract class DatagramSocketImpl implements SocketOptions { /** * File descriptor that is used to address this socket. - * - * @since Android 1.0 */ protected FileDescriptor fd; /** * The number of the local port to which this socket is bound. - * - * @since Android 1.0 */ protected int localPort; /** * Constructs an unbound datagram socket implementation. - * - * @since Android 1.0 */ public DatagramSocketImpl() { localPort = -1; @@ -61,28 +53,24 @@ public abstract class DatagramSocketImpl implements SocketOptions { * the port on the localhost to bind. * @param addr * the address on the multihomed localhost to bind. - * @exception SocketException + * @throws SocketException * if an error occurs while binding, for example, if the port * has been already bound. - * @since Android 1.0 */ protected abstract void bind(int port, InetAddress addr) throws SocketException; /** * Closes this socket. - * - * @since Android 1.0 */ protected abstract void close(); /** * This method allocates the socket descriptor in the underlying operating * system. - * + * * @throws SocketException * if an error occurs while creating the socket. - * @since Android 1.0 */ protected abstract void create() throws SocketException; @@ -91,7 +79,6 @@ public abstract class DatagramSocketImpl implements SocketOptions { * if the socket is closed or not bound. * * @return the current file descriptor of this socket. - * @since Android 1.0 */ protected FileDescriptor getFileDescriptor() { return fd; @@ -101,7 +88,6 @@ public abstract class DatagramSocketImpl implements SocketOptions { * Gets the local address to which the socket is bound. * * @return the local address to which the socket is bound. - * @since Android 1.0 */ InetAddress getLocalAddress() { return Platform.getNetworkSystem().getSocketLocalAddress(fd, @@ -112,7 +98,6 @@ public abstract class DatagramSocketImpl implements SocketOptions { * Gets the local port of this socket. * * @return the local port to which this socket is bound. - * @since Android 1.0 */ protected int getLocalPort() { return localPort; @@ -124,9 +109,8 @@ public abstract class DatagramSocketImpl implements SocketOptions { * @param optID * the ID of the socket option to be retrieved. * @return the requested option value. - * @exception SocketException + * @throws SocketException * if an error occurs while accessing the option. - * @since Android 1.0 */ public abstract Object getOption(int optID) throws SocketException; @@ -139,7 +123,6 @@ public abstract class DatagramSocketImpl implements SocketOptions { * value. * @deprecated Replaced by {@link #getTimeToLive} * @see #getTimeToLive() - * @since Android 1.0 */ @Deprecated protected abstract byte getTTL() throws IOException; @@ -148,12 +131,11 @@ public abstract class DatagramSocketImpl implements SocketOptions { * Gets the time-to-live (TTL) for multicast packets sent on this socket. * The TTL option defines how many routers a packet may be pass before it is * discarded. - * + * * @return the time-to-live option as an integer value. * @throws IOException * if an error occurs while getting the time-to-live option * value. - * @since Android 1.0 */ protected abstract int getTimeToLive() throws IOException; @@ -167,7 +149,6 @@ public abstract class DatagramSocketImpl implements SocketOptions { * @throws IOException * if an error occurs while joining the specified multicast * group. - * @since Android 1.0 */ protected abstract void join(InetAddress addr) throws IOException; @@ -184,7 +165,6 @@ public abstract class DatagramSocketImpl implements SocketOptions { * @throws IOException * if an error occurs while joining the specified multicast * group. - * @since Android 1.0 */ protected abstract void joinGroup(SocketAddress addr, NetworkInterface netInterface) throws IOException; @@ -197,7 +177,6 @@ public abstract class DatagramSocketImpl implements SocketOptions { * @throws IOException * if an error occurs while leaving the group or no multicast * address was assigned. - * @since Android 1.0 */ protected abstract void leave(InetAddress addr) throws IOException; @@ -211,7 +190,6 @@ public abstract class DatagramSocketImpl implements SocketOptions { * removed. * @throws IOException * if an error occurs while leaving the group. - * @since Android 1.0 */ protected abstract void leaveGroup(SocketAddress addr, NetworkInterface netInterface) throws IOException; @@ -224,9 +202,8 @@ public abstract class DatagramSocketImpl implements SocketOptions { * @param sender * the origin address of a packet. * @return the address of {@code sender} as an integer value. - * @exception IOException + * @throws IOException * if an error or a timeout occurs while reading the address. - * @since Android 1.0 */ protected abstract int peek(InetAddress sender) throws IOException; @@ -238,9 +215,8 @@ public abstract class DatagramSocketImpl implements SocketOptions { * * @param pack * the datagram packet container to fill in the received data. - * @exception IOException + * @throws IOException * if an error or timeout occurs while receiving data. - * @since Android 1.0 */ protected abstract void receive(DatagramPacket pack) throws IOException; @@ -250,9 +226,8 @@ public abstract class DatagramSocketImpl implements SocketOptions { * * @param pack * the datagram packet to be sent. - * @exception IOException + * @throws IOException * if an error occurs while sending the packet. - * @since Android 1.0 */ protected abstract void send(DatagramPacket pack) throws IOException; @@ -263,9 +238,8 @@ public abstract class DatagramSocketImpl implements SocketOptions { * the ID of the socket option to be set. * @param val * the value of the option. - * @exception SocketException + * @throws SocketException * if an error occurs while setting the option. - * @since Android 1.0 */ public abstract void setOption(int optID, Object val) throws SocketException; @@ -279,7 +253,6 @@ public abstract class DatagramSocketImpl implements SocketOptions { * <= 255. * @throws IOException * if an error occurs while setting the option. - * @since Android 1.0 */ protected abstract void setTimeToLive(int ttl) throws IOException; @@ -294,7 +267,6 @@ public abstract class DatagramSocketImpl implements SocketOptions { * if an error occurs while setting the option. * @deprecated Replaced by {@link #setTimeToLive} * @see #setTimeToLive(int) - * @since Android 1.0 */ @Deprecated protected abstract void setTTL(byte ttl) throws IOException; @@ -306,10 +278,9 @@ public abstract class DatagramSocketImpl implements SocketOptions { * the address of the target host which has to be connected. * @param port * the port on the target host which has to be connected. - * @exception SocketException + * @throws SocketException * if the datagram socket cannot be connected to the * specified remote address and port. - * @since Android 1.0 */ protected void connect(InetAddress inetAddr, int port) throws SocketException { @@ -318,8 +289,6 @@ public abstract class DatagramSocketImpl implements SocketOptions { /** * Disconnects this socket from the remote host. - * - * @since Android 1.0 */ protected void disconnect() { // do nothing @@ -335,9 +304,8 @@ public abstract class DatagramSocketImpl implements SocketOptions { * @param pack * the datagram packet used to store the data. * @return the port the packet was received from. - * @exception IOException + * @throws IOException * if an error occurs while peeking at the data. - * @since Android 1.0 */ protected abstract int peekData(DatagramPacket pack) throws IOException; } diff --git a/libcore/luni/src/main/java/java/net/DatagramSocketImplFactory.java b/libcore/luni/src/main/java/java/net/DatagramSocketImplFactory.java index b97146e45..ce9173808 100644 --- a/libcore/luni/src/main/java/java/net/DatagramSocketImplFactory.java +++ b/libcore/luni/src/main/java/java/net/DatagramSocketImplFactory.java @@ -23,7 +23,6 @@ package java.net; * implementation. * * @see DatagramSocket - * @since Android 1.0 */ public interface DatagramSocketImplFactory { @@ -31,7 +30,6 @@ public interface DatagramSocketImplFactory { * Creates a new {@code DatagramSocketImpl} instance. * * @return the new datagram socket implementation. - * @since Android 1.0 */ DatagramSocketImpl createDatagramSocketImpl(); } diff --git a/libcore/luni/src/main/java/java/net/FileNameMap.java b/libcore/luni/src/main/java/java/net/FileNameMap.java index e8ac35fba..16fa0e0f0 100644 --- a/libcore/luni/src/main/java/java/net/FileNameMap.java +++ b/libcore/luni/src/main/java/java/net/FileNameMap.java @@ -26,7 +26,6 @@ package java.net; * @see URLConnection#getFileNameMap() * @see URLConnection#guessContentTypeFromName(String) * @see URLStreamHandler - * @since Android 1.0 */ public interface FileNameMap { @@ -36,7 +35,6 @@ public interface FileNameMap { * @param fileName * the name of the file to consider. * @return the appropriate MIME type of the given file. - * @since Android 1.0 */ public String getContentTypeFor(String fileName); } diff --git a/libcore/luni/src/main/java/java/net/HttpRetryException.java b/libcore/luni/src/main/java/java/net/HttpRetryException.java index 5f613ab67..4ad14bdac 100644 --- a/libcore/luni/src/main/java/java/net/HttpRetryException.java +++ b/libcore/luni/src/main/java/java/net/HttpRetryException.java @@ -21,8 +21,6 @@ import java.io.IOException; /** * If a HTTP request has to be retried, this exception will be thrown if the * request cannot be retried automatically. - * - * @since Android 1.0 */ public class HttpRetryException extends IOException { @@ -40,7 +38,6 @@ public class HttpRetryException extends IOException { * the detail message for this exception. * @param code * the HTTP response code from target host. - * @since Android 1.0 */ public HttpRetryException(String detail, int code) { super(detail); @@ -58,7 +55,6 @@ public class HttpRetryException extends IOException { * the HTTP response code from target host. * @param location * the destination URL of the redirection. - * @since Android 1.0 */ public HttpRetryException(String detail, int code, String location) { super(detail); @@ -68,9 +64,8 @@ public class HttpRetryException extends IOException { /** * Gets the location value. - * + * * @return the stored location from the HTTP header. - * @since Android 1.0 */ public String getLocation() { return location; @@ -78,9 +73,8 @@ public class HttpRetryException extends IOException { /** * Gets the detail message. - * + * * @return the detail message. - * @since Android 1.0 */ public String getReason() { return getMessage(); @@ -88,9 +82,8 @@ public class HttpRetryException extends IOException { /** * Gets the response code. - * + * * @return the HTTP response code. - * @since Android 1.0 */ public int responseCode() { return responseCode; diff --git a/libcore/luni/src/main/java/java/net/HttpURLConnection.java b/libcore/luni/src/main/java/java/net/HttpURLConnection.java index 55cc36398..6be891feb 100644 --- a/libcore/luni/src/main/java/java/net/HttpURLConnection.java +++ b/libcore/luni/src/main/java/java/net/HttpURLConnection.java @@ -29,7 +29,6 @@ import org.apache.harmony.luni.util.Msg; * @see URL * @see URLConnection * @see URLStreamHandler - * @since Android 1.0 */ public abstract class HttpURLConnection extends URLConnection { @SuppressWarnings("nls") @@ -39,8 +38,6 @@ public abstract class HttpURLConnection extends URLConnection { /** * The HTTP request method of this {@code HttpURLConnection}. The default * value is {@code "GET"}. - * - * @since Android 1.0 */ protected String method = "GET"; //$NON-NLS-1$ @@ -53,24 +50,17 @@ public abstract class HttpURLConnection extends URLConnection { *
        • 3xx: Relocation/Redirection
        • *
        • 4xx: Client Error
        • *
        • 5xx: Server Error
        • - *

          - * - * @since Android 1.0 */ protected int responseCode = -1; /** * The HTTP response message which corresponds to the response code. - * - * @since Android 1.0 */ protected String responseMessage; /** * Flag to define whether the protocol will automatically follow redirects * or not. The default value is {@code true}. - * - * @since Android 1.0 */ protected boolean instanceFollowRedirects = followRedirects; @@ -80,8 +70,6 @@ public abstract class HttpURLConnection extends URLConnection { * If the HTTP chunked encoding is enabled this parameter defines the * chunk-length. Default value is {@code -1} that means the chunked encoding * mode is disabled. - * - * @since Android 1.0 */ protected int chunkLength = -1; @@ -89,8 +77,6 @@ public abstract class HttpURLConnection extends URLConnection { * If using HTTP fixed-length streaming mode this parameter defines the * fixed length of content. Default value is {@code -1} that means the * fixed-length streaming mode is disabled. - * - * @since Android 1.0 */ protected int fixedContentLength = -1; @@ -102,255 +88,184 @@ public abstract class HttpURLConnection extends URLConnection { // 5XX: server error /** * Numeric status code, 202: Accepted - * - * @since Android 1.0 */ public final static int HTTP_ACCEPTED = 202; /** * Numeric status code, 502: Bad Gateway - * - * @since Android 1.0 */ public final static int HTTP_BAD_GATEWAY = 502; /** * Numeric status code, 405: Bad Method - * - * @since Android 1.0 */ public final static int HTTP_BAD_METHOD = 405; /** * Numeric status code, 400: Bad Request - * - * @since Android 1.0 */ public final static int HTTP_BAD_REQUEST = 400; /** * Numeric status code, 408: Client Timeout - * - * @since Android 1.0 */ public final static int HTTP_CLIENT_TIMEOUT = 408; /** * Numeric status code, 409: Conflict - * - * @since Android 1.0 */ public final static int HTTP_CONFLICT = 409; /** * Numeric status code, 201: Created - * - * @since Android 1.0 */ public final static int HTTP_CREATED = 201; /** * Numeric status code, 413: Entity too large - * - * @since Android 1.0 */ public final static int HTTP_ENTITY_TOO_LARGE = 413; /** * Numeric status code, 403: Forbidden - * - * @since Android 1.0 */ public final static int HTTP_FORBIDDEN = 403; /** * Numeric status code, 504: Gateway timeout - * - * @since Android 1.0 */ public final static int HTTP_GATEWAY_TIMEOUT = 504; /** * Numeric status code, 410: Gone - * - * @since Android 1.0 */ public final static int HTTP_GONE = 410; /** * Numeric status code, 500: Internal error - * - * @since Android 1.0 */ public final static int HTTP_INTERNAL_ERROR = 500; /** * Numeric status code, 411: Length required - * - * @since Android 1.0 */ public final static int HTTP_LENGTH_REQUIRED = 411; /** * Numeric status code, 301 Moved permanently - * - * @since Android 1.0 */ public final static int HTTP_MOVED_PERM = 301; /** * Numeric status code, 302: Moved temporarily - * - * @since Android 1.0 */ public final static int HTTP_MOVED_TEMP = 302; /** * Numeric status code, 300: Multiple choices - * - * @since Android 1.0 */ public final static int HTTP_MULT_CHOICE = 300; /** * Numeric status code, 204: No content - * - * @since Android 1.0 */ public final static int HTTP_NO_CONTENT = 204; /** * Numeric status code, 406: Not acceptable - * - * @since Android 1.0 */ public final static int HTTP_NOT_ACCEPTABLE = 406; /** * Numeric status code, 203: Not authoritative - * - * @since Android 1.0 */ public final static int HTTP_NOT_AUTHORITATIVE = 203; /** * Numeric status code, 404: Not found - * - * @since Android 1.0 */ public final static int HTTP_NOT_FOUND = 404; /** * Numeric status code, 501: Not implemented - * - * @since Android 1.0 */ public final static int HTTP_NOT_IMPLEMENTED = 501; /** * Numeric status code, 304: Not modified - * - * @since Android 1.0 */ public final static int HTTP_NOT_MODIFIED = 304; /** * Numeric status code, 200: OK - * - * @since Android 1.0 */ public final static int HTTP_OK = 200; /** * Numeric status code, 206: Partial - * - * @since Android 1.0 */ public final static int HTTP_PARTIAL = 206; /** * Numeric status code, 402: Payment required - * - * @since Android 1.0 */ public final static int HTTP_PAYMENT_REQUIRED = 402; /** * Numeric status code, 412: Precondition failed - * - * @since Android 1.0 */ public final static int HTTP_PRECON_FAILED = 412; /** * Numeric status code, 407: Proxy authentication required - * - * @since Android 1.0 */ public final static int HTTP_PROXY_AUTH = 407; /** * Numeric status code, 414: Request too long - * - * @since Android 1.0 */ public final static int HTTP_REQ_TOO_LONG = 414; /** * Numeric status code, 205: Reset - * - * @since Android 1.0 */ public final static int HTTP_RESET = 205; /** * Numeric status code, 303: See other - * - * @since Android 1.0 */ public final static int HTTP_SEE_OTHER = 303; /** * Numeric status code, 500: Internal error - * + * * @deprecated Use {@link #HTTP_INTERNAL_ERROR} - * @since Android 1.0 */ @Deprecated public final static int HTTP_SERVER_ERROR = 500; /** * Numeric status code, 305: Use proxy - * - * @since Android 1.0 */ public final static int HTTP_USE_PROXY = 305; /** * Numeric status code, 401: Unauthorized - * - * @since Android 1.0 */ public final static int HTTP_UNAUTHORIZED = 401; /** * Numeric status code, 415: Unsupported type - * - * @since Android 1.0 */ public final static int HTTP_UNSUPPORTED_TYPE = 415; /** * Numeric status code, 503: Unavailable - * - * @since Android 1.0 */ public final static int HTTP_UNAVAILABLE = 503; /** * Numeric status code, 505: Version not supported - * - * @since Android 1.0 */ public final static int HTTP_VERSION = 505; @@ -362,7 +277,6 @@ public abstract class HttpURLConnection extends URLConnection { * the URL of this connection. * @see URL * @see URLConnection - * @since Android 1.0 */ protected HttpURLConnection(URL url) { super(url); @@ -373,7 +287,6 @@ public abstract class HttpURLConnection extends URLConnection { * * @see URLConnection#connect() * @see URLConnection#connected - * @since Android 1.0 */ public abstract void disconnect(); @@ -383,7 +296,6 @@ public abstract class HttpURLConnection extends URLConnection { * can be used to read the data the server will send back. * * @return the error input stream returned by the server. - * @since Android 1.0 */ public java.io.InputStream getErrorStream() { return null; @@ -396,7 +308,6 @@ public abstract class HttpURLConnection extends URLConnection { * * @return the value of the flag. * @see #setFollowRedirects - * @since Android 1.0 */ public static boolean getFollowRedirects() { return followRedirects; @@ -412,7 +323,6 @@ public abstract class HttpURLConnection extends URLConnection { * @throws IOException * if an IO exception occurs during the creation of the * permission object. - * @since Android 1.0 */ @Override public java.security.Permission getPermission() throws IOException { @@ -432,7 +342,6 @@ public abstract class HttpURLConnection extends URLConnection { * @return the request method string. * @see #method * @see #setRequestMethod - * @since Android 1.0 */ public String getRequestMethod() { return method; @@ -445,7 +354,6 @@ public abstract class HttpURLConnection extends URLConnection { * @throws IOException * if there is an IO error during the retrieval. * @see #getResponseMessage - * @since Android 1.0 */ public int getResponseCode() throws IOException { // Call getInputStream() first since getHeaderField() doesn't return @@ -478,7 +386,6 @@ public abstract class HttpURLConnection extends URLConnection { * @throws IOException * if there is an error during the retrieval. * @see #getResponseCode() - * @since Android 1.0 */ public String getResponseMessage() throws IOException { if (responseMessage != null) { @@ -496,7 +403,6 @@ public abstract class HttpURLConnection extends URLConnection { * @param auto * the value to enable or disable this option. * @see SecurityManager#checkSetFactory() - * @since Android 1.0 */ public static void setFollowRedirects(boolean auto) { SecurityManager security = System.getSecurityManager(); @@ -517,7 +423,6 @@ public abstract class HttpURLConnection extends URLConnection { * supported by this HTTP implementation. * @see #getRequestMethod() * @see #method - * @since Android 1.0 */ public void setRequestMethod(String method) throws ProtocolException { if (connected) { @@ -540,7 +445,6 @@ public abstract class HttpURLConnection extends URLConnection { * * @return {@code true} if this connection passes a proxy server, false * otherwise. - * @since Android 1.0 */ public abstract boolean usingProxy(); @@ -549,7 +453,6 @@ public abstract class HttpURLConnection extends URLConnection { * * @return {@code true} if this connection follows redirects, false * otherwise. - * @since Android 1.0 */ public boolean getInstanceFollowRedirects() { return instanceFollowRedirects; @@ -561,7 +464,6 @@ public abstract class HttpURLConnection extends URLConnection { * @param followRedirects * {@code true} if this connection will follows redirects, false * otherwise. - * @since Android 1.0 */ public void setInstanceFollowRedirects(boolean followRedirects) { instanceFollowRedirects = followRedirects; @@ -579,7 +481,6 @@ public abstract class HttpURLConnection extends URLConnection { * found. * @return the header field represented in milliseconds since January 1, * 1970 GMT. - * @since Android 1.0 */ @Override public long getHeaderFieldDate(String field, long defaultValue) { @@ -598,7 +499,6 @@ public abstract class HttpURLConnection extends URLConnection { * if already connected or an other mode already set. * @throws IllegalArgumentException * if {@code contentLength} is less than zero. - * @since Android 1.0 */ public void setFixedLengthStreamingMode(int contentLength) { if (super.connected) { @@ -624,7 +524,6 @@ public abstract class HttpURLConnection extends URLConnection { * the length of a chunk. * @throws IllegalStateException * if already connected or an other mode already set. - * @since Android 1.0 */ public void setChunkedStreamingMode(int chunklen) { if (super.connected) { diff --git a/libcore/luni/src/main/java/java/net/Inet4Address.java b/libcore/luni/src/main/java/java/net/Inet4Address.java index dd6b0881a..b467b6419 100644 --- a/libcore/luni/src/main/java/java/net/Inet4Address.java +++ b/libcore/luni/src/main/java/java/net/Inet4Address.java @@ -28,18 +28,22 @@ import java.io.ObjectStreamException; * address ({@code b.bbb} ) allows to represent a class A network address as * net.host. If there is only one part ({@code bbbb} ) the address is * represented without any byte rearrangement. - * - * @since Android 1.0 */ public final class Inet4Address extends InetAddress { private static final long serialVersionUID = 3286316764910316507L; + final static InetAddress ANY = new Inet4Address(new byte[] { 0, 0, 0, 0 }); + final static InetAddress LOOPBACK = new Inet4Address( + new byte[] { 127, 0, 0, 1 }, "localhost"); //$NON-NLS-1$ + Inet4Address(byte[] address) { + family = AF_INET; ipaddress = address; } Inet4Address(byte[] address, String name) { + family = AF_INET; ipaddress = address; hostName = name; } @@ -50,7 +54,6 @@ public final class Inet4Address extends InetAddress { * * @return {@code true} if this instance represents a multicast address, * {@code false} otherwise. - * @since Android 1.0 */ @Override public boolean isMulticastAddress() { @@ -63,7 +66,6 @@ public final class Inet4Address extends InetAddress { * * @return {@code true} if this instance represents the wildcard ANY * address, {@code false} otherwise. - * @since Android 1.0 */ @Override public boolean isAnyLocalAddress() { @@ -81,7 +83,6 @@ public final class Inet4Address extends InetAddress { * * @return {@code true} if this instance represents a lookback address, * {@code false} otherwise. - * @since Android 1.0 */ @Override public boolean isLoopbackAddress() { @@ -95,11 +96,9 @@ public final class Inet4Address extends InetAddress { * Default Address Selection for Internet Protocol Version 6 (IPv6) states * IPv4 auto-configuration addresses, prefix 169.254/16, IPv4 loopback * addresses, prefix 127/8, are assigned link-local scope. - *

          - * + * * @return {@code true} if this instance represents a link-local address, * {@code false} otherwise. - * @since Android 1.0 */ @Override public boolean isLinkLocalAddress() { @@ -115,11 +114,9 @@ public final class Inet4Address extends InetAddress { * Default Address Selection for Internet Protocol Version 6 (IPv6) states * IPv4 private addresses, prefixes 10/8, 172.16/12, and 192.168/16, are * assigned site-local scope. - *

          * * @return {@code true} if this instance represents a site-local address, * {@code false} otherwise. - * @since Android 1.0 */ @Override public boolean isSiteLocalAddress() { @@ -135,7 +132,6 @@ public final class Inet4Address extends InetAddress { * * @return {@code true} if the address is in the global multicast group, * {@code false} otherwise. - * @since Android 1.0 */ @Override public boolean isMCGlobal() { @@ -174,7 +170,6 @@ public final class Inet4Address extends InetAddress { * addresses. * * @return {@code false} for all IPv4 addresses. - * @since Android 1.0 */ @Override public boolean isMCNodeLocal() { @@ -188,7 +183,6 @@ public final class Inet4Address extends InetAddress { * * @return {@code true} if this instance represents a link-local address, * {@code false} otherwise. - * @since Android 1.0 */ @Override public boolean isMCLinkLocal() { @@ -202,7 +196,6 @@ public final class Inet4Address extends InetAddress { * * @return {@code true} if this instance represents a site-local address, * {@code false} otherwise. - * @since Android 1.0 */ @Override public boolean isMCSiteLocal() { @@ -217,7 +210,6 @@ public final class Inet4Address extends InetAddress { * * @return {@code true} if this instance represents a organization-local * address, {@code false} otherwise. - * @since Android 1.0 */ @Override public boolean isMCOrgLocal() { @@ -225,51 +217,22 @@ public final class Inet4Address extends InetAddress { return prefix >= 0xEFC0 && prefix <= 0xEFC3; } - /** - * Returns a textual representation of this IP address. - * - * @return the textual representation of this host address. - * @since Android 1.0 - */ - @Override - public String getHostAddress() { - String hostAddress = ""; //$NON-NLS-1$ - for (int i = 0; i < 4; i++) { - hostAddress += ipaddress[i] & 255; - if (i != 3) { - hostAddress += "."; //$NON-NLS-1$ - } - } - return hostAddress; - } + // BEGIN android-removed + // public String getHostAddress() { + // } + // END android-removed - /** - * Gets the hashcode of the represented IP address. - * - * @return the appropriate hashcode value. - * @since Android 1.0 - */ - @Override - public int hashCode() { - return InetAddress.bytesToInt(ipaddress, 0); - } + // BEGIN android-removed + // public int hashCode() { + // } + // END android-removed - /** - * Compares this instance with the IP address in the object {@code obj} and - * returns {@code true} if they are of the same type and represent the same - * IP address, {@code false} otherwise. - * - * @param obj - * the object to be tested for equality. - * @return {@code true} if the addresses are equal, {@code false} otherwise. - * @since Android 1.0 - */ - @Override - public boolean equals(Object obj) { - return super.equals(obj); - } + // BEGIN android-removed + // public boolean equals(Object obj) { + // } + // END android-removed private Object writeReplace() throws ObjectStreamException { - return new InetAddress(ipaddress, hostName); + return new Inet4Address(ipaddress, hostName); } } diff --git a/libcore/luni/src/main/java/java/net/Inet6Address.java b/libcore/luni/src/main/java/java/net/Inet6Address.java index 4c10faa62..f333770ad 100644 --- a/libcore/luni/src/main/java/java/net/Inet6Address.java +++ b/libcore/luni/src/main/java/java/net/Inet6Address.java @@ -28,23 +28,15 @@ import org.apache.harmony.luni.util.Msg; /** * This class represents a 128 bit long IPv6 address. - * - * @since Android 1.0 */ public final class Inet6Address extends InetAddress { private static final long serialVersionUID = 6880410070516793377L; - static final byte[] any_bytes = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0 }; - - static final byte[] localhost_bytes = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1 }; - - static final InetAddress ANY = new Inet6Address(any_bytes); - - static final InetAddress LOOPBACK = new Inet6Address(localhost_bytes, - "localhost"); //$NON-NLS-1$ + static final InetAddress ANY = new Inet6Address(new byte[] + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }); + static final InetAddress LOOPBACK = new Inet6Address(new byte[] + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, "localhost"); //$NON-NLS-1$ int scope_id; @@ -60,11 +52,13 @@ public final class Inet6Address extends InetAddress { transient NetworkInterface scopedIf; Inet6Address(byte address[]) { + family = AF_INET6; ipaddress = address; scope_id = 0; } Inet6Address(byte address[], String name) { + family = AF_INET6; hostName = name; ipaddress = address; scope_id = 0; @@ -80,9 +74,9 @@ public final class Inet6Address extends InetAddress { * the name associated with the address. * @param scope_id * the scope id for link- or site-local addresses. - * @since Android 1.0 */ Inet6Address(byte address[], String name, int scope_id) { + family = AF_INET6; hostName = name; ipaddress = address; this.scope_id = scope_id; @@ -104,7 +98,6 @@ public final class Inet6Address extends InetAddress { * @return the Inet6Address instance representing the IP address. * @throws UnknownHostException * if the address is null or has an invalid length. - * @since Android 1.0 */ public static Inet6Address getByAddress(String host, byte[] addr, int scope_id) throws UnknownHostException { @@ -135,7 +128,6 @@ public final class Inet6Address extends InetAddress { * if the address is {@code null} or has an invalid length or * the interface doesn't have a numeric scope id for the given * address type. - * @since Android 1.0 */ public static Inet6Address getByAddress(String host, byte[] addr, NetworkInterface nif) throws UnknownHostException { @@ -177,10 +169,11 @@ public final class Inet6Address extends InetAddress { /** * Returns {@code true} if one of following cases applies: *

          - *

        • 1. both addresses are site local
        • - *
        • 2. both addresses are link local
        • - *
        • 3. {@code ia} is neither site local nor link local
        • - *

          + *
            + *
          1. both addresses are site local
          2. + *
          3. both addresses are link local
          4. + *
          5. {@code ia} is neither site local nor link local
          6. + *
          */ private boolean compareLocalType(Inet6Address ia) { if (ia.isSiteLocalAddress() && isSiteLocalAddress()) { @@ -203,7 +196,6 @@ public final class Inet6Address extends InetAddress { * the network address. * @param scope_id * the scope id for link- or site-local addresses. - * @since Android 1.0 */ Inet6Address(byte address[], int scope_id) { ipaddress = address; @@ -219,7 +211,6 @@ public final class Inet6Address extends InetAddress { * * @return {@code true} if this address is in the multicast group, {@code * false} otherwise. - * @since Android 1.0 */ @Override public boolean isMulticastAddress() { @@ -233,7 +224,6 @@ public final class Inet6Address extends InetAddress { * * @return {@code true} if this instance represents a wildcard address, * {@code false} otherwise. - * @since Android 1.0 */ @Override public boolean isAnyLocalAddress() { @@ -251,7 +241,6 @@ public final class Inet6Address extends InetAddress { * * @return {@code true} if this instance represents the loopback address, * {@code false} otherwise. - * @since Android 1.0 */ @Override public boolean isLoopbackAddress() { @@ -277,7 +266,6 @@ public final class Inet6Address extends InetAddress { * * @return {@code true} if this instance represents a link-local address, * {@code false} otherwise. - * @since Android 1.0 */ @Override public boolean isLinkLocalAddress() { @@ -292,7 +280,6 @@ public final class Inet6Address extends InetAddress { * * @return {@code true} if this instance represents a site-local address, * {@code false} otherwise. - * @since Android 1.0 */ @Override public boolean isSiteLocalAddress() { @@ -307,7 +294,6 @@ public final class Inet6Address extends InetAddress { * * @return {@code true} if this instance represents a global multicast * address, {@code false} otherwise. - * @since Android 1.0 */ @Override public boolean isMCGlobal() { @@ -323,7 +309,6 @@ public final class Inet6Address extends InetAddress { * * @return {@code true} if this instance represents a node-local multicast * address, {@code false} otherwise. - * @since Android 1.0 */ @Override public boolean isMCNodeLocal() { @@ -339,7 +324,6 @@ public final class Inet6Address extends InetAddress { * * @return {@code true} if this instance represents a link-local multicast * address, {@code false} otherwise. - * @since Android 1.0 */ @Override public boolean isMCLinkLocal() { @@ -355,7 +339,6 @@ public final class Inet6Address extends InetAddress { * * @return {@code true} if this instance represents a site-local multicast * address, {@code false} otherwise. - * @since Android 1.0 */ @Override public boolean isMCSiteLocal() { @@ -371,7 +354,6 @@ public final class Inet6Address extends InetAddress { * * @return {@code true} if this instance represents a org-local multicast * address, {@code false} otherwise. - * @since Android 1.0 */ @Override public boolean isMCOrgLocal() { @@ -380,16 +362,10 @@ public final class Inet6Address extends InetAddress { return (ipaddress[0] == -1) && (ipaddress[1] & 15) == 8; } - /** - * Gets the textual representation of this IP address. - * - * @return the as a dotted string formatted IP address. - * @since Android 1.0 - */ - @Override - public String getHostAddress() { - return Inet6Util.createIPAddrStringFromByteArray(ipaddress); - } + // BEGIN android-removed + // public String getHostAddress() { + // } + // END android-removed /** * Gets the scope id as a number if this address is linked to an interface. @@ -397,7 +373,6 @@ public final class Inet6Address extends InetAddress { * * @return the scope_id of this address or 0 when not linked with an * interface. - * @since Android 1.0 */ public int getScopeId() { if (scope_id_set) { @@ -411,7 +386,6 @@ public final class Inet6Address extends InetAddress { * network interface. Otherwise returns {@code null}. * * @return the scoped network interface of this address. - * @since Android 1.0 */ public NetworkInterface getScopedInterface() { if (scope_ifname_set) { @@ -420,33 +394,13 @@ public final class Inet6Address extends InetAddress { return null; } - /** - * Gets the hashcode of the represented IP address. - * - * @return the appropriate hashcode value. - * @since Android 1.0 - */ - @Override - public int hashCode() { - /* Returns the low order int as the hash code */ - return bytesToInt(ipaddress, 12); - } + // BEGIN android-removed + // public int hashCode() {} + // END android-removed - /** - * Compares this instance with the IP address in the object {@code obj} and - * returns {@code true} if they are of the same type and represent the same - * IP address, {@code false} otherwise. The scope id does not seem to be - * part of the comparison. - * - * @param obj - * the object to be tested for equality. - * @return {@code true} if the addresses are equal, {@code false} otherwise. - * @since Android 1.0 - */ - @Override - public boolean equals(Object obj) { - return super.equals(obj); - } + // BEGIN android-removed + // public boolean equals(Object obj) {} + // END android-removed /** * Returns whether this address is IPv4 compatible or not. An IPv4 @@ -455,7 +409,6 @@ public final class Inet6Address extends InetAddress { * * @return {@code true} if this instance represents an IPv4 compatible * address, {@code false} otherwise. - * @since Android 1.0 */ public boolean isIPv4CompatibleAddress() { for (int i = 0; i < 12; i++) { @@ -506,7 +459,6 @@ public final class Inet6Address extends InetAddress { * IP address. * * @return the description, as host/address. - * @since Android 1.0 */ @Override public String toString() { diff --git a/libcore/luni/src/main/java/java/net/InetAddress.java b/libcore/luni/src/main/java/java/net/InetAddress.java index 72c7669dc..33c25f353 100644 --- a/libcore/luni/src/main/java/java/net/InetAddress.java +++ b/libcore/luni/src/main/java/java/net/InetAddress.java @@ -25,7 +25,9 @@ import java.io.ObjectStreamException; import java.io.ObjectStreamField; import java.io.Serializable; import java.security.AccessController; +import java.util.Arrays; import java.util.ArrayList; +import java.util.Comparator; import java.util.Enumeration; import java.util.StringTokenizer; @@ -41,22 +43,11 @@ import org.apache.harmony.luni.util.PriviAction; * encapsulates an IP address and provides name and reverse name resolution * functions. The address is stored in network order, but as a signed (rather * than unsigned) integer. - * - * @since Android 1.0 */ public class InetAddress extends Object implements Serializable { - final static byte[] any_bytes = { 0, 0, 0, 0 }; - - final static byte[] localhost_bytes = { 127, 0, 0, 1 }; - - static InetAddress ANY = new Inet4Address(any_bytes); - private final static INetworkSystem NETIMPL = Platform.getNetworkSystem(); - final static InetAddress LOOPBACK = new Inet4Address(localhost_bytes, - "localhost"); //$NON-NLS-1$ - private static final String ERRMSG_CONNECTION_REFUSED = "Connection refused"; //$NON-NLS-1$ private static final long serialVersionUID = 3286316764910316507L; @@ -79,84 +70,81 @@ public class InetAddress extends Object implements Serializable { private int addrCount; - int family = 2; + int family = 0; + static final int AF_INET = 2; + static final int AF_INET6 = 10; byte[] ipaddress; // BEGIN android-removed // // Fill in the JNI id caches // private static native void oneTimeInitialization(boolean supportsIPv6); - // + // // static { // oneTimeInitialization(true); // } // END android-removed /** - * Constructs an InetAddress. + * Constructs an {@code InetAddress}. + * + * Note: this constructor should not be used. Creating an InetAddress + * without specifying whether it's an IPv4 or IPv6 address does not make + * sense, because subsequent code cannot know which of of the subclasses' + * methods need to be called to implement a given InetAddress method. The + * proper way to create an InetAddress is to call new Inet4Address or + * Inet6Address or to use one of the static methods that return + * InetAddresses (e.g., getByAddress). That is why the API does not have + * public constructors for any of these classes. */ InetAddress() { super(); } + // BEGIN android-removed /** * Constructs an {@code InetAddress}, representing the {@code address} and * {@code hostName}. - * + * * @param address * the network address. */ - InetAddress(byte[] address) { - super(); - this.ipaddress = address; - } + // InetAddress(byte[] address) { + // super(); + // this.ipaddress = address; + // } + // END android-removed + // BEGIN android-removed /** * Constructs an {@code InetAddress}, representing the {@code address} and * {@code hostName}. - * + * * @param address * the network address. + * */ - InetAddress(byte[] address, String hostName) { - super(); - this.ipaddress = address; - this.hostName = hostName; - } + // InetAddress(byte[] address, String hostName) { + // super(); + // this.ipaddress = address; + // this.hostName = hostName; + // } + // END android-removed // BEGIN android-removed - // Removed in newer version of harmony - // /** - // * Returns the IP address of the argument {@code addr} as a byte array. The - // * elements are in network order (the highest order address byte is in the - // * zeroth element). - // * - // * @return the network address as a byte array. - // */ - // static byte[] addressOf(int addr) { - // int temp = addr; - // byte array[] = new byte[4]; - // array[3] = (byte) (temp & 0xFF); - // array[2] = (byte) ((temp >>>= 8) & 0xFF); - // array[1] = (byte) ((temp >>>= 8) & 0xFF); - // array[0] = (byte) ((temp >>>= 8) & 0xFF); - // return array; + // CacheElement cacheElement() { + // return new CacheElement(); // } - // BEGIN android-removed - - CacheElement cacheElement() { - return new CacheElement(); - } + // END android-removed /** * Compares this {@code InetAddress} instance against the specified address * in {@code obj}. Two addresses are equal if their address byte arrays have * the same length and if the bytes in the arrays are equal. - * + * * @param obj * the object to be tested for equality. * @return {@code true} if both objects are equal, {@code false} otherwise. - * @since Android 1.0 */ @Override public boolean equals(Object obj) { @@ -164,141 +152,137 @@ public class InetAddress extends Object implements Serializable { if (!(obj instanceof InetAddress)) { return false; } + return Arrays.equals(this.ipaddress, ((InetAddress) obj).ipaddress); // END android-changed - - // now check if their byte arrays match... - byte[] objIPaddress = ((InetAddress) obj).ipaddress; - // BEGIN android-added - if (objIPaddress.length != ipaddress.length) { - return false; - } - // END android-added - for (int i = 0; i < objIPaddress.length; i++) { - if (objIPaddress[i] != this.ipaddress[i]) { - return false; - } - } - return true; } /** * Returns the IP address represented by this {@code InetAddress} instance * as a byte array. The elements are in network order (the highest order * address byte is in the zeroth element). - * + * * @return the address in form of a byte array. - * @since Android 1.0 */ public byte[] getAddress() { return ipaddress.clone(); } + // BEGIN android-added + static final Comparator SHORTEST_FIRST = new Comparator() { + public int compare(byte[] a1, byte[] a2) { + return a1.length - a2.length; + } + }; + + static final Comparator LONGEST_FIRST = new Comparator() { + public int compare(byte[] a1, byte[] a2) { + return a2.length - a1.length; + } + }; + + /** + * Converts an array of byte arrays representing raw IP addresses of a host + * to an array of InetAddress objects, sorting to respect the value of the + * system preferIPv6Addresses preference. + * + * @param rawAddresses the raw addresses to convert. + * @param hostName the hostname corresponding to the IP address. + * @return the corresponding InetAddresses, appropriately sorted. + */ + static InetAddress[] bytesToInetAddresses(byte[][] rawAddresses, + String hostName) { + // Sort the raw byte arrays. + Comparator comparator = preferIPv6Addresses() + ? LONGEST_FIRST : SHORTEST_FIRST; + Arrays.sort(rawAddresses, comparator); + + // Convert the byte arrays to InetAddresses. + InetAddress[] returnedAddresses = new InetAddress[rawAddresses.length]; + for (int i = 0; i < rawAddresses.length; i++) { + byte[] rawAddress = rawAddresses[i]; + if (rawAddress.length == 16) { + returnedAddresses[i] = new Inet6Address(rawAddress, hostName); + } else if (rawAddress.length == 4) { + returnedAddresses[i] = new Inet4Address(rawAddress, hostName); + } else { + // Cannot happen, because the underlying code only returns + // addresses that are 4 or 16 bytes long. + throw new AssertionError("Impossible address length " + + rawAddress.length); + } + } + return returnedAddresses; + } + // END android-added + /** * Gets all IP addresses associated with the given {@code host} identified - * by name or IP address in dot-notation. The IP address is resolved by the + * by name or literal IP address. The IP address is resolved by the * configured name service. If the host name is empty or {@code null} an - * {@code UnknownHostException} is thrown. If the host name is a dotted IP + * {@code UnknownHostException} is thrown. If the host name is a literal IP * address string an array with the corresponding single {@code InetAddress} * is returned. - * - * @param host - * the host's name or IP to be resolved to an address. + * + * @param host the hostname or literal IP string to be resolved. * @return the array of addresses associated with the specified host. - * @throws UnknownHostException - * if the address lookup fails. - * @since Android 1.0 + * @throws UnknownHostException if the address lookup fails. */ public static InetAddress[] getAllByName(String host) throws UnknownHostException { // BEGIN android-changed - // Added change taken from newer harmony concerning zero length hostname. - // Added special handling for localhost, since it doesn't work properly. - // TODO Get rid of this later... - if (host == null || 0 == host.length() || - "localhost".equalsIgnoreCase(host)) { - return new InetAddress[] { preferIPv6Addresses() - ? Inet6Address.LOOPBACK - : LOOPBACK }; - } + return getAllByNameImpl(host, true); // END android-changed + } + + // BEGIN android-added + /** + * Implementation of getAllByName. + * + * @param host the hostname or literal IP string to be resolved. + * @param returnUnshared requests a result that is modifiable by the caller. + * @return the array of addresses associated with the specified host. + * @throws UnknownHostException if the address lookup fails. + */ + static InetAddress[] getAllByNameImpl(String host, boolean returnUnshared) + throws UnknownHostException { + if (host == null || 0 == host.length()) { + if (preferIPv6Addresses()) { + return new InetAddress[] { Inet6Address.LOOPBACK, + Inet4Address.LOOPBACK }; + } else { + return new InetAddress[] { Inet4Address.LOOPBACK, + Inet6Address.LOOPBACK }; + } + } + + // Special-case "0" for legacy IPv4 applications. + if (host.equals("0")) { //$NON-NLS-1$ + return new InetAddress[] { Inet4Address.ANY }; + } if (isHostName(host)) { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkConnect(host, -1); } - // BEGIN android-changed - byte[][] rawAddresses = getallbyname(host, - Socket.preferIPv4Stack()); - InetAddress[] returnedAddresses = new - InetAddress[rawAddresses.length]; - for (int i = 0; i < rawAddresses.length; i++) { - byte[] rawAddress = rawAddresses[i]; - if (rawAddress.length == 16) { - returnedAddresses[i] = new Inet6Address(rawAddress, host); - } else if (rawAddress.length == 4) { - returnedAddresses[i] = new Inet4Address(rawAddress, host); - } else { - // Cannot happen, because the underlying code only returns - // addresses that are 4 or 16 bytes long. - throw new AssertionError("Impossible address length " + - rawAddress.length); - } - } - - // ok we may have to re-order to make sure the - // preferIPv6Addresses is respected - // END android-changed - InetAddress[] orderedAddresses = null; - if (returnedAddresses != null) { - orderedAddresses = new InetAddress[returnedAddresses.length]; - int curPosition = 0; - if (InetAddress.preferIPv6Addresses()) { - for (int i = 0; i < returnedAddresses.length; i++) { - if (returnedAddresses[i] instanceof Inet6Address) { - orderedAddresses[curPosition] = returnedAddresses[i]; - curPosition++; - } - } - for (int i = 0; i < returnedAddresses.length; i++) { - // BEGIN android-changed - if (!(returnedAddresses[i] instanceof Inet6Address)) { - // END android-changed - orderedAddresses[curPosition] = returnedAddresses[i]; - curPosition++; - } - } - } else { - for (int i = 0; i < returnedAddresses.length; i++) { - // BEGIN android-changed - if (!(returnedAddresses[i] instanceof Inet6Address)) { - // END android-changed - orderedAddresses[curPosition] = returnedAddresses[i]; - curPosition++; - } - } - for (int i = 0; i < returnedAddresses.length; i++) { - if (returnedAddresses[i] instanceof Inet6Address) { - orderedAddresses[curPosition] = returnedAddresses[i]; - curPosition++; - } - } - } + if (returnUnshared) { + return lookupHostByName(host).clone(); + } else { + return lookupHostByName(host); } - return orderedAddresses; } - byte[] hBytes = Inet6Util.createByteArrayFromIPAddressString(host); - // BEGIN android-added - // Copied from a newer version of harmony + byte[] hBytes = NETIMPL.ipStringToByteArray(host); if (hBytes.length == 4) { return (new InetAddress[] { new Inet4Address(hBytes) }); } else if (hBytes.length == 16) { return (new InetAddress[] { new Inet6Address(hBytes) }); + } else { + throw new UnknownHostException( + Msg.getString("K0339")); //$NON-NLS-1$ } - // END android-added - return (new InetAddress[] { new InetAddress(hBytes) }); } + // END android-added /** * Returns the address of a host according to the given host string name @@ -306,57 +290,54 @@ public class InetAddress extends Object implements Serializable { * string IP address. If the latter, the {@code hostName} field is * determined upon demand. {@code host} can be {@code null} which means that * an address of the loopback interface is returned. - * + * * @param host * the hostName to be resolved to an address or {@code null}. * @return the {@code InetAddress} instance representing the host. * @throws UnknownHostException * if the address lookup fails. - * @since Android 1.0 */ - public static InetAddress getByName(String host) - throws UnknownHostException { - // BEGIN android-changed - // Added special handling for localhost, since it doesn't work properly. - // TODO Get rid of this later... - if (host == null || 0 == host.length() || - "localhost".equalsIgnoreCase(host)) { - return InetAddress.LOOPBACK; - } - // END android-changed - if (host.equals("0")) { //$NON-NLS-1$ - return InetAddress.ANY; - } + public static InetAddress getByName(String host) throws UnknownHostException { + return getAllByNameImpl(host, false)[0]; + } - if (isHostName(host)) { - SecurityManager security = System.getSecurityManager(); - if (security != null) { - security.checkConnect(host, -1); - } - return lookupHostByName(host); + // BEGIN android-added + /** + * Convenience method to convert a byte array to a string, converting + * exceptions to runtime exceptions. This is used when passing in byte + * arrays that have been verified to be correct and is necessary because + * some methods, such as getHostName(), are not allowed to throw exceptions + * but are implemented using byteArrayToIpString(), which throws + * UnknownHostException. Exceptions should never occur when the address is + * valid, but they cannot be simply ignored because they could be due to + * runtime errors such as out-of-memory conditions. + * + * @param ipaddress the byte array to convert + */ + private static String ipAddressToString(byte[] ipaddress) { + try { + return NETIMPL.byteArrayToIpString(ipaddress); + } catch(UnknownHostException e) { + throw new RuntimeException(e); } - - return createHostNameFromIPAddress(host); } + // END android-added /** * Gets the textual representation of this IP address. - * - * @return the textual representation of this host address in form of a - * dotted string. - * @since Android 1.0 + * + * @return the textual representation of host's IP address. */ public String getHostAddress() { - return inetNtoaImpl(bytesToInt(ipaddress, 0)); + return ipAddressToString(ipaddress); } /** * Gets the host name of this IP address. If the IP address could not be * resolved, the textual representation in a dotted-quad-notation is * returned. - * + * * @return the corresponding string name of this IP address. - * @since Android 1.0 */ public String getHostName() { try { @@ -365,18 +346,17 @@ public class InetAddress extends Object implements Serializable { if (ipaddress.length == 4) { address = bytesToInt(ipaddress, 0); if (address == 0) { - return hostName = inetNtoaImpl(address); + return hostName = ipAddressToString(ipaddress); } } hostName = getHostByAddrImpl(ipaddress).hostName; if (hostName.equals("localhost") && ipaddress.length == 4 //$NON-NLS-1$ && address != 0x7f000001) { - return hostName = inetNtoaImpl(address); + return hostName = ipAddressToString(ipaddress); } } } catch (UnknownHostException e) { - return hostName = Inet6Util - .createIPAddrStringFromByteArray(ipaddress); + return hostName = ipAddressToString(ipaddress); } SecurityManager security = System.getSecurityManager(); try { @@ -385,7 +365,7 @@ public class InetAddress extends Object implements Serializable { security.checkConnect(hostName, -1); } } catch (SecurityException e) { - return Inet6Util.createIPAddrStringFromByteArray(ipaddress); + return ipAddressToString(ipaddress); } return hostName; } @@ -395,9 +375,8 @@ public class InetAddress extends Object implements Serializable { * address. If a security manager is set, it is checked if the method caller * is allowed to get the hostname. Otherwise, the textual representation in * a dotted-quad-notation is returned. - * + * * @return the fully qualified domain name of this IP address. - * @since Android 1.0 */ public String getCanonicalHostName() { String canonicalName; @@ -406,12 +385,12 @@ public class InetAddress extends Object implements Serializable { if (ipaddress.length == 4) { address = bytesToInt(ipaddress, 0); if (address == 0) { - return inetNtoaImpl(address); + return ipAddressToString(ipaddress); } } canonicalName = getHostByAddrImpl(ipaddress).hostName; } catch (UnknownHostException e) { - return Inet6Util.createIPAddrStringFromByteArray(ipaddress); + return ipAddressToString(ipaddress); } SecurityManager security = System.getSecurityManager(); try { @@ -420,7 +399,7 @@ public class InetAddress extends Object implements Serializable { security.checkConnect(canonicalName, -1); } } catch (SecurityException e) { - return Inet6Util.createIPAddrStringFromByteArray(ipaddress); + return ipAddressToString(ipaddress); } return canonicalName; } @@ -429,14 +408,10 @@ public class InetAddress extends Object implements Serializable { * Gets the local host address if the security policy allows this. * Otherwise, gets the loopback address which allows this machine to be * contacted. - *

          - * The current implementation returns always the loopback address. - *

          - * + * * @return the {@code InetAddress} representing the local host. * @throws UnknownHostException * if the address lookup fails. - * @since Android 1.0 */ public static InetAddress getLocalHost() throws UnknownHostException { String host = getHostNameImpl(); @@ -446,34 +421,44 @@ public class InetAddress extends Object implements Serializable { security.checkConnect(host, -1); } } catch (SecurityException e) { - return InetAddress.LOOPBACK; + return Inet4Address.LOOPBACK; } - return lookupHostByName(host); + return lookupHostByName(host)[0]; } /** * Gets the hashcode of the represented IP address. - * + * * @return the appropriate hashcode value. - * @since Android 1.0 */ @Override public int hashCode() { - return bytesToInt(ipaddress, 0); + // BEGIN android-changed + return Arrays.hashCode(ipaddress); + // END android-changed } - /** - * Returns whether this address is an IP multicast address or not. - * + // BEGIN android-changed + /* + * Returns whether this address is an IP multicast address or not. This + * implementation returns always {@code false}. + * * @return {@code true} if this address is in the multicast group, {@code * false} otherwise. - * @since Android 1.0 */ public boolean isMulticastAddress() { - return ((ipaddress[0] & 255) >>> 4) == 0xE; + return false; } + // END android-changed + - static synchronized InetAddress lookupHostByName(String host) + /** + * Resolves a hostname to its IP addresses using a cache for faster lookups. + * + * @param host the hostname to resolve. + * @return the IP addresses of the host. + */ + static synchronized InetAddress[] lookupHostByName(String host) throws UnknownHostException { int ttl = -1; @@ -490,40 +475,48 @@ public class InetAddress extends Object implements Serializable { // Ignored } CacheElement element = null; + // BEGIN android-changed if (ttl == 0) { Cache.clear(); } else { element = Cache.get(host); - // BEGIN android-changed if (element != null && ttl > 0) { long delta = System.nanoTime() - element.nanoTimeAdded; if (delta > secondsToNanos(ttl)) { element = null; } } - // END android-changed } if (element != null) { - return element.inetAddress(); + return element.addresses(); } + // END android-changed + + // TODO Clean up NegativeCache; there's no need to maintain the failure message // now try the negative cache String failedMessage = NegativeCache.getFailedMessage(host); if (failedMessage != null) { - throw new UnknownHostException(host + " - " + failedMessage); //$NON-NLS-1$ + throw new UnknownHostException(host); } - InetAddress anInetAddress; + // BEGIN android-changed + // TODO: Avoid doing I/O from a static synchronized lock. + byte[][] rawAddresses; try { - anInetAddress = getHostByNameImpl(host, preferIPv6Addresses()); + rawAddresses = getallbyname(host, Socket.preferIPv4Stack()); } catch (UnknownHostException e) { // put the entry in the negative cache NegativeCache.put(host, e.getMessage()); - throw new UnknownHostException(host + " - " + e.getMessage()); //$NON-NLS-1$ + // use host for message to match RI, save the cause for giggles + throw (UnknownHostException)new UnknownHostException(host).initCause(e); } - Cache.add(anInetAddress); - return anInetAddress; + InetAddress[] addresses = bytesToInetAddresses(rawAddresses, host); + + Cache.add(host, addresses); + return addresses; + // END android-changed } // BEGIN android-added @@ -540,17 +533,17 @@ public class InetAddress extends Object implements Serializable { // throws UnknownHostException; // END android-deleted - // BEGIN android-changed + // BEGIN android-added /** * Resolves a host name to its IP addresses. Thread safe. */ private static native byte[][] getallbyname(String name, - boolean preferIPv4Stack); - // END android-changed + boolean preferIPv4Stack) throws UnknownHostException; + // END android-added /** * Query the IP stack for the host address. The host is in address form. - * + * * @param addr * the host address to lookup. * @throws UnknownHostException @@ -561,7 +554,14 @@ public class InetAddress extends Object implements Serializable { // throws UnknownHostException; static InetAddress getHostByAddrImpl(byte[] addr) throws UnknownHostException { - return new InetAddress(addr, gethostbyaddr(addr)); + if (addr.length == 4) { + return new Inet4Address(addr, gethostbyaddr(addr)); + } else if (addr.length == 16) { + return new Inet6Address(addr, gethostbyaddr(addr)); + } else { + throw new UnknownHostException(Msg.getString( + "K0339")); //$NON-NLS-1$ + } } /** @@ -570,10 +570,9 @@ public class InetAddress extends Object implements Serializable { private static native String gethostbyaddr(byte[] addr); // END android-changed - static int inetAddr(String host) throws UnknownHostException { - return (host.equals("255.255.255.255")) ? 0xFFFFFFFF //$NON-NLS-1$ - : inetAddrImpl(host); - } + // BEGIN android-removed + // static int inetAddr(String host) throws UnknownHostException + // END android-removed /** * Convert a string containing an IPv4 Internet Protocol dotted address into @@ -581,66 +580,37 @@ public class InetAddress extends Object implements Serializable { * exception, so this value should not be used as an argument. See also * inetAddr(String). */ - // BEGIN android-changed + // BEGIN android-removed // static native int inetAddrImpl(String host) throws UnknownHostException; - static int inetAddrImpl(String host) throws UnknownHostException { - // TODO Probably not exactly what we want, and also inefficient. Provide native later. - try { - String[] args = host.split("\\."); - - int a = Integer.parseInt(args[0]) << 24; - int b = Integer.parseInt(args[1]) << 16; - int c = Integer.parseInt(args[2]) << 8; - int d = Integer.parseInt(args[3]) ; - - return a | b | c | d; - } catch (Exception ex) { - throw new UnknownHostException(host); - } - } - // END android-changed + // END android-removed /** * Convert a binary address into a string containing an Ipv4 Internet * Protocol dotted address. */ - // BEGIN android-changed + // BEGIN android-removed // static native String inetNtoaImpl(int hipAddr); - static String inetNtoaImpl(int hipAddr) { - // TODO Inefficient and probably wrong. Provide proper (native?) implementation later. - int a = (hipAddr >> 24) & 0xFF; - int b = (hipAddr >> 16) & 0xFF; - int c = (hipAddr >> 8) & 0xFF; - int d = (hipAddr ) & 0xFF; - - return "" + a + "." + b + "." + c + "." + d; - } - // END android-changed + // END android-removed + // BEGIN android-removed /** * Query the IP stack for the host address. The host is in string name form. - * + * * @param name * the host name to lookup - * @param preferIPv6Addresses + * @param preferIPv6Address * address preference if underlying platform is V4/V6 * @return InetAddress the host address * @throws UnknownHostException * if an error occurs during lookup */ - // BEGIN android-changed // static native InetAddress getHostByNameImpl(String name, - // boolean preferIPv6Address) throws UnknownHostException; - static InetAddress getHostByNameImpl(String name, - boolean preferIPv6Address) throws UnknownHostException { - // TODO Mapped Harmony to Android native. Get rid of indirection later. - return getAllByName(name)[0]; - } - // END android-changed + // boolean preferIPv6Address) throws UnknownHostException; + // END android-removed /** * Gets the host name of the system. - * + * * @return String the system hostname */ // BEGIN android-changed @@ -654,10 +624,10 @@ public class InetAddress extends Object implements Serializable { static String getHostNameInternal(String host) throws UnknownHostException { if (host == null || 0 == host.length()) { - return InetAddress.LOOPBACK.getHostAddress(); + return Inet4Address.LOOPBACK.getHostAddress(); } if (isHostName(host)) { - return lookupHostByName(host).getHostAddress(); + return lookupHostByName(host)[0].getHostAddress(); } return host; } @@ -665,38 +635,38 @@ public class InetAddress extends Object implements Serializable { /** * Returns a string containing a concise, human-readable description of this * IP address. - * + * * @return the description, as host/address. - * @since Android 1.0 */ @Override public String toString() { return (hostName == null ? "" : hostName) + "/" + getHostAddress(); //$NON-NLS-1$ //$NON-NLS-2$ } - class CacheElement { - // BEGIN android-changed - // Partly copied from a newer version of harmony + // BEGIN android-changed + // Partly copied from a newer version of harmony + static class CacheElement { final long nanoTimeAdded = System.nanoTime(); CacheElement next; + final String hostName; + final InetAddress[] addresses; - CacheElement() { - super(); + CacheElement(String hostName, InetAddress[] addresses) { + this.addresses = addresses; + this.hostName = hostName; } - // END android-changed String hostName() { return hostName; } - InetAddress inetAddress() { - return InetAddress.this; + InetAddress[] addresses() { + return addresses; } + // END android-changed } - // BEGIN android-changed - // Copied from a newer version of harmony static class Cache { private static int maxSize = 5; @@ -709,8 +679,8 @@ public class InetAddress extends Object implements Serializable { head = null; } - static synchronized void add(InetAddress value) { - CacheElement newElement = value.cacheElement(); + static synchronized void add(String hostName, InetAddress[] addresses) { + CacheElement newElement = new CacheElement(hostName, addresses); if (size < maxSize) { size++; } else { @@ -764,24 +734,26 @@ public class InetAddress extends Object implements Serializable { } } } - // END android-changed /** * Returns true if the string is a host name, false if it is an IP Address. */ private static boolean isHostName(String value) { - return !(Inet6Util.isValidIPV4Address(value) || Inet6Util - .isValidIP6Address(value)); + try { + NETIMPL.ipStringToByteArray(value); + return false; + } catch (UnknownHostException e) { + return true; + } } /** * Returns whether this address is a loopback address or not. This * implementation returns always {@code false}. Valid IPv4 loopback * addresses are 127.d.d.d The only valid IPv6 loopback address is ::1. - * + * * @return {@code true} if this instance represents a loopback address, * {@code false} otherwise. - * @since Android 1.0 */ public boolean isLoopbackAddress() { return false; @@ -793,14 +765,11 @@ public class InetAddress extends Object implements Serializable { *

          * Valid IPv6 link-local addresses are FE80::0 through to * FEBF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF. - *

          *

          * There are no valid IPv4 link-local addresses. - *

          - * + * * @return {@code true} if this instance represents a link-local address, * {@code false} otherwise. - * @since Android 1.0 */ public boolean isLinkLocalAddress() { return false; @@ -812,14 +781,11 @@ public class InetAddress extends Object implements Serializable { *

          * Valid IPv6 site-local addresses are FEC0::0 through to * FEFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF. - *

          *

          * There are no valid IPv4 site-local addresses. - *

          - * + * * @return {@code true} if this instance represents a site-local address, * {@code false} otherwise. - * @since Android 1.0 */ public boolean isSiteLocalAddress() { return false; @@ -832,15 +798,12 @@ public class InetAddress extends Object implements Serializable { * Valid IPv6 link-global multicast addresses are FFxE:/112 where x is a set * of flags, and the additional 112 bits make up the global multicast * address space. - *

          *

          * Valid IPv4 global multicast addresses are between: 224.0.1.0 to * 238.255.255.255. - *

          - * + * * @return {@code true} if this instance represents a global multicast * address, {@code false} otherwise. - * @since Android 1.0 */ public boolean isMCGlobal() { return false; @@ -853,14 +816,11 @@ public class InetAddress extends Object implements Serializable { * Valid IPv6 node-local multicast addresses are FFx1:/112 where x is a set * of flags, and the additional 112 bits make up the node-local multicast * address space. - *

          *

          * There are no valid IPv4 node-local multicast addresses. - *

          - * + * * @return {@code true} if this instance represents a node-local multicast * address, {@code false} otherwise. - * @since Android 1.0 */ public boolean isMCNodeLocal() { return false; @@ -873,14 +833,11 @@ public class InetAddress extends Object implements Serializable { * Valid IPv6 link-local multicast addresses are FFx2:/112 where x is a set * of flags, and the additional 112 bits make up the link-local multicast * address space. - *

          *

          * Valid IPv4 link-local addresses are between: 224.0.0.0 to 224.0.0.255 - *

          - * + * * @return {@code true} if this instance represents a link-local multicast * address, {@code false} otherwise. - * @since Android 1.0 */ public boolean isMCLinkLocal() { return false; @@ -893,15 +850,12 @@ public class InetAddress extends Object implements Serializable { * Valid IPv6 site-local multicast addresses are FFx5:/112 where x is a set * of flags, and the additional 112 bits make up the site-local multicast * address space. - *

          *

          * Valid IPv4 site-local addresses are between: 239.252.0.0 to * 239.255.255.255 - *

          - * + * * @return {@code true} if this instance represents a site-local multicast * address, {@code false} otherwise. - * @since Android 1.0 */ public boolean isMCSiteLocal() { return false; @@ -914,15 +868,12 @@ public class InetAddress extends Object implements Serializable { * Valid IPv6 organization-local multicast addresses are FFx8:/112 where x * is a set of flags, and the additional 112 bits make up the * organization-local multicast address space. - *

          *

          * Valid IPv4 organization-local addresses are between: 239.192.0.0 to * 239.251.255.255 - *

          - * + * * @return {@code true} if this instance represents a organization-local * multicast address, {@code false} otherwise. - * @since Android 1.0 */ public boolean isMCOrgLocal() { return false; @@ -931,10 +882,9 @@ public class InetAddress extends Object implements Serializable { /** * Returns whether this is a wildcard address or not. This implementation * returns always {@code false}. - * + * * @return {@code true} if this instance represents a wildcard address, * {@code false} otherwise. - * @since Android 1.0 */ public boolean isAnyLocalAddress() { return false; @@ -944,7 +894,7 @@ public class InetAddress extends Object implements Serializable { * Tries to reach this {@code InetAddress}. This method first tries to use * ICMP (ICMP ECHO REQUEST). When first step fails, a TCP connection * on port 7 (Echo) of the remote host is established. - * + * * @param timeout * timeout in milliseconds before the test fails if no connection * could be established. @@ -954,7 +904,6 @@ public class InetAddress extends Object implements Serializable { * if an error occurs during an I/O operation. * @throws IllegalArgumentException * if timeout is less than zero. - * @since Android 1.0 */ public boolean isReachable(int timeout) throws IOException { return isReachable(null, 0, timeout); @@ -964,7 +913,7 @@ public class InetAddress extends Object implements Serializable { * Tries to reach this {@code InetAddress}. This method first tries to use * ICMP (ICMP ECHO REQUEST). When first step fails, a TCP connection * on port 7 (Echo) of the remote host is established. - * + * * @param netif * the network interface on which to connection should be * established. @@ -979,7 +928,6 @@ public class InetAddress extends Object implements Serializable { * if an error occurs during an I/O operation. * @throws IllegalArgumentException * if ttl or timeout is less than zero. - * @since Android 1.0 */ public boolean isReachable(NetworkInterface netif, final int ttl, final int timeout) throws IOException { @@ -1073,8 +1021,8 @@ public class InetAddress extends Object implements Serializable { // InetAddress.this, addr, ttl, timeout); // } else { try { - threadReached = isReachableByTCP(InetAddress.this, - addr, timeout); + threadReached = isReachableByTCP(addr, + InetAddress.this, timeout); } catch (IOException e) { // do nothing } @@ -1122,7 +1070,7 @@ public class InetAddress extends Object implements Serializable { // int ttl, int timeout) throws IOException { // return isReachableByMultiThread(netif, ttl, timeout, true); // } - // + // // private boolean isReachableByTCPUseMultiThread(NetworkInterface netif, // int ttl, int timeout) throws IOException { // return isReachableByMultiThread(netif, ttl, timeout, false); @@ -1135,10 +1083,10 @@ public class InetAddress extends Object implements Serializable { // define traffic only for parameter int traffic = 0; boolean reached = false; - NETIMPL.createSocket(fd, NetUtil.preferIPv4Stack()); + NETIMPL.createStreamSocket(fd, NetUtil.preferIPv4Stack()); try { if (null != source) { - NETIMPL.bind(fd, 0, source); + NETIMPL.bind(fd, source, 0); } NETIMPL.connectStreamWithTimeoutSocket(fd, 7, timeout, traffic, dest); @@ -1161,24 +1109,21 @@ public class InetAddress extends Object implements Serializable { * exactly 16 bytes. If not, an {@code UnknownHostException} is thrown. *

          * The IP address is not validated by a name service. - *

          *

          * The high order byte is {@code ipAddress[0]}. - *

          - * + * * @param ipAddress * is either a 4 (IPv4) or 16 (IPv6) byte long array. * @return an {@code InetAddress} instance representing the given IP address * {@code ipAddress}. * @throws UnknownHostException * if the given byte array has no valid length. - * @since Android 1.0 */ public static InetAddress getByAddress(byte[] ipAddress) throws UnknownHostException { // simply call the method by the same name specifying the default scope // id of 0 - return getByAddress(ipAddress, 0); + return getByAddressInternal(null, ipAddress, 0); } /** @@ -1187,7 +1132,7 @@ public class InetAddress extends Object implements Serializable { * exactly 16 bytes. If not, an {@code UnknownHostException} is thrown. The * IP address is not validated by a name service. The high order byte is * {@code ipAddress[0]}. - * + * * @param ipAddress * either a 4 (IPv4) or 16 (IPv6) byte array. * @param scope_id @@ -1198,52 +1143,35 @@ public class InetAddress extends Object implements Serializable { */ static InetAddress getByAddress(byte[] ipAddress, int scope_id) throws UnknownHostException { - byte[] copy_address; - if (ipAddress != null && ipAddress.length == 4) { - copy_address = new byte[4]; - for (int i = 0; i < 4; i++) { - copy_address[i] = ipAddress[i]; - } - // BEGIN adnroid-changed - // Copied from a newer version of harmony - return new Inet4Address(copy_address); - // END android-changed - } - - if (ipAddress != null && ipAddress.length == 16) { - // First check to see if the address is an IPv6-mapped - // IPv4 address. If it is, then we can make it a IPv4 - // address, otherwise, we'll create an IPv6 address. - if (isIPv4MappedAddress(ipAddress)) { - copy_address = new byte[4]; - for (int i = 0; i < 4; i++) { - copy_address[i] = ipAddress[12 + i]; - } - return new Inet4Address(copy_address); - } - copy_address = ipAddress.clone(); - return new Inet6Address(copy_address, scope_id); - } - throw new UnknownHostException(Msg.getString("K0339")); //$NON-NLS-1$ + return getByAddressInternal(null, ipAddress, scope_id); } private static boolean isIPv4MappedAddress(byte ipAddress[]) { // Check if the address matches ::FFFF:d.d.d.d // The first 10 bytes are 0. The next to are -1 (FF). // The last 4 bytes are varied. + if (ipAddress == null || ipAddress.length != 16) { + return false; + } for (int i = 0; i < 10; i++) { if (ipAddress[i] != 0) { return false; } } - if (ipAddress[10] != -1 || ipAddress[11] != -1) { return false; } - return true; } + private static byte[] ipv4MappedToIPv4(byte[] mappedAddress) { + byte[] ipv4Address = new byte[4]; + for(int i = 0; i < 4; i++) { + ipv4Address[i] = mappedAddress[12 + i]; + } + return ipv4Address; + } + /** * Returns the {@code InetAddress} corresponding to the array of bytes, and * the given hostname. In the case of an IPv4 address there must be exactly @@ -1251,15 +1179,12 @@ public class InetAddress extends Object implements Serializable { * UnknownHostException} will be thrown. *

          * The host name and IP address are not validated. - *

          *

          * The hostname either be a machine alias or a valid IPv6 or IPv4 address * format. - *

          *

          * The high order byte is {@code ipAddress[0]}. - *

          - * + * * @param hostName * the string representation of hostname or IP address. * @param ipAddress @@ -1268,7 +1193,6 @@ public class InetAddress extends Object implements Serializable { * and hostname. * @throws UnknownHostException * if the given byte array has no valid length. - * @since Android 1.0 */ public static InetAddress getByAddress(String hostName, byte[] ipAddress) throws UnknownHostException { @@ -1284,7 +1208,7 @@ public class InetAddress extends Object implements Serializable { * UnknownHostException} is thrown. The host name and IP address are not * validated. The hostname either be a machine alias or a valid IPv6 or IPv4 * address format. The high order byte is {@code ipAddress[0]}. - * + * * @param hostName * string representation of hostname or IP address. * @param ipAddress @@ -1297,36 +1221,36 @@ public class InetAddress extends Object implements Serializable { */ static InetAddress getByAddressInternal(String hostName, byte[] ipAddress, int scope_id) throws UnknownHostException { - byte[] copy_address; - if (ipAddress != null && ipAddress.length == 4) { - copy_address = new byte[4]; - for (int i = 0; i < 4; i++) { - copy_address[i] = ipAddress[i]; - } - return new Inet4Address(ipAddress, hostName); + if (ipAddress == null) { + // We don't throw NullPointerException here for RI compatibility, + // but we do say "address is null" (K0331), instead of "addr is of + // illegal length". + throw new UnknownHostException( + Msg.getString("K0331", hostName)); //$NON-NLS-1$ } - - if (ipAddress != null && ipAddress.length == 16) { - // First check to see if the address is an IPv6-mapped - // IPv4 address. If it is, then we can make it a IPv4 - // address, otherwise, we'll create an IPv6 address. - if (isIPv4MappedAddress(ipAddress)) { - copy_address = new byte[4]; - for (int i = 0; i < 4; i++) { - copy_address[i] = ipAddress[12 + i]; + switch (ipAddress.length) { + case 4: + return new Inet4Address(ipAddress.clone()); + case 16: + // First check to see if the address is an IPv6-mapped + // IPv4 address. If it is, then we can make it a IPv4 + // address, otherwise, we'll create an IPv6 address. + if (isIPv4MappedAddress(ipAddress)) { + return new Inet4Address(ipv4MappedToIPv4(ipAddress)); + } else { + return new Inet6Address(ipAddress.clone(), scope_id); + } + default: + if (hostName != null) { + // "Invalid IP Address is neither 4 or 16 bytes: " + throw new UnknownHostException( + Msg.getString("K0332", hostName)); //$NON-NLS-1$ + } else { + // "Invalid IP Address is neither 4 or 16 bytes" + throw new UnknownHostException( + Msg.getString("K0339")); //$NON-NLS-1$ } - return new Inet4Address(ipAddress, hostName); - } - - copy_address = new byte[16]; - for (int i = 0; i < 16; i++) { - copy_address[i] = ipAddress[i]; - } - - return new Inet6Address(ipAddress, hostName, scope_id); } - - throw new UnknownHostException(Msg.getString("K0332", hostName)); //$NON-NLS-1$ } /** @@ -1362,173 +1286,6 @@ public class InetAddress extends Object implements Serializable { return value; } - /** - * Creates an InetAddress based on the {@code ipAddressString}. No error - * handling is performed here. - */ - static InetAddress createHostNameFromIPAddress(String ipAddressString) - throws UnknownHostException { - - InetAddress address = null; - - if (Inet6Util.isValidIPV4Address(ipAddressString)) { - byte[] byteAddress = new byte[4]; - String[] parts = ipAddressString.split("\\."); //$NON-NLS-1$ - int length = parts.length; - if (length == 1) { - long value = Long.parseLong(parts[0]); - for (int i = 0; i < 4; i++) { - byteAddress[i] = (byte) (value >> ((3 - i) * 8)); - } - } else { - for (int i = 0; i < length; i++) { - byteAddress[i] = (byte) Integer.parseInt(parts[i]); - } - } - - // adjust for 2/3 parts address - if (length == 2) { - byteAddress[3] = byteAddress[1]; - byteAddress[1] = 0; - } - if (length == 3) { - byteAddress[3] = byteAddress[2]; - byteAddress[2] = 0; - } - - address = new Inet4Address(byteAddress); - } else { // otherwise it must be ipv6 - - if (ipAddressString.charAt(0) == '[') { - ipAddressString = ipAddressString.substring(1, ipAddressString - .length() - 1); - } - - StringTokenizer tokenizer = new StringTokenizer(ipAddressString, - ":.%", true); //$NON-NLS-1$ - ArrayList hexStrings = new ArrayList(); - ArrayList decStrings = new ArrayList(); - String scopeString = null; - String token = ""; //$NON-NLS-1$ - String prevToken = ""; //$NON-NLS-1$ - String prevPrevToken = ""; //$NON-NLS-1$ - int doubleColonIndex = -1; // If a double colon exists, we need to - // insert 0s. - - // Go through the tokens, including the separators ':' and '.' - // When we hit a : or . the previous token will be added to either - // the hex list or decimal list. In the case where we hit a :: - // we will save the index of the hexStrings so we can add zeros - // in to fill out the string - while (tokenizer.hasMoreTokens()) { - prevPrevToken = prevToken; - prevToken = token; - token = tokenizer.nextToken(); - - if (token.equals(":")) { //$NON-NLS-1$ - if (prevToken.equals(":")) { //$NON-NLS-1$ - doubleColonIndex = hexStrings.size(); - } else if (!prevToken.equals("")) { //$NON-NLS-1$ - hexStrings.add(prevToken); - } - } else if (token.equals(".")) { //$NON-NLS-1$ - decStrings.add(prevToken); - } else if (token.equals("%")) { //$NON-NLS-1$ - // add the last word before the % properly - if (!prevToken.equals(":") && !prevToken.equals(".")) { //$NON-NLS-1$ //$NON-NLS-2$ - if (prevPrevToken.equals(":")) { //$NON-NLS-1$ - hexStrings.add(prevToken); - } else if (prevPrevToken.equals(".")) { //$NON-NLS-1$ - decStrings.add(prevToken); - } - } - - // the rest should be the scope string - scopeString = tokenizer.nextToken(); - while (tokenizer.hasMoreTokens()) { - scopeString = scopeString + tokenizer.nextToken(); - } - } - } - - if (prevToken.equals(":")) { //$NON-NLS-1$ - if (token.equals(":")) { //$NON-NLS-1$ - doubleColonIndex = hexStrings.size(); - } else { - hexStrings.add(token); - } - } else if (prevToken.equals(".")) { //$NON-NLS-1$ - decStrings.add(token); - } - - // figure out how many hexStrings we should have - // also check if it is a IPv4 address - int hexStringsLength = 8; - - // If we have an IPv4 address tagged on at the end, subtract - // 4 bytes, or 2 hex words from the total - if (decStrings.size() > 0) { - hexStringsLength -= 2; - } - - // if we hit a double Colon add the appropriate hex strings - if (doubleColonIndex != -1) { - int numberToInsert = hexStringsLength - hexStrings.size(); - for (int i = 0; i < numberToInsert; i++) { - hexStrings.add(doubleColonIndex, "0"); //$NON-NLS-1$ - } - } - - byte ipByteArray[] = new byte[16]; - - // Finally convert these strings to bytes... - for (int i = 0; i < hexStrings.size(); i++) { - Inet6Util.convertToBytes(hexStrings.get(i), ipByteArray, i * 2); - } - - // Now if there are any decimal values, we know where they go... - for (int i = 0; i < decStrings.size(); i++) { - ipByteArray[i + 12] = (byte) (Integer.parseInt(decStrings - .get(i)) & 255); - } - - // now check to see if this guy is actually and IPv4 address - // an ipV4 address is ::FFFF:d.d.d.d - boolean ipV4 = true; - for (int i = 0; i < 10; i++) { - if (ipByteArray[i] != 0) { - ipV4 = false; - break; - } - } - - if (ipByteArray[10] != -1 || ipByteArray[11] != -1) { - ipV4 = false; - } - - if (ipV4) { - byte ipv4ByteArray[] = new byte[4]; - for (int i = 0; i < 4; i++) { - ipv4ByteArray[i] = ipByteArray[i + 12]; - } - address = InetAddress.getByAddress(ipv4ByteArray); - } else { - int scopeId = 0; - if (scopeString != null) { - try { - scopeId = Integer.parseInt(scopeString); - } catch (Exception e) { - // this should not occur as we should not get into this - // function unless the address is in a valid format - } - } - address = InetAddress.getByAddress(ipByteArray, scopeId); - } - } - - return address; - } - static boolean preferIPv6Addresses() { String result = AccessController.doPrivileged(new PriviAction( "java.net.preferIPv6Addresses")); //$NON-NLS-1$ diff --git a/libcore/luni/src/main/java/java/net/InetSocketAddress.java b/libcore/luni/src/main/java/java/net/InetSocketAddress.java index 69010539d..08e75a99b 100644 --- a/libcore/luni/src/main/java/java/net/InetSocketAddress.java +++ b/libcore/luni/src/main/java/java/net/InetSocketAddress.java @@ -23,8 +23,6 @@ import java.io.ObjectInputStream; /** * This class represents a socket endpoint described by a IP address and a port * number. It is a concrete implementation of {@code SocketAddress} for IP. - * - * @since Android 1.0 */ public class InetSocketAddress extends SocketAddress { @@ -37,13 +35,12 @@ public class InetSocketAddress extends SocketAddress { private int port; /** - * Creates a socket endpoint with the given port number {@code port} and the - * wildcard address {@code InetAddress.ANY}. The range for valid port numbers - * is between 0 and 65535 inclusive. - * + * Creates a socket endpoint with the given port number {@code port} and + * no specified address. The range for valid port numbers is between 0 and + * 65535 inclusive. + * * @param port * the specified port number to which this socket is bound. - * @since Android 1.0 */ public InetSocketAddress(int port) { this((InetAddress) null, port); @@ -53,20 +50,19 @@ public class InetSocketAddress extends SocketAddress { * Creates a socket endpoint with the given port number {@code port} and * {@code address}. The range for valid port numbers is between 0 and 65535 * inclusive. If {@code address} is {@code null} this socket is bound to the - * wildcard address {@code InetAddress.ANY}. - * + * IPv4 wildcard address. + * * @param port * the specified port number to which this socket is bound. * @param address * the specified address to which this socket is bound. - * @since Android 1.0 */ public InetSocketAddress(InetAddress address, int port) { if (port < 0 || port > 65535) { throw new IllegalArgumentException(); } if (address == null) { - addr = InetAddress.ANY; + addr = Inet4Address.ANY; } else { addr = address; } @@ -79,7 +75,7 @@ public class InetSocketAddress extends SocketAddress { * hostname {@code host}. The hostname is tried to be resolved and cannot be * {@code null}. The range for valid port numbers is between 0 and 65535 * inclusive. - * + * * @param port * the specified port number to which this socket is bound. * @param host @@ -88,14 +84,13 @@ public class InetSocketAddress extends SocketAddress { * if a {@link SecurityManager} is installed and its {@code * checkConnect()} method does not allow the resolving of the * host name. - * @since Android 1.0 */ public InetSocketAddress(String host, int port) { this(host, port, true); } /* - * Internal contructor for InetSocketAddress(String, int) and + * Internal constructor for InetSocketAddress(String, int) and * createUnresolved(String, int); */ InetSocketAddress(String host, int port, boolean needResolved) { @@ -126,7 +121,7 @@ public class InetSocketAddress extends SocketAddress { * Creates an {@code InetSocketAddress} without trying to resolve the * hostname into an {@code InetAddress}. The address field is marked as * unresolved. - * + * * @param host * the specified hostname to which this socket is bound. * @param port @@ -135,7 +130,6 @@ public class InetSocketAddress extends SocketAddress { * @throws IllegalArgumentException * if the hostname {@code host} is {@code null} or the port is * not in the range between 0 and 65535. - * @since Android 1.0 */ public static InetSocketAddress createUnresolved(String host, int port) { return new InetSocketAddress(host, port, false); @@ -143,9 +137,8 @@ public class InetSocketAddress extends SocketAddress { /** * Gets the port number of this socket. - * + * * @return the socket endpoint port number. - * @since Android 1.0 */ public final int getPort() { return port; @@ -153,9 +146,8 @@ public class InetSocketAddress extends SocketAddress { /** * Gets the address of this socket. - * + * * @return the socket endpoint address. - * @since Android 1.0 */ public final InetAddress getAddress() { return addr; @@ -163,9 +155,8 @@ public class InetSocketAddress extends SocketAddress { /** * Gets the hostname of this socket. - * + * * @return the socket endpoint hostname. - * @since Android 1.0 */ public final String getHostName() { return (null != addr) ? addr.getHostName() : hostname; @@ -173,10 +164,9 @@ public class InetSocketAddress extends SocketAddress { /** * Returns whether this socket address is unresolved or not. - * + * * @return {@code true} if this socket address is unresolved, {@code false} * otherwise. - * @since Android 1.0 */ public final boolean isUnresolved() { return addr == null; @@ -185,9 +175,8 @@ public class InetSocketAddress extends SocketAddress { /** * Gets a string representation of this socket included the address and the * port number. - * + * * @return the address and port number as a textual representation. - * @since Android 1.0 */ @Override public String toString() { @@ -204,12 +193,11 @@ public class InetSocketAddress extends SocketAddress { * Compares two socket endpoints and returns true if they are equal. Two * socket endpoints are equal if the IP address or the hostname of both are * equal and they are bound to the same port. - * + * * @param socketAddr * the object to be tested for equality. * @return {@code true} if this socket and the given socket object {@code * socketAddr} are equal, {@code false} otherwise. - * @since Android 1.0 */ @Override public final boolean equals(Object socketAddr) { @@ -243,9 +231,8 @@ public class InetSocketAddress extends SocketAddress { /** * Gets the hashcode of this socket. - * + * * @return the appropriate hashcode. - * @since Android 1.0 */ @Override public final int hashCode() { diff --git a/libcore/luni/src/main/java/java/net/JarURLConnection.java b/libcore/luni/src/main/java/java/net/JarURLConnection.java index 11545fa9d..da39d9c4d 100644 --- a/libcore/luni/src/main/java/java/net/JarURLConnection.java +++ b/libcore/luni/src/main/java/java/net/JarURLConnection.java @@ -38,15 +38,11 @@ import java.util.jar.Manifest; * jar:http://www.example.com/applets/archive.jar!/test.class} *
        • Directory Entry: {@code * jar:http://www.example.com/applets/archive.jar!/applets/}
        • - * - * @since Android 1.0 */ public abstract class JarURLConnection extends URLConnection { /** * The location part of the represented URL. - * - * @since Android 1.0 */ protected URLConnection jarFileURLConnection; @@ -60,12 +56,11 @@ public abstract class JarURLConnection extends URLConnection { /** * Constructs an instance of {@code JarURLConnection} that refers to the * specified URL. - * + * * @param url * the URL that contains the location to connect to. * @throws MalformedURLException * if an invalid URL has been entered. - * @since Android 1.0 */ protected JarURLConnection(URL url) throws MalformedURLException { super(url); @@ -74,10 +69,12 @@ public abstract class JarURLConnection extends URLConnection { if ((sepIdx = file.indexOf("!/")) < 0) { //$NON-NLS-1$ throw new MalformedURLException(); } - if (file.length() == sepIdx + 2) { + fileURL = new URL(url.getFile().substring(0,sepIdx)); //$NON-NLS-1$ + sepIdx += 2; + if (file.length() == sepIdx) { return; } - entryName = file.substring(sepIdx + 2, file.length()); + entryName = file.substring(sepIdx, file.length()); if (null != url.getRef()) { entryName += "#" + url.getRef(); //$NON-NLS-1$ } @@ -86,12 +83,11 @@ public abstract class JarURLConnection extends URLConnection { /** * Returns all attributes of the {@code JarEntry} referenced by this {@code * JarURLConnection}. - * + * * @return the attributes of the referenced {@code JarEntry}. - * @exception IOException + * @throws IOException * if an I/O exception occurs while retrieving the * JAR-entries. - * @since Android 1.0 */ public Attributes getAttributes() throws java.io.IOException { JarEntry jEntry = getJarEntry(); @@ -102,12 +98,11 @@ public abstract class JarURLConnection extends URLConnection { * Returns all certificates of the {@code JarEntry} referenced by this * {@code JarURLConnection} instance. This method will return {@code null} * until the {@code InputStream} has been completely verified. - * + * * @return the certificates of the {@code JarEntry} as an array. - * @exception IOException + * @throws IOException * if there is an I/O exception occurs while getting the * {@code JarEntry}. - * @since Android 1.0 */ public Certificate[] getCertificates() throws java.io.IOException { JarEntry jEntry = getJarEntry(); @@ -122,9 +117,8 @@ public abstract class JarURLConnection extends URLConnection { * Gets the name of the entry referenced by this {@code JarURLConnection}. * The return value will be {@code null} if this instance refers to a JAR * file rather than an JAR file entry. - * + * * @return the {@code JarEntry} name this instance refers to. - * @since Android 1.0 */ public String getEntryName() { return entryName; @@ -133,12 +127,11 @@ public abstract class JarURLConnection extends URLConnection { /** * Gets the {@code JarEntry} object of the entry referenced by this {@code * JarURLConnection}. - * + * * @return the referenced {@code JarEntry} object or {@code null} if no * entry name is specified. * @throws IOException * if an error occurs while getting the file or file-entry. - * @since Android 1.0 */ public JarEntry getJarEntry() throws IOException { if (!connected) { @@ -153,56 +146,44 @@ public abstract class JarURLConnection extends URLConnection { /** * Gets the manifest file associated with this JAR-URL. - * + * * @return the manifest of the referenced JAR-file. * @throws IOException * if an error occurs while getting the manifest file. - * @since Android 1.0 */ public Manifest getManifest() throws java.io.IOException { - return getJarFile().getManifest(); + return (Manifest)getJarFile().getManifest().clone(); } /** * Gets the {@code JarFile} object referenced by this {@code * JarURLConnection}. - * + * * @return the referenced JarFile object. - * @exception IOException + * @throws IOException * if an I/O exception occurs while retrieving the JAR-file. - * @since Android 1.0 */ public abstract JarFile getJarFile() throws java.io.IOException; /** * Gets the URL to the JAR-file referenced by this {@code JarURLConnection}. - * + * * @return the URL to the JAR-file or {@code null} if there was an error * retrieving the URL. - * @since Android 1.0 */ public URL getJarFileURL() { - if (fileURL != null) { - return fileURL; - } - try { - return fileURL = new URL(url.getFile().substring(0, - url.getFile().indexOf("!/"))); //$NON-NLS-1$ - } catch (MalformedURLException e) { - return null; - } + return fileURL; } /** * Gets all attributes of the manifest file referenced by this {@code * JarURLConnection}. If this instance refers to a JAR-file rather than a * JAR-file entry, {@code null} will be returned. - * + * * @return the attributes of the manifest file or {@code null}. - * @exception IOException + * @throws IOException * if an I/O exception occurs while retrieving the {@code * JarFile}. - * @since Android 1.0 */ public Attributes getMainAttributes() throws java.io.IOException { Manifest m = getJarFile().getManifest(); diff --git a/libcore/luni/src/main/java/java/net/MalformedURLException.java b/libcore/luni/src/main/java/java/net/MalformedURLException.java index 386228b1d..cf8aa46d9 100644 --- a/libcore/luni/src/main/java/java/net/MalformedURLException.java +++ b/libcore/luni/src/main/java/java/net/MalformedURLException.java @@ -24,7 +24,6 @@ import java.io.IOException; * incorrect specification. * * @see URL - * @since Android 1.0 */ public class MalformedURLException extends IOException { @@ -32,8 +31,6 @@ public class MalformedURLException extends IOException { /** * Constructs a new instance of this class with its walkback filled in. - * - * @since Android 1.0 */ public MalformedURLException() { super(); @@ -45,7 +42,6 @@ public class MalformedURLException extends IOException { * * @param detailMessage * the detail message for this exception instance. - * @since Android 1.0 */ public MalformedURLException(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/net/MulticastSocket.java b/libcore/luni/src/main/java/java/net/MulticastSocket.java index cf208901e..60d967b48 100644 --- a/libcore/luni/src/main/java/java/net/MulticastSocket.java +++ b/libcore/luni/src/main/java/java/net/MulticastSocket.java @@ -20,15 +20,14 @@ package java.net; import java.io.IOException; import java.util.Enumeration; -import org.apache.harmony.luni.net.SocketImplProvider; +import org.apache.harmony.luni.net.PlainDatagramSocketImpl; import org.apache.harmony.luni.util.Msg; /** * This class implements a multicast socket for sending and receiving IP * multicast datagram packets. - * + * * @see DatagramSocket - * @since Android 1.0 */ public class MulticastSocket extends DatagramSocket { @@ -39,10 +38,9 @@ public class MulticastSocket extends DatagramSocket { /** * Constructs a multicast socket, bound to any available port on the * localhost. - * + * * @throws IOException * if an error occurs creating or binding the socket. - * @since Android 1.0 */ public MulticastSocket() throws IOException { super(); @@ -52,12 +50,11 @@ public class MulticastSocket extends DatagramSocket { /** * Constructs a multicast socket, bound to the specified port on the * localhost. - * + * * @param aPort * the port to bind on the localhost. * @throws IOException * if an error occurs creating or binding the socket. - * @since Android 1.0 */ public MulticastSocket(int aPort) throws IOException { super(aPort); @@ -67,12 +64,11 @@ public class MulticastSocket extends DatagramSocket { /** * Gets the network address used by this socket. This is useful on * multihomed machines. - * + * * @return the address of the network interface through which the datagram * packets are sent or received. - * @exception SocketException + * @throws SocketException * if an error occurs while getting the interface address. - * @since Android 1.0 */ public InetAddress getInterface() throws SocketException { checkClosedAndBind(false); @@ -104,12 +100,12 @@ public class MulticastSocket extends DatagramSocket { /** * Gets the network interface used by this socket. This is useful on * multihomed machines. - * + * * @return the network interface used by this socket or {@code null} if no * interface is set. - * @exception SocketException + * @throws SocketException * if an error occurs while getting the interface. - * @since Android 1.0 + * @since 1.4 */ public NetworkInterface getNetworkInterface() throws SocketException { checkClosedAndBind(false); @@ -150,7 +146,7 @@ public class MulticastSocket extends DatagramSocket { && (InetAddress.preferIPv6Addresses() == true)) { theAddresses[0] = Inet6Address.ANY; } else { - theAddresses[0] = InetAddress.ANY; + theAddresses[0] = Inet4Address.ANY; } return new NetworkInterface(null, null, theAddresses, NetworkInterface.UNSET_INTERFACE_INDEX); @@ -162,11 +158,10 @@ public class MulticastSocket extends DatagramSocket { /** * Gets the time-to-live (TTL) for multicast packets sent on this socket. - * + * * @return the default value for the time-to-life field. - * @exception IOException + * @throws IOException * if an error occurs reading the default value. - * @since Android 1.0 */ public int getTimeToLive() throws IOException { checkClosedAndBind(false); @@ -175,13 +170,12 @@ public class MulticastSocket extends DatagramSocket { /** * Gets the time-to-live (TTL) for multicast packets sent on this socket. - * + * * @return the default value for the time-to-life field. - * @exception IOException + * @throws IOException * if an error occurs reading the default value. * @deprecated Replaced by {@link #getTimeToLive} * @see #getTimeToLive() - * @since Android 1.0 */ @Deprecated public byte getTTL() throws IOException { @@ -189,21 +183,15 @@ public class MulticastSocket extends DatagramSocket { return impl.getTTL(); } - @Override - boolean isMulticastSocket() { - return true; - } - /** * Adds this socket to the specified multicast group. A socket must join a * group before data may be received. A socket may be a member of multiple * groups but may join any group only once. - * + * * @param groupAddr * the multicast group to be joined. - * @exception IOException + * @throws IOException * if an error occurs while joining a group. - * @since Android 1.0 */ public void joinGroup(InetAddress groupAddr) throws IOException { checkClosedAndBind(false); @@ -221,19 +209,19 @@ public class MulticastSocket extends DatagramSocket { * Adds this socket to the specified multicast group. A socket must join a * group before data may be received. A socket may be a member of multiple * groups but may join any group only once. - * + * * @param groupAddress * the multicast group to be joined. * @param netInterface * the network interface on which the datagram packets will be * received. - * @exception IOException + * @throws IOException * if the specified address is not a multicast address. - * @exception SecurityException + * @throws SecurityException * if the caller is not authorized to join the group. - * @exception IllegalArgumentException + * @throws IllegalArgumentException * if no multicast group is specified. - * @since Android 1.0 + * @since 1.4 */ public void joinGroup(SocketAddress groupAddress, NetworkInterface netInterface) throws IOException { @@ -271,14 +259,15 @@ public class MulticastSocket extends DatagramSocket { /** * Removes this socket from the specified multicast group. - * + * * @param groupAddr * the multicast group to be left. - * @exception IOException + * @throws NullPointerException + * if {@code groupAddr} is {@code null}. + * @throws IOException * if the specified group address is not a multicast address. - * @exception SecurityException + * @throws SecurityException * if the caller is not authorized to leave the group. - * @since Android 1.0 */ public void leaveGroup(InetAddress groupAddr) throws IOException { checkClosedAndBind(false); @@ -294,19 +283,19 @@ public class MulticastSocket extends DatagramSocket { /** * Removes this socket from the specified multicast group. - * + * * @param groupAddress * the multicast group to be left. * @param netInterface * the network interface on which the addresses should be * dropped. - * @exception IOException + * @throws IOException * if the specified group address is not a multicast address. - * @exception SecurityException + * @throws SecurityException * if the caller is not authorized to leave the group. - * @exception IllegalArgumentException - * if no multicast group is specified. - * @since Android 1.0 + * @throws IllegalArgumentException + * if {@code groupAddress} is {@code null}. + * @since 1.4 */ public void leaveGroup(SocketAddress groupAddress, NetworkInterface netInterface) throws IOException { @@ -344,16 +333,15 @@ public class MulticastSocket extends DatagramSocket { /** * Send the packet on this socket. The packet must satisfy the security * policy before it may be sent. - * + * * @param pack * the {@code DatagramPacket} to send * @param ttl * the TTL setting for this transmission, overriding the socket * default - * @exception IOException + * @throws IOException * if an error occurs while sending data or setting options. * @deprecated use {@link #setTimeToLive}. - * @since Android 1.0 */ @Deprecated public void send(DatagramPacket pack, byte ttl) throws IOException { @@ -384,13 +372,12 @@ public class MulticastSocket extends DatagramSocket { * Sets the interface address used by this socket. This allows to send * multicast packets on a different interface than the default interface of * the local system. This is useful on multihomed machines. - * + * * @param addr * the multicast interface network address to set. - * @exception SocketException + * @throws SocketException * if an error occurs while setting the network interface * address option. - * @since Android 1.0 */ public void setInterface(InetAddress addr) throws SocketException { checkClosedAndBind(false); @@ -398,7 +385,7 @@ public class MulticastSocket extends DatagramSocket { throw new NullPointerException(); } if (addr.isAnyLocalAddress()) { - impl.setOption(SocketOptions.IP_MULTICAST_IF, InetAddress.ANY); + impl.setOption(SocketOptions.IP_MULTICAST_IF, Inet4Address.ANY); } else if (addr instanceof Inet4Address) { impl.setOption(SocketOptions.IP_MULTICAST_IF, addr); // keep the address used to do the set as we must return the same @@ -435,13 +422,13 @@ public class MulticastSocket extends DatagramSocket { /** * Sets the network interface used by this socket. This is useful for * multihomed machines. - * + * * @param netInterface * the multicast network interface to set. - * @exception SocketException + * @throws SocketException * if an error occurs while setting the network interface * option. - * @since Android 1.0 + * @since 1.4 */ public void setNetworkInterface(NetworkInterface netInterface) throws SocketException { @@ -462,7 +449,7 @@ public class MulticastSocket extends DatagramSocket { if (netInterface.getIndex() == NetworkInterface.UNSET_INTERFACE_INDEX) { // set the address using IP_MULTICAST_IF to make sure this // works for both IPV4 and IPV6 - impl.setOption(SocketOptions.IP_MULTICAST_IF, InetAddress.ANY); + impl.setOption(SocketOptions.IP_MULTICAST_IF, Inet4Address.ANY); try { // we have the index so now we pass set the interface @@ -529,13 +516,12 @@ public class MulticastSocket extends DatagramSocket { /** * Sets the time-to-live (TTL) for multicast packets sent on this socket. * Valid TTL values are between 0 and 255 inclusive. - * + * * @param ttl * the default time-to-live field value for packets sent on this * socket. {@code 0 <= ttl <= 255}. - * @exception IOException + * @throws IOException * if an error occurs while setting the TTL option value. - * @since Android 1.0 */ public void setTimeToLive(int ttl) throws IOException { checkClosedAndBind(false); @@ -548,15 +534,14 @@ public class MulticastSocket extends DatagramSocket { /** * Sets the time-to-live (TTL) for multicast packets sent on this socket. * Valid TTL values are between 0 and 255 inclusive. - * + * * @param ttl * the default time-to-live field value for packets sent on this * socket: {@code 0 <= ttl <= 255}. - * @exception IOException + * @throws IOException * if an error occurs while setting the TTL option value. * @deprecated Replaced by {@link #setTimeToLive} * @see #setTimeToLive(int) - * @since Android 1.0 */ @Deprecated public void setTTL(byte ttl) throws IOException { @@ -568,11 +553,10 @@ public class MulticastSocket extends DatagramSocket { synchronized void createSocket(int aPort, InetAddress addr) throws SocketException { impl = factory != null ? factory.createDatagramSocketImpl() - : SocketImplProvider.getMulticastSocketImpl(); + : new PlainDatagramSocketImpl(); impl.create(); try { - // the required default options are now set in the VM where they - // should be + impl.setOption(SocketOptions.SO_REUSEADDR, Boolean.TRUE); impl.bind(aPort, addr); isBound = true; } catch (SocketException e) { @@ -585,14 +569,14 @@ public class MulticastSocket extends DatagramSocket { * Constructs a {@code MulticastSocket} bound to the host/port specified by * the {@code SocketAddress}, or an unbound {@code DatagramSocket} if the * {@code SocketAddress} is {@code null}. - * + * * @param localAddr * the local machine address and port to bind to. * @throws IllegalArgumentException * if the {@code SocketAddress} is not supported. * @throws IOException * if an error occurs creating or binding the socket. - * @since Android 1.0 + * @since 1.4 */ public MulticastSocket(SocketAddress localAddr) throws IOException { super(localAddr); @@ -601,12 +585,12 @@ public class MulticastSocket extends DatagramSocket { /** * Gets the state of the {@code SocketOptions.IP_MULTICAST_LOOP}. - * + * * @return {@code true} if the IP multicast loop is enabled, {@code false} * otherwise. * @throws SocketException * if the socket is closed or the option is invalid. - * @since Android 1.0 + * @since 1.4 */ public boolean getLoopbackMode() throws SocketException { checkClosedAndBind(false); @@ -616,13 +600,13 @@ public class MulticastSocket extends DatagramSocket { /** * Sets the {@code SocketOptions.IP_MULTICAST_LOOP}. - * + * * @param loop * the value for the socket option socket {@code * SocketOptions.IP_MULTICAST_LOOP}. * @throws SocketException * if the socket is closed or the option is invalid. - * @since Android 1.0 + * @since 1.4 */ public void setLoopbackMode(boolean loop) throws SocketException { checkClosedAndBind(false); diff --git a/libcore/luni/src/main/java/java/net/NegCacheElement.java b/libcore/luni/src/main/java/java/net/NegCacheElement.java index 7285a14f7..e195f316e 100644 --- a/libcore/luni/src/main/java/java/net/NegCacheElement.java +++ b/libcore/luni/src/main/java/java/net/NegCacheElement.java @@ -4,9 +4,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -18,28 +18,27 @@ package java.net; /** * This class is used to hold information about failed host name lookups. - * + * * @see NegativeCache - * @since Android 1.0 */ class NegCacheElement { // we need the time to figure out when the entry is stale // BEGIN android-changed final long nanoTimeAdded = System.nanoTime(); + // END android-changed + + // holds the name of the lookup failure message for this host + final String failedMessage; - // holds the name of the host for which the lookup failed - final String hostName; - /** * Constructor used to set the hostname for the entry for which the lookup * failed. - * - * @param hostName + * + * @param failedMessage * name of the host for which the lookup failed. */ - NegCacheElement(String hostName) { - this.hostName = hostName; + NegCacheElement(String failedMessage) { + this.failedMessage = failedMessage; } - // END android-changed } diff --git a/libcore/luni/src/main/java/java/net/NegativeCache.java b/libcore/luni/src/main/java/java/net/NegativeCache.java index 58078fc09..d6b15159d 100644 --- a/libcore/luni/src/main/java/java/net/NegativeCache.java +++ b/libcore/luni/src/main/java/java/net/NegativeCache.java @@ -4,9 +4,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -25,83 +25,60 @@ import org.apache.harmony.luni.util.PriviAction; /** * This class is used to maintain the negative name lookup cache, which caches * host names which could not be resolved, as a security feature. - * + * * @see NegCacheElement - * @since Android 1.0 */ -class NegativeCache extends LinkedHashMap { - - private static final long serialVersionUID = 1L; - - // BEGIN android-changed - // Copied from a newer version of harmony - private static NegativeCache negCache; - - // maximum number of entries in the cache +class NegativeCache { + // maximum number of entries in the cache private static final int MAX_NEGATIVE_ENTRIES = 5; // the loading for the cache - private static final float LOADING = 0.75F; - // END android-changed + private static final float LOAD_FACTOR = 0.75F; - /** - * Returns the hostname for the cache element. - * - * @return hostName name of the host for which the lookup failed. - * @since Android 1.0 - */ - NegativeCache(int initialCapacity, float loadFactor, boolean accessOrder) { - super(initialCapacity, loadFactor, accessOrder); - } + private static final Map negCache + = new LinkedHashMap( + 2 * MAX_NEGATIVE_ENTRIES, LOAD_FACTOR, true) { - /** - * Returns whether the eldest entry should be removed. It is removed if the - * size has grown beyond the maximum size allowed for the cache. A {@code - * LinkedHashMap} is created such that the least recently used entry is - * deleted. - * - * @param eldest - * the map entry which will be deleted if we return {@code true}. - * @since Android 1.0 - */ - @Override - protected boolean removeEldestEntry(Map.Entry eldest) { - return size() > MAX_NEGATIVE_ENTRIES; + /** + * Returns whether the eldest entry should be removed. It is removed if + * the size has grown beyond the maximum size allowed for the cache. + * + * @param eldest + * the LRU entry, which will be deleted if we return true. + */ + @Override protected boolean removeEldestEntry( + Entry eldest) { + return size() > MAX_NEGATIVE_ENTRIES; + } + }; + + /** Ensures non-instantiability */ + private NegativeCache() { } /** * Adds the host name and the corresponding name lookup fail message to the * cache. - * + * * @param hostName * the name of the host for which the lookup failed. * @param failedMessage * the message returned when the lookup fails. - * @since Android 1.0 */ static synchronized void put(String hostName, String failedMessage) { - // BEGIN android-note - // Made synchronized. copied from a newer version of harmony - // END android-note - checkCacheExists(); negCache.put(hostName, new NegCacheElement(failedMessage)); } /** * Returns the message of the negative cache if the entry has not yet * expired. - * + * * @param hostName * the name of the host for which we look up the entry. * @return the message which was returned when the host lookup failed if the * entry has not yet expired. - * @since Android 1.0 */ static synchronized String getFailedMessage(String hostName) { - // BEGIN android-note - // Made synchronized. copied from a newer version of harmony - // END android-note - checkCacheExists(); NegCacheElement element = negCache.get(hostName); if (element != null) { // check if element is still valid @@ -111,9 +88,10 @@ class NegativeCache extends LinkedHashMap { int ttl = 10; try { if (ttlValue != null) { - ttl = Integer.decode(ttlValue).intValue(); + ttl = Integer.decode(ttlValue); } } catch (NumberFormatException e) { + // If exception, go with ttl == 10 } if (ttl == 0) { negCache.clear(); @@ -130,10 +108,7 @@ class NegativeCache extends LinkedHashMap { } } if (element != null) { - // BEGIN android-changed - // Copied from a newer version of harmony - return element.hostName; - // END android-changed + return element.failedMessage; } return null; } @@ -146,24 +121,4 @@ class NegativeCache extends LinkedHashMap { return ttl * 1000000000; } // END android-added - - /** - * This method checks whether the cache was already created and if not - * creates it. - */ - static synchronized void checkCacheExists() { - // BEGIN android-note - // Made synchronized. copied from a newer version of harmony - // END android-note - if (negCache == null) { - /* - * Create with the access order set so ordering is based on when the - * entries were last accessed. We make the default cache size one - * greater than the maximum number of entries as we will grow to one - * larger and then delete the LRU entry - */ - negCache = new NegativeCache( - MAX_NEGATIVE_ENTRIES + 1, LOADING, true); - } - } } diff --git a/libcore/luni/src/main/java/java/net/NetPermission.java b/libcore/luni/src/main/java/java/net/NetPermission.java index e7cca8b65..bac9d8920 100644 --- a/libcore/luni/src/main/java/java/net/NetPermission.java +++ b/libcore/luni/src/main/java/java/net/NetPermission.java @@ -31,11 +31,9 @@ package java.net; *
          Allows a stream (protocol) handler to be set when constructing an URL * object
          * - *

          * * @see java.security.BasicPermission * @see SecurityManager - * @since Android 1.0 */ public final class NetPermission extends java.security.BasicPermission { @@ -46,7 +44,6 @@ public final class NetPermission extends java.security.BasicPermission { * * @param name * the name of the new NetPermission instance. - * @since Android 1.0 */ public NetPermission(String name) { super(name); @@ -60,7 +57,6 @@ public final class NetPermission extends java.security.BasicPermission { * the name of the new {@code NetPermission} instance. * @param actions * the ignored action string. - * @since Android 1.0 */ public NetPermission(String name, String actions) { super(name, actions); diff --git a/libcore/luni/src/main/java/java/net/NetworkInterface.java b/libcore/luni/src/main/java/java/net/NetworkInterface.java index 68452e6b0..091eb9f9c 100644 --- a/libcore/luni/src/main/java/java/net/NetworkInterface.java +++ b/libcore/luni/src/main/java/java/net/NetworkInterface.java @@ -28,8 +28,6 @@ import org.apache.harmony.luni.util.Msg; * interface is defined by its address and a platform dependent name. The class * provides methods to get all information about the available interfaces of the * system or to identify the local interface of a joined multicast group. - * - * @since Android 1.0 */ public final class NetworkInterface extends Object { @@ -115,7 +113,6 @@ public final class NetworkInterface extends Object { * Gets the name associated with this network interface. * * @return the name of this {@code NetworkInterface} instance. - * @since Android 1.0 */ public String getName() { return name; @@ -125,7 +122,6 @@ public final class NetworkInterface extends Object { * Gets a list of addresses bound to this network interface. * * @return the address list of the represented network interface. - * @since Android 1.0 */ public Enumeration getInetAddresses() { /* @@ -192,7 +188,6 @@ public final class NetworkInterface extends Object { * * @return the display name of this network interface or the name if the * display name is not available. - * @since Android 1.0 */ public String getDisplayName() { /* @@ -217,7 +212,6 @@ public final class NetworkInterface extends Object { * information. * @throws NullPointerException * if the given interface's name is {@code null}. - * @since Android 1.0 */ public static NetworkInterface getByName(String interfaceName) throws SocketException { @@ -244,7 +238,7 @@ public final class NetworkInterface extends Object { /** * Gets the specific network interface according to the given address. - * + * * @param address * the address to identify the searched network interface. * @return the network interface with the specified address if one exists or @@ -254,7 +248,6 @@ public final class NetworkInterface extends Object { * information. * @throws NullPointerException * if the given interface address is invalid. - * @since Android 1.0 */ public static NetworkInterface getByInetAddress(InetAddress address) throws SocketException { @@ -302,7 +295,6 @@ public final class NetworkInterface extends Object { * @throws SocketException * if an error occurs while getting the network interface * information. - * @since Android 1.0 */ public static Enumeration getNetworkInterfaces() throws SocketException { @@ -343,7 +335,6 @@ public final class NetworkInterface extends Object { * @return {@code true} if the specified object is equal to this {@code * NetworkInterface}, {@code false} otherwise. * @see #hashCode - * @since Android 1.0 */ @Override public boolean equals(Object obj) { @@ -412,7 +403,6 @@ public final class NetworkInterface extends Object { * generated using this name. * * @return the hashcode value for this {@code NetworkInterface} instance. - * @since Android 1.0 */ @Override public int hashCode() { @@ -427,7 +417,6 @@ public final class NetworkInterface extends Object { * network interface. * * @return the textual representation for this network interface. - * @since Android 1.0 */ @Override public String toString() { diff --git a/libcore/luni/src/main/java/java/net/NoRouteToHostException.java b/libcore/luni/src/main/java/java/net/NoRouteToHostException.java index 042ccd4b9..f2e2c3634 100644 --- a/libcore/luni/src/main/java/java/net/NoRouteToHostException.java +++ b/libcore/luni/src/main/java/java/net/NoRouteToHostException.java @@ -21,8 +21,6 @@ package java.net; * The {@code NoRouteToHostException} will be thrown while attempting to connect * to a remote host but the host cannot be reached for instance because of a * badly configured router or a blocking firewall. - * - * @since Android 1.0 */ public class NoRouteToHostException extends SocketException { @@ -30,8 +28,6 @@ public class NoRouteToHostException extends SocketException { /** * Constructs a new instance of this exception with its walkback filled in. - * - * @since Android 1.0 */ public NoRouteToHostException() { super(); @@ -43,7 +39,6 @@ public class NoRouteToHostException extends SocketException { * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public NoRouteToHostException(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/net/PasswordAuthentication.java b/libcore/luni/src/main/java/java/net/PasswordAuthentication.java index 324f878d5..735810d76 100644 --- a/libcore/luni/src/main/java/java/net/PasswordAuthentication.java +++ b/libcore/luni/src/main/java/java/net/PasswordAuthentication.java @@ -20,9 +20,8 @@ package java.net; /** * This immutable class is a data structure that encapsulates username and * password which is used by the {@code Authenticator} class. - * + * * @see Authenticator - * @since Android 1.0 */ public final class PasswordAuthentication { @@ -38,7 +37,6 @@ public final class PasswordAuthentication { * the username to store. * @param password * the associated password to store. - * @since Android 1.0 */ public PasswordAuthentication(String userName, char[] password) { this.userName = userName; @@ -51,7 +49,6 @@ public final class PasswordAuthentication { * longer needed. * * @return the copied password. - * @since Android 1.0 */ public char[] getPassword() { return password.clone(); @@ -61,7 +58,6 @@ public final class PasswordAuthentication { * Gets the username stored by this instance. * * @return the stored username. - * @since Android 1.0 */ public String getUserName() { return userName; diff --git a/libcore/luni/src/main/java/java/net/PortUnreachableException.java b/libcore/luni/src/main/java/java/net/PortUnreachableException.java index 973a97828..2b5b25bfb 100644 --- a/libcore/luni/src/main/java/java/net/PortUnreachableException.java +++ b/libcore/luni/src/main/java/java/net/PortUnreachableException.java @@ -27,8 +27,6 @@ public class PortUnreachableException extends SocketException { /** * Constructs a new instance of this class with its walkback filled in. - * - * @since Android 1.0 */ public PortUnreachableException() { } @@ -39,7 +37,6 @@ public class PortUnreachableException extends SocketException { * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public PortUnreachableException(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/net/ProtocolException.java b/libcore/luni/src/main/java/java/net/ProtocolException.java index f32dedcc5..ebbdc44db 100644 --- a/libcore/luni/src/main/java/java/net/ProtocolException.java +++ b/libcore/luni/src/main/java/java/net/ProtocolException.java @@ -21,8 +21,6 @@ package java.net; * Signals that either a connection attempt to a socket of the wrong type, the * application of an unsupported operation or that a general error in the * underlying protocol has occurred. - * - * @since Android 1.0 */ public class ProtocolException extends java.io.IOException { @@ -30,8 +28,6 @@ public class ProtocolException extends java.io.IOException { /** * Constructs a new instance of this class with its walkback filled in. - * - * @since Android 1.0 */ public ProtocolException() { super(); @@ -43,7 +39,6 @@ public class ProtocolException extends java.io.IOException { * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public ProtocolException(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/net/Proxy.java b/libcore/luni/src/main/java/java/net/Proxy.java index df0383d5d..344233908 100644 --- a/libcore/luni/src/main/java/java/net/Proxy.java +++ b/libcore/luni/src/main/java/java/net/Proxy.java @@ -25,8 +25,6 @@ import org.apache.harmony.luni.util.Msg; *
        • DIRECT
        • *
        • HTTP
        • *
        • SOCKS
        • https URI stands for https connection. *
        • ftp URI stands for ftp connection.
        • *
        • socket:://ip:port URI stands for tcp client sockets connection.
        • - *

          - * + * * @param uri * the target URI object. * @return a list containing all applicable proxies. If no proxy is * available, the list contains only the {@code Proxy.NO_PROXY} * element. - * @since Android 1.0 + * @throws IllegalArgumentException + * if {@code uri} is {@code null}. */ public abstract List select(URI uri); @@ -125,8 +119,9 @@ public abstract class ProxySelector { * @param ioe * the exception which was thrown during connection * establishment. + * @throws IllegalArgumentException + * if any argument is {@code null}. * @see #select(URI) - * @since Android 1.0 */ public abstract void connectFailed(URI uri, SocketAddress sa, IOException ioe); diff --git a/libcore/luni/src/main/java/java/net/ProxySelectorImpl.java b/libcore/luni/src/main/java/java/net/ProxySelectorImpl.java index 79ce0cf07..fa8d049ff 100644 --- a/libcore/luni/src/main/java/java/net/ProxySelectorImpl.java +++ b/libcore/luni/src/main/java/java/net/ProxySelectorImpl.java @@ -30,9 +30,8 @@ import org.apache.harmony.luni.util.PriviAction; /** * Default implementation for {@code ProxySelector}. - * - * @since Android 1.0 */ +@SuppressWarnings("unchecked") class ProxySelectorImpl extends ProxySelector { private static final int HTTP_PROXY_PORT = 80; @@ -239,7 +238,7 @@ class ProxySelectorImpl extends ProxySelector { */ private boolean isNonProxyHost(String host, String nonProxyHosts) { // nonProxyHosts is not set - if (null == nonProxyHosts) { + if (null == host || null == nonProxyHosts) { return false; } // Construct regex expression of nonProxyHosts diff --git a/libcore/luni/src/main/java/java/net/ResponseCache.java b/libcore/luni/src/main/java/java/net/ResponseCache.java index e7a0aca82..427b63df7 100644 --- a/libcore/luni/src/main/java/java/net/ResponseCache.java +++ b/libcore/luni/src/main/java/java/net/ResponseCache.java @@ -28,24 +28,20 @@ import java.util.Map; * getDefault()}. If {@code URLConnection#useCaches} is set, {@code * URLConnection} class will use {@code ResponseCache} to store and get * resources. - *

          *

          * Whether the resource is cached depends on the implementation of {@code * ResponseCache}. If so, a {@code CacheResponse} is returned from which the * stream handler reads. If the stream handler fails to get a resource from the * cache, it must get the resource from its original location. - *

          *

          * To write to the cache, the protocol handlers call {@code put()}, upon which a * {@code CacheRequest} is supplied to which the resources are written. - *

          - * + * * @see #put(URI, URLConnection) * @see CacheRequest * @see CacheResponse * @see URLConnection * @see URLStreamHandler - * @since Android 1.0 */ public abstract class ResponseCache { @@ -92,8 +88,6 @@ public abstract class ResponseCache { /** * Creates a new instance of this class. - * - * @since Android 1.0 */ public ResponseCache() { super(); @@ -106,7 +100,6 @@ public abstract class ResponseCache { * @throws SecurityException * if a security manager is installed but it doesn't have the * {@code NetPermission("getResponseCache")}. - * @since Android 1.0 */ public static ResponseCache getDefault() { checkGetResponseCachePermission(); @@ -126,7 +119,6 @@ public abstract class ResponseCache { * @throws SecurityException * if a security manager is installed but it doesn't have the * {@code NetPermission("setResponseCache")}. - * @since Android 1.0 */ public static void setDefault(ResponseCache responseCache) { checkSetResponseCachePermission(); @@ -149,7 +141,6 @@ public abstract class ResponseCache { * if an I/O error occurs while getting the cached data. * @throws IllegalArgumentException * if any one of the parameters is set to {@code null}. - * @since Android 1.0 */ public abstract CacheResponse get(URI uri, String rqstMethod, Map> rqstHeaders) throws IOException; @@ -171,7 +162,6 @@ public abstract class ResponseCache { * if an I/O error occurs while adding the resource. * @throws IllegalArgumentException * if any one of the parameters is set to {@code null}. - * @since Android 1.0 */ public abstract CacheRequest put(URI uri, URLConnection conn) throws IOException; diff --git a/libcore/luni/src/main/java/java/net/SecureCacheResponse.java b/libcore/luni/src/main/java/java/net/SecureCacheResponse.java index fff6b8245..0031b3ab2 100644 --- a/libcore/luni/src/main/java/java/net/SecureCacheResponse.java +++ b/libcore/luni/src/main/java/java/net/SecureCacheResponse.java @@ -27,14 +27,11 @@ import javax.net.ssl.SSLPeerUnverifiedException; * protocol like TLS or SSL. * * @see ResponseCache - * @since Android 1.0 */ public abstract class SecureCacheResponse extends CacheResponse { /** * Creates a new instance of this class. - * - * @since Android 1.0 */ public SecureCacheResponse() { super(); @@ -45,7 +42,6 @@ public abstract class SecureCacheResponse extends CacheResponse { * to retrieve the network resource. * * @return the cipher suite string. - * @since Android 1.0 */ public abstract String getCipherSuite(); @@ -57,7 +53,6 @@ public abstract class SecureCacheResponse extends CacheResponse { * * @return the certificate chain that was sent to the server. If no * certificate chain was sent, the method returns {@code null}. - * @since Android 1.0 */ public abstract List getLocalCertificateChain(); @@ -71,7 +66,6 @@ public abstract class SecureCacheResponse extends CacheResponse { * @return the server's certificate chain. * @throws SSLPeerUnverifiedException * if the peer is unverified. - * @since Android 1.0 */ public abstract List getServerCertificateChain() throws SSLPeerUnverifiedException; @@ -84,7 +78,6 @@ public abstract class SecureCacheResponse extends CacheResponse { * @return a principal object representing the server's principal. * @throws SSLPeerUnverifiedException * if the peer is unverified. - * @since Android 1.0 */ public abstract Principal getPeerPrincipal() throws SSLPeerUnverifiedException; @@ -97,7 +90,6 @@ public abstract class SecureCacheResponse extends CacheResponse { * @return the local principal object being sent to the server. Returns an * {@code X500Principal} object for X509-based cipher suites. If no * principal was sent, it returns {@code null}. - * @since Android 1.0 */ public abstract Principal getLocalPrincipal(); } diff --git a/libcore/luni/src/main/java/java/net/ServerSocket.java b/libcore/luni/src/main/java/java/net/ServerSocket.java index 5f7e079c5..f9d5b2206 100644 --- a/libcore/luni/src/main/java/java/net/ServerSocket.java +++ b/libcore/luni/src/main/java/java/net/ServerSocket.java @@ -20,9 +20,8 @@ package java.net; import java.io.IOException; import java.nio.channels.ServerSocketChannel; -import org.apache.harmony.luni.net.SocketImplProvider; +import org.apache.harmony.luni.net.PlainServerSocketImpl; import org.apache.harmony.luni.platform.Platform; - import org.apache.harmony.luni.util.Msg; /** @@ -30,8 +29,6 @@ import org.apache.harmony.luni.util.Msg; * connections. A {@code ServerSocket} handles the requests and sends back an * appropriate reply. The actual tasks that a server socket must accomplish are * implemented by an internal {@code SocketImpl} instance. - * - * @since Android 1.0 */ public class ServerSocket { @@ -44,7 +41,7 @@ public class ServerSocket { private boolean isBound; private boolean isClosed; - + static { Platform.getNetworkSystem().oneTimeInitialization(true); } @@ -52,14 +49,13 @@ public class ServerSocket { /** * Constructs a new {@code ServerSocket} instance which is not bound to any * port. The default number of pending connections may be backlogged. - * + * * @throws IOException * if an error occurs while creating the server socket. - * @since Android 1.0 */ public ServerSocket() throws IOException { impl = factory != null ? factory.createSocketImpl() - : SocketImplProvider.getServerSocketImpl(); + : new PlainServerSocketImpl(); } /** @@ -81,15 +77,14 @@ public class ServerSocket { * Constructs a new {@code ServerSocket} instance bound to the nominated * port on the localhost. The default number of pending connections may be * backlogged. If {@code aport} is 0 a free port is assigned to the socket. - * + * * @param aport * the port number to listen for connection requests on. * @throws IOException * if an error occurs while creating the server socket. - * @since Android 1.0 */ public ServerSocket(int aport) throws IOException { - this(aport, defaultBacklog(), InetAddress.ANY); + this(aport, defaultBacklog(), Inet4Address.ANY); } /** @@ -97,7 +92,7 @@ public class ServerSocket { * port on the localhost. The number of pending connections that may be * backlogged is specified by {@code backlog}. If {@code aport} is 0 a free * port is assigned to the socket. - * + * * @param aport * the port number to listen for connection requests on. * @param backlog @@ -105,10 +100,9 @@ public class ServerSocket { * will be rejected. * @throws IOException * if an error occurs while creating the server socket. - * @since Android 1.0 */ public ServerSocket(int aport, int backlog) throws IOException { - this(aport, backlog, InetAddress.ANY); + this(aport, backlog, Inet4Address.ANY); } /** @@ -116,7 +110,7 @@ public class ServerSocket { * local host address and port. The number of pending connections that may * be backlogged is specified by {@code backlog}. If {@code aport} is 0 a * free port is assigned to the socket. - * + * * @param aport * the port number to listen for connection requests on. * @param localAddr @@ -126,15 +120,14 @@ public class ServerSocket { * will be rejected. * @throws IOException * if an error occurs while creating the server socket. - * @since Android 1.0 */ public ServerSocket(int aport, int backlog, InetAddress localAddr) throws IOException { super(); checkListen(aport); impl = factory != null ? factory.createSocketImpl() - : SocketImplProvider.getServerSocketImpl(); - InetAddress addr = localAddr == null ? InetAddress.ANY : localAddr; + : new PlainServerSocketImpl(); + InetAddress addr = localAddr == null ? Inet4Address.ANY : localAddr; synchronized (this) { impl.create(true); @@ -154,11 +147,10 @@ public class ServerSocket { * Waits for an incoming request and blocks until the connection is opened. * This method returns a socket object representing the just opened * connection. - * + * * @return the connection representing socket. * @throws IOException * if an error occurs while accepting a new connection. - * @since Android 1.0 */ public Socket accept() throws IOException { checkClosedAndCreate(false); @@ -190,10 +182,9 @@ public class ServerSocket { * Checks whether the server may listen for connection requests on {@code * aport}. Throws an exception if the port is outside the valid range * {@code 0 <= aport <= 65535 }or does not satisfy the security policy. - * + * * @param aPort * the candidate port to listen on. - * @since Android 1.0 */ void checkListen(int aPort) { if (aPort < 0 || aPort > 65535) { @@ -208,10 +199,9 @@ public class ServerSocket { /** * Closes this server socket and its implementation. Any attempt to connect * to this socket thereafter will fail. - * + * * @throws IOException * if an error occurs while closing this socket. - * @since Android 1.0 */ public void close() throws IOException { isClosed = true; @@ -222,7 +212,7 @@ public class ServerSocket { * Answer the default number of pending connections on a server socket. If * the backlog value maximum is reached, any subsequent incoming request is * rejected. - * + * * @return int the default number of pending connection requests */ static int defaultBacklog() { @@ -232,9 +222,8 @@ public class ServerSocket { /** * Gets the local IP address of this server socket or {@code null} if the * socket is unbound. This is useful for multihomed hosts. - * + * * @return the local address of this server socket. - * @since Android 1.0 */ public InetAddress getInetAddress() { if (!isBound()) { @@ -246,9 +235,8 @@ public class ServerSocket { /** * Gets the local port of this server socket or {@code -1} if the socket is * unbound. - * + * * @return the local port this server is listening on. - * @since Android 1.0 */ public int getLocalPort() { if (!isBound()) { @@ -260,11 +248,10 @@ public class ServerSocket { /** * Gets the timeout period of this server socket. This is the time the * server will wait listening for accepted connections before exiting. - * + * * @return the listening timeout value of this server socket. * @throws IOException * if the option cannot be retrieved. - * @since Android 1.0 */ public synchronized int getSoTimeout() throws IOException { if (!isCreated) { @@ -287,13 +274,12 @@ public class ServerSocket { /** * Invokes the server socket implementation to accept a connection on the * given socket {@code aSocket}. - * + * * @param aSocket * the concrete {@code SocketImpl} to accept the connection * request on. * @throws IOException * if the connection cannot be accepted. - * @since Android 1.0 */ protected final void implAccept(Socket aSocket) throws IOException { impl.accept(aSocket.impl); @@ -304,13 +290,12 @@ public class ServerSocket { * Sets the server socket implementation factory of this instance. This * method may only be invoked with sufficient security privilege and only * once during the application lifetime. - * + * * @param aFactory * the streaming socket factory to be used for further socket * instantiations. * @throws IOException * if the factory could not be set or is already set. - * @since Android 1.0 */ public static synchronized void setSocketFactory(SocketImplFactory aFactory) throws IOException { @@ -328,12 +313,11 @@ public class ServerSocket { * Sets the timeout period of this server socket. This is the time the * server will wait listening for accepted connections before exiting. This * value must be a positive number. - * + * * @param timeout * the listening timeout value of this server socket. * @throws SocketException * if an error occurs while setting the option. - * @since Android 1.0 */ public synchronized void setSoTimeout(int timeout) throws SocketException { checkClosedAndCreate(true); @@ -347,13 +331,12 @@ public class ServerSocket { * Returns a textual representation of this server socket including the * address, port and the state. The port field is set to {@code 0} if there * is no connection to the server socket. - * + * * @return the textual socket representation. - * @since Android 1.0 */ @Override public String toString() { - StringBuffer result = new StringBuffer(64); + StringBuilder result = new StringBuilder(64); result.append("ServerSocket["); //$NON-NLS-1$ if (!isBound()) { return result.append("unbound]").toString(); //$NON-NLS-1$ @@ -371,7 +354,7 @@ public class ServerSocket { * number of pending connections may be backlogged. If the {@code localAddr} * is set to {@code null} the socket will be bound to an available local * address on any free port of the system. - * + * * @param localAddr * the local address and port to bind on. * @throws IllegalArgumentException @@ -379,7 +362,6 @@ public class ServerSocket { * @throws IOException * if the socket is already bound or a problem occurs during * binding. - * @since Android 1.0 */ public void bind(SocketAddress localAddr) throws IOException { bind(localAddr, defaultBacklog()); @@ -391,7 +373,7 @@ public class ServerSocket { * available local address on any free port of the system. The value for * {@code backlog} must e greater than {@code 0} otherwise the default value * will be used. - * + * * @param localAddr * the local machine address and port to bind on. * @param backlog @@ -402,7 +384,6 @@ public class ServerSocket { * @throws IOException * if the socket is already bound or a problem occurs during * binding. - * @since Android 1.0 */ public void bind(SocketAddress localAddr, int backlog) throws IOException { checkClosedAndCreate(true); @@ -410,7 +391,7 @@ public class ServerSocket { throw new BindException(Msg.getString("K0315")); //$NON-NLS-1$ } int port = 0; - InetAddress addr = InetAddress.ANY; + InetAddress addr = Inet4Address.ANY; if (localAddr != null) { if (!(localAddr instanceof InetSocketAddress)) { throw new IllegalArgumentException(Msg.getString( @@ -443,9 +424,8 @@ public class ServerSocket { /** * Gets the local socket address of this server socket or {@code null} if * the socket is unbound. This is useful on multihomed hosts. - * + * * @return the local socket address and port this socket is bound to. - * @since Android 1.0 */ public SocketAddress getLocalSocketAddress() { if (!isBound()) { @@ -457,9 +437,8 @@ public class ServerSocket { /** * Returns whether this server socket is bound to a local address and port * or not. - * + * * @return {@code true} if this socket is bound, {@code false} otherwise. - * @since Android 1.0 */ public boolean isBound() { return isBound; @@ -467,9 +446,8 @@ public class ServerSocket { /** * Returns whether this server socket is closed or not. - * + * * @return {@code true} if this socket is closed, {@code false} otherwise. - * @since Android 1.0 */ public boolean isClosed() { return isClosed; @@ -504,12 +482,11 @@ public class ServerSocket { /** * Sets the value for the socket option {@code SocketOptions.SO_REUSEADDR}. - * + * * @param reuse * the socket option setting. * @throws SocketException * if an error occurs while setting the option value. - * @since Android 1.0 */ public void setReuseAddress(boolean reuse) throws SocketException { checkClosedAndCreate(true); @@ -519,11 +496,10 @@ public class ServerSocket { /** * Gets the value of the socket option {@code SocketOptions.SO_REUSEADDR}. - * + * * @return {@code true} if the option is enabled, {@code false} otherwise. * @throws SocketException * if an error occurs while reading the option value. - * @since Android 1.0 */ public boolean getReuseAddress() throws SocketException { checkClosedAndCreate(true); @@ -534,13 +510,12 @@ public class ServerSocket { /** * Sets the server socket receive buffer size {@code * SocketOptions.SO_RCVBUF}. - * + * * @param size * the buffer size in bytes. * @throws SocketException * if an error occurs while setting the size or the size is * invalid. - * @since Android 1.0 */ public void setReceiveBufferSize(int size) throws SocketException { checkClosedAndCreate(true); @@ -553,11 +528,10 @@ public class ServerSocket { /** * Gets the value for the receive buffer size socket option {@code * SocketOptions.SO_RCVBUF}. - * + * * @return the receive buffer size of this socket. * @throws SocketException * if an error occurs while reading the option value. - * @since Android 1.0 */ public int getReceiveBufferSize() throws SocketException { checkClosedAndCreate(true); @@ -568,9 +542,8 @@ public class ServerSocket { * Gets the related channel if this instance was created by a * {@code ServerSocketChannel}. The current implementation returns always {@code * null}. - * + * * @return the related {@code ServerSocketChannel} if any. - * @since Android 1.0 */ public ServerSocketChannel getChannel() { return null; @@ -580,8 +553,7 @@ public class ServerSocket { * Sets performance preferences for connection time, latency and bandwidth. *

          * This method does currently nothing. - *

          - * + * * @param connectionTime * the value representing the importance of a short connecting * time. @@ -589,7 +561,6 @@ public class ServerSocket { * the value representing the importance of low latency. * @param bandwidth * the value representing the importance of high bandwidth. - * @since Android 1.0 */ public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) { diff --git a/libcore/luni/src/main/java/java/net/Socket.java b/libcore/luni/src/main/java/java/net/Socket.java index b29b9657d..52895668d 100644 --- a/libcore/luni/src/main/java/java/net/Socket.java +++ b/libcore/luni/src/main/java/java/net/Socket.java @@ -22,17 +22,19 @@ import java.io.InputStream; import java.io.OutputStream; import java.nio.channels.SocketChannel; import java.security.AccessController; +// BEGIN android-added +import java.util.logging.Logger; +import java.util.logging.Level; +// END android-added import org.apache.harmony.luni.net.NetUtil; -import org.apache.harmony.luni.net.SocketImplProvider; +import org.apache.harmony.luni.net.PlainSocketImpl; import org.apache.harmony.luni.platform.Platform; import org.apache.harmony.luni.util.Msg; import org.apache.harmony.luni.util.PriviAction; /** * Provides a client-side TCP socket. - * - * @since Android 1.0 */ public class Socket { @@ -66,7 +68,16 @@ public class Socket { static final int TCP_NODELAY = 4; static final int FLAG_SHUTDOWN = 8; - + + static private Logger logger; + + static private Logger getLogger() { + if (logger == null) { + logger = Logger.getLogger(Socket.class.getName()); + } + return logger; + } + static { Platform.getNetworkSystem().oneTimeInitialization(true); } @@ -75,14 +86,13 @@ public class Socket { * Creates a new unconnected socket. When a SocketImplFactory is defined it * creates the internal socket implementation, otherwise the default socket * implementation will be used for this socket. - * + * * @see SocketImplFactory * @see SocketImpl - * @since Android 1.0 */ public Socket() { impl = factory != null ? factory.createSocketImpl() - : SocketImplProvider.getSocketImpl(); + : new PlainSocketImpl(); } /** @@ -95,8 +105,7 @@ public class Socket { * proxy server:
          * {@code Socket sock = new Socket(new Proxy(Proxy.Type.SOCKS, new * InetSocketAddress("test.domain.org", 2130)));} - *

          - * + * * @param proxy * the specified proxy for this socket. * @throws IllegalArgumentException @@ -107,7 +116,6 @@ public class Socket { * connect to the given proxy. * @see SocketImplFactory * @see SocketImpl - * @since Android 1.0 */ public Socket(Proxy proxy) { if (null == proxy || Proxy.Type.HTTP == proxy.type()) { @@ -127,15 +135,73 @@ public class Socket { checkConnectPermission(host, port); } impl = factory != null ? factory.createSocketImpl() - : SocketImplProvider.getSocketImpl(proxy); + : new PlainSocketImpl(proxy); this.proxy = proxy; } + // BEGIN android-added + /** + * Tries to connect a socket to all IP addresses of the given hostname. + * + * @param dstName + * the target host name or IP address to connect to. + * @param dstPort + * the port on the target host to connect to. + * @param localAddress + * the address on the local host to bind to. + * @param localPort + * the port on the local host to bind to. + * @param streaming + * if {@code true} a streaming socket is returned, a datagram + * socket otherwise. + * @throws UnknownHostException + * if the host name could not be resolved into an IP address. + * @throws IOException + * if an error occurs while creating the socket. + * @throws SecurityException + * if a security manager exists and it denies the permission to + * connect to the given address and port. + */ + private void tryAllAddresses(String dstName, int dstPort, InetAddress + localAddress, int localPort, boolean streaming) throws IOException { + InetAddress[] dstAddresses = InetAddress.getAllByName(dstName); + // Loop through all the destination addresses except the last, trying to + // connect to each one and ignoring errors. There must be at least one + // address, or getAllByName would have thrown UnknownHostException. + InetAddress dstAddress; + for (int i = 0; i < dstAddresses.length - 1; i++) { + dstAddress = dstAddresses[i]; + try { + checkDestination(dstAddress, dstPort); + startupSocket(dstAddress, dstPort, localAddress, localPort, + streaming); + return; + } catch(SecurityException e1) { + getLogger().log(Level.INFO, dstAddress + "(" + dstPort + "): " + + e1.getClass().getName() + ": " + e1.getMessage()); + } catch(IOException e2) { + getLogger().log(Level.INFO, dstAddress + "(" + dstPort + "): " + + e2.getClass().getName() + ": " + e2.getMessage()); + } + } + + // Now try to connect to the last address in the array, handing back to + // the caller any exceptions that are thrown. + dstAddress = dstAddresses[dstAddresses.length - 1]; + checkDestination(dstAddress, dstPort); + startupSocket(dstAddress, dstPort, localAddress, localPort, streaming); + } + // END android-added + /** * Creates a new streaming socket connected to the target host specified by * the parameters {@code dstName} and {@code dstPort}. The socket is bound * to any available port on the local host. - * + *

          Implementation note: this implementation tries each + * IP address for the given hostname until it either connects successfully + * or it exhausts the set. It will try both IPv4 and IPv6 addresses in the + * order specified by {@code preferIPv6Addresses}. + * * @param dstName * the target host name or IP address to connect to. * @param dstPort @@ -147,14 +213,12 @@ public class Socket { * @throws SecurityException * if a security manager exists and it denies the permission to * connect to the given address and port. - * @since Android 1.0 */ public Socket(String dstName, int dstPort) throws UnknownHostException, IOException { - this(); - InetAddress dstAddress = InetAddress.getByName(dstName); - checkDestination(dstAddress, dstPort); - startupSocket(dstAddress, dstPort, null, 0, true); + // BEGIN android-changed + this(dstName, dstPort, null, 0); + // END android-changed } /** @@ -162,9 +226,13 @@ public class Socket { * the parameters {@code dstName} and {@code dstPort}. On the local endpoint * the socket is bound to the given address {@code localAddress} on port * {@code localPort}. - * + * * If {@code host} is {@code null} a loopback address is used to connect to. - * + *

          Implementation note: this implementation tries each + * IP address for the given hostname until it either connects successfully + * or it exhausts the set. It will try both IPv4 and IPv6 addresses in the + * order specified by {@code preferIPv6Addresses}. + * * @param dstName * the target host name or IP address to connect to. * @param dstPort @@ -180,21 +248,24 @@ public class Socket { * @throws SecurityException * if a security manager exists and it denies the permission to * connect to the given address and port. - * @since Android 1.0 */ public Socket(String dstName, int dstPort, InetAddress localAddress, int localPort) throws IOException { this(); - InetAddress dstAddress = InetAddress.getByName(dstName); - checkDestination(dstAddress, dstPort); - startupSocket(dstAddress, dstPort, localAddress, localPort, true); + // BEGIN android-changed + tryAllAddresses(dstName, dstPort, localAddress, localPort, true); + // END android-changed } /** * Creates a new streaming or datagram socket connected to the target host * specified by the parameters {@code hostName} and {@code port}. The socket * is bound to any available port on the local host. - * + *

          Implementation note: this implementation tries each + * IP address for the given hostname until it either connects successfully + * or it exhausts the set. It will try both IPv4 and IPv6 addresses in the + * order specified by {@code preferIPv6Addresses}. + * * @param hostName * the target host name or IP address to connect to. * @param port @@ -212,22 +283,21 @@ public class Socket { * @deprecated Use {@code Socket(String, int)} instead of this for streaming * sockets or an appropriate constructor of {@code * DatagramSocket} for UDP transport. - * @since Android 1.0 */ @Deprecated public Socket(String hostName, int port, boolean streaming) throws IOException { this(); - InetAddress host = InetAddress.getByName(hostName); - checkDestination(host, port); - startupSocket(host, port, null, 0, streaming); + // BEGIN android-changed + tryAllAddresses(hostName, port, null, 0, streaming); + // END android-changed } /** * Creates a new streaming socket connected to the target host specified by * the parameters {@code dstAddress} and {@code dstPort}. The socket is * bound to any available port on the local host. - * + * * @param dstAddress * the target host address to connect to. * @param dstPort @@ -237,7 +307,6 @@ public class Socket { * @throws SecurityException * if a security manager exists and it denies the permission to * connect to the given address and port. - * @since Android 1.0 */ public Socket(InetAddress dstAddress, int dstPort) throws IOException { this(); @@ -250,7 +319,7 @@ public class Socket { * the parameters {@code dstAddress} and {@code dstPort}. On the local * endpoint the socket is bound to the given address {@code localAddress} on * port {@code localPort}. - * + * * @param dstAddress * the target host address to connect to. * @param dstPort @@ -264,7 +333,6 @@ public class Socket { * @throws SecurityException * if a security manager exists and it denies the permission to * connect to the given address and port. - * @since Android 1.0 */ public Socket(InetAddress dstAddress, int dstPort, InetAddress localAddress, int localPort) throws IOException { @@ -277,7 +345,7 @@ public class Socket { * Creates a new streaming or datagram socket connected to the target host * specified by the parameters {@code addr} and {@code port}. The socket is * bound to any available port on the local host. - * + * * @param addr * the Internet address to connect to. * @param port @@ -293,7 +361,6 @@ public class Socket { * @deprecated Use {@code Socket(InetAddress, int)} instead of this for * streaming sockets or an appropriate constructor of {@code * DatagramSocket} for UDP transport. - * @since Android 1.0 */ @Deprecated public Socket(InetAddress addr, int port, boolean streaming) @@ -305,12 +372,11 @@ public class Socket { /** * Creates an unconnected socket with the given socket implementation. - * + * * @param anImpl * the socket implementation to be used. * @throws SocketException * if an error occurs while creating the socket. - * @since Android 1.0 */ protected Socket(SocketImpl anImpl) throws SocketException { impl = anImpl; @@ -319,7 +385,7 @@ public class Socket { /** * Checks whether the connection destination satisfies the security policy * and the validity of the port range. - * + * * @param destAddr * the destination host address. * @param dstPort @@ -336,7 +402,7 @@ public class Socket { /** * Checks whether the connection destination satisfies the security policy. - * + * * @param hostname * the destination hostname. * @param dstPort @@ -352,10 +418,9 @@ public class Socket { /** * Closes the socket. It is not possible to reconnect or rebind to this * socket thereafter which means a new socket instance has to be created. - * + * * @throws IOException * if an error occurs while closing the socket. - * @since Android 1.0 */ public synchronized void close() throws IOException { isClosed = true; @@ -364,10 +429,9 @@ public class Socket { /** * Gets the IP address of the target host this socket is connected to. - * + * * @return the IP address of the connected target host or {@code null} if * this socket is not yet connected. - * @since Android 1.0 */ public InetAddress getInetAddress() { if (!isConnected()) { @@ -378,12 +442,11 @@ public class Socket { /** * Gets an input stream to read data from this socket. - * + * * @return the byte-oriented input stream. * @throws IOException * if an error occurs while creating the input stream or the * socket is in an invalid state. - * @since Android 1.0 */ public InputStream getInputStream() throws IOException { checkClosedAndCreate(false); @@ -395,13 +458,12 @@ public class Socket { /** * Gets the setting of the socket option {@code SocketOptions.SO_KEEPALIVE}. - * + * * @return {@code true} if the {@code SocketOptions.SO_KEEPALIVE} is * enabled, {@code false} otherwise. * @throws SocketException * if an error occurs while reading the socket option. * @see SocketOptions#SO_KEEPALIVE - * @since Android 1.0 */ public boolean getKeepAlive() throws SocketException { checkClosedAndCreate(true); @@ -411,14 +473,13 @@ public class Socket { /** * Gets the local IP address this socket is bound to. - * + * * @return the local IP address of this socket or {@code InetAddress.ANY} if * the socket is unbound. - * @since Android 1.0 */ public InetAddress getLocalAddress() { if (!isBound()) { - return InetAddress.ANY; + return Inet4Address.ANY; } return Platform.getNetworkSystem().getSocketLocalAddress(impl.fd, InetAddress.preferIPv6Addresses()); @@ -426,10 +487,9 @@ public class Socket { /** * Gets the local port this socket is bound to. - * + * * @return the local port of this socket or {@code -1} if the socket is * unbound. - * @since Android 1.0 */ public int getLocalPort() { if (!isBound()) { @@ -440,12 +500,11 @@ public class Socket { /** * Gets an output stream to write data into this socket. - * + * * @return the byte-oriented output stream. * @throws IOException * if an error occurs while creating the output stream or the * socket is in an invalid state. - * @since Android 1.0 */ public OutputStream getOutputStream() throws IOException { checkClosedAndCreate(false); @@ -457,10 +516,9 @@ public class Socket { /** * Gets the port number of the target host this socket is connected to. - * + * * @return the port number of the connected target host or {@code 0} if this * socket is not yet connected. - * @since Android 1.0 */ public int getPort() { if (!isConnected()) { @@ -471,13 +529,12 @@ public class Socket { /** * Gets the value of the socket option {@code SocketOptions.SO_LINGER}. - * + * * @return the current value of the option {@code SocketOptions.SO_LINGER} * or {@code -1} if this option is disabled. * @throws SocketException * if an error occurs while reading the socket option. * @see SocketOptions#SO_LINGER - * @since Android 1.0 */ public int getSoLinger() throws SocketException { checkClosedAndCreate(true); @@ -486,12 +543,11 @@ public class Socket { /** * Gets the receive buffer size of this socket. - * + * * @return the current value of the option {@code SocketOptions.SO_RCVBUF}. * @throws SocketException * if an error occurs while reading the socket option. * @see SocketOptions#SO_RCVBUF - * @since Android 1.0 */ public synchronized int getReceiveBufferSize() throws SocketException { checkClosedAndCreate(true); @@ -500,12 +556,11 @@ public class Socket { /** * Gets the send buffer size of this socket. - * + * * @return the current value of the option {@code SocketOptions.SO_SNDBUF}. * @throws SocketException * if an error occurs while reading the socket option. * @see SocketOptions#SO_SNDBUF - * @since Android 1.0 */ public synchronized int getSendBufferSize() throws SocketException { checkClosedAndCreate(true); @@ -515,13 +570,12 @@ public class Socket { /** * Gets the timeout for this socket during which a reading operation shall * block while waiting for data. - * + * * @return the current value of the option {@code SocketOptions.SO_TIMEOUT} * or {@code 0} which represents an infinite timeout. * @throws SocketException * if an error occurs while reading the socket option. * @see SocketOptions#SO_TIMEOUT - * @since Android 1.0 */ public synchronized int getSoTimeout() throws SocketException { checkClosedAndCreate(true); @@ -530,13 +584,12 @@ public class Socket { /** * Gets the setting of the socket option {@code SocketOptions.TCP_NODELAY}. - * + * * @return {@code true} if the {@code SocketOptions.TCP_NODELAY} is enabled, * {@code false} otherwise. * @throws SocketException * if an error occurs while reading the socket option. * @see SocketOptions#TCP_NODELAY - * @since Android 1.0 */ public boolean getTcpNoDelay() throws SocketException { checkClosedAndCreate(true); @@ -546,13 +599,12 @@ public class Socket { /** * Sets the state of the {@code SocketOptions.SO_KEEPALIVE} for this socket. - * + * * @param value * the state whether this option is enabled or not. * @throws SocketException * if an error occurs while setting the option. * @see SocketOptions#SO_KEEPALIVE - * @since Android 1.0 */ public void setKeepAlive(boolean value) throws SocketException { if (impl != null) { @@ -565,12 +617,11 @@ public class Socket { /** * Sets the internal factory for creating socket implementations. This may * only be executed once during the lifetime of the application. - * + * * @param fac * the socket implementation factory to be set. * @throws IOException * if the factory has been already set. - * @since Android 1.0 */ public static synchronized void setSocketImplFactory(SocketImplFactory fac) throws IOException { @@ -586,7 +637,7 @@ public class Socket { /** * Sets the send buffer size of this socket. - * + * * @param size * the buffer size in bytes. This value must be a positive number * greater than {@code 0}. @@ -594,7 +645,6 @@ public class Socket { * if an error occurs while setting the size or the given value * is an invalid size. * @see SocketOptions#SO_SNDBUF - * @since Android 1.0 */ public synchronized void setSendBufferSize(int size) throws SocketException { checkClosedAndCreate(true); @@ -606,7 +656,7 @@ public class Socket { /** * Sets the receive buffer size of this socket. - * + * * @param size * the buffer size in bytes. This value must be a positive number * greater than {@code 0}. @@ -614,7 +664,6 @@ public class Socket { * if an error occurs while setting the size or the given value * is an invalid size. * @see SocketOptions#SO_RCVBUF - * @since Android 1.0 */ public synchronized void setReceiveBufferSize(int size) throws SocketException { @@ -629,7 +678,7 @@ public class Socket { * Sets the state of the {@code SocketOptions.SO_LINGER} with the given * timeout in seconds. The timeout value for this option is silently limited * to the maximum of {@code 65535}. - * + * * @param on * the state whether this option is enabled or not. * @param timeout @@ -637,7 +686,6 @@ public class Socket { * @throws SocketException * if an error occurs while setting the option. * @see SocketOptions#SO_LINGER - * @since Android 1.0 */ public void setSoLinger(boolean on, int timeout) throws SocketException { checkClosedAndCreate(true); @@ -665,14 +713,13 @@ public class Socket { * operation will block indefinitely if this option value is set to {@code * 0}. The timeout must be set before calling the read operation. A * {@code SocketTimeoutException} is thrown when this timeout expires. - * + * * @param timeout * the reading timeout value as number greater than {@code 0} or * {@code 0} for an infinite timeout. * @throws SocketException * if an error occurs while setting the option. * @see SocketOptions#SO_TIMEOUT - * @since Android 1.0 */ public synchronized void setSoTimeout(int timeout) throws SocketException { checkClosedAndCreate(true); @@ -684,13 +731,12 @@ public class Socket { /** * Sets the state of the {@code SocketOptions.TCP_NODELAY} for this socket. - * + * * @param on * the state whether this option is enabled or not. * @throws SocketException * if an error occurs while setting the option. * @see SocketOptions#TCP_NODELAY - * @since Android 1.0 */ public void setTcpNoDelay(boolean on) throws SocketException { checkClosedAndCreate(true); @@ -700,7 +746,7 @@ public class Socket { /** * Creates a stream socket, binds it to the nominated local address/port, * then connects it to the nominated destination address/port. - * + * * @param dstAddress * the destination host address. * @param dstPort @@ -721,7 +767,7 @@ public class Socket { throw new IllegalArgumentException(Msg.getString("K0046")); //$NON-NLS-1$ } - InetAddress addr = localAddress == null ? InetAddress.ANY + InetAddress addr = localAddress == null ? Inet4Address.ANY : localAddress; synchronized (this) { impl.create(streaming); @@ -743,9 +789,8 @@ public class Socket { /** * Returns a {@code String} containing a concise, human-readable description of the * socket. - * + * * @return the textual representation of this socket. - * @since Android 1.0 */ @Override public String toString() { @@ -759,12 +804,11 @@ public class Socket { * Closes the input stream of this socket. Any further data sent to this * socket will be discarded. Reading from this socket after this method has * been called will return the value {@code EOF}. - * + * * @throws IOException * if an error occurs while closing the socket input stream. * @throws SocketException * if the input stream is already closed. - * @since Android 1.0 */ public void shutdownInput() throws IOException { if (isInputShutdown()) { @@ -779,12 +823,11 @@ public class Socket { * Closes the output stream of this socket. All buffered data will be sent * followed by the termination sequence. Writing to the closed output stream * will cause an {@code IOException}. - * + * * @throws IOException * if an error occurs while closing the socket output stream. * @throws SocketException * if the output stream is already closed. - * @since Android 1.0 */ public void shutdownOutput() throws IOException { if (isOutputShutdown()) { @@ -798,7 +841,7 @@ public class Socket { /** * Checks whether the socket is closed, and throws an exception. Otherwise * creates the underlying SocketImpl. - * + * * @throws SocketException * if the socket is closed. */ @@ -840,9 +883,8 @@ public class Socket { * Gets the local address and port of this socket as a SocketAddress or * {@code null} if the socket is unbound. This is useful on multihomed * hosts. - * + * * @return the bound local socket address and port. - * @since Android 1.0 */ public SocketAddress getLocalSocketAddress() { if (!isBound()) { @@ -854,9 +896,8 @@ public class Socket { /** * Gets the remote address and port of this socket as a {@code * SocketAddress} or {@code null} if the socket is not connected. - * + * * @return the remote socket address and port. - * @since Android 1.0 */ public SocketAddress getRemoteSocketAddress() { if (!isConnected()) { @@ -867,10 +908,9 @@ public class Socket { /** * Returns whether this socket is bound to a local address and port. - * + * * @return {@code true} if the socket is bound to a local address, {@code * false} otherwise. - * @since Android 1.0 */ public boolean isBound() { return isBound; @@ -878,9 +918,8 @@ public class Socket { /** * Returns whether this socket is connected to a remote host. - * + * * @return {@code true} if the socket is connected, {@code false} otherwise. - * @since Android 1.0 */ public boolean isConnected() { return isConnected; @@ -888,9 +927,8 @@ public class Socket { /** * Returns whether this socket is closed. - * + * * @return {@code true} if the socket is closed, {@code false} otherwise. - * @since Android 1.0 */ public boolean isClosed() { return isClosed; @@ -901,7 +939,7 @@ public class Socket { * the SocketAddress {@code localAddr}. If {@code localAddr} is set to * {@code null}, this socket will be bound to an available local address on * any free port. - * + * * @param localAddr * the specific address and port on the local machine to bind to. * @throws IllegalArgumentException @@ -909,7 +947,6 @@ public class Socket { * @throws IOException * if the socket is already bound or an error occurs while * binding. - * @since Android 1.0 */ public void bind(SocketAddress localAddr) throws IOException { checkClosedAndCreate(true); @@ -918,7 +955,7 @@ public class Socket { } int port = 0; - InetAddress addr = InetAddress.ANY; + InetAddress addr = Inet4Address.ANY; if (localAddr != null) { if (!(localAddr instanceof InetSocketAddress)) { throw new IllegalArgumentException(Msg.getString( @@ -946,7 +983,7 @@ public class Socket { /** * Connects this socket to the given remote host address and port specified * by the SocketAddress {@code remoteAddr}. - * + * * @param remoteAddr * the address and port of the remote host to connect to. * @throws IllegalArgumentException @@ -954,7 +991,6 @@ public class Socket { * @throws IOException * if the socket is already connected or an error occurs while * connecting. - * @since Android 1.0 */ public void connect(SocketAddress remoteAddr) throws IOException { connect(remoteAddr, 0); @@ -965,7 +1001,7 @@ public class Socket { * by the SocketAddress {@code remoteAddr} with the specified timeout. The * connecting method will block until the connection is established or an * error occurred. - * + * * @param remoteAddr * the address and port of the remote host to connect to. * @param timeout @@ -977,7 +1013,6 @@ public class Socket { * @throws IOException * if the socket is already connected or an error occurs while * connecting. - * @since Android 1.0 */ public void connect(SocketAddress remoteAddr, int timeout) throws IOException { @@ -1012,7 +1047,7 @@ public class Socket { // options on create // impl.create(true); if (!NetUtil.usingSocks(proxy)) { - impl.bind(InetAddress.ANY, 0); + impl.bind(Inet4Address.ANY, 0); } isBound = true; } @@ -1028,22 +1063,20 @@ public class Socket { /** * Returns whether the incoming channel of the socket has already been * closed. - * + * * @return {@code true} if reading from this socket is not possible anymore, * {@code false} otherwise. - * @since Android 1.0 */ public boolean isInputShutdown() { return isInputShutdown; } /** - * Returns whether the outgoing channel of the socket has already been + * Returns whether the outgoing channel of the socket has already been * closed. - * + * * @return {@code true} if writing to this socket is not possible anymore, * {@code false} otherwise. - * @since Android 1.0 */ public boolean isOutputShutdown() { return isOutputShutdown; @@ -1051,13 +1084,12 @@ public class Socket { /** * Sets the state of the {@code SocketOptions.SO_REUSEADDR} for this socket. - * + * * @param reuse * the state whether this option is enabled or not. * @throws SocketException * if an error occurs while setting the option. * @see SocketOptions#SO_REUSEADDR - * @since Android 1.0 */ public void setReuseAddress(boolean reuse) throws SocketException { checkClosedAndCreate(true); @@ -1067,13 +1099,12 @@ public class Socket { /** * Gets the setting of the socket option {@code SocketOptions.SO_REUSEADDR}. - * + * * @return {@code true} if the {@code SocketOptions.SO_REUSEADDR} is * enabled, {@code false} otherwise. * @throws SocketException * if an error occurs while reading the socket option. * @see SocketOptions#SO_REUSEADDR - * @since Android 1.0 */ public boolean getReuseAddress() throws SocketException { checkClosedAndCreate(true); @@ -1085,13 +1116,12 @@ public class Socket { * Sets the state of the {@code SocketOptions.SO_OOBINLINE} for this socket. * When this option is enabled urgent data can be received in-line with * normal data. - * + * * @param oobinline * whether this option is enabled or not. * @throws SocketException * if an error occurs while setting the option. * @see SocketOptions#SO_OOBINLINE - * @since Android 1.0 */ public void setOOBInline(boolean oobinline) throws SocketException { checkClosedAndCreate(true); @@ -1101,13 +1131,12 @@ public class Socket { /** * Gets the setting of the socket option {@code SocketOptions.SO_OOBINLINE}. - * + * * @return {@code true} if the {@code SocketOptions.SO_OOBINLINE} is * enabled, {@code false} otherwise. * @throws SocketException * if an error occurs while reading the socket option. * @see SocketOptions#SO_OOBINLINE - * @since Android 1.0 */ public boolean getOOBInline() throws SocketException { checkClosedAndCreate(true); @@ -1119,14 +1148,13 @@ public class Socket { * Sets the value of the {@code SocketOptions.IP_TOS} for this socket. See * the specification RFC 1349 for more information about the type of service * field. - * + * * @param value * the value to be set for this option with a valid range of * {@code 0-255}. * @throws SocketException * if an error occurs while setting the option. * @see SocketOptions#IP_TOS - * @since Android 1.0 */ public void setTrafficClass(int value) throws SocketException { checkClosedAndCreate(true); @@ -1138,12 +1166,11 @@ public class Socket { /** * Gets the value of the socket option {@code SocketOptions.IP_TOS}. - * + * * @return the value which represents the type of service. * @throws SocketException * if an error occurs while reading the socket option. * @see SocketOptions#IP_TOS - * @since Android 1.0 */ public int getTrafficClass() throws SocketException { checkClosedAndCreate(true); @@ -1153,12 +1180,11 @@ public class Socket { /** * Sends the given single byte data which is represented by the lowest octet * of {@code value} as "TCP urgent data". - * + * * @param value * the byte of urgent data to be sent. * @throws IOException * if an error occurs while sending urgent data. - * @since Android 1.0 */ public void sendUrgentData(int value) throws IOException { if (!impl.supportsUrgentData()) { @@ -1170,7 +1196,7 @@ public class Socket { /** * Set the appropriate flags for a socket created by {@code * ServerSocket.accept()}. - * + * * @see ServerSocket#implAccept */ void accepted() { @@ -1186,9 +1212,8 @@ public class Socket { /** * Gets the SocketChannel of this socket, if one is available. The current * implementation of this method returns always {@code null}. - * + * * @return the related SocketChannel or {@code null} if no channel exists. - * @since Android 1.0 */ public SocketChannel getChannel() { return null; @@ -1198,8 +1223,7 @@ public class Socket { * Sets performance preferences for connectionTime, latency and bandwidth. *

          * This method does currently nothing. - *

          - * + * * @param connectionTime * the value representing the importance of a short connecting * time. @@ -1207,7 +1231,6 @@ public class Socket { * the value representing the importance of low latency. * @param bandwidth * the value representing the importance of high bandwidth. - * @since Android 1.0 */ public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) { diff --git a/libcore/luni/src/main/java/java/net/SocketAddress.java b/libcore/luni/src/main/java/java/net/SocketAddress.java index 756b5fdba..32a4c365b 100644 --- a/libcore/luni/src/main/java/java/net/SocketAddress.java +++ b/libcore/luni/src/main/java/java/net/SocketAddress.java @@ -23,15 +23,11 @@ import java.io.Serializable; * This abstract class represents a protocol-independent base for * socket-endpoint representing classes. The class has to be implemented * according to a specific protocol. - * - * @since Android 1.0 */ public abstract class SocketAddress implements Serializable { /** * Creates a new {@code SocketAddress} instance. - * - * @since Android 1.0 */ public SocketAddress() { super(); diff --git a/libcore/luni/src/main/java/java/net/SocketException.java b/libcore/luni/src/main/java/java/net/SocketException.java index b9b3a1407..23d37448e 100644 --- a/libcore/luni/src/main/java/java/net/SocketException.java +++ b/libcore/luni/src/main/java/java/net/SocketException.java @@ -22,8 +22,6 @@ import java.io.IOException; /** * This {@code SocketException} may be thrown during socket creation or setting * options, and is the superclass of all other socket related exceptions. - * - * @since Android 1.0 */ public class SocketException extends IOException { @@ -32,8 +30,6 @@ public class SocketException extends IOException { /** * Constructs a new {@code SocketException} instance with its walkback * filled in. - * - * @since Android 1.0 */ public SocketException() { super(); @@ -45,7 +41,6 @@ public class SocketException extends IOException { * * @param detailMessage * the detail message of this exception. - * @since Android 1.0 */ public SocketException(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/net/SocketImpl.java b/libcore/luni/src/main/java/java/net/SocketImpl.java index f011e637d..6ac41cde2 100644 --- a/libcore/luni/src/main/java/java/net/SocketImpl.java +++ b/libcore/luni/src/main/java/java/net/SocketImpl.java @@ -20,7 +20,6 @@ package java.net; import java.io.FileDescriptor; import java.io.IOException; import java.io.InputStream; -import java.io.InterruptedIOException; import java.io.OutputStream; import org.apache.harmony.luni.platform.INetworkSystem; @@ -35,36 +34,26 @@ import org.apache.harmony.luni.util.Msg; * ServerSocket} on a well known port (referred to as listener) used to * establish a connection and the resulting {@code Socket} (referred to as * host). - * - * @since Android 1.0 */ public abstract class SocketImpl implements SocketOptions { /** * The remote address this socket is connected to. - * - * @since Android 1.0 */ protected InetAddress address; /** * The remote port this socket is connected to. - * - * @since Android 1.0 */ protected int port; /** * The file descriptor of this socket. - * - * @since Android 1.0 */ protected FileDescriptor fd; /** * The local port this socket is connected to. - * - * @since Android 1.0 */ protected int localport; @@ -80,9 +69,8 @@ public abstract class SocketImpl implements SocketOptions { /** * Creates a new connection-oriented socket implementation. - * + * * @see SocketImplFactory - * @since Android 1.0 */ public SocketImpl() { this.netImpl = Platform.getNetworkSystem(); @@ -91,73 +79,67 @@ public abstract class SocketImpl implements SocketOptions { /** * Waits for an incoming request and blocks until the connection is opened * on the given socket. - * + * * @param newSocket * the socket to accept connections on. * @throws IOException * if an error occurs while accepting a new connection. - * @since Android 1.0 */ protected abstract void accept(SocketImpl newSocket) throws IOException; /** * Returns the available number of bytes which are readable from this socket * without blocking. - * + * * @return the number of bytes that may be read without blocking. * @throws IOException * if an error occurs while reading the number of bytes. - * @since Android 1.0 */ protected abstract int available() throws IOException; /** * Binds this socket to the specified local host address and port number. - * + * * @param address * the local machine address to bind this socket to. * @param port * the port on the local machine to bind this socket to. * @throws IOException * if an error occurs while binding this socket. - * @since Android 1.0 */ protected abstract void bind(InetAddress address, int port) throws IOException; /** * Closes this socket. This makes later access invalid. - * + * * @throws IOException * if an error occurs while closing this socket. - * @since Android 1.0 */ protected abstract void close() throws IOException; /** * Connects this socket to the specified remote host and port number. - * + * * @param host * the remote host this socket has to be connected to. * @param port * the remote port on which this socket has to be connected. * @throws IOException * if an error occurs while connecting to the remote host. - * @since Android 1.0 */ protected abstract void connect(String host, int port) throws IOException; /** * Connects this socket to the specified remote host address and port * number. - * + * * @param address * the remote host address this socket has to be connected to. * @param port * the remote port on which this socket has to be connected. * @throws IOException * if an error occurs while connecting to the remote host. - * @since Android 1.0 */ protected abstract void connect(InetAddress address, int port) throws IOException; @@ -165,21 +147,19 @@ public abstract class SocketImpl implements SocketOptions { /** * Creates a new unconnected socket. The argument {@code isStreaming} * defines whether the new socket is a streaming or a datagram socket. - * + * * @param isStreaming * defines whether the type of the new socket is streaming or * datagram. * @throws IOException * if an error occurs while creating the socket. - * @since Android 1.0 */ protected abstract void create(boolean isStreaming) throws IOException; /** * Gets the file descriptor of this socket. - * + * * @return the file descriptor of this socket. - * @since Android 1.0 */ protected FileDescriptor getFileDescriptor() { return fd; @@ -187,9 +167,8 @@ public abstract class SocketImpl implements SocketOptions { /** * Gets the remote address this socket is connected to. - * + * * @return the remote address of this socket. - * @since Android 1.0 */ protected InetAddress getInetAddress() { return address; @@ -197,11 +176,10 @@ public abstract class SocketImpl implements SocketOptions { /** * Gets the input stream of this socket. - * + * * @return the input stream of this socket. * @throws IOException * if an error occurs while accessing the input stream. - * @since Android 1.0 */ protected abstract InputStream getInputStream() throws IOException; @@ -209,9 +187,8 @@ public abstract class SocketImpl implements SocketOptions { * Gets the local port number of this socket. The field is initialized to * {@code -1} and upon demand will go to the IP stack to get the bound * value. See the class comment for the context of the local port. - * + * * @return the local port number this socket is bound to. - * @since Android 1.0 */ protected int getLocalPort() { return localport; @@ -219,32 +196,29 @@ public abstract class SocketImpl implements SocketOptions { /** * Gets the value of the given socket option. - * + * * @param optID * the socket option to retrieve. * @return the option value. * @throws SocketException * if an error occurs while accessing the option. - * @since Android 1.0 */ public abstract Object getOption(int optID) throws SocketException; /** * Gets the output stream of this socket. - * + * * @return the output stream of this socket. * @throws IOException * if an error occurs while accessing the output stream. - * @since Android 1.0 */ protected abstract OutputStream getOutputStream() throws IOException; /** * Gets the remote port number of this socket. This value is not meaningful * when this instance is wrapped by a {@code ServerSocket}. - * + * * @return the remote port this socket is connected to. - * @since Android 1.0 */ protected int getPort() { return port; @@ -255,60 +229,23 @@ public abstract class SocketImpl implements SocketOptions { * connection requests are queued up to the limit specified by {@code * backlog}. Additional requests are rejected. The method {@code listen()} * may only be invoked on streaming sockets. - * + * * @param backlog * the maximum number of outstanding connection requests. * @throws IOException * if an error occurs while listening. - * @since Android 1.0 */ protected abstract void listen(int backlog) throws IOException; - /** - * In the IP stack, read at most {@code count} bytes off the socket - * into the {@code buffer}, at the {@code offset}. If the timeout - * is zero, block indefinitely waiting for data, otherwise wait the - * specified period (in milliseconds). - * - * @param buffer - * the buffer to read into - * @param offset - * the offset into the buffer - * @param count - * the max number of bytes to read - * @return int the actual number of bytes read - * @exception IOException - * thrown if an error occurs while reading - */ - int read(byte[] buffer, int offset, int count) throws IOException { - if (shutdownInput) { - return -1; - } - try { - // BEGIN android-added - int receiveTimeout = (Integer)getOption(SocketOptions.SO_TIMEOUT); - // END android-added - int read = this.netImpl.receiveStream(fd, buffer, offset, count, - receiveTimeout); - if (read == -1) { - shutdownInput = true; - } - return read; - } catch (InterruptedIOException e) { - throw new SocketTimeoutException(e.getMessage()); - } - } - /** * Sets the value for the specified socket option. - * + * * @param optID * the socket option to be set. * @param val * the option value. * @throws SocketException * if an error occurs while setting the option. - * @since Android 1.0 */ public abstract void setOption(int optID, Object val) throws SocketException; @@ -316,14 +253,13 @@ public abstract class SocketImpl implements SocketOptions { /** * Returns a string containing a concise, human-readable description of the * socket. - * + * * @return the textual representation of this socket. - * @since Android 1.0 */ @SuppressWarnings("nls") @Override public String toString() { - return new StringBuffer(100).append("Socket[addr=").append( + return new StringBuilder(100).append("Socket[addr=").append( getInetAddress()).append(",port=").append(port).append( ",localport=").append(getLocalPort()).append("]").toString(); } @@ -331,7 +267,7 @@ public abstract class SocketImpl implements SocketOptions { /** * In the IP stack, write at most {@code count} bytes on the socket * from the {@code buffer}, from the {@code offset}. - * + * * @param buffer * the buffer to read into * @param offset @@ -339,18 +275,15 @@ public abstract class SocketImpl implements SocketOptions { * @param count * the number of bytes to write * @return int the actual number of bytes written - * @exception IOException + * @throws IOException * thrown if an error occurs while writing */ int write(byte[] buffer, int offset, int count) throws IOException { if (!streaming) { - // BEGIN android-changed - // copied from newer harmony version - return this.netImpl - .sendDatagram2(fd, buffer, offset, count, port, address); - // END android-changed + return this.netImpl.sendDatagram2(fd, buffer, offset, count, port, + address); } - return this.netImpl.sendStream(fd, buffer, offset, count); + return this.netImpl.write(fd, buffer, offset, count); } /** @@ -358,11 +291,9 @@ public abstract class SocketImpl implements SocketOptions { *

          * This default implementation always throws an {@link IOException} to * indicate that the subclass should have overridden this method. - *

          - * + * * @throws IOException * always because this method should be overridden. - * @since Android 1.0 */ protected void shutdownInput() throws IOException { // KA025=Method has not been implemented @@ -374,11 +305,9 @@ public abstract class SocketImpl implements SocketOptions { *

          * This default implementation always throws an {@link IOException} to * indicate that the subclass should have overridden this method. - *

          - * + * * @throws IOException * always because this method should be overridden. - * @since Android 1.0 */ protected void shutdownOutput() throws IOException { // KA025=Method has not been implemented @@ -389,14 +318,13 @@ public abstract class SocketImpl implements SocketOptions { * Connects this socket to the remote host address and port number specified * by the {@code SocketAddress} object with the given timeout. This method * will block indefinitely if the timeout is set to zero. - * + * * @param remoteAddr * the remote host address and port number to connect to. * @param timeout * the timeout value in milliseconds. * @throws IOException * if an error occurs while connecting. - * @since Android 1.0 */ protected abstract void connect(SocketAddress remoteAddr, int timeout) throws IOException; @@ -404,9 +332,8 @@ public abstract class SocketImpl implements SocketOptions { /** * Returns whether the socket supports urgent data or not. Subclasses should * override this method. - * + * * @return {@code false} because subclasses must override this method. - * @since Android 1.0 */ protected boolean supportsUrgentData() { return false; @@ -414,26 +341,24 @@ public abstract class SocketImpl implements SocketOptions { /** * Sends the single byte of urgent data on the socket. - * + * * @param value * the byte of urgent data. * @throws IOException * if an error occurs sending urgent data. - * @since Android 1.0 */ protected abstract void sendUrgentData(int value) throws IOException; /** * Sets performance preference for connection time, latency and bandwidth. * Does nothing by default. - * + * * @param connectionTime * the importance of connect time. * @param latency * the importance of latency. * @param bandwidth * the importance of bandwidth. - * @since Android 1.0 */ protected void setPerformancePreferences(int connectionTime, int latency, int bandwidth) { diff --git a/libcore/luni/src/main/java/java/net/SocketImplFactory.java b/libcore/luni/src/main/java/java/net/SocketImplFactory.java index 3cc42b0a6..57b0bc3ea 100644 --- a/libcore/luni/src/main/java/java/net/SocketImplFactory.java +++ b/libcore/luni/src/main/java/java/net/SocketImplFactory.java @@ -19,16 +19,13 @@ package java.net; /** * This interface defines a factory for socket implementations. - * - * @since Android 1.0 */ public interface SocketImplFactory { - + /** * Creates a new {@code SocketImpl} instance. * * @return the created {@code SocketImpl} instance. - * @since Android 1.0 */ SocketImpl createSocketImpl(); } diff --git a/libcore/luni/src/main/java/java/net/SocketOptions.java b/libcore/luni/src/main/java/java/net/SocketOptions.java index 6e1753f94..38c9301fa 100644 --- a/libcore/luni/src/main/java/java/net/SocketOptions.java +++ b/libcore/luni/src/main/java/java/net/SocketOptions.java @@ -25,7 +25,6 @@ package java.net; * * @see SocketImpl * @see DatagramSocketImpl - * @since Android 1.0 */ public interface SocketOptions { @@ -38,8 +37,6 @@ public interface SocketOptions { * during this timeout the socket is closed normally otherwise forcefully. * Valid values for this option are in the range {@code 0 <= SO_LINGER <= * 65535}. - * - * @since Android 1.0 */ public static final int SO_LINGER = 128; @@ -47,8 +44,6 @@ public interface SocketOptions { * Timeout for blocking operations. The argument value is specified in * milliseconds. An {@code InterruptedIOException} is thrown if this timeout * expires. - * - * @since Android 1.0 */ public static final int SO_TIMEOUT = 4102; @@ -57,8 +52,6 @@ public interface SocketOptions { * a side-effect though, this could lead to a low packet efficiency. The * socket implementation uses the Nagle's algorithm to try to reach a higher * packet efficiency if this option is disabled. - * - * @since Android 1.0 */ public static final int TCP_NODELAY = 1; @@ -69,8 +62,6 @@ public interface SocketOptions { /** * This option specifies the interface which is used to send multicast * packets. It's only available on a {@code MulticastSocket}. - * - * @since Android 1.0 */ public static final int IP_MULTICAST_IF = 16; @@ -78,8 +69,6 @@ public interface SocketOptions { * This option can be used to set one specific interface on a multihomed * host on which incoming connections are accepted. It's only available on * server-side sockets. - * - * @since Android 1.0 */ public static final int SO_BINDADDR = 15; @@ -87,8 +76,6 @@ public interface SocketOptions { * This option specifies whether a reuse of a local address is allowed even * if an other socket is not yet removed by the operating system. It's only * available on a {@code MulticastSocket}. - * - * @since Android 1.0 */ public static final int SO_REUSEADDR = 4; @@ -96,15 +83,11 @@ public interface SocketOptions { /** * Buffer size of the outgoing channel. - * - * @since Android 1.0 */ public static final int SO_SNDBUF = 4097; /** * Buffer size of the incoming channel. - * - * @since Android 1.0 */ public static final int SO_RCVBUF = 4098; @@ -113,16 +96,12 @@ public interface SocketOptions { /** * This option specifies whether socket implementations can send keepalive * messages if no data has been sent for a longer time. - * - * @since Android 1.0 */ public static final int SO_KEEPALIVE = 8; /** * This option specifies the value for the Type-of-Service (TOS) field of * the IP header. - * - * @since Android 1.0 */ public static final int IP_TOS = 3; @@ -130,23 +109,17 @@ public interface SocketOptions { * This option specifies whether the local loopback of multicast packets is * enabled or disabled. This option is enabled by default on multicast * sockets. - * - * @since Android 1.0 */ public static final int IP_MULTICAST_LOOP = 18; /** * This option can be used to enable broadcasting on datagram sockets. - * - * @since Android 1.0 */ public static final int SO_BROADCAST = 32; /** * This option specifies whether sending TCP urgent data is supported on * this socket or not. - * - * @since Android 1.0 */ public static final int SO_OOBINLINE = 4099; @@ -155,8 +128,6 @@ public interface SocketOptions { * host on which incoming connections are accepted. It's only available on * server-side sockets. This option supports setting outgoing interfaces * with either IPv4 or IPv6 addresses. - * - * @since Android 1.0 */ public static final int IP_MULTICAST_IF2 = 31; @@ -168,7 +139,6 @@ public interface SocketOptions { * the option identifier. * @throws SocketException * if an error occurs reading the option value. - * @since Android 1.0 */ public Object getOption(int optID) throws SocketException; @@ -181,7 +151,6 @@ public interface SocketOptions { * the value to be set for the option. * @throws SocketException * if an error occurs setting the option value. - * @since Android 1.0 */ public void setOption(int optID, Object val) throws SocketException; } diff --git a/libcore/luni/src/main/java/java/net/SocketPermission.java b/libcore/luni/src/main/java/java/net/SocketPermission.java index 72d77e707..4353e2ed3 100644 --- a/libcore/luni/src/main/java/java/net/SocketPermission.java +++ b/libcore/luni/src/main/java/java/net/SocketPermission.java @@ -14,9 +14,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -// BEGIN andorid-note -// This class was copied from a newer version of harmony -// END andorid-note package java.net; @@ -57,16 +54,13 @@ import org.apache.harmony.luni.util.Msg; * LOW-HIGH where LOW and HIGH are valid port numbers. If * either LOW or HIGH is omitted it is equivalent to entering the * lowest or highest possible value respectively. For example: - * + * *
            * {@code SocketPermission("www.company.com:7000-", "connect,accept")}
            * 
          - * + * * represents the permission to connect to and accept connections from {@code * www.company.com} on ports in the range {@code 7000} to {@code 65535}. - *

          - * - * @since Android 1.0 */ public final class SocketPermission extends Permission implements Serializable { @@ -122,13 +116,11 @@ public final class SocketPermission extends Permission implements Serializable { * possible operations {@code "connect"}, {@code "listen"}, {@code "accept"} * , and {@code "resolve"}. They are case-insensitive and can be put * together in any order. {@code "resolve"} is implied per default. - *

          - * + * * @param host * the hostname this permission is valid for. * @param action * the action string of this permission. - * @since Android 1.0 */ public SocketPermission(String host, String action) { super(host.equals("") ? "localhost" : host); //$NON-NLS-1$ //$NON-NLS-2$ @@ -149,14 +141,13 @@ public final class SocketPermission extends Permission implements Serializable { /** * Compares the argument {@code o} to this instance and returns {@code true} * if they represent the same permission using a class specific comparison. - * + * * @param o * the object to compare with this {@code SocketPermission} * instance. * @return {@code true} if they represent the same permission, {@code false} * otherwise. * @see #hashCode - * @since Android 1.0 */ @Override public boolean equals(Object o) { @@ -187,10 +178,9 @@ public final class SocketPermission extends Permission implements Serializable { * Returns the hash value for this {@code SocketPermission} instance. Any * two objects which returns {@code true} when passed to {@code equals()} * must return the same value as a result of this method. - * + * * @return the hashcode value for this instance. * @see #equals - * @since Android 1.0 */ @Override public int hashCode() { @@ -201,9 +191,8 @@ public final class SocketPermission extends Permission implements Serializable { * Gets a comma-separated list of all actions allowed by this permission. If * more than one action is returned they follow this order: {@code connect}, * {@code listen}, {@code accept}, {@code resolve}. - * + * * @return the comma-separated action list. - * @since Android 1.0 */ @Override public String getActions() { @@ -212,7 +201,7 @@ public final class SocketPermission extends Permission implements Serializable { /** * Stores the actions for this permission as a bit field. - * + * * @param actions * java.lang.String the action list */ @@ -222,7 +211,7 @@ public final class SocketPermission extends Permission implements Serializable { } boolean parsing = true; String action; - StringBuffer sb = new StringBuffer(); + StringBuilder sb = new StringBuilder(); int pos = 0, length = actions.length(); while (parsing) { char c; @@ -255,13 +244,12 @@ public final class SocketPermission extends Permission implements Serializable { * permission actions, hosts and ports must be implied by this permission * instance in order to return {@code true}. This permission may imply * additional actions not present in the argument permission. - * + * * @param p * the socket permission which has to be implied by this * instance. * @return {@code true} if this permission instance implies all permissions * represented by {@code p}, {@code false} otherwise. - * @since Android 1.0 */ @Override public boolean implies(Permission p) { @@ -293,15 +281,14 @@ public final class SocketPermission extends Permission implements Serializable { /** * Creates a new {@code PermissionCollection} to store {@code * SocketPermission} objects. - * + * * @return the new permission collection. - * @since Android 1.0 */ @Override public PermissionCollection newPermissionCollection() { return new SocketPermissionCollection(); } - + /** * Parse the port, including the minPort, maxPort * @param hostPort the host[:port] one @@ -319,14 +306,14 @@ public final class SocketPermission extends Permission implements Serializable { portMax = 80; return; } - + if (":*".equals(port)) { // The port range should be 0-65535 portMin = 0; portMax = 65535; return; } - + // Omit ':' port = port.substring(1); int negIdx = port.indexOf('-'); @@ -349,21 +336,23 @@ public final class SocketPermission extends Permission implements Serializable { try { portMin = Integer.valueOf(strPortMin).intValue(); portMax = Integer.valueOf(strPortMax).intValue(); - + if (portMin > portMax) { - throw new IllegalArgumentException(Msg.getString("K0049") + " " + port); //$NON-NLS-1$ + // K0049=MinPort is greater than MaxPort\: {0} + throw new IllegalArgumentException(Msg.getString("K0049", port)); //$NON-NLS-1$ } } catch (NumberFormatException e) { - throw new IllegalArgumentException(Msg.getString("K004a") + " " + port); //$NON-NLS-1$ + // K004a=Invalid port number specified\: {0} + throw new IllegalArgumentException(Msg.getString("K004a", port)); //$NON-NLS-1$ } } /** * Creates a canonical action list. - * + * * @param action * java.lang.String - * + * * @return java.lang.String */ private String toCanonicalActionString(String action) { @@ -371,7 +360,7 @@ public final class SocketPermission extends Permission implements Serializable { return actionNames[SP_RESOLVE]; // If none specified return the } // implied action resolve - StringBuffer sb = new StringBuffer(); + StringBuilder sb = new StringBuilder(); if ((actionsMask & SP_CONNECT) == SP_CONNECT) { sb.append(','); sb.append(actionNames[SP_CONNECT]); @@ -403,15 +392,20 @@ public final class SocketPermission extends Permission implements Serializable { } /** - * Get the host part from the host[:port] one. - * The host should be + * Get the host part from the host[:port] one. The host should be + * + *
                *      host = (hostname | IPv4address | IPv6reference | IPv6 in full uncompressed form)
          -     * The wildcard "*" may be included once in a DNS name host specification. If it is included, 
          -     * it must be in the leftmost position
          -     * 
          +     * 
          + * + * The wildcard "*" may be included once in a DNS name host specification. + * If it is included, it must be in the leftmost position + * * @param host - * @return - * @throws IllegalArgumentException if the host is invalid. + * the {@code host[:port]} string. + * @return the host name. + * @throws IllegalArgumentException + * if the host is invalid. */ private String getHostString(String host) throws IllegalArgumentException { host = host.trim(); @@ -431,7 +425,7 @@ public final class SocketPermission extends Permission implements Serializable { } int lastIdx = host.lastIndexOf(':'); - + if (idx == lastIdx) { if (-1 != idx) { // only one colon, should be port @@ -456,21 +450,22 @@ public final class SocketPermission extends Permission implements Serializable { if (Inet6Util.isIP6AddressInFullForm(host)) { return host.toLowerCase(); } - throw new IllegalArgumentException(Msg.getString("K004a") + " " - + host); + // K004a=Invalid port number specified\: {0} + throw new IllegalArgumentException(Msg.getString("K004a", host)); } // forward bracket found int bbracketIdx = host.indexOf(']'); if (-1 == bbracketIdx) { // no back bracket found, wrong - throw new IllegalArgumentException(Msg.getString("K004a") + " " - + host); + // K004a=Invalid port number specified\: {0} + throw new IllegalArgumentException(Msg.getString("K004a", host)); } host = host.substring(0, bbracketIdx + 1); if (Inet6Util.isValidIP6Address(host)) { return host.toLowerCase(); } - throw new IllegalArgumentException(Msg.getString("K004a") + " " + host); + // K004a=Invalid port number specified\: {0} + throw new IllegalArgumentException(Msg.getString("K004a", host)); } /** diff --git a/libcore/luni/src/main/java/java/net/SocketPermissionCollection.java b/libcore/luni/src/main/java/java/net/SocketPermissionCollection.java index 777f9a70b..60bb831a8 100644 --- a/libcore/luni/src/main/java/java/net/SocketPermissionCollection.java +++ b/libcore/luni/src/main/java/java/net/SocketPermissionCollection.java @@ -26,8 +26,6 @@ import java.util.Vector; * This class represents a list of {@code SocketPermission} objects and provides * a method to check whether or not a specific permission is implied by this * {@code SocketPermissionCollection}. - * - * @since Android 1.0 */ final class SocketPermissionCollection extends PermissionCollection { @@ -62,8 +60,6 @@ final class SocketPermissionCollection extends PermissionCollection { * Returns whether this permission collection implies {@code permission}. * Basically it tests whether {@code permission} is the subset of this * collection. - * - * @since Android 1.0 */ @Override public boolean implies(Permission permission) { diff --git a/libcore/luni/src/main/java/java/net/SocketTimeoutException.java b/libcore/luni/src/main/java/java/net/SocketTimeoutException.java index 32ba18333..c1c762e0e 100644 --- a/libcore/luni/src/main/java/java/net/SocketTimeoutException.java +++ b/libcore/luni/src/main/java/java/net/SocketTimeoutException.java @@ -22,8 +22,6 @@ import java.io.InterruptedIOException; /** * This exception is thrown when a timeout expired on a socket {@code read} or * {@code accept} operation. - * - * @since Android 1.0 */ public class SocketTimeoutException extends InterruptedIOException { @@ -32,8 +30,6 @@ public class SocketTimeoutException extends InterruptedIOException { /** * Creates a new {@code SocketTimeoutException} instance with its walkback * filled in. - * - * @since Android 1.0 */ public SocketTimeoutException() { super(); @@ -45,7 +41,6 @@ public class SocketTimeoutException extends InterruptedIOException { * * @param detailMessage * the detail message of this exception. - * @since Android 1.0 */ public SocketTimeoutException(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/net/URI.java b/libcore/luni/src/main/java/java/net/URI.java index 4b1f4dfed..85c16eba5 100644 --- a/libcore/luni/src/main/java/java/net/URI.java +++ b/libcore/luni/src/main/java/java/net/URI.java @@ -28,8 +28,6 @@ import org.apache.harmony.luni.util.Msg; /** * This class represents an instance of a URI as defined by RFC 2396. - * - * @since Android 1.0 */ public final class URI implements Comparable, Serializable { @@ -78,13 +76,12 @@ public final class URI implements Comparable, Serializable { /** * Creates a new URI instance according to the given string {@code uri}. - * + * * @param uri * the textual URI representation to be parsed into a URI object. * @throws URISyntaxException * if the given string {@code uri} doesn't fit to the * specification RFC2396 or could not be parsed correctly. - * @since Android 1.0 */ public URI(String uri) throws URISyntaxException { new Helper().parseURI(uri, false); @@ -96,8 +93,7 @@ public final class URI implements Comparable, Serializable { * string will be parsed later on to create the URI instance. *

          * {@code [scheme:]scheme-specific-part[#fragment]} - *

          - * + * * @param scheme * the scheme part of the URI. * @param ssp @@ -107,11 +103,10 @@ public final class URI implements Comparable, Serializable { * @throws URISyntaxException * if the temporary created string doesn't fit to the * specification RFC2396 or could not be parsed correctly. - * @since Android 1.0 */ public URI(String scheme, String ssp, String frag) throws URISyntaxException { - StringBuffer uri = new StringBuffer(); + StringBuilder uri = new StringBuilder(); if (scheme != null) { uri.append(scheme); uri.append(':'); @@ -135,8 +130,7 @@ public final class URI implements Comparable, Serializable { * string will be parsed later on to create the URI instance. *

          * {@code [scheme:][user-info@]host[:port][path][?query][#fragment]} - *

          - * + * * @param scheme * the scheme part of the URI. * @param userinfo @@ -156,7 +150,6 @@ public final class URI implements Comparable, Serializable { * @throws URISyntaxException * if the temporary created string doesn't fit to the * specification RFC2396 or could not be parsed correctly. - * @since Android 1.0 */ public URI(String scheme, String userinfo, String host, int port, String path, String query, String fragment) @@ -173,7 +166,7 @@ public final class URI implements Comparable, Serializable { throw new URISyntaxException(path, Msg.getString("K0302")); //$NON-NLS-1$ } - StringBuffer uri = new StringBuffer(); + StringBuilder uri = new StringBuilder(); if (scheme != null) { uri.append(scheme); uri.append(':'); @@ -230,8 +223,7 @@ public final class URI implements Comparable, Serializable { * string will be parsed later on to create the URI instance. *

          * {@code [scheme:]host[path][#fragment]} - *

          - * + * * @param scheme * the scheme part of the URI. * @param host @@ -243,7 +235,6 @@ public final class URI implements Comparable, Serializable { * @throws URISyntaxException * if the temporary created string doesn't fit to the * specification RFC2396 or could not be parsed correctly. - * @since Android 1.0 */ public URI(String scheme, String host, String path, String fragment) throws URISyntaxException { @@ -256,8 +247,7 @@ public final class URI implements Comparable, Serializable { * string will be parsed later on to create the URI instance. *

          * {@code [scheme:][//authority][path][?query][#fragment]} - *

          - * + * * @param scheme * the scheme part of the URI. * @param authority @@ -272,7 +262,6 @@ public final class URI implements Comparable, Serializable { * @throws URISyntaxException * if the temporary created string doesn't fit to the * specification RFC2396 or could not be parsed correctly. - * @since Android 1.0 */ public URI(String scheme, String authority, String path, String query, String fragment) throws URISyntaxException { @@ -281,7 +270,7 @@ public final class URI implements Comparable, Serializable { throw new URISyntaxException(path, Msg.getString("K0302")); //$NON-NLS-1$ } - StringBuffer uri = new StringBuffer(); + StringBuilder uri = new StringBuilder(); if (scheme != null) { uri.append(scheme); uri.append(':'); @@ -718,7 +707,7 @@ public final class URI implements Comparable, Serializable { // case for a closed bracket at end of IP [x:x:x:...x] case ']': if (i != length - 1) { - return false; // must be last charcter + return false; // must be last character } if (ipAddress.charAt(0) != '[') { return false; // must have a open [ @@ -856,11 +845,10 @@ public final class URI implements Comparable, Serializable { * in the natural case-sensitive way. A hierarchical URI is less than an * opaque URI and if one part is {@code null} the URI with the undefined * part is less than the other one. - * + * * @param uri * the URI this instance has to compare with. * @return the value representing the order of the two instances. - * @since Android 1.0 */ public int compareTo(URI uri) { int ret = 0; @@ -969,11 +957,10 @@ public final class URI implements Comparable, Serializable { /** * Parses the given argument {@code uri} and creates an appropriate URI * instance. - * + * * @param uri * the string which has to be parsed to create the URI instance. * @return the created instance representing the given URI. - * @since Android 1.0 */ public static URI create(String uri) { URI result = null; @@ -1007,7 +994,7 @@ public final class URI implements Comparable, Serializable { * converts the hex values following the '%' to lowercase */ private String convertHexToLowerCase(String s) { - StringBuffer result = new StringBuffer(""); //$NON-NLS-1$ + StringBuilder result = new StringBuilder(""); //$NON-NLS-1$ if (s.indexOf('%') == -1) { return s; } @@ -1057,12 +1044,11 @@ public final class URI implements Comparable, Serializable { * Compares this URI instance with the given argument {@code o} and * determines if both are equal. Two URI instances are equal if all single * parts are identical in their meaning. - * + * * @param o * the URI this instance has to be compared with. * @return {@code true} if both URI instances point to the same resource, * {@code false} otherwise. - * @since Android 1.0 */ @Override public boolean equals(Object o) { @@ -1147,9 +1133,8 @@ public final class URI implements Comparable, Serializable { /** * Gets the decoded authority part of this URI. - * + * * @return the decoded authority part or {@code null} if undefined. - * @since Android 1.0 */ public String getAuthority() { return decode(authority); @@ -1159,7 +1144,6 @@ public final class URI implements Comparable, Serializable { * Gets the decoded fragment part of this URI. * * @return the decoded fragment part or {@code null} if undefined. - * @since Android 1.0 */ public String getFragment() { return decode(fragment); @@ -1169,7 +1153,6 @@ public final class URI implements Comparable, Serializable { * Gets the host part of this URI. * * @return the host part or {@code null} if undefined. - * @since Android 1.0 */ public String getHost() { return host; @@ -1179,7 +1162,6 @@ public final class URI implements Comparable, Serializable { * Gets the decoded path part of this URI. * * @return the decoded path part or {@code null} if undefined. - * @since Android 1.0 */ public String getPath() { return decode(path); @@ -1189,7 +1171,6 @@ public final class URI implements Comparable, Serializable { * Gets the port number of this URI. * * @return the port number or {@code -1} if undefined. - * @since Android 1.0 */ public int getPort() { return port; @@ -1199,7 +1180,6 @@ public final class URI implements Comparable, Serializable { * Gets the decoded query part of this URI. * * @return the decoded query part or {@code null} if undefined. - * @since Android 1.0 */ public String getQuery() { return decode(query); @@ -1209,7 +1189,6 @@ public final class URI implements Comparable, Serializable { * Gets the authority part of this URI in raw form. * * @return the encoded authority part or {@code null} if undefined. - * @since Android 1.0 */ public String getRawAuthority() { return authority; @@ -1219,7 +1198,6 @@ public final class URI implements Comparable, Serializable { * Gets the fragment part of this URI in raw form. * * @return the encoded fragment part or {@code null} if undefined. - * @since Android 1.0 */ public String getRawFragment() { return fragment; @@ -1229,7 +1207,6 @@ public final class URI implements Comparable, Serializable { * Gets the path part of this URI in raw form. * * @return the encoded path part or {@code null} if undefined. - * @since Android 1.0 */ public String getRawPath() { return path; @@ -1239,7 +1216,6 @@ public final class URI implements Comparable, Serializable { * Gets the query part of this URI in raw form. * * @return the encoded query part or {@code null} if undefined. - * @since Android 1.0 */ public String getRawQuery() { return query; @@ -1249,17 +1225,15 @@ public final class URI implements Comparable, Serializable { * Gets the scheme-specific part of this URI in raw form. * * @return the encoded scheme-specific part or {@code null} if undefined. - * @since Android 1.0 */ public String getRawSchemeSpecificPart() { return schemespecificpart; } - + /** * Gets the user-info part of this URI in raw form. * * @return the encoded user-info part or {@code null} if undefined. - * @since Android 1.0 */ public String getRawUserInfo() { return userinfo; @@ -1269,17 +1243,15 @@ public final class URI implements Comparable, Serializable { * Gets the scheme part of this URI. * * @return the scheme part or {@code null} if undefined. - * @since Android 1.0 */ public String getScheme() { return scheme; } - + /** * Gets the decoded scheme-specific part of this URI. * * @return the decoded scheme-specific part or {@code null} if undefined. - * @since Android 1.0 */ public String getSchemeSpecificPart() { return decode(schemespecificpart); @@ -1289,7 +1261,6 @@ public final class URI implements Comparable, Serializable { * Gets the decoded user-info part of this URI. * * @return the decoded user-info part or {@code null} if undefined. - * @since Android 1.0 */ public String getUserInfo() { return decode(userinfo); @@ -1297,9 +1268,8 @@ public final class URI implements Comparable, Serializable { /** * Gets the hashcode value of this URI instance. - * + * * @return the appropriate hashcode value. - * @since Android 1.0 */ @Override public int hashCode() { @@ -1314,7 +1284,6 @@ public final class URI implements Comparable, Serializable { * defined in this URI. * * @return {@code true} if this URI is absolute, {@code false} otherwise. - * @since Android 1.0 */ public boolean isAbsolute() { return absolute; @@ -1327,7 +1296,6 @@ public final class URI implements Comparable, Serializable { * undefined. * * @return {@code true} if the URI is opaque, {@code false} otherwise. - * @since Android 1.0 */ public boolean isOpaque() { return opaque; @@ -1389,7 +1357,7 @@ public final class URI implements Comparable, Serializable { } // put the path back together - StringBuffer newpath = new StringBuffer(); + StringBuilder newpath = new StringBuilder(); if (path.startsWith("/")) { //$NON-NLS-1$ newpath.append('/'); } @@ -1424,10 +1392,9 @@ public final class URI implements Comparable, Serializable { /** * Normalizes the path part of this URI. - * + * * @return an URI object which represents this instance with a normalized * path. - * @since Android 1.0 */ public URI normalize() { if (opaque) { @@ -1450,12 +1417,11 @@ public final class URI implements Comparable, Serializable { * Tries to parse the authority component of this URI to divide it into the * host, port, and user-info. If this URI is already determined as a * ServerAuthority this instance will be returned without changes. - * + * * @return this instance with the components of the parsed server authority. * @throws URISyntaxException * if the authority part could not be parsed as a server-based * authority. - * @since Android 1.0 */ public URI parseServerAuthority() throws URISyntaxException { if (!serverAuthority) { @@ -1467,11 +1433,10 @@ public final class URI implements Comparable, Serializable { /** * Makes the given URI {@code relative} to a relative URI against the URI * represented by this instance. - * + * * @param relative * the URI which has to be relativized against this URI. * @return the relative URI. - * @since Android 1.0 */ public URI relativize(URI relative) { if (relative.opaque || opaque) { @@ -1516,17 +1481,17 @@ public final class URI implements Comparable, Serializable { result.query = relative.query; // the result URI is the remainder of the relative URI's path result.path = relativePath.substring(thisPath.length()); + result.setSchemeSpecificPart(); return result; } /** * Resolves the given URI {@code relative} against the URI represented by * this instance. - * + * * @param relative * the URI which has to be resolved against this URI. * @return the resolved URI. - * @since Android 1.0 */ public URI resolve(URI relative) { if (relative.absolute || opaque) { @@ -1583,7 +1548,7 @@ public final class URI implements Comparable, Serializable { */ private void setSchemeSpecificPart() { // ssp = [//authority][path][?query] - StringBuffer ssp = new StringBuffer(); + StringBuilder ssp = new StringBuilder(); if (authority != null) { ssp.append("//" + authority); //$NON-NLS-1$ } @@ -1602,12 +1567,11 @@ public final class URI implements Comparable, Serializable { * Creates a new URI instance by parsing the given string {@code relative} * and resolves the created URI against the URI represented by this * instance. - * + * * @param relative * the given string to create the new URI instance which has to * be resolved later on. * @return the created and resolved URI. - * @since Android 1.0 */ public URI resolve(String relative) { return resolve(create(relative)); @@ -1652,9 +1616,8 @@ public final class URI implements Comparable, Serializable { /** * Returns the textual string representation of this URI instance using the * US-ASCII encoding. - * + * * @return the US-ASCII string representation of this URI. - * @since Android 1.0 */ public String toASCIIString() { return encodeOthers(toString()); @@ -1662,14 +1625,13 @@ public final class URI implements Comparable, Serializable { /** * Returns the textual string representation of this URI instance. - * + * * @return the textual string representation of this URI. - * @since Android 1.0 */ @Override public String toString() { if (string == null) { - StringBuffer result = new StringBuffer(); + StringBuilder result = new StringBuilder(); if (scheme != null) { result.append(scheme); result.append(':'); @@ -1708,7 +1670,7 @@ public final class URI implements Comparable, Serializable { * and converts escaped octets to lowercase. */ private String getHashString() { - StringBuffer result = new StringBuffer(); + StringBuilder result = new StringBuilder(); if (scheme != null) { result.append(scheme.toLowerCase()); result.append(':'); @@ -1751,12 +1713,11 @@ public final class URI implements Comparable, Serializable { /** * Converts this URI instance to a URL. - * + * * @return the created URL representing the same resource as this URI. * @throws MalformedURLException * if an error occurs while creating the URL or no protocol * handler could be found. - * @since Android 1.0 */ public URL toURL() throws MalformedURLException { if (!absolute) { diff --git a/libcore/luni/src/main/java/java/net/URIEncoderDecoder.java b/libcore/luni/src/main/java/java/net/URIEncoderDecoder.java index a9ef3c171..d2b00442c 100644 --- a/libcore/luni/src/main/java/java/net/URIEncoderDecoder.java +++ b/libcore/luni/src/main/java/java/net/URIEncoderDecoder.java @@ -27,8 +27,6 @@ import org.apache.harmony.luni.util.Msg; * application/x-www-form-urlencoded} MIME content type. It contains helper * methods used by the URI class, and performs encoding and decoding in a * slightly different way than {@code URLEncoder} and {@code URLDecoder}. - * - * @since Android 1.0 */ class URIEncoderDecoder { @@ -43,8 +41,7 @@ class URIEncoderDecoder { * US-ASCII set, and are not ISO Control or are not ISO Space characters) *

          * called from {@code URI.Helper.parseURI()} to validate each component - *

          - * + * * @param s * {@code java.lang.String} the string to be validated * @param legal @@ -100,14 +97,12 @@ class URIEncoderDecoder { * by '%'. *

          * For example: '#' -> %23 - *

          * Other characters, which are unicode chars that are not US-ASCII, and are * not ISO Control or are not ISO Space chars, are preserved. *

          * Called from {@code URI.quoteComponent()} (for multiple argument * constructors) - *

          - * + * * @param s * java.lang.String the string to be converted * @param legal @@ -117,7 +112,7 @@ class URIEncoderDecoder { */ static String quoteIllegal(String s, String legal) throws UnsupportedEncodingException { - StringBuffer buf = new StringBuffer(); + StringBuilder buf = new StringBuilder(); for (int i = 0; i < s.length(); i++) { char ch = s.charAt(i); if ((ch >= 'a' && ch <= 'z') @@ -145,16 +140,15 @@ class URIEncoderDecoder { * converted into their hexidecimal value prepended by '%'. *

          * For example: Euro currency symbol -> "%E2%82%AC". - *

          + *

          * Called from URI.toASCIIString() - *

          - * + * * @param s * java.lang.String the string to be converted * @return java.lang.String the converted string */ static String encodeOthers(String s) throws UnsupportedEncodingException { - StringBuffer buf = new StringBuffer(); + StringBuilder buf = new StringBuilder(); for (int i = 0; i < s.length(); i++) { char ch = s.charAt(i); if (ch <= 127) { @@ -178,11 +172,10 @@ class URIEncoderDecoder { *'%' and two following hex digit characters are converted to the * equivalent byte value. All other characters are passed through * unmodified. - *

          + *

          * e.g. "A%20B%20C %24%25" -> "A B C $%" *

          * Called from URI.getXYZ() methods - *

          * * @param s * java.lang.String The encoded string. @@ -190,7 +183,7 @@ class URIEncoderDecoder { */ static String decode(String s) throws UnsupportedEncodingException { - StringBuffer result = new StringBuffer(); + StringBuilder result = new StringBuilder(); ByteArrayOutputStream out = new ByteArrayOutputStream(); for (int i = 0; i < s.length();) { char c = s.charAt(i); diff --git a/libcore/luni/src/main/java/java/net/URISyntaxException.java b/libcore/luni/src/main/java/java/net/URISyntaxException.java index e7e332e57..2daf35f42 100644 --- a/libcore/luni/src/main/java/java/net/URISyntaxException.java +++ b/libcore/luni/src/main/java/java/net/URISyntaxException.java @@ -22,8 +22,6 @@ import org.apache.harmony.luni.util.Msg; /** * A {@code URISyntaxException} will be thrown if some information could not be parsed * while creating a URI. - * - * @since Android 1.0 */ public class URISyntaxException extends Exception { @@ -49,7 +47,6 @@ public class URISyntaxException extends Exception { * {@code null}. * @throws IllegalArgumentException * if the value for {@code index} is lesser than {@code -1}. - * @since Android 1.0 */ public URISyntaxException(String input, String reason, int index) { super(reason); @@ -77,7 +74,6 @@ public class URISyntaxException extends Exception { * @throws NullPointerException * if one of the arguments {@code input} or {@code reason} is * {@code null}. - * @since Android 1.0 */ public URISyntaxException(String input, String reason) { super(reason); @@ -95,7 +91,6 @@ public class URISyntaxException extends Exception { * index is unknown/unavailable. * * @return the index of the syntax error. - * @since Android 1.0 */ public int getIndex() { return index; @@ -105,7 +100,6 @@ public class URISyntaxException extends Exception { * Gets a description of the syntax error. * * @return the string describing the syntax error. - * @since Android 1.0 */ public String getReason() { return super.getMessage(); @@ -115,7 +109,6 @@ public class URISyntaxException extends Exception { * Gets the initial string that contains an invalid syntax. * * @return the string that caused the exception. - * @since Android 1.0 */ public String getInput() { return input; @@ -128,7 +121,6 @@ public class URISyntaxException extends Exception { * * @return a sting containing information about the exception. * @see java.lang.Throwable#getMessage() - * @since Android 1.0 */ @Override public String getMessage() { diff --git a/libcore/luni/src/main/java/java/net/URL.java b/libcore/luni/src/main/java/java/net/URL.java index 2bc8c6c4c..9b0fdc46b 100644 --- a/libcore/luni/src/main/java/java/net/URL.java +++ b/libcore/luni/src/main/java/java/net/URL.java @@ -34,8 +34,6 @@ import org.apache.harmony.luni.util.Util; * which generates the output dynamically. A URL is divided in its parts * protocol, host name, port, path, file, user-info, query, reference and * authority. However, not each of this parts has to be defined. - * - * @since Android 1.0 */ public final class URL implements java.io.Serializable { private static final long serialVersionUID = -7627629688361524110L; @@ -131,11 +129,9 @@ public final class URL implements java.io.Serializable { *

          * A security check is performed to verify whether the current policy allows * to set the stream handler factory. - *

          - * + * * @param streamFactory * the factory to be used for creating stream protocol handlers. - * @since Android 1.0 */ public static synchronized void setURLStreamHandlerFactory( URLStreamHandlerFactory streamFactory) { @@ -158,7 +154,6 @@ public final class URL implements java.io.Serializable { * @throws MalformedURLException * if the given string {@code spec} could not be parsed as a * URL. - * @since Android 1.0 */ public URL(String spec) throws MalformedURLException { this((URL) null, spec, (URLStreamHandler) null); @@ -178,7 +173,6 @@ public final class URL implements java.io.Serializable { * @throws MalformedURLException * if the given string {@code spec} could not be parsed as a URL * or an invalid protocol has been found. - * @since Android 1.0 */ public URL(URL context, String spec) throws MalformedURLException { this(context, spec, (URLStreamHandler) null); @@ -202,7 +196,6 @@ public final class URL implements java.io.Serializable { * @throws MalformedURLException * if the given string {@code spec} could not be parsed as a URL * or an invalid protocol has been found. - * @since Android 1.0 */ public URL(URL context, String spec, URLStreamHandler handler) throws MalformedURLException { @@ -235,8 +228,6 @@ public final class URL implements java.io.Serializable { // According to RFC 2396 scheme part should match // the following expression: // alpha *( alpha | digit | "+" | "-" | "." ) - // BEGIN android-changed - // copied from newer version of harmony char c = protocol.charAt(0); boolean valid = ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); for (int i = 1; valid && (i < protocol.length()); i++) { @@ -253,10 +244,9 @@ public final class URL implements java.io.Serializable { index = -1; } else { // Ignore case in protocol names. - // Scheme is defined by ASCII characters. + // Scheme is defined by ASCII characters. protocol = Util.toASCIILowerCase(protocol); } - // END android-changed } } @@ -338,7 +328,6 @@ public final class URL implements java.io.Serializable { * @throws MalformedURLException * if the combination of all arguments do not represent a valid * URL or the protocol is invalid. - * @since Android 1.0 */ public URL(String protocol, String host, String file) throws MalformedURLException { @@ -361,7 +350,6 @@ public final class URL implements java.io.Serializable { * @throws MalformedURLException * if the combination of all arguments do not represent a valid * URL or the protocol is invalid. - * @since Android 1.0 */ public URL(String protocol, String host, int port, String file) throws MalformedURLException { @@ -371,7 +359,7 @@ public final class URL implements java.io.Serializable { /** * Creates a new URL instance using the given arguments. The URL uses the * specified port instead of the default port for the given protocol. - * + * * @param protocol * the protocol of the new URL. * @param host @@ -386,25 +374,26 @@ public final class URL implements java.io.Serializable { * @throws MalformedURLException * if the combination of all arguments do not represent a valid * URL or the protocol is invalid. - * @since Android 1.0 + * @throws SecurityException + * if {@code handler} is non-{@code null}, and a security + * manager is installed that disallows user-defined protocol + * handlers. */ public URL(String protocol, String host, int port, String file, URLStreamHandler handler) throws MalformedURLException { if (port < -1) { - throw new MalformedURLException(org.apache.harmony.luni.util.Msg - .getString("K0325", port)); //$NON-NLS-1$ + throw new MalformedURLException(Msg.getString("K0325", port)); //$NON-NLS-1$ } if (host != null && host.indexOf(":") != -1 && host.charAt(0) != '[') { //$NON-NLS-1$ host = "[" + host + "]"; //$NON-NLS-1$ //$NON-NLS-2$ } - if (protocol != null) { - this.protocol = protocol; - } else { - throw new NullPointerException(Msg.getString("K00b3", protocol)); //$NON-NLS-1$ + if (protocol == null) { + throw new NullPointerException(Msg.getString("K00b3", "null")); //$NON-NLS-1$ //$NON-NLS-2$ } + this.protocol = protocol; this.host = host; this.port = port; @@ -427,8 +416,7 @@ public final class URL implements java.io.Serializable { setupStreamHandler(); if (strmHandler == null) { throw new MalformedURLException( - org.apache.harmony.luni.util.Msg.getString( - "K00b3", protocol)); //$NON-NLS-1$ + Msg.getString("K00b3", protocol)); //$NON-NLS-1$ } } else { SecurityManager sm = System.getSecurityManager(); @@ -479,7 +467,6 @@ public final class URL implements java.io.Serializable { * the file to be set. * @param ref * the reference to be set. - * @since Android 1.0 */ protected void set(String protocol, String host, int port, String file, String ref) { @@ -506,8 +493,7 @@ public final class URL implements java.io.Serializable { * the URL this instance has to be compared with. * @return {@code true} if both instances represents the same URL, {@code * false} otherwise. - * @see #hashCode - * @since Android 1.0 + * @see #hashCode() */ @Override public boolean equals(Object o) { @@ -532,7 +518,6 @@ public final class URL implements java.io.Serializable { * the URL to compare against. * @return {@code true} if both instances refer to the same resource, * {@code false} otherwise. - * @since Android 1.0 */ public boolean sameFile(URL otherURL) { return strmHandler.sameFile(this, otherURL); @@ -542,7 +527,6 @@ public final class URL implements java.io.Serializable { * Gets the hashcode value of this URL instance. * * @return the appropriate hashcode value. - * @since Android 1.0 */ @Override public int hashCode() { @@ -560,7 +544,6 @@ public final class URL implements java.io.Serializable { * Note that this will overwrite any existing stream handler with the new * one. Senders must check if the strmHandler is null before calling the * method if they do not want this behavior (a speed optimization). - *

          */ void setupStreamHandler() { // Check for a cached (previously looked up) handler for @@ -629,12 +612,10 @@ public final class URL implements java.io.Serializable { *
        • Image for pictures
        • *
        • AudioClip for audio sequences
        • *
        • {@link InputStream} for all other data
        • - *

          * * @return the content of the referred resource. * @throws IOException * if an error occurs obtaining the content. - * @since Android 1.0 */ public final Object getContent() throws IOException { return openConnection().getContent(); @@ -654,7 +635,6 @@ public final class URL implements java.io.Serializable { * type. * @throws IOException * if an error occurs obtaining the content. - * @since Android 1.0 */ // Param not generic in spec @SuppressWarnings("unchecked") @@ -668,7 +648,6 @@ public final class URL implements java.io.Serializable { * @return the stream which allows to read the resource. * @throws IOException * if an error occurs while opening the InputStream. - * @since Android 1.0 */ public final InputStream openStream() throws java.io.IOException { return openConnection().getInputStream(); @@ -681,7 +660,6 @@ public final class URL implements java.io.Serializable { * @return the connection to this URL. * @throws IOException * if an error occurs while opening the connection. - * @since Android 1.0 */ public URLConnection openConnection() throws IOException { return strmHandler.openConnection(this); @@ -693,7 +671,6 @@ public final class URL implements java.io.Serializable { * @return the URI instance that represents this URL. * @throws URISyntaxException * if this URL cannot be converted into a URI. - * @since Android 1.0 */ public URI toURI() throws URISyntaxException { return new URI(toExternalForm()); @@ -718,7 +695,6 @@ public final class URL implements java.io.Serializable { * @throws UnsupportedOperationException * if the protocol handler does not support opening connections * through proxies. - * @since Android 1.0 */ public URLConnection openConnection(Proxy proxy) throws IOException { if (null == proxy) { @@ -733,7 +709,6 @@ public final class URL implements java.io.Serializable { * {@code toExternalForm()}. * * @return the string representation of this URL. - * @since Android 1.0 */ @Override public String toString() { @@ -745,7 +720,6 @@ public final class URL implements java.io.Serializable { * this URL. * * @return the string representation of this URL. - * @since Android 1.0 */ public String toExternalForm() { if (strmHandler == null) { @@ -798,8 +772,7 @@ public final class URL implements java.io.Serializable { *

          * Note that, we really only need the readObject method but the spec that * says readObject will be ignored if no writeObject is present. - *

          - * + * * @param s * the stream to write on. * @throws IOException @@ -814,7 +787,6 @@ public final class URL implements java.io.Serializable { * * @return the file name this URL refers to or an empty string if the file * part is not set. - * @since Android 1.0 */ public String getFile() { return file; @@ -824,7 +796,6 @@ public final class URL implements java.io.Serializable { * Gets the value of the host part of this URL. * * @return the host name or IP address of this URL. - * @since Android 1.0 */ public String getHost() { return host; @@ -834,7 +805,6 @@ public final class URL implements java.io.Serializable { * Gets the port number of this URL or {@code -1} if the port is not set. * * @return the port number of this URL. - * @since Android 1.0 */ public int getPort() { return port; @@ -844,7 +814,6 @@ public final class URL implements java.io.Serializable { * Gets the protocol of this URL. * * @return the protocol type of this URL. - * @since Android 1.0 */ public String getProtocol() { return protocol; @@ -854,7 +823,6 @@ public final class URL implements java.io.Serializable { * Gets the value of the reference part of this URL. * * @return the reference part of this URL. - * @since Android 1.0 */ public String getRef() { return ref; @@ -864,7 +832,6 @@ public final class URL implements java.io.Serializable { * Gets the value of the query part of this URL. * * @return the query part of this URL. - * @since Android 1.0 */ public String getQuery() { return query; @@ -874,7 +841,6 @@ public final class URL implements java.io.Serializable { * Gets the value of the path part of this URL. * * @return the path part of this URL. - * @since Android 1.0 */ public String getPath() { return path; @@ -884,7 +850,6 @@ public final class URL implements java.io.Serializable { * Gets the value of the user-info part of this URL. * * @return the user-info part of this URL. - * @since Android 1.0 */ public String getUserInfo() { return userInfo; @@ -894,7 +859,6 @@ public final class URL implements java.io.Serializable { * Gets the value of the authority part of this URL. * * @return the authority part of this URL. - * @since Android 1.0 */ public String getAuthority() { return authority; @@ -921,7 +885,6 @@ public final class URL implements java.io.Serializable { * the query to be set. * @param ref * the reference to be set. - * @since Android 1.0 */ protected void set(String protocol, String host, int port, String authority, String userInfo, String path, String query, @@ -941,21 +904,13 @@ public final class URL implements java.io.Serializable { this.query = query; } - // BEGIN android-removed - // copied from newer version of harmony - // URLStreamHandler getStreamHandler() { - // return strmHandler; - // } - // END android-removed - /** * Gets the default port number of the protocol used by this URL. If no * default port is defined by the protocol or the {@code URLStreamHandler}, * {@code -1} will be returned. - * + * * @return the default port number according to the protocol of this URL. * @see URLStreamHandler#getDefaultPort - * @since Android 1.0 */ public int getDefaultPort() { return strmHandler.getDefaultPort(); diff --git a/libcore/luni/src/main/java/java/net/URLClassLoader.java b/libcore/luni/src/main/java/java/net/URLClassLoader.java index c70ed4538..6dfa3850e 100644 --- a/libcore/luni/src/main/java/java/net/URLClassLoader.java +++ b/libcore/luni/src/main/java/java/net/URLClassLoader.java @@ -17,12 +17,15 @@ package java.net; +import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.FilePermission; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.security.AccessControlContext; import java.security.AccessController; @@ -35,22 +38,14 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; -import java.util.HashSet; -import java.util.Hashtable; -import java.util.IdentityHashMap; import java.util.List; -import java.util.ListIterator; import java.util.Map; -import java.util.Set; import java.util.StringTokenizer; -import java.util.Vector; import java.util.jar.Attributes; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.jar.Manifest; -import java.util.zip.ZipEntry; -import org.apache.harmony.luni.util.InvalidJarIndexException; import org.apache.harmony.luni.util.Msg; /** @@ -58,33 +53,17 @@ import org.apache.harmony.luni.util.Msg; * list of URLs which can refer to either directories or JAR files. Classes * loaded by this {@code URLClassLoader} are granted permission to access the * URLs contained in the URL search list. - * - * @since Android 1.0 */ public class URLClassLoader extends SecureClassLoader { - private static URL[] NO_PATH = new URL[0]; + ArrayList originalUrls; - @SuppressWarnings("unchecked") - private static Hashtable[] newHashtableArray(int size) { - return new Hashtable[size]; - } - - URL[] urls, orgUrls; - - Set invalidUrls = Collections.synchronizedSet(new HashSet()); - - private Map resCache = - Collections.synchronizedMap(new IdentityHashMap(32)); - - private Object lock = new Object(); + List searchList; + ArrayList handlerList; + Map handlerMap = new HashMap(); private URLStreamHandlerFactory factory; - HashMap extensions; - - Hashtable[] indexes; - private AccessControlContext currentContext; static class SubURLClassLoader extends URLClassLoader { @@ -103,7 +82,7 @@ public class URLClassLoader extends SecureClassLoader { * Overrides the {@code loadClass()} of {@code ClassLoader}. It calls * the security manager's {@code checkPackageAccess()} before * attempting to load the class. - * + * * @return the Class object. * @param className * String the name of the class to search for. @@ -115,7 +94,7 @@ public class URLClassLoader extends SecureClassLoader { */ @Override protected synchronized Class loadClass(String className, - boolean resolveClass) throws ClassNotFoundException { + boolean resolveClass) throws ClassNotFoundException { SecurityManager sm = System.getSecurityManager(); if (sm != null && !checkingPackageAccess) { int index = className.lastIndexOf('.'); @@ -132,12 +111,503 @@ public class URLClassLoader extends SecureClassLoader { } } + static class IndexFile { + + private HashMap> map; + //private URLClassLoader host; + + + static IndexFile readIndexFile(JarFile jf, JarEntry indexEntry, URL url) { + BufferedReader in = null; + InputStream is = null; + try { + // Add mappings from resource to jar file + String parentURLString = getParentURL(url).toExternalForm(); + String prefix = "jar:" //$NON-NLS-1$ + + parentURLString + "/"; //$NON-NLS-1$ + is = jf.getInputStream(indexEntry); + in = new BufferedReader(new InputStreamReader(is, "UTF8")); + HashMap> pre_map = new HashMap>(); + // Ignore the 2 first lines (index version) + if (in.readLine() == null) return null; + if (in.readLine() == null) return null; + TOP_CYCLE: + while (true) { + String line = in.readLine(); + if (line == null) { + break; + } + URL jar = new URL(prefix + line + "!/"); //$NON-NLS-1$ + while (true) { + line = in.readLine(); + if (line == null) { + break TOP_CYCLE; + } + if ("".equals(line)) { + break; + } + ArrayList list; + if (pre_map.containsKey(line)) { + list = pre_map.get(line); + } else { + list = new ArrayList(); + pre_map.put(line, list); + } + list.add(jar); + } + } + if (!pre_map.isEmpty()) { + return new IndexFile(pre_map); + } + } catch (MalformedURLException e) { + // Ignore this jar's index + } catch (IOException e) { + // Ignore this jar's index + } + finally { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + } + } + if (is != null) { + try { + is.close(); + } catch (IOException e) { + } + } + } + return null; + } + + private static URL getParentURL(URL url) throws IOException { + URL fileURL = ((JarURLConnection) url.openConnection()).getJarFileURL(); + String file = fileURL.getFile(); + String parentFile = new File(file).getParent(); + parentFile = parentFile.replace(File.separatorChar, '/'); + if (parentFile.charAt(0) != '/') { + parentFile = "/" + parentFile; //$NON-NLS-1$ + } + URL parentURL = new URL(fileURL.getProtocol(), fileURL + .getHost(), fileURL.getPort(), parentFile); + return parentURL; + } + + public IndexFile(HashMap> map) { + this.map = map; + } + + ArrayList get(String name) { + return map.get(name); + } + } + + class URLHandler { + URL url; + URL codeSourceUrl; + + public URLHandler(URL url) { + this.url = url; + this.codeSourceUrl = url; + } + + void findResources(String name, ArrayList resources) { + URL res = findResource(name); + if (res != null && !resources.contains(res)) { + resources.add(res); + } + } + + Class findClass(String packageName, String name, String origName) { + URL resURL = targetURL(url, name); + if (resURL != null) { + try { + InputStream is = resURL.openStream(); + return createClass(is, packageName, origName); + } catch (IOException e) { + } + } + return null; + } + + + Class createClass(InputStream is, String packageName, String origName) { + if (is == null) { + return null; + } + byte[] clBuf = null; + try { + clBuf = getBytes(is); + } catch (IOException e) { + return null; + } finally { + try { + is.close(); + } catch (IOException e) { + } + } + if (packageName != null) { + String packageDotName = packageName.replace('/', '.'); + Package packageObj = getPackage(packageDotName); + if (packageObj == null) { + definePackage(packageDotName, null, null, + null, null, null, null, null); + } else { + if (packageObj.isSealed()) { + throw new SecurityException(Msg + .getString("K004c")); //$NON-NLS-1$ + } + } + } + return defineClass(origName, clBuf, 0, clBuf.length, new CodeSource(codeSourceUrl, (Certificate[]) null)); + } + + URL findResource(String name) { + URL resURL = targetURL(url, name); + if (resURL != null) { + try { + URLConnection uc = resURL.openConnection(); + uc.getInputStream().close(); + // HTTP can return a stream on a non-existent file + // So check for the return code; + if (!resURL.getProtocol().equals("http")) { //$NON-NLS-1$ + return resURL; + } + int code; + if ((code = ((HttpURLConnection) uc).getResponseCode()) >= 200 + && code < 300) { + return resURL; + } + } catch (SecurityException e) { + return null; + } catch (IOException e) { + return null; + } + } + return null; + } + + URL targetURL(URL base, String name) { + try { + String file = base.getFile() + URIEncoderDecoder.quoteIllegal(name, + "/@" + URI.someLegal); + + return new URL(base.getProtocol(), base.getHost(), base.getPort(), + file, null); + } catch (UnsupportedEncodingException e) { + return null; + } catch (MalformedURLException e) { + return null; + } + } + + } + + class URLJarHandler extends URLHandler { + final JarFile jf; + final String prefixName; + final IndexFile index; + final Map subHandlers = new HashMap(); + + public URLJarHandler(URL url, URL jarURL, JarFile jf, String prefixName) { + super(url); + this.jf = jf; + this.prefixName = prefixName; + this.codeSourceUrl = jarURL; + final JarEntry je = jf.getJarEntry("META-INF/INDEX.LIST"); //$NON-NLS-1$ + this.index = (je == null ? null : IndexFile.readIndexFile(jf, je, url)); + } + + public URLJarHandler(URL url, URL jarURL, JarFile jf, String prefixName, IndexFile index) { + super(url); + this.jf = jf; + this.prefixName = prefixName; + this.index = index; + this.codeSourceUrl = jarURL; + } + + IndexFile getIndex() { + return index; + } + + @Override + void findResources(String name, ArrayList resources) { + URL res = findResourceInOwn(name); + if (res != null && !resources.contains(res)) { + resources.add(res); + } + if (index != null) { + int pos = name.lastIndexOf("/"); //$NON-NLS-1$ + // only keep the directory part of the resource + // as index.list only keeps track of directories and root files + String indexedName = (pos > 0) ? name.substring(0, pos) : name; + ArrayList urls = index.get(indexedName); + if (urls != null) { + urls.remove(url); + for (URL url : urls) { + URLHandler h = getSubHandler(url); + if (h != null) { + h.findResources(name, resources); + } + } + } + } + + } + + @Override + Class findClass(String packageName, String name, String origName) { + String entryName = prefixName + name; + JarEntry entry = jf.getJarEntry(entryName); + if (entry != null) { + /** + * Avoid recursive load class, especially the class + * is an implementation class of security provider + * and the jar is signed. + */ + try { + Manifest manifest = jf.getManifest(); + return createClass(entry, manifest, packageName, origName); + } catch (IOException e) { + } + } + if (index != null) { + ArrayList urls; + if (packageName == null) { + urls = index.get(name); + } else { + urls = index.get(packageName); + } + if (urls != null) { + urls.remove(url); + for (URL url : urls) { + URLHandler h = getSubHandler(url); + if (h != null) { + Class res = h.findClass(packageName, name, origName); + if (res != null) { + return res; + } + } + } + } + } + return null; + } + + private Class createClass(JarEntry entry, Manifest manifest, String packageName, String origName) { + InputStream is = null; + byte[] clBuf = null; + try { + is = jf.getInputStream(entry); + clBuf = getBytes(is); + } catch (IOException e) { + return null; + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException e) { + } + } + } + if (packageName != null) { + String packageDotName = packageName.replace('/', '.'); + Package packageObj = getPackage(packageDotName); + if (packageObj == null) { + if (manifest != null) { + definePackage(packageDotName, manifest, + codeSourceUrl); + } else { + definePackage(packageDotName, null, null, + null, null, null, null, null); + } + } else { + boolean exception = packageObj.isSealed(); + if (manifest != null) { + if (isSealed(manifest, packageName + "/")) { + exception = !packageObj + .isSealed(codeSourceUrl); + } + } + if (exception) { + throw new SecurityException(Msg + .getString("K0352", packageName)); //$NON-NLS-1$ + } + } + } + CodeSource codeS = new CodeSource(codeSourceUrl, entry.getCertificates()); + return defineClass(origName, clBuf, 0, clBuf.length, codeS); + } + + URL findResourceInOwn(String name) { + String entryName = prefixName + name; + if (jf.getEntry(entryName) != null) { + return targetURL(url, name); + } + return null; + } + + @Override + URL findResource(String name) { + URL res = findResourceInOwn(name); + if (res != null) { + return res; + } + if (index != null) { + int pos = name.lastIndexOf("/"); //$NON-NLS-1$ + // only keep the directory part of the resource + // as index.list only keeps track of directories and root files + String indexedName = (pos > 0) ? name.substring(0, pos) : name; + ArrayList urls = index.get(indexedName); + if (urls != null) { + urls.remove(url); + for (URL url : urls) { + URLHandler h = getSubHandler(url); + if (h != null) { + res = h.findResource(name); + if (res != null) { + return res; + } + } + } + } + } + return null; + } + + private synchronized URLHandler getSubHandler(URL url) { + URLHandler sub = subHandlers.get(url); + if (sub != null) { + return sub; + } + String protocol = url.getProtocol(); + if (protocol.equals("jar")) { //$NON-NLS-1$ + sub = createURLJarHandler(url); + } else if (protocol.equals("file")) { //$NON-NLS-1$ + sub = createURLSubJarHandler(url); + } else { + sub = createURLHandler(url); + } + if (sub != null) { + subHandlers.put(url, sub); + } + return sub; + } + + private URLHandler createURLSubJarHandler(URL url) { + String prefixName; + String file = url.getFile(); + if (url.getFile().endsWith("!/")) { //$NON-NLS-1$ + prefixName = ""; + } else { + int sepIdx = file.lastIndexOf("!/"); //$NON-NLS-1$ + if (sepIdx == -1) { + // Invalid URL, don't look here again + return null; + } + sepIdx += 2; + prefixName = file.substring(sepIdx); + } + try { + URL jarURL = ((JarURLConnection) url + .openConnection()).getJarFileURL(); + JarURLConnection juc = (JarURLConnection) new URL( + "jar", "", //$NON-NLS-1$ //$NON-NLS-2$ + jarURL.toExternalForm() + "!/").openConnection(); //$NON-NLS-1$ + JarFile jf = juc.getJarFile(); + URLJarHandler jarH = new URLJarHandler(url, jarURL, jf, prefixName, null); + // TODO : to think what we should do with indexes & manifest.class file here + return jarH; + } catch (IOException e) { + } + return null; + } + + } + + class URLFileHandler extends URLHandler { + private String prefix; + + public URLFileHandler(URL url) { + super(url); + String baseFile = url.getFile(); + String host = url.getHost(); + int hostLength = 0; + if (host != null) { + hostLength = host.length(); + } + StringBuilder buf = new StringBuilder(2 + hostLength + + baseFile.length()); + if (hostLength > 0) { + buf.append("//").append(host); //$NON-NLS-1$ + } + // baseFile always ends with '/' + buf.append(baseFile); + prefix = buf.toString(); + } + + @Override + Class findClass(String packageName, String name, String origName) { + String filename = prefix + name; + try { + filename = URLDecoder.decode(filename, "UTF-8"); //$NON-NLS-1$ + } catch (IllegalArgumentException e) { + return null; + } catch (UnsupportedEncodingException e) { + return null; + } + + File file = new File(filename); + if (file.exists()) { + try { + InputStream is = new FileInputStream(file); + return createClass(is, packageName, origName); + } catch (FileNotFoundException e) { + } + } + return null; + } + + @Override + URL findResource(String name) { + int idx = 0; + String filename; + + // Do not create a UNC path, i.e. \\host + while (idx < name.length() && + ((name.charAt(idx) == '/') || (name.charAt(idx) == '\\'))) { + idx++; + } + + if (idx > 0) { + name = name.substring(idx); + } + + try { + filename = URLDecoder.decode(prefix, "UTF-8") + name; //$NON-NLS-1$ + + if (new File(filename).exists()) { + return targetURL(url, name); + } + return null; + } catch (IllegalArgumentException e) { + return null; + } catch (UnsupportedEncodingException e) { + // must not happen + throw new AssertionError(e); + } + } + + } + + /** * Constructs a new {@code URLClassLoader} instance. The newly created * instance will have the system ClassLoader as its parent. URLs that end * with "/" are assumed to be directories, otherwise they are assumed to be * JAR files. - * + * * @param urls * the list of URLs where a specific class or file could be * found. @@ -145,7 +615,6 @@ public class URLClassLoader extends SecureClassLoader { * if a security manager exists and its {@code * checkCreateClassLoader()} method doesn't allow creation of * new ClassLoaders. - * @since Android 1.0 */ public URLClassLoader(URL[] urls) { this(urls, ClassLoader.getSystemClassLoader(), null); @@ -155,7 +624,7 @@ public class URLClassLoader extends SecureClassLoader { * Constructs a new URLClassLoader instance. The newly created instance will * have the system ClassLoader as its parent. URLs that end with "/" are * assumed to be directories, otherwise they are assumed to be JAR files. - * + * * @param urls * the list of URLs where a specific class or file could be * found. @@ -165,7 +634,6 @@ public class URLClassLoader extends SecureClassLoader { * if a security manager exists and its {@code * checkCreateClassLoader()} method doesn't allow creation of * new class loaders. - * @since Android 1.0 */ public URLClassLoader(URL[] urls, ClassLoader parent) { this(urls, parent, null); @@ -173,267 +641,81 @@ public class URLClassLoader extends SecureClassLoader { /** * Adds the specified URL to the search list. - * + * * @param url * the URL which is to add. - * @since Android 1.0 */ protected void addURL(URL url) { try { - URL search = createSearchURL(url); - urls = addURL(urls, search); - orgUrls = addURL(orgUrls, url); - synchronized (extensions) { - extensions.put(search, null); - } + originalUrls.add(url); + searchList.add(createSearchURL(url)); } catch (MalformedURLException e) { } } - /** - * Returns an array with the given URL added to the given array. - * - * @param urlArray - * {@code java.net.URL[]} the source array - * @param url - * {@code java.net.URL} the URL to be added - * @return java.net.URL[] an array made of the given array and the new URL - */ - URL[] addURL(URL[] urlArray, URL url) { - URL[] newPath = new URL[urlArray.length + 1]; - System.arraycopy(urlArray, 0, newPath, 0, urlArray.length); - newPath[urlArray.length] = url; - Hashtable[] newIndexes = newHashtableArray(indexes.length + 1); - System.arraycopy(indexes, 0, newIndexes, 0, indexes.length); - indexes = newIndexes; - return newPath; - } - /** * Returns all known URLs which point to the specified resource. - * + * * @param name * the name of the requested resource. * @return the enumeration of URLs which point to the specified resource. * @throws IOException * if an I/O error occurs while attempting to connect. - * @since Android 1.0 */ @Override public Enumeration findResources(final String name) throws IOException { if (name == null) { return null; } - Vector result = AccessController.doPrivileged( - new PrivilegedAction>() { - public Vector run() { - return findResources(urls, name, new Vector()); + ArrayList result = AccessController.doPrivileged( + new PrivilegedAction>() { + public ArrayList run() { + ArrayList results = new ArrayList(); + findResourcesImpl(name, results); + return results; } }, currentContext); SecurityManager sm; int length = result.size(); if (length > 0 && (sm = System.getSecurityManager()) != null) { - Vector reduced = new Vector(length); + ArrayList reduced = new ArrayList(length); for (int i = 0; i < length; i++) { - URL url = result.elementAt(i); + URL url = result.get(i); try { sm.checkPermission(url.openConnection().getPermission()); - reduced.addElement(url); + reduced.add(url); } catch (IOException e) { } catch (SecurityException e) { } } result = reduced; } - return result.elements(); + return Collections.enumeration(result); } - /** - * Returns a Vector of URLs among the given ones that contain the specified - * resource. - * - * @return Vector the enumeration of URLs that contain the specified - * resource. - * @param searchURLs - * {@code java.net.URL[]} the array to be searched. - * @param name - * {@code java.lang.String} the name of the requested resource. - */ - Vector findResources(URL[] searchURLs, String name, Vector result) { - boolean findInExtensions = searchURLs == urls; - for (int i = 0; i < searchURLs.length; i++) { - if (!invalidUrls.contains(searchURLs[i])) { - URL[] search = new URL[] { searchURLs[i] }; - URL res = findResourceImpl(search, name); - if (!invalidUrls.contains(search[0])) { - if (res != null && !result.contains(res)) { - result.addElement(res); - } - if (findInExtensions) { - findInExtensions(explore(searchURLs[i], i), name, i, - result, false); - } - } + void findResourcesImpl(String name, ArrayList result) { + int n = 0; + while (true) { + URLHandler handler = getHandler(n++); + if (handler == null) { + break; } + handler.findResources(name, result); } - return result; } - /** - * Returns an {@code Object[]} containing a class, a URL, and a vector of - * URLs, two of which are {@code null}, according to the caller, which is - * identified by the {@code int} type. - * - * @return Object[] a 3-element array : {Class, URL, Vector}. The non-null - * element contains the resource(s) found, which are searched in in - * {@code indexes[i]}. - * @param i - * the index of 'indexes' array to use. - * @param name - * the resource to look for : either a resource or a class. - * @param resources - * {@code boolean} indicates that a vector of URL should be - * returned as the non {@code null} element in {@code Object[]}. - * @param url - * if {@code true} a URL should be returned as the non-null - * element, if {@code false} a class should be returned. - */ - Object findInIndex(int i, String name, Vector resources, boolean url) { - Hashtable index = indexes[i]; - if (index != null) { - int pos = name.lastIndexOf("/"); //$NON-NLS-1$ - // only keep the directory part of the resource - // as index.list only keeps track of directories and root files - String indexedName = (pos > 0) ? name.substring(0, pos) : name; - URL[] jarURLs; - if (resources != null) { - jarURLs = index.get(indexedName); - if (jarURLs != null) { - findResources(jarURLs, name, resources); - } - } else if (url) { - jarURLs = index.get(indexedName); - if (jarURLs != null) { - return findResourceImpl(jarURLs, name); - } - } else { - String clsName = name; - String partialName = clsName.replace('.', '/'); - int position; - if ((position = partialName.lastIndexOf('/')) != -1) { - String packageName = partialName.substring(0, position); - jarURLs = index.get(packageName); - } else { - String className = partialName.substring(0, partialName - .length()) - + ".class"; //$NON-NLS-1$ - jarURLs = index.get(className); - } - if (jarURLs != null) { - Class c = findClassImpl(jarURLs, clsName); - // InvalidJarException is thrown when a mapping for a class - // is not valid, i.e. we can't find the class by following - // the mapping. - if (c == null) { - throw new InvalidJarIndexException(); - } - return c; - } - } - } - return null; - } - - /** - * Returns an {@code Object[]} containing a Class, a URL, and a Vector of - * URLs, two of which are {@code null}, according to the caller, which is - * identified by the {@code int} type. - * - * @return Object[] a 3-element array : {Class, URL, Vector}. The non-null - * element contains the resource(s) found, which are searched in - * newExtensions. - * @param newExtensions - * URL[] the URLs to look in for. - * @param name - * the resource to look for : either a resource or a class. - * @param i - * the index of 'indexes' array to use. - * @param resources - * indicates that a Vector of URL should be returned as the - * non-null element in {@code Object[]}. - * @param url - * if {@code true} a URL should be returned as the non-null - * element, if {@code false} a class should be returned. - */ - Object findInExtensions(URL[] newExtensions, String name, int i, - Vector resources, boolean url) { - if (newExtensions != null) { - for (int k = 0; k < newExtensions.length; k++) { - if (newExtensions[k] != null) { - URL[] search = new URL[] { newExtensions[k] }; - if (resources != null) { - URL res = findResourceImpl(search, name); - if (!invalidUrls.contains(search[0])) { // the URL does - // not exist - if (res != null && !resources.contains(res)) { - resources.addElement(res); - } - findInExtensions(explore(newExtensions[k], i), - name, i, resources, url); - } - } else { - Object result; - if (url) { - result = findResourceImpl(search, name); - } else { - result = findClassImpl(search, name); - } - if (result != null) { - return result; - } - if (!invalidUrls.contains(search[0])) { // the URL - // exists - result = findInExtensions(explore(newExtensions[k], - i), name, i, null, url); - if (result != null) { - return result; - } - } - } - } - } - } else { - try { - return findInIndex(i, name, resources, url); - } catch (InvalidJarIndexException ex) { - // Ignore misleading/wrong jar index - return null; - } - } - return null; - } /** * Converts an input stream into a byte array. - * - * @return byte[] the byte array + * * @param is * the input stream + * @return byte[] the byte array */ - private static byte[] getBytes(InputStream is, boolean readAvailable) + private static byte[] getBytes(InputStream is) throws IOException { - if (readAvailable) { - byte[] buf = new byte[is.available()]; - is.read(buf, 0, buf.length); - is.close(); - return buf; - } byte[] buf = new byte[4096]; - int size = is.available(); - if (size < 1024) { - size = 1024; - } - ByteArrayOutputStream bos = new ByteArrayOutputStream(size); + ByteArrayOutputStream bos = new ByteArrayOutputStream(4096); int count; while ((count = is.read(buf)) > 0) { bos.write(buf, 0, count); @@ -448,11 +730,10 @@ public class URLClassLoader extends SecureClassLoader { * read permission to the file is added to the permission collection. * Otherwise, connecting to and accepting connections from the URL is * granted. - * + * * @param codesource * the code source object whose permissions have to be known. * @return the list of permissions according to the code source object. - * @since Android 1.0 */ @Override protected PermissionCollection getPermissions(final CodeSource codesource) { @@ -494,12 +775,11 @@ public class URLClassLoader extends SecureClassLoader { /** * Returns the search list of this {@code URLClassLoader}. - * + * * @return the list of all known URLs of this instance. - * @since Android 1.0 */ public URL[] getURLs() { - return orgUrls.clone(); + return originalUrls.toArray(new URL[originalUrls.size()]); } /** @@ -515,12 +795,11 @@ public class URLClassLoader extends SecureClassLoader { * system {@code ClassLoader} as its parent. The method {@code loadClass()} * of the new instance will call {@code * SecurityManager.checkPackageAccess()} before loading a class. - * + * * @param urls * the list of URLs that is passed to the new {@code * URLClassloader}. * @return the created {@code URLClassLoader} instance. - * @since Android 1.0 */ public static URLClassLoader newInstance(final URL[] urls) { URLClassLoader sub = AccessController @@ -538,17 +817,16 @@ public class URLClassLoader extends SecureClassLoader { * specified {@code ClassLoader} as its parent. The method {@code * loadClass()} of the new instance will call the SecurityManager's {@code * checkPackageAccess()} before loading a class. - * + * * @param urls * the list of URLs that is passed to the new URLClassloader. * @param parentCl * the parent class loader that is passed to the new * URLClassloader. * @return the created {@code URLClassLoader} instance. - * @since Android 1.0 */ public static URLClassLoader newInstance(final URL[] urls, - final ClassLoader parentCl) { + final ClassLoader parentCl) { URLClassLoader sub = AccessController .doPrivileged(new PrivilegedAction() { public URLClassLoader run() { @@ -565,7 +843,7 @@ public class URLClassLoader extends SecureClassLoader { * use the specified factory to create stream handlers. URLs that end with * "/" are assumed to be directories, otherwise they are assumed to be JAR * files. - * + * * @param searchUrls * the list of URLs where a specific class or file could be * found. @@ -578,10 +856,9 @@ public class URLClassLoader extends SecureClassLoader { * if a security manager exists and its {@code * checkCreateClassLoader()} method doesn't allow creation of * new {@code ClassLoader}s. - * @since Android 1.0 */ public URLClassLoader(URL[] searchUrls, ClassLoader parent, - URLStreamHandlerFactory factory) { + URLStreamHandlerFactory factory) { super(parent); // Required for pre-v1.2 security managers to work SecurityManager security = System.getSecurityManager(); @@ -592,33 +869,28 @@ public class URLClassLoader extends SecureClassLoader { // capture the context of the thread that creates this URLClassLoader currentContext = AccessController.getContext(); int nbUrls = searchUrls.length; - urls = new URL[nbUrls]; - orgUrls = new URL[nbUrls]; - // Search each jar for CLASS-PATH attribute in manifest - extensions = new HashMap(nbUrls * 2); + originalUrls = new ArrayList(nbUrls); + handlerList = new ArrayList(nbUrls); + searchList = Collections.synchronizedList(new ArrayList(nbUrls)); for (int i = 0; i < nbUrls; i++) { + originalUrls.add(searchUrls[i]); try { - urls[i] = createSearchURL(searchUrls[i]); - extensions.put(urls[i], null); + searchList.add(createSearchURL(searchUrls[i])); } catch (MalformedURLException e) { } - orgUrls[i] = searchUrls[i]; } - // Search each jar for META-INF/INDEX.LIST - indexes = newHashtableArray(nbUrls); } /** * Tries to locate and load the specified class using the known URLs. If the * class could be found, a class object representing the loaded class will * be returned. - * - * @return the class that has been loaded. + * * @param clsName * the name of the class which has to be found. + * @return the class that has been loaded. * @throws ClassNotFoundException * if the specified class cannot be loaded. - * @since Android 1.0 */ @Override protected Class findClass(final String clsName) @@ -626,7 +898,7 @@ public class URLClassLoader extends SecureClassLoader { Class cls = AccessController.doPrivileged( new PrivilegedAction>() { public Class run() { - return findClassImpl(urls, clsName); + return findClassImpl(clsName); } }, currentContext); if (cls != null) { @@ -639,7 +911,7 @@ public class URLClassLoader extends SecureClassLoader { * Returns an URL that will be checked if it contains the class or resource. * If the file component of the URL is not a directory, a Jar URL will be * created. - * + * * @return java.net.URL a test URL */ private URL createSearchURL(URL url) throws MalformedURLException { @@ -656,19 +928,19 @@ public class URLClassLoader extends SecureClassLoader { return new URL("jar", "", //$NON-NLS-1$ //$NON-NLS-2$ -1, url.toString() + "!/"); //$NON-NLS-1$ } + // use jar protocol as the stream handler protocol return new URL("jar", "", //$NON-NLS-1$ //$NON-NLS-2$ -1, url.toString() + "!/", //$NON-NLS-1$ - factory.createURLStreamHandler(protocol)); + factory.createURLStreamHandler("jar"));//$NON-NLS-1$ } /** * Returns an URL referencing the specified resource or {@code null} if the * resource could not be found. - * + * * @param name * the name of the requested resource. * @return the URL which points to the given resource. - * @since Android 1.0 */ @Override public URL findResource(final String name) { @@ -677,7 +949,7 @@ public class URLClassLoader extends SecureClassLoader { } URL result = AccessController.doPrivileged(new PrivilegedAction() { public URL run() { - return findResourceImpl(urls, name); + return findResourceImpl(name); } }, currentContext); SecurityManager sm; @@ -696,161 +968,108 @@ public class URLClassLoader extends SecureClassLoader { /** * Returns a URL among the given ones referencing the specified resource or * null if no resource could be found. - * + * + * @param resName java.lang.String the name of the requested resource * @return URL URL for the resource. - * @param searchList - * java.net.URL[] the array to be searched - * @param resName - * java.lang.String the name of the requested resource */ - URL findResourceImpl(URL[] searchList, String resName) { - boolean findInExtensions = searchList == urls; - int i = 0; - while (i < searchList.length) { - if (searchList[i] == null) { - // KA024=One of urls is null + URL findResourceImpl(String resName) { + int n = 0; + + while (true) { + URLHandler handler = getHandler(n++); + if (handler == null) { + break; + } + URL res = handler.findResource(resName); + if (res != null) { + return res; + } + } + return null; + } + + URLHandler getHandler(int num) { + if (num < handlerList.size()) { + return handlerList.get(num); + } + makeNewHandler(); + if (num < handlerList.size()) { + return handlerList.get(num); + } + return null; + } + + private synchronized void makeNewHandler() { + while (!searchList.isEmpty()) { + URL nextCandidate = searchList.remove(0); + if (nextCandidate == null) { // KA024=One of urls is null throw new NullPointerException(Msg.getString("KA024")); //$NON-NLS-1$ - } else if (!invalidUrls.contains(searchList[i])) { - JarFile jf = null; - try { - URL currentUrl = searchList[i]; - String protocol = currentUrl.getProtocol(); - - if (protocol.equals("jar")) { //$NON-NLS-1$ - jf = resCache.get(currentUrl); - if (jf == null) { - if (invalidUrls.contains(currentUrl)) { - continue; - } - // each jf should be found only once - // so we do this job in the synchronized block - synchronized (lock) { - // Check the cache again in case another thread - // updated it while we're waiting on lock - jf = resCache.get(currentUrl); - if (jf == null) { - if (invalidUrls.contains(currentUrl)) { - continue; - } - /* - * If the connection for currentUrl or resURL is - * used, getJarFile() will throw an exception if the - * entry doesn't exist. - */ - URL jarURL = ((JarURLConnection) currentUrl - .openConnection()).getJarFileURL(); - try { - JarURLConnection juc = (JarURLConnection) new URL( - "jar", "", //$NON-NLS-1$ //$NON-NLS-2$ - jarURL.toExternalForm() + "!/").openConnection(); //$NON-NLS-1$ - jf = juc.getJarFile(); - resCache.put(currentUrl, jf); - } catch (IOException e) { - // Don't look for this jar file again - invalidUrls.add(searchList[i]); - throw e; - } - } - } - } - String entryName; - if (currentUrl.getFile().endsWith("!/")) { //$NON-NLS-1$ - entryName = resName; - } else { - String file = currentUrl.getFile(); - int sepIdx = file.lastIndexOf("!/"); //$NON-NLS-1$ - if (sepIdx == -1) { - // Invalid URL, don't look here again - invalidUrls.add(searchList[i]); - continue; - } - sepIdx += 2; - entryName = new StringBuffer(file.length() - sepIdx - + resName.length()).append( - file.substring(sepIdx)).append(resName) - .toString(); - } - if (jf.getEntry(entryName) != null) { - return targetURL(currentUrl, resName); - } - } else if (protocol.equals("file")) { //$NON-NLS-1$ - String baseFile = currentUrl.getFile(); - String host = currentUrl.getHost(); - int hostLength = 0; - if (host != null) { - hostLength = host.length(); - } - StringBuffer buf = new StringBuffer(2 + hostLength - + baseFile.length() + resName.length()); - if (hostLength > 0) { - buf.append("//").append(host); //$NON-NLS-1$ - } - // baseFile always ends with '/' - buf.append(baseFile); - String fixedResName = resName; - // Do not create a UNC path, i.e. \\host - while (fixedResName.startsWith("/") //$NON-NLS-1$ - || fixedResName.startsWith("\\")) { //$NON-NLS-1$ - fixedResName = fixedResName.substring(1); - } - buf.append(fixedResName); - - String filename = buf.toString(); - - try { - filename = URLDecoder.decode(filename, "UTF-8"); //$NON-NLS-1$ - } catch (IllegalArgumentException e) { - return null; - } + } + if (!handlerMap.containsKey(nextCandidate)) { + URLHandler result; + String protocol = nextCandidate.getProtocol(); + if (protocol.equals("jar")) { //$NON-NLS-1$ + result = createURLJarHandler(nextCandidate); + } else if (protocol.equals("file")) { //$NON-NLS-1$ + result = createURLFileHandler(nextCandidate); + } else { + result = createURLHandler(nextCandidate); + } + if (result != null) { + handlerMap.put(nextCandidate, result); + handlerList.add(result); + return; + } + } + } + } - if (new File(filename).exists()) { - return targetURL(currentUrl, fixedResName); - } - } else { - URL resURL = targetURL(currentUrl, resName); - URLConnection uc = resURL.openConnection(); - try { - uc.getInputStream().close(); - } catch (SecurityException e) { - return null; - } - // HTTP can return a stream on a non-existent file - // So check for the return code; - if (!resURL.getProtocol().equals("http")) { //$NON-NLS-1$ - return resURL; - } - int code; - if ((code = ((HttpURLConnection) uc).getResponseCode()) >= 200 - && code < 300) { - return resURL; + private URLHandler createURLHandler(URL url) { + return new URLHandler(url); + } + + private URLHandler createURLFileHandler(URL url) { + return new URLFileHandler(url); + } + + private URLHandler createURLJarHandler(URL url) { + String prefixName; + String file = url.getFile(); + if (url.getFile().endsWith("!/")) { //$NON-NLS-1$ + prefixName = ""; + } else { + int sepIdx = file.lastIndexOf("!/"); //$NON-NLS-1$ + if (sepIdx == -1) { + // Invalid URL, don't look here again + return null; + } + sepIdx += 2; + prefixName = file.substring(sepIdx); + } + try { + URL jarURL = ((JarURLConnection) url + .openConnection()).getJarFileURL(); + JarURLConnection juc = (JarURLConnection) new URL( + "jar", "", //$NON-NLS-1$ //$NON-NLS-2$ + jarURL.toExternalForm() + "!/").openConnection(); //$NON-NLS-1$ + JarFile jf = juc.getJarFile(); + URLJarHandler jarH = new URLJarHandler(url, jarURL, jf, prefixName); + + if (jarH.getIndex() == null) { + try { + Manifest manifest = jf.getManifest(); + if (manifest != null) { + String classpath = manifest.getMainAttributes().getValue( + Attributes.Name.CLASS_PATH); + if (classpath != null) { + searchList.addAll(0, getInternalURLs(url, classpath)); } } - } catch (MalformedURLException e) { - // Keep iterating through the URL list } catch (IOException e) { - } catch (SecurityException e) { - } - if ((jf != null) && findInExtensions) { - if (indexes[i] != null) { - try { - URL result = (URL) findInIndex(i, resName, null, - true); - if (result != null) { - return result; - } - } catch (InvalidJarIndexException ex) { - // Ignore invalid/misleading JAR index file - } - } else { - URL result = (URL) findInExtensions(explore( - searchList[i], i), resName, i, null, true); - if (result != null) { - return result; - } - } } } - ++i; + return jarH; + } catch (IOException e) { } return null; } @@ -858,7 +1077,7 @@ public class URLClassLoader extends SecureClassLoader { /** * Defines a new package using the information extracted from the specified * manifest. - * + * * @param packageName * the name of the new package. * @param manifest @@ -869,10 +1088,9 @@ public class URLClassLoader extends SecureClassLoader { * @return the created package. * @throws IllegalArgumentException * if a package with the given name already exists. - * @since Android 1.0 */ protected Package definePackage(String packageName, Manifest manifest, - URL url) throws IllegalArgumentException { + URL url) throws IllegalArgumentException { Attributes mainAttributes = manifest.getMainAttributes(); String dirName = packageName.replace('.', '/') + "/"; //$NON-NLS-1$ Attributes packageAttributes = manifest.getAttributes(dirName); @@ -921,7 +1139,7 @@ public class URLClassLoader extends SecureClassLoader { return definePackage(packageName, specificationTitle, specificationVersion, specificationVendor, implementationTitle, implementationVersion, implementationVendor, isSealed(manifest, - dirName) ? url : null); + dirName) ? url : null); } private boolean isSealed(Manifest manifest, String dirName) { @@ -940,19 +1158,18 @@ public class URLClassLoader extends SecureClassLoader { /** * returns URLs referenced in the string classpath. - * + * * @param root * the jar URL that classpath is related to * @param classpath * the relative URLs separated by spaces - * * @return URL[] the URLs contained in the string classpath. */ - private URL[] getInternalURLs(URL root, String classpath) { + private ArrayList getInternalURLs(URL root, String classpath) { // Class-path attribute is composed of space-separated values. StringTokenizer tokenizer = new java.util.StringTokenizer(classpath); - Vector addedURLs = new Vector(); - String file = root.getFile(); + ArrayList addedURLs = new ArrayList(); + String file = root.getFile(); int jarIndex = file.lastIndexOf("!/") - 1; //$NON-NLS-1$ int index = file.lastIndexOf("/", jarIndex) + 1; //$NON-NLS-1$ if (index == 0) { @@ -967,383 +1184,38 @@ public class URLClassLoader extends SecureClassLoader { String element = tokenizer.nextToken(); if (!element.equals("")) { //$NON-NLS-1$ try { - URL newURL = new URL(protocol, host, port, file + element - + "!/"); //$NON-NLS-1$ - synchronized (extensions) { - if (!extensions.containsKey(newURL)) { - extensions.put(newURL, null); - addedURLs.add(newURL); - } - } + // Take absolute path case into consideration + URL url = new URL(new URL(file), element); + addedURLs.add(createSearchURL(url)); } catch (MalformedURLException e) { // Nothing is added } } } - URL[] newURLs = addedURLs.toArray(new URL[] {}); - return newURLs; + return addedURLs; } - /** - * @param in - * InputStream the stream to read lines from - * @return List a list of String lines - */ - private List readLines(InputStream in) throws IOException { - byte[] buff = new byte[144]; - List lines = new ArrayList(); - int pos = 0; - int next; - while ((next = in.read()) != -1) { - if (next == '\n') { - lines.add(new String(buff, 0, pos, "UTF8")); //$NON-NLS-1$ - pos = 0; - continue; - } - if (next == '\r') { - lines.add(new String(buff, 0, pos, "UTF8")); //$NON-NLS-1$ - pos = 0; - if ((next = in.read()) == '\n') { - continue; - } - } - if (pos == buff.length) { - byte[] newBuf = new byte[buff.length * 2]; - System.arraycopy(buff, 0, newBuf, 0, buff.length); - buff = newBuf; - } - buff[pos++] = (byte) next; - } - if (pos > 0) { - lines.add(new String(buff, 0, pos, "UTF8")); //$NON-NLS-1$ + Class findClassImpl(String className) { + String partialName = className.replace('.', '/'); + final String classFileName = new StringBuilder(partialName).append(".class").toString(); //$NON-NLS-1$ + String packageName = null; + int position = partialName.lastIndexOf('/'); + if ((position = partialName.lastIndexOf('/')) != -1) { + packageName = partialName.substring(0, position); } - return lines; - } - - private URL targetURL(URL base, String name) throws MalformedURLException { - try { - String file = base.getFile() + URIEncoderDecoder.quoteIllegal(name, - "/@" + URI.someLegal); - - return new URL(base.getProtocol(), base.getHost(), base.getPort(), - file, null); - } catch (UnsupportedEncodingException e) { - MalformedURLException e2 = new MalformedURLException(e.toString()); - - e2.initCause(e); - throw e2; - } - - } - - /** - * @param searchURLs - * java.net.URL[] the URLs to search in - * @param clsName - * java.lang.String the class name to be found - * @return Class the class found or null if not found - */ - Class findClassImpl(URL[] searchURLs, String clsName) { - boolean readAvailable = false; - boolean findInExtensions = searchURLs == urls; - final String name = new StringBuffer(clsName.replace('.', '/')).append( - ".class").toString(); //$NON-NLS-1$ - for (int i = 0; i < searchURLs.length; i++) { - if (searchURLs[i] == null) { - // KA024=One of urls is null - throw new NullPointerException(Msg.getString("KA024")); //$NON-NLS-1$ - } else if (!invalidUrls.contains(searchURLs[i])) { - Manifest manifest = null; - InputStream is = null; - JarEntry entry = null; - JarFile jf = null; - byte[] clBuf = null; - try { - URL thisURL = searchURLs[i]; - String protocol = thisURL.getProtocol(); - if (protocol.equals("jar")) { //$NON-NLS-1$ - jf = resCache.get(thisURL); - if ((jf == null) && (!invalidUrls.contains(thisURL))) { - synchronized (lock) { - // Check the cache again in case another thread updated it - // updated it while we're waiting on lock - jf = resCache.get(thisURL); - if (jf == null) { - if (invalidUrls.contains(thisURL)) { - continue; - } - // If the connection for testURL or thisURL is used, - // getJarFile() will throw an exception if the entry - // doesn't exist. - URL jarURL = ((JarURLConnection) thisURL - .openConnection()).getJarFileURL(); - try { - JarURLConnection juc = (JarURLConnection) new URL( - "jar", "", jarURL.toExternalForm() + "!/") //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - .openConnection(); - jf = juc.getJarFile(); - resCache.put(thisURL, jf); - } catch (IOException e) { - // Don't look for this jar file again - invalidUrls.add(searchURLs[i]); - throw e; - } - } - } - } - if (thisURL.getFile().endsWith("!/")) { //$NON-NLS-1$ - entry = jf.getJarEntry(name); - } else { - String file = thisURL.getFile(); - int sepIdx = file.lastIndexOf("!/"); //$NON-NLS-1$ - if (sepIdx == -1) { - // Invalid URL, don't look here again - invalidUrls.add(searchURLs[i]); - continue; - } - sepIdx += 2; - String entryName = new StringBuffer(file.length() - - sepIdx + name.length()).append( - file.substring(sepIdx)).append(name) - .toString(); - entry = jf.getJarEntry(entryName); - } - if (entry != null) { - readAvailable = true; - is = jf.getInputStream(entry); - /** - * Avoid recursive load class, especially the class - * is an implementation class of security provider - * and the jar is signed. - */ - Class loadedClass = findLoadedClass(clsName); - if (null != loadedClass) { - is.close(); - return loadedClass; - } - manifest = jf.getManifest(); - } - } else if (protocol.equals("file")) { //$NON-NLS-1$ - String filename = thisURL.getFile(); - String host = thisURL.getHost(); - if (host != null && host.length() > 0) { - filename = new StringBuffer(host.length() - + filename.length() + name.length() + 2) - .append("//").append(host).append(filename) //$NON-NLS-1$ - .append(name).toString(); - } else { - filename = new StringBuffer(filename.length() - + name.length()).append(filename).append( - name).toString(); - } - - // Just return null for caller to throw - // ClassNotFoundException. - try { - filename = URLDecoder.decode(filename, "UTF-8"); //$NON-NLS-1$ - } catch (IllegalArgumentException e) { - return null; - } - - File file = new File(filename); - // Don't throw exceptions for speed - if (file.exists()) { - is = new FileInputStream(file); - readAvailable = true; - } else { - continue; - } - } else { - is = targetURL(thisURL, name).openStream(); - } - } catch (MalformedURLException e) { - // Keep iterating through the URL list - } catch (IOException e) { - } - if (is != null) { - URL codeSourceURL = null; - Certificate[] certificates = null; - CodeSource codeS = null; - try { - codeSourceURL = findInExtensions ? orgUrls[i] - : ((JarURLConnection) searchURLs[i] - .openConnection()).getJarFileURL(); - } catch (IOException e) { - codeSourceURL = searchURLs[i]; - } - if (is != null) { - try { - clBuf = getBytes(is, readAvailable); - is.close(); - } catch (IOException e) { - return null; - } - } - if (entry != null) { - certificates = entry.getCertificates(); - } - // Use the original URL, not the possible jar URL - codeS = new CodeSource(codeSourceURL, certificates); - int dotIndex = clsName.lastIndexOf("."); //$NON-NLS-1$ - if (dotIndex != -1) { - String packageName = clsName.substring(0, dotIndex); - synchronized (this) { - Package packageObj = getPackage(packageName); - if (packageObj == null) { - if (manifest != null) { - definePackage(packageName, manifest, - codeSourceURL); - } else { - definePackage(packageName, null, null, - null, null, null, null, null); - } - } else { - boolean exception = false; - if (manifest != null) { - String dirName = packageName.replace('.', - '/') - + "/"; //$NON-NLS-1$ - if (isSealed(manifest, dirName)) { - exception = !packageObj - .isSealed(codeSourceURL); - } - } else { - exception = packageObj.isSealed(); - } - if (exception) { - throw new SecurityException(Msg - .getString("K004c")); //$NON-NLS-1$ - } - } - } - } - return defineClass(clsName, clBuf, 0, clBuf.length, codeS); - } - if ((jf != null) && findInExtensions) { - if (indexes[i] != null) { - try { - Class c = (Class) findInIndex(i, clsName, - null, false); - if (c != null) { - return c; - } - } catch (InvalidJarIndexException ex) { - // Ignore misleading/wrong jar index - } - } else { - Class c = (Class) findInExtensions(explore( - searchURLs[i], i), clsName, i, null, false); - if (c != null) { - return c; - } - } - } + int n = 0; + while (true) { + URLHandler handler = getHandler(n++); + if (handler == null) { + break; } - } - return null; - } - - /** - * @param url - * URL the URL to explore - * @param indexNumber - * int the index in extensions to consider - * - * @return URL[] the URLs of bundled extensions that have been found (i.e. - * the URL of jar files in the class-path attribute), or null if - * none. if an INDEX.LIST has been found, an empty array is returned - */ - URL[] explore(URL url, int indexNumber) { - URL[] internal; - synchronized (extensions) { - internal = extensions.get(url); - } - if (internal != null) { - return internal; - } - if (indexes[indexNumber] != null) { - return null; - } - - if (!url.getProtocol().equals("jar")) { //$NON-NLS-1$ - return null; - } - - JarFile jf = resCache.get(url); - // Add mappings from INDEX.LIST - ZipEntry ze = jf.getEntry("META-INF/INDEX.LIST"); //$NON-NLS-1$ - if (ze != null) { - if (url.equals(urls[indexNumber])) { - try { - Hashtable index = new Hashtable( - 15); - InputStream indexIS = jf.getInputStream(ze); - List lines = readLines(indexIS); - indexIS.close(); - ListIterator iterator = lines.listIterator(); - // Ignore the 2 first lines (index version) - iterator.next(); - iterator.next(); - // Add mappings from resource to jar file - URL fileURL = ((JarURLConnection) url.openConnection()) - .getJarFileURL(); - String file = fileURL.getFile(); - String parentFile = new File(file).getParent(); - parentFile = parentFile.replace(File.separatorChar, '/'); - if (parentFile.charAt(0) != '/') { - parentFile = "/" + parentFile; //$NON-NLS-1$ - } - URL parentURL = new URL(fileURL.getProtocol(), fileURL - .getHost(), fileURL.getPort(), parentFile); - while (iterator.hasNext()) { - URL jar = new URL("jar:" //$NON-NLS-1$ - + parentURL.toExternalForm() + "/" //$NON-NLS-1$ - + iterator.next() + "!/"); //$NON-NLS-1$ - String resource = null; - while (iterator.hasNext() - && !(resource = iterator.next()).equals("")) { //$NON-NLS-1$ - if (index.containsKey(resource)) { - URL[] jars = index.get(resource); - URL[] newJars = new URL[jars.length + 1]; - System.arraycopy(jars, 0, newJars, 0, - jars.length); - newJars[jars.length] = jar; - index.put(resource, newJars); - } else { - URL[] jars = { jar }; - index.put(resource, jars); - } - } - } - indexes[indexNumber] = index; - } catch (MalformedURLException e) { - // Ignore this jar's index - } catch (IOException e) { - // Ignore this jar's index - } + Class res = handler.findClass(packageName, classFileName, className); + if (res != null) { + return res; } - return null; } + return null; - // Returns URLs referenced by the class-path attribute. - Manifest manifest = null; - try { - manifest = jf.getManifest(); - } catch (IOException e) { - } - String classpath = null; - if (manifest != null) { - classpath = manifest.getMainAttributes().getValue( - Attributes.Name.CLASS_PATH); - } - synchronized (extensions) { - internal = extensions.get(url); - if (internal == null) { - internal = classpath != null ? getInternalURLs(url, classpath) - : NO_PATH; - extensions.put(url, internal); - } - } - return internal; } + } diff --git a/libcore/luni/src/main/java/java/net/URLConnection.java b/libcore/luni/src/main/java/java/net/URLConnection.java index 6c1a1922c..573a2d2b2 100644 --- a/libcore/luni/src/main/java/java/net/URLConnection.java +++ b/libcore/luni/src/main/java/java/net/URLConnection.java @@ -23,6 +23,7 @@ import java.io.OutputStream; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Collections; +import java.util.Date; import java.util.Hashtable; import java.util.List; import java.util.Map; @@ -31,22 +32,17 @@ import java.util.StringTokenizer; import org.apache.harmony.luni.internal.net.www.MimeTable; import org.apache.harmony.luni.util.Msg; import org.apache.harmony.luni.util.PriviAction; -import org.apache.harmony.luni.util.Util; /** * Concrete implementations of the abstract {@code URLConnection} class provide * a communication link to a URL for exchanging data with a specific protocol * type. A {@code URLConnection} can only be set up after the instantiation but * before connecting to the remote resource. - * - * @since Android 1.0 */ public abstract class URLConnection { /** * The URL which represents the remote target of this {@code URLConnection}. - * - * @since Android 1.0 */ protected URL url; @@ -63,16 +59,12 @@ public abstract class URLConnection { /** * The data must be modified more recently than this time in milliseconds * since January 1, 1970, GMT to be transmitted. - * - * @since Android 1.0 */ protected long ifModifiedSince; /** * Specifies whether the using of caches is enabled or the data has to be * recent for every request. - * - * @since Android 1.0 */ protected boolean useCaches = defaultUseCaches; @@ -80,30 +72,22 @@ public abstract class URLConnection { * Specifies whether this {@code URLConnection} is already connected to the * remote resource. If this field is set to {@code true} the flags for * setting up the connection are not changeable anymore. - * - * @since Android 1.0 */ protected boolean connected; /** * Specifies whether this {@code URLConnection} allows sending data. - * - * @since Android 1.0 */ protected boolean doOutput; /** * Specifies whether this {@code URLConnection} allows receiving data. - * - * @since Android 1.0 */ protected boolean doInput = true; /** * Specifies whether this {@code URLConnection} allows user interaction as * it is needed for authentication purposes. - * - * @since Android 1.0 */ protected boolean allowUserInteraction = defaultAllowUserInteraction; @@ -127,11 +111,10 @@ public abstract class URLConnection { /** * Creates a new {@code URLConnection} instance pointing to the resource * specified by the given URL. - * + * * @param url * the URL which represents the resource this {@code * URLConnection} will point to. - * @since Android 1.0 */ protected URLConnection(URL url) { this.url = url; @@ -140,20 +123,18 @@ public abstract class URLConnection { /** * Establishes the connection to the earlier configured resource. The * connection can only be set up before this method has been called. - * + * * @throws IOException * if an error occurs while connecting to the resource. - * @since Android 1.0 */ public abstract void connect() throws IOException; /** * Gets the option value which indicates whether user interaction is allowed * on this {@code URLConnection}. - * + * * @return the value of the option {@code allowUserInteraction}. * @see #allowUserInteraction - * @since Android 1.0 */ public boolean getAllowUserInteraction() { return allowUserInteraction; @@ -166,11 +147,10 @@ public abstract class URLConnection { * header field "Content-Type". If none is found it will guess the content * type from the filename extension. If that fails the stream itself will be * used to guess the content type. - * + * * @return the content representing object. * @throws IOException * if an error occurs obtaining the content. - * @since Android 1.0 */ public Object getContent() throws java.io.IOException { if (!connected) { @@ -196,14 +176,13 @@ public abstract class URLConnection { * type from the filename extension. If that fails the stream itself will be * used to guess the content type. The content type must match with one of * the list {@code types}. - * + * * @param types * the list of acceptable content types. * @return the content representing object or {@code null} if the content * type does not match with one of the specified types. * @throws IOException * if an error occurs obtaining the content. - * @since Android 1.0 */ // Param is not generic in spec @SuppressWarnings("unchecked") @@ -226,9 +205,8 @@ public abstract class URLConnection { /** * Gets the content encoding type specified by the response header field * {@code content-encoding} or {@code null} if this field is not set. - * + * * @return the value of the response header field {@code content-encoding}. - * @since Android 1.0 */ public String getContentEncoding() { return getHeaderField("Content-Encoding"); //$NON-NLS-1$ @@ -237,7 +215,7 @@ public abstract class URLConnection { /** * Returns the specific ContentHandler that will handle the type {@code * contentType}. - * + * * @param type * The type that needs to be handled * @return An instance of the Content Handler @@ -286,7 +264,8 @@ public abstract class URLConnection { .doPrivileged(new PrivilegedAction() { public Object run() { try { - String className = "org.apache.harmony.luni.internal.net.www.content." //$NON-NLS-1$ + // Try looking up AWT image content handlers + String className = "org.apache.harmony.awt.www.content." //$NON-NLS-1$ + typeString; return Class.forName(className).newInstance(); } catch (ClassNotFoundException e) { @@ -312,9 +291,8 @@ public abstract class URLConnection { /** * Gets the content length in bytes specified by the response header field * {@code content-length} or {@code -1} if this field is not set. - * + * * @return the value of the response header field {@code content-length}. - * @since Android 1.0 */ public int getContentLength() { return getHeaderFieldInt("Content-Length", -1); //$NON-NLS-1$ @@ -323,9 +301,8 @@ public abstract class URLConnection { /** * Gets the MIME-type of the content specified by the response header field * {@code content-type} or {@code null} if type is unknown. - * + * * @return the value of the response header field {@code content-type}. - * @since Android 1.0 */ public String getContentType() { return getHeaderField("Content-Type"); //$NON-NLS-1$ @@ -335,9 +312,8 @@ public abstract class URLConnection { * Gets the timestamp when this response has been sent as a date in * milliseconds since January 1, 1970 GMT or {@code 0} if this timestamp is * unknown. - * + * * @return the sending timestamp of the current response. - * @since Android 1.0 */ public long getDate() { return getHeaderFieldDate("Date", 0); //$NON-NLS-1$ @@ -345,11 +321,10 @@ public abstract class URLConnection { /** * Gets the default setting whether this connection allows user interaction. - * + * * @return the value of the default setting {@code * defaultAllowUserInteraction}. * @see #allowUserInteraction - * @since Android 1.0 */ public static boolean getDefaultAllowUserInteraction() { return defaultAllowUserInteraction; @@ -359,12 +334,11 @@ public abstract class URLConnection { * Gets the default value for the specified request {@code field} or {@code * null} if the field could not be found. The current implementation of this * method returns always {@code null}. - * + * * @param field * the request field whose default value shall be returned. * @return the default value for the given field. * @deprecated Use {@link #getRequestProperty} - * @since Android 1.0 */ @Deprecated public static String getDefaultRequestProperty(String field) { @@ -373,10 +347,9 @@ public abstract class URLConnection { /** * Gets the default setting whether this connection allows using caches. - * + * * @return the value of the default setting {@code defaultUseCaches}. * @see #useCaches - * @since Android 1.0 */ public boolean getDefaultUseCaches() { return defaultUseCaches; @@ -385,11 +358,10 @@ public abstract class URLConnection { /** * Gets the value of the option {@code doInput} which specifies whether this * connection allows to receive data. - * + * * @return {@code true} if this connection allows input, {@code false} * otherwise. * @see #doInput - * @since Android 1.0 */ public boolean getDoInput() { return doInput; @@ -398,11 +370,10 @@ public abstract class URLConnection { /** * Gets the value of the option {@code doOutput} which specifies whether * this connection allows to send data. - * + * * @return {@code true} if this connection allows output, {@code false} * otherwise. * @see #doOutput - * @since Android 1.0 */ public boolean getDoOutput() { return doOutput; @@ -411,9 +382,8 @@ public abstract class URLConnection { /** * Gets the timestamp when this response will be expired in milliseconds * since January 1, 1970 GMT or {@code 0} if this timestamp is unknown. - * + * * @return the value of the response header field {@code expires}. - * @since Android 1.0 */ public long getExpiration() { return getHeaderFieldDate("Expires", 0); //$NON-NLS-1$ @@ -422,9 +392,8 @@ public abstract class URLConnection { /** * Gets the table which is used by all {@code URLConnection} instances to * determine the MIME-type according to a file extension. - * + * * @return the file name map to determine the MIME-type. - * @since Android 1.0 */ public static FileNameMap getFileNameMap() { // Must use lazy initialization or there is a bootstrap problem @@ -440,11 +409,10 @@ public abstract class URLConnection { * Gets the header value at the field position {@code pos} or {@code null} * if the header has fewer than {@code pos} fields. The current * implementation of this method returns always {@code null}. - * + * * @param pos * the field position of the response header. * @return the value of the field at position {@code pos}. - * @since Android 1.0 */ public String getHeaderField(int pos) { return null; @@ -454,9 +422,9 @@ public abstract class URLConnection { * Gets an unchangeable map of the response-header fields and values. The * response-header field names are the key values of the map. The map values * are lists of header field values associated with a particular key name. - * + * * @return the response-header representing generic map. - * @since Android 1.0 + * @since 1.4 */ public Map> getHeaderFields() { return Collections.emptyMap(); @@ -466,9 +434,9 @@ public abstract class URLConnection { * Gets an unchangeable map of general request properties used by this * connection. The request property names are the key values of the map. The * map values are lists of property values of the corresponding key name. - * + * * @return the request-property representing generic map. - * @since Android 1.0 + * @since 1.4 */ public Map> getRequestProperties() { if (connected) { @@ -480,7 +448,7 @@ public abstract class URLConnection { /** * Adds the given property to the request header. Existing properties with * the same name will not be overwritten by this method. - * + * * @param field * the request property field name to add. * @param newValue @@ -489,7 +457,7 @@ public abstract class URLConnection { * if the connection has been already established. * @throws NullPointerException * if the property name is {@code null}. - * @since Android 1.0 + * @since 1.4 */ public void addRequestProperty(String field, String newValue) { if (connected) { @@ -504,11 +472,10 @@ public abstract class URLConnection { * Gets the value of the header field specified by {@code key} or {@code * null} if there is no field with this name. The current implementation of * this method returns always {@code null}. - * + * * @param key * the name of the header field. * @return the value of the header field. - * @since Android 1.0 */ public String getHeaderField(String key) { return null; @@ -518,34 +485,37 @@ public abstract class URLConnection { * Gets the specified header value as a date in milliseconds since January * 1, 1970 GMT. Returns the {@code defaultValue} if no such header field * could be found. - * + * * @param field * the header field name whose value is needed. * @param defaultValue * the default value if no field has been found. * @return the value of the specified header field as a date in * milliseconds. - * @since Android 1.0 */ + @SuppressWarnings("deprecation") public long getHeaderFieldDate(String field, long defaultValue) { String date = getHeaderField(field); if (date == null) { return defaultValue; } - return Util.parseDate(date); + try { + return Date.parse(date); + } catch (Exception e) { + return defaultValue; + } } /** * Gets the specified header value as a number. Returns the {@code * defaultValue} if no such header field could be found or the value could * not be parsed as an {@code Integer}. - * + * * @param field * the header field name whose value is needed. * @param defaultValue * the default value if no field has been found. * @return the value of the specified header field as a number. - * @since Android 1.0 */ public int getHeaderFieldInt(String field, int defaultValue) { try { @@ -559,11 +529,10 @@ public abstract class URLConnection { * Gets the name of the header field at the given position {@code posn} or * {@code null} if there are fewer than {@code posn} fields. The current * implementation of this method returns always {@code null}. - * + * * @param posn * the position of the header field which has to be returned. * @return the header field name at the given position. - * @since Android 1.0 */ public String getHeaderFieldKey(int posn) { return null; @@ -573,10 +542,9 @@ public abstract class URLConnection { * Gets the point of time since when the data must be modified to be * transmitted. Some protocols transmit data only if it has been modified * more recently than a particular time. - * + * * @return the time in milliseconds since January 1, 1970 GMT. * @see #ifModifiedSince - * @since Android 1.0 */ public long getIfModifiedSince() { return ifModifiedSince; @@ -586,11 +554,10 @@ public abstract class URLConnection { * Gets an {@code InputStream} for reading data from the resource pointed by * this {@code URLConnection}. It throws an UnknownServiceException by * default. This method must be overridden by its subclasses. - * + * * @return the InputStream to read data from. * @throws IOException * if no InputStream could be created. - * @since Android 1.0 */ public InputStream getInputStream() throws IOException { throw new UnknownServiceException(Msg.getString("K004d")); //$NON-NLS-1$ @@ -599,9 +566,8 @@ public abstract class URLConnection { /** * Gets the value of the response header field {@code last-modified} or * {@code 0} if this value is not set. - * + * * @return the value of the {@code last-modified} header field. - * @since Android 1.0 */ public long getLastModified() { if (lastModified != -1) { @@ -614,11 +580,10 @@ public abstract class URLConnection { * Gets an {@code OutputStream} for writing data to this {@code * URLConnection}. It throws an {@code UnknownServiceException} by default. * This method must be overridden by its subclasses. - * + * * @return the OutputStream to write data. * @throws IOException * if no OutputStream could be created. - * @since Android 1.0 */ public OutputStream getOutputStream() throws IOException { throw new UnknownServiceException(Msg.getString("K005f")); //$NON-NLS-1$ @@ -631,12 +596,11 @@ public abstract class URLConnection { * necessary. By default, this method returns {@code AllPermission}. * Subclasses should overwrite this method to return an appropriate * permission object. - * + * * @return the permission object representing the needed permissions to open * this connection. * @throws IOException * if an I/O error occurs while creating the permission object. - * @since Android 1.0 */ public java.security.Permission getPermission() throws IOException { return new java.security.AllPermission(); @@ -646,13 +610,12 @@ public abstract class URLConnection { * Gets the value of the request header property specified by {code field} * or {@code null} if there is no field with this name. The current * implementation of this method returns always {@code null}. - * + * * @param field * the name of the request header property. * @return the value of the property. * @throws IllegalStateException * if the connection has been already established. - * @since Android 1.0 */ public String getRequestProperty(String field) { if (connected) { @@ -663,9 +626,8 @@ public abstract class URLConnection { /** * Gets the URL represented by this {@code URLConnection}. - * + * * @return the URL of this connection. - * @since Android 1.0 */ public URL getURL() { return url; @@ -674,9 +636,8 @@ public abstract class URLConnection { /** * Gets the value of the flag which specifies whether this {@code * URLConnection} allows to use caches. - * + * * @return {@code true} if using caches is allowed, {@code false} otherwise. - * @since Android 1.0 */ public boolean getUseCaches() { return useCaches; @@ -686,12 +647,11 @@ public abstract class URLConnection { * Determines the MIME-type of the given resource {@code url} by resolving * the filename extension with the internal FileNameMap. Any fragment * identifier is removed before processing. - * + * * @param url * the URL with the filename to get the MIME type. * @return the guessed content type or {@code null} if the type could not be * determined. - * @since Android 1.0 */ public static String guessContentTypeFromName(String url) { return getFileNameMap().getContentTypeFor(url); @@ -700,7 +660,7 @@ public abstract class URLConnection { /** * Determines the MIME-type of the resource represented by the input stream * {@code is} by reading its first few characters. - * + * * @param is * the resource representing input stream to determine the * content type. @@ -708,41 +668,97 @@ public abstract class URLConnection { * determined. * @throws IOException * if an I/O error occurs while reading from the input stream. - * @since Android 1.0 */ + @SuppressWarnings("nls") public static String guessContentTypeFromStream(InputStream is) throws IOException { + if (!is.markSupported()) { return null; } - is.mark(4); - char[] chars = new char[4]; - for (int i = 0; i < chars.length; i++) { - chars[i] = (char) is.read(); - } + // Look ahead up to 64 bytes for the longest encoded header + is.mark(64); + byte[] bytes = new byte[64]; + int length = is.read(bytes); is.reset(); - if ((chars[0] == 'P') && (chars[1] == 'K')) { - return "application/zip"; //$NON-NLS-1$ + + // Check for Unicode BOM encoding indicators + String encoding = "ASCII"; + int start = 0; + if (length > 1) { + if ((bytes[0] == (byte) 0xFF) && (bytes[1] == (byte) 0xFE)) { + encoding = "UTF-16LE"; + start = 2; + length -= length & 1; + } + if ((bytes[0] == (byte) 0xFE) && (bytes[1] == (byte) 0xFF)) { + encoding = "UTF-16BE"; + start = 2; + length -= length & 1; + } + if (length > 2) { + if ((bytes[0] == (byte) 0xEF) && (bytes[1] == (byte) 0xBB) + && (bytes[2] == (byte) 0xBF)) { + encoding = "UTF-8"; + start = 3; + } + if (length > 3) { + if ((bytes[0] == (byte) 0x00) && (bytes[1] == (byte) 0x00) + && (bytes[2] == (byte) 0xFE) + && (bytes[3] == (byte) 0xFF)) { + encoding = "UTF-32BE"; + start = 4; + length -= length & 3; + } + if ((bytes[0] == (byte) 0xFF) && (bytes[1] == (byte) 0xFE) + && (bytes[2] == (byte) 0x00) + && (bytes[3] == (byte) 0x00)) { + encoding = "UTF-32LE"; + start = 4; + length -= length & 3; + } + } + } } - if ((chars[0] == 'G') && (chars[1] == 'I')) { - return "image/gif"; //$NON-NLS-1$ + + String header = new String(bytes, start, length - start, encoding); + + // Check binary types + if (header.startsWith("PK")) { + return "application/zip"; } - if (new String(chars).trim().startsWith("<")) { //$NON-NLS-1$ - return "text/html"; //$NON-NLS-1$ + if (header.startsWith("GI")) { + return "image/gif"; } + + // Check text types + String textHeader = header.trim().toUpperCase(); + if (textHeader.startsWith(" timeout) { @@ -989,9 +993,8 @@ public abstract class URLConnection { /** * Gets the configured connecting timeout. - * + * * @return the connecting timeout value in milliseconds. - * @since Android 1.0 */ public int getConnectTimeout() { return connectTimeout; @@ -1003,12 +1006,11 @@ public abstract class URLConnection { * SocketTimeoutException} is thrown if the connection could not be * established in this time. Default is {@code 0} which stands for an * infinite timeout. - * + * * @param timeout * the reading timeout in milliseconds. * @throws IllegalArgumentException * if the parameter {@code timeout} is less than zero. - * @since Android 1.0 */ public void setReadTimeout(int timeout) { if (0 > timeout) { @@ -1020,9 +1022,8 @@ public abstract class URLConnection { /** * Gets the configured timeout for reading from the input stream of an * established connection to the resource. - * + * * @return the reading timeout value in milliseconds. - * @since Android 1.0 */ public int getReadTimeout() { return readTimeout; @@ -1031,9 +1032,8 @@ public abstract class URLConnection { /** * Returns the string representation containing the name of this class and * the URL. - * + * * @return the string representation of this {@code URLConnection} instance. - * @since Android 1.0 */ @Override public String toString() { diff --git a/libcore/luni/src/main/java/java/net/URLDecoder.java b/libcore/luni/src/main/java/java/net/URLDecoder.java index d272aa730..fd2753cf0 100644 --- a/libcore/luni/src/main/java/java/net/URLDecoder.java +++ b/libcore/luni/src/main/java/java/net/URLDecoder.java @@ -17,20 +17,23 @@ package java.net; -import java.io.ByteArrayOutputStream; import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.IllegalCharsetNameException; +import java.nio.charset.UnsupportedCharsetException; import org.apache.harmony.luni.util.Msg; -import org.apache.harmony.luni.util.Util; /** * This class is used to decode a string which is encoded in the {@code * application/x-www-form-urlencoded} MIME content type. - * - * @since Android 1.0 */ public class URLDecoder { + static Charset defaultCharset; + /** * Decodes the argument which is assumed to be encoded in the {@code * x-www-form-urlencoded} MIME content type. @@ -39,19 +42,32 @@ public class URLDecoder { * characters are converted to the equivalent byte value. All other * characters are passed through unmodified. For example "A+B+C %24%25" -> * "A B C $%". - *

          - * + * * @param s * the encoded string. * @return the decoded clear-text representation of the given string. * @deprecated use {@link #decode(String, String)} instead. - * @since Android 1.0 */ @Deprecated public static String decode(String s) { - return Util.decode(s, true); + + if (defaultCharset == null) { + try { + defaultCharset = Charset.forName( + System.getProperty("file.encoding")); //$NON-NLS-1$ + } catch (IllegalCharsetNameException e) { + // Ignored + } catch (UnsupportedCharsetException e) { + // Ignored + } + + if (defaultCharset == null) { + defaultCharset = Charset.forName("ISO-8859-1"); //$NON-NLS-1$ + } + } + return decode(s, defaultCharset); } - + /** * Decodes the argument which is assumed to be encoded in the {@code * x-www-form-urlencoded} MIME content type using the specified encoding @@ -61,8 +77,7 @@ public class URLDecoder { * characters are converted to the equivalent byte value. All other * characters are passed through unmodified. For example "A+B+C %24%25" -> * "A B C $%". - *

          - * + * * @param s * the encoded string. * @param enc @@ -70,7 +85,6 @@ public class URLDecoder { * @return the decoded clear-text representation of the given string. * @throws UnsupportedEncodingException * if the specified encoding scheme is invalid. - * @since Android 1.0 */ public static String decode(String s, String enc) throws UnsupportedEncodingException { @@ -81,40 +95,80 @@ public class URLDecoder { // If the given encoding is an empty string throw an exception. if (enc.length() == 0) { - throw new UnsupportedEncodingException(Msg - .getString("K00a5", "enc")); //$NON-NLS-1$ //$NON-NLS-2$ + throw new UnsupportedEncodingException( + // K00a5=Invalid parameter - {0} + Msg.getString("K00a5", "enc")); //$NON-NLS-1$ //$NON-NLS-2$ + } + + if (s.indexOf('%') == -1) { + if (s.indexOf('+') == -1) + return s; + char str[] = s.toCharArray(); + for (int i = 0; i < str.length; i++) { + if (str[i] == '+') + str[i] = ' '; + } + return new String(str); + } + + Charset charset = null; + try { + charset = Charset.forName(enc); + } catch (IllegalCharsetNameException e) { + throw (UnsupportedEncodingException) (new UnsupportedEncodingException( + enc).initCause(e)); + } catch (UnsupportedCharsetException e) { + throw (UnsupportedEncodingException) (new UnsupportedEncodingException( + enc).initCause(e)); } - StringBuffer result = new StringBuffer(s.length()); - ByteArrayOutputStream out = new ByteArrayOutputStream(); + return decode(s, charset); + } + + private static String decode(String s, Charset charset) { + + char str_buf[] = new char[s.length()]; + byte buf[] = new byte[s.length() / 3]; + int buf_len = 0; + for (int i = 0; i < s.length();) { char c = s.charAt(i); if (c == '+') { - result.append(' '); + str_buf[buf_len] = ' '; } else if (c == '%') { - out.reset(); + + int len = 0; do { if (i + 2 >= s.length()) { - throw new IllegalArgumentException(Msg.getString( - "K01fe", i)); //$NON-NLS-1$ + throw new IllegalArgumentException( + // K01fe=Incomplete % sequence at\: {0} + Msg.getString("K01fe", i)); //$NON-NLS-1$ } int d1 = Character.digit(s.charAt(i + 1), 16); int d2 = Character.digit(s.charAt(i + 2), 16); if (d1 == -1 || d2 == -1) { - throw new IllegalArgumentException(Msg.getString( - "K01ff", //$NON-NLS-1$ - s.substring(i, i + 3), String.valueOf(i))); + throw new IllegalArgumentException( + // K01ff=Invalid % sequence ({0}) at\: {1} + Msg.getString( + "K01ff", //$NON-NLS-1$ + s.substring(i, i + 3), + String.valueOf(i))); } - out.write((byte) ((d1 << 4) + d2)); + buf[len++] = (byte) ((d1 << 4) + d2); i += 3; } while (i < s.length() && s.charAt(i) == '%'); - result.append(out.toString(enc)); + + CharBuffer cb = charset.decode(ByteBuffer.wrap(buf, 0, len)); + len = cb.length(); + System.arraycopy(cb.array(), 0, str_buf, buf_len, len); + buf_len += len; continue; } else { - result.append(c); + str_buf[buf_len] = c; } i++; + buf_len++; } - return result.toString(); + return new String(str_buf, 0, buf_len); } } diff --git a/libcore/luni/src/main/java/java/net/URLEncoder.java b/libcore/luni/src/main/java/java/net/URLEncoder.java index 69f2d373c..b764cc7c9 100644 --- a/libcore/luni/src/main/java/java/net/URLEncoder.java +++ b/libcore/luni/src/main/java/java/net/URLEncoder.java @@ -22,8 +22,6 @@ import java.io.UnsupportedEncodingException; /** * This class is used to encode a string using the format required by * {@code application/x-www-form-urlencoded} MIME content type. - * - * @since Android 1.0 */ public class URLEncoder { @@ -43,17 +41,15 @@ public class URLEncoder { * and characters '.', '-', '*', '_' are converted into their hexadecimal * value prepended by '%'. For example: '#' -> %23. In addition, spaces are * substituted by '+' - *

          - * + * * @param s * the string to be encoded. * @return the encoded string. * @deprecated use {@link #encode(String, String)} instead. - * @since Android 1.0 */ @Deprecated public static String encode(String s) { - StringBuffer buf = new StringBuffer(); + StringBuilder buf = new StringBuilder(); for (int i = 0; i < s.length(); i++) { char ch = s.charAt(i); if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') @@ -81,8 +77,7 @@ public class URLEncoder { * and characters '.', '-', '*', '_' are converted into their hexadecimal * value prepended by '%'. For example: '#' -> %23. In addition, spaces are * substituted by '+' - *

          - * + * * @param s * the string to be encoded. * @param enc @@ -90,10 +85,10 @@ public class URLEncoder { * @return the encoded string. * @throws UnsupportedEncodingException * if the specified encoding scheme is invalid. - * @since Android 1.0 */ public static String encode(String s, String enc) throws UnsupportedEncodingException { + if (s == null || enc == null) { throw new NullPointerException(); } diff --git a/libcore/luni/src/main/java/java/net/URLStreamHandler.java b/libcore/luni/src/main/java/java/net/URLStreamHandler.java index f05c8bd7b..54e550b7f 100644 --- a/libcore/luni/src/main/java/java/net/URLStreamHandler.java +++ b/libcore/luni/src/main/java/java/net/URLStreamHandler.java @@ -26,11 +26,8 @@ import org.apache.harmony.luni.util.URLUtil; * The abstract class {@code URLStreamHandler} is the base for all classes which * can handle the communication with a URL object over a particular protocol * type. - * - * @since Android 1.0 */ public abstract class URLStreamHandler { - /** * Establishes a new connection to the resource specified by the URL {@code * u}. Since different protocols also have unique ways of connecting, it @@ -41,7 +38,6 @@ public abstract class URLStreamHandler { * @return the opened URLConnection to the specified resource. * @throws IOException * if an I/O error occurs during opening the connection. - * @since Android 1.0 */ protected abstract URLConnection openConnection(URL u) throws IOException; @@ -62,7 +58,6 @@ public abstract class URLStreamHandler { * wrong. * @throws UnsupportedOperationException * if the protocol handler doesn't support this method. - * @since Android 1.0 */ protected URLConnection openConnection(URL u, Proxy proxy) throws IOException { @@ -74,7 +69,7 @@ public abstract class URLStreamHandler { * generally have the following format: *

          * http://www.company.com/java/file1.java#reference - *

          + *

          * The string is parsed in HTTP format. If the protocol has a different URL * format this method must be overridden. * @@ -88,7 +83,6 @@ public abstract class URLStreamHandler { * the string position to stop parsing. * @see #toExternalForm * @see URL - * @since Android 1.0 */ protected void parseURL(URL u, String str, int start, int end) { // For compatibility, refer to Harmony-2941 @@ -259,7 +253,6 @@ public abstract class URLStreamHandler { * the reference. * @deprecated use setURL(URL, String String, int, String, String, String, * String, String) instead. - * @since Android 1.0 */ @Deprecated protected void setURL(URL u, String protocol, String host, int port, @@ -292,7 +285,6 @@ public abstract class URLStreamHandler { * the query. * @param ref * the reference. - * @since Android 1.0 */ protected void setURL(URL u, String protocol, String host, int port, String authority, String userInfo, String file, String query, @@ -311,11 +303,9 @@ public abstract class URLStreamHandler { * @return the clear text representation of the specified URL. * @see #parseURL * @see URL#toExternalForm() - * @since Android 1.0 */ protected String toExternalForm(URL url) { - StringBuffer answer = new StringBuffer(url.getProtocol().length() - + url.getFile().length() + 16); + StringBuilder answer = new StringBuilder(); answer.append(url.getProtocol()); answer.append(':'); String authority = url.getAuthority(); @@ -326,8 +316,9 @@ public abstract class URLStreamHandler { String file = url.getFile(); String ref = url.getRef(); - // file is never null - answer.append(file); + if (file != null) { + answer.append(file); + } if (ref != null) { answer.append('#'); answer.append(ref); @@ -346,7 +337,6 @@ public abstract class URLStreamHandler { * the second URL to compare. * @return {@code true} if the URLs are the same, {@code false} otherwise. * @see #hashCode - * @since Android 1.0 */ protected boolean equals(URL url1, URL url2) { if (!sameFile(url1, url2)) { @@ -364,9 +354,8 @@ public abstract class URLStreamHandler { /** * Returns the default port of the protocol used by the handled URL. The * current implementation returns always {@code -1}. - * + * * @return the appropriate default port number of the protocol. - * @since Android 1.0 */ protected int getDefaultPort() { return -1; @@ -374,11 +363,10 @@ public abstract class URLStreamHandler { /** * Returns the host address of the given URL. - * + * * @param url * the URL object where to read the host address from. * @return the host address of the specified URL. - * @since Android 1.0 */ protected InetAddress getHostAddress(URL url) { try { @@ -398,7 +386,6 @@ public abstract class URLStreamHandler { * @param url * the URL to determine the hashcode. * @return the hashcode of the given URL. - * @since Android 1.0 */ protected int hashCode(URL url) { return toExternalForm(url).hashCode(); @@ -406,14 +393,13 @@ public abstract class URLStreamHandler { /** * Compares two URL objects whether they refer to the same host. - * + * * @param url1 * the first URL to be compared. * @param url2 * the second URL to be compared. * @return {@code true} if both URLs refer to the same host, {@code false} * otherwise. - * @since Android 1.0 */ protected boolean hostsEqual(URL url1, URL url2) { String host1 = getHost(url1), host2 = getHost(url2); @@ -439,7 +425,6 @@ public abstract class URLStreamHandler { * the second URL to be compared. * @return {@code true} if both URLs refer to the same file, {@code false} * otherwise. - * @since Android 1.0 */ protected boolean sameFile(URL url1, URL url2) { String s1 = url1.getProtocol(); diff --git a/libcore/luni/src/main/java/java/net/URLStreamHandlerFactory.java b/libcore/luni/src/main/java/java/net/URLStreamHandlerFactory.java index e69641011..4e50792b6 100644 --- a/libcore/luni/src/main/java/java/net/URLStreamHandlerFactory.java +++ b/libcore/luni/src/main/java/java/net/URLStreamHandlerFactory.java @@ -20,11 +20,9 @@ package java.net; /** * Defines a factory which creates an {@code URLStreamHandler} for a specified * protocol. It is used by the class {@code URL}. - * - * @since Android 1.0 */ public interface URLStreamHandlerFactory { - + /** * Creates a new {@code URLStreamHandler} instance for the given {@code * protocol}. @@ -32,7 +30,6 @@ public interface URLStreamHandlerFactory { * @param protocol * the protocol for which a handler is needed. * @return the created handler. - * @since Android 1.0 */ URLStreamHandler createURLStreamHandler(String protocol); } diff --git a/libcore/luni/src/main/java/java/net/UnknownHostException.java b/libcore/luni/src/main/java/java/net/UnknownHostException.java index 9f7c63af8..db055587a 100644 --- a/libcore/luni/src/main/java/java/net/UnknownHostException.java +++ b/libcore/luni/src/main/java/java/net/UnknownHostException.java @@ -21,8 +21,6 @@ import java.io.IOException; /** * Is thrown when a hostname can not be resolved. - * - * @since Android 1.0 */ public class UnknownHostException extends IOException { @@ -31,8 +29,6 @@ public class UnknownHostException extends IOException { /** * Constructs a new {@code UnknownHostException} instance with its walkback * filled in. - * - * @since Android 1.0 */ public UnknownHostException() { super(); @@ -44,7 +40,6 @@ public class UnknownHostException extends IOException { * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public UnknownHostException(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/net/UnknownServiceException.java b/libcore/luni/src/main/java/java/net/UnknownServiceException.java index 4865d6dd3..6ad2a19b2 100644 --- a/libcore/luni/src/main/java/java/net/UnknownServiceException.java +++ b/libcore/luni/src/main/java/java/net/UnknownServiceException.java @@ -24,8 +24,6 @@ import java.io.IOException; * particular service requested by the URL connection. This could be happened if * there is an invalid MIME type or the application wants to send data over a * read-only connection. - * - * @since Android 1.0 */ public class UnknownServiceException extends IOException { @@ -34,8 +32,6 @@ public class UnknownServiceException extends IOException { /** * Constructs a new {@code UnknownServiceException} instance with its * walkback filled in. - * - * @since Android 1.0 */ public UnknownServiceException() { super(); @@ -47,7 +43,6 @@ public class UnknownServiceException extends IOException { * * @param detailMessage * the detail message for this exception. - * @since Android 1.0 */ public UnknownServiceException(String detailMessage) { super(detailMessage); diff --git a/libcore/luni/src/main/java/java/util/AbstractCollection.java b/libcore/luni/src/main/java/java/util/AbstractCollection.java index 9f2644586..6511b1151 100644 --- a/libcore/luni/src/main/java/java/util/AbstractCollection.java +++ b/libcore/luni/src/main/java/java/util/AbstractCollection.java @@ -17,7 +17,6 @@ package java.util; - import java.lang.reflect.Array; /** @@ -26,23 +25,18 @@ import java.lang.reflect.Array; * iterator()} and {@code size()} to create an immutable collection. To create a * modifiable collection it's necessary to override the {@code add()} method that * currently throws an {@code UnsupportedOperationException}. - * - * @since Android 1.0 + * + * @since 1.2 */ public abstract class AbstractCollection implements Collection { /** - * Constructs a new instance of this {@code AbstractCollection}. - * - * @since Android 1.0 + * Constructs a new instance of this AbstractCollection. */ protected AbstractCollection() { super(); } - /** - * @see Collection#add - */ public boolean add(E object) { throw new UnsupportedOperationException(); } @@ -64,18 +58,17 @@ public abstract class AbstractCollection implements Collection { * the collection of objects. * @return {@code true} if this {@code Collection} is modified, {@code false} * otherwise. - * @exception UnsupportedOperationException - * when adding to this {@code Collection} is not supported. - * @exception ClassCastException - * when the class of an object is inappropriate for this + * @throws UnsupportedOperationException + * if adding to this {@code Collection} is not supported. + * @throws ClassCastException + * if the class of an object is inappropriate for this * {@code Collection}. - * @exception IllegalArgumentException - * when an object cannot be added to this {@code Collection}. - * @exception NullPointerException - * when {@code collection} is {@code null}, or if it contains + * @throws IllegalArgumentException + * if an object cannot be added to this {@code Collection}. + * @throws NullPointerException + * if {@code collection} is {@code null}, or if it contains * {@code null} elements and this {@code Collection} does not support * such elements. - * @since Android 1.0 */ public boolean addAll(Collection collection) { boolean result = false; @@ -97,13 +90,12 @@ public abstract class AbstractCollection implements Collection { * Concrete implementations usually can clear a {@code Collection} more efficiently * and should therefore overwrite this method. * - * @exception UnsupportedOperationException - * when the iterator does not support removing elements from + * @throws UnsupportedOperationException + * it the iterator does not support removing elements from * this {@code Collection} * @see #iterator * @see #isEmpty * @see #size - * @since Android 1.0 */ public void clear() { Iterator it = iterator(); @@ -126,12 +118,11 @@ public abstract class AbstractCollection implements Collection { * the object to search for. * @return {@code true} if object is an element of this {@code Collection}, {@code * false} otherwise. - * @exception ClassCastException + * @throws ClassCastException * if the object to look for isn't of the correct type. - * @exception NullPointerException + * @throws NullPointerException * if the object to look for is {@code null} and this * {@code Collection} doesn't support {@code null} elements. - * @since Android 1.0 */ public boolean contains(Object object) { Iterator it = iterator(); @@ -161,16 +152,15 @@ public abstract class AbstractCollection implements Collection { * the collection of objects. * @return {@code true} if all objects in the specified {@code Collection} are * elements of this {@code Collection}, {@code false} otherwise. - * @exception ClassCastException + * @throws ClassCastException * if one or more elements of {@code collection} isn't of the * correct type. - * @exception NullPointerException + * @throws NullPointerException * if {@code collection} contains at least one {@code null} * element and this {@code Collection} doesn't support {@code null} * elements. - * @exception NullPointerException + * @throws NullPointerException * if {@code collection} is {@code null}. - * @since Android 1.0 */ public boolean containsAll(Collection collection) { Iterator it = collection.iterator(); @@ -188,9 +178,8 @@ public abstract class AbstractCollection implements Collection { * * @return {@code true} if this {@code Collection} has no elements, {@code false} * otherwise. - * + * * @see #size - * @since Android 1.0 */ public boolean isEmpty() { return size() == 0; @@ -206,7 +195,6 @@ public abstract class AbstractCollection implements Collection { * by concrete {@code Collection} implementations. * * @return an iterator for accessing the {@code Collection} contents. - * @since Android 1.0 */ public abstract Iterator iterator(); @@ -226,14 +214,13 @@ public abstract class AbstractCollection implements Collection { * the object to remove. * @return {@code true} if this {@code Collection} is modified, {@code false} * otherwise. - * @exception UnsupportedOperationException - * when removing from this {@code Collection} is not supported. - * @exception ClassCastException - * when the object passed is not of the correct type. - * @exception NullPointerException + * @throws UnsupportedOperationException + * if removing from this {@code Collection} is not supported. + * @throws ClassCastException + * if the object passed is not of the correct type. + * @throws NullPointerException * if {@code object} is {@code null} and this {@code Collection} * doesn't support {@code null} elements. - * @since Android 1.0 */ public boolean remove(Object object) { Iterator it = iterator(); @@ -272,18 +259,17 @@ public abstract class AbstractCollection implements Collection { * the collection of objects to remove. * @return {@code true} if this {@code Collection} is modified, {@code false} * otherwise. - * @exception UnsupportedOperationException - * when removing from this {@code Collection} is not supported. - * @exception ClassCastException + * @throws UnsupportedOperationException + * if removing from this {@code Collection} is not supported. + * @throws ClassCastException * if one or more elements of {@code collection} isn't of the * correct type. - * @exception NullPointerException + * @throws NullPointerException * if {@code collection} contains at least one {@code null} * element and this {@code Collection} doesn't support {@code null} * elements. - * @exception NullPointerException + * @throws NullPointerException * if {@code collection} is {@code null}. - * @since Android 1.0 */ public boolean removeAll(Collection collection) { boolean result = false; @@ -314,18 +300,17 @@ public abstract class AbstractCollection implements Collection { * the collection of objects to retain. * @return {@code true} if this {@code Collection} is modified, {@code false} * otherwise. - * @exception UnsupportedOperationException - * when removing from this {@code Collection} is not supported. - * @exception ClassCastException + * @throws UnsupportedOperationException + * if removing from this {@code Collection} is not supported. + * @throws ClassCastException * if one or more elements of {@code collection} * isn't of the correct type. - * @exception NullPointerException + * @throws NullPointerException * if {@code collection} contains at least one * {@code null} element and this {@code Collection} doesn't support * {@code null} elements. - * @exception NullPointerException + * @throws NullPointerException * if {@code collection} is {@code null}. - * @since Android 1.0 */ public boolean retainAll(Collection collection) { boolean result = false; @@ -348,7 +333,6 @@ public abstract class AbstractCollection implements Collection { * @return how many objects this {@code Collection} contains, or {@code Integer.MAX_VALUE} * if there are more than {@code Integer.MAX_VALUE} elements in this * {@code Collection}. - * @since Android 1.0 */ public abstract int size(); @@ -367,10 +351,10 @@ public abstract class AbstractCollection implements Collection { int size = size(), index = 0; if (size > contents.length) { Class ct = contents.getClass().getComponentType(); - contents = (T[])Array.newInstance(ct, size); + contents = (T[]) Array.newInstance(ct, size); } - for (E entry: this) { - contents[index++] = (T)entry; + for (E entry : this) { + contents[index++] = (T) entry; } if (index < contents.length) { contents[index] = null; @@ -384,7 +368,6 @@ public abstract class AbstractCollection implements Collection { * are separated by ', ' (comma and space). * * @return the string representation of this {@code Collection}. - * @since Android 1.0 */ @Override public String toString() { @@ -402,7 +385,7 @@ public abstract class AbstractCollection implements Collection { } else { buffer.append("(this Collection)"); //$NON-NLS-1$ } - if(it.hasNext()) { + if (it.hasNext()) { buffer.append(", "); //$NON-NLS-1$ } } diff --git a/libcore/luni/src/main/java/java/util/AbstractList.java b/libcore/luni/src/main/java/java/util/AbstractList.java index cfd8c6f19..8f1e79fee 100644 --- a/libcore/luni/src/main/java/java/util/AbstractList.java +++ b/libcore/luni/src/main/java/java/util/AbstractList.java @@ -24,16 +24,14 @@ package java.util; * methods {@code get()} and {@code size()}, and to create a * modifiable {@code List} it's necessary to override the {@code add()} method that * currently throws an {@code UnsupportedOperationException}. - * - * @since Android 1.0 + * + * @since 1.2 */ public abstract class AbstractList extends AbstractCollection implements List { /** * A counter for changes to the list. - * - * @since Android 1.0 */ protected transient int modCount; @@ -67,22 +65,25 @@ public abstract class AbstractList extends AbstractCollection implements } public void remove() { - if (expectedModCount == modCount) { - try { - AbstractList.this.remove(lastPosition); - } catch (IndexOutOfBoundsException e) { - throw new IllegalStateException(); - } - if (modCount != expectedModCount) { - expectedModCount++; - } - if (pos == lastPosition) { - pos--; - } - lastPosition = -1; - } else { + if (this.lastPosition == -1) { + throw new IllegalStateException(); + } + + if (expectedModCount != modCount) { + throw new ConcurrentModificationException(); + } + + try { + AbstractList.this.remove(lastPosition); + } catch (IndexOutOfBoundsException e) { throw new ConcurrentModificationException(); } + + expectedModCount = modCount; + if (pos == lastPosition) { + pos--; + } + lastPosition = -1; } } @@ -107,7 +108,7 @@ public abstract class AbstractList extends AbstractCollection implements pos++; lastPosition = -1; if (modCount != expectedModCount) { - expectedModCount++; + expectedModCount = modCount; } } else { throw new ConcurrentModificationException(); @@ -359,7 +360,10 @@ public abstract class AbstractList extends AbstractCollection implements @Override public int size() { - return size; + if (modCount == fullList.modCount) { + return size; + } + throw new ConcurrentModificationException(); } void sizeChanged(boolean increment) { @@ -374,8 +378,6 @@ public abstract class AbstractList extends AbstractCollection implements /** * Constructs a new instance of this AbstractList. - * - * @since Android 1.0 */ protected AbstractList() { super(); @@ -386,15 +388,15 @@ public abstract class AbstractList extends AbstractCollection implements * The object is inserted before any previous element at the specified * location. If the location is equal to the size of this List, the object * is added at the end. - * + *

          * Concrete implementations that would like to support the add functionality - * must override this method. - * + * must override this method. + * * @param location * the index at which to insert. * @param object * the object to add. - * + * * @throws UnsupportedOperationException * if adding to this List is not supported. * @throws ClassCastException @@ -404,7 +406,6 @@ public abstract class AbstractList extends AbstractCollection implements * if the object cannot be added to this List * @throws IndexOutOfBoundsException * if {@code location < 0 || >= size()} - * @since Android 1.0 */ public void add(int location, E object) { throw new UnsupportedOperationException(); @@ -412,12 +413,12 @@ public abstract class AbstractList extends AbstractCollection implements /** * Adds the specified object at the end of this List. - * - * + * + * * @param object * the object to add * @return true - * + * * @throws UnsupportedOperationException * if adding to this List is not supported * @throws ClassCastException @@ -425,7 +426,6 @@ public abstract class AbstractList extends AbstractCollection implements * List * @throws IllegalArgumentException * if the object cannot be added to this List - * @since Android 1.0 */ @Override public boolean add(E object) { @@ -437,7 +437,7 @@ public abstract class AbstractList extends AbstractCollection implements * Inserts the objects in the specified Collection at the specified location * in this List. The objects are added in the order they are returned from * the collection's iterator. - * + * * @param location * the index at which to insert. * @param collection @@ -451,7 +451,6 @@ public abstract class AbstractList extends AbstractCollection implements * if an object cannot be added to this list. * @throws IndexOutOfBoundsException * if {@code location < 0 || > size()} - * @since Android 1.0 */ public boolean addAll(int location, Collection collection) { Iterator it = collection.iterator(); @@ -463,12 +462,11 @@ public abstract class AbstractList extends AbstractCollection implements /** * Removes all elements from this list, leaving it empty. - * + * * @throws UnsupportedOperationException * if removing from this list is not supported. * @see List#isEmpty * @see List#size - * @since Android 1.0 */ @Override public void clear() { @@ -479,13 +477,12 @@ public abstract class AbstractList extends AbstractCollection implements * Compares the specified object to this list and return true if they are * equal. Two lists are equal when they both contain the same objects in the * same order. - * + * * @param object * the object to compare to this object. * @return {@code true} if the specified object is equal to this list, * {@code false} otherwise. * @see #hashCode - * @since Android 1.0 */ @Override public boolean equals(Object object) { @@ -512,24 +509,22 @@ public abstract class AbstractList extends AbstractCollection implements /** * Returns the element at the specified location in this list. - * + * * @param location * the index of the element to return. * @return the element at the specified index. * @throws IndexOutOfBoundsException * if {@code location < 0 || >= size()} - * @since Android 1.0 */ public abstract E get(int location); /** * Returns the hash code of this list. The hash code is calculated by taking * each element's hashcode into account. - * + * * @return the hash code. * @see #equals * @see List#hashCode() - * @since Android 1.0 */ @Override public int hashCode() { @@ -545,12 +540,11 @@ public abstract class AbstractList extends AbstractCollection implements /** * Searches this list for the specified object and returns the index of the * first occurrence. - * + * * @param object * the object to search for. * @return the index of the first occurrence of the object, or -1 if it was * not found. - * @since Android 1.0 */ public int indexOf(Object object) { ListIterator it = listIterator(); @@ -573,10 +567,9 @@ public abstract class AbstractList extends AbstractCollection implements /** * Returns an iterator on the elements of this list. The elements are * iterated in the same order as they occur in the list. - * + * * @return an iterator on the elements of this list. * @see Iterator - * @since Android 1.0 */ @Override public Iterator iterator() { @@ -586,12 +579,11 @@ public abstract class AbstractList extends AbstractCollection implements /** * Searches this list for the specified object and returns the index of the * last occurrence. - * + * * @param object * the object to search for. * @return the index of the last occurrence of the object, or -1 if the * object was not found. - * @since Android 1.0 */ public int lastIndexOf(Object object) { ListIterator it = listIterator(size()); @@ -614,10 +606,9 @@ public abstract class AbstractList extends AbstractCollection implements /** * Returns a ListIterator on the elements of this list. The elements are * iterated in the same order that they occur in the list. - * + * * @return a ListIterator on the elements of this list * @see ListIterator - * @since Android 1.0 */ public ListIterator listIterator() { return listIterator(0); @@ -627,14 +618,13 @@ public abstract class AbstractList extends AbstractCollection implements * Returns a list iterator on the elements of this list. The elements are * iterated in the same order as they occur in the list. The iteration * starts at the specified location. - * + * * @param location * the index at which to start the iteration. * @return a ListIterator on the elements of this list. * @throws IndexOutOfBoundsException * if {@code location < 0 || location > size()} * @see ListIterator - * @since Android 1.0 */ public ListIterator listIterator(int location) { return new FullListIterator(location); @@ -642,7 +632,7 @@ public abstract class AbstractList extends AbstractCollection implements /** * Removes the object at the specified location from this list. - * + * * @param location * the index of the object to remove. * @return the removed object. @@ -650,7 +640,6 @@ public abstract class AbstractList extends AbstractCollection implements * if removing from this list is not supported. * @throws IndexOutOfBoundsException * if {@code location < 0 || >= size()} - * @since Android 1.0 */ public E remove(int location) { throw new UnsupportedOperationException(); @@ -659,7 +648,7 @@ public abstract class AbstractList extends AbstractCollection implements /** * Removes the objects in the specified range from the start to the end * index minus one. - * + * * @param start * the index at which to start removing. * @param end @@ -668,7 +657,6 @@ public abstract class AbstractList extends AbstractCollection implements * if removing from this list is not supported. * @throws IndexOutOfBoundsException * if {@code start < 0} or {@code start >= size()}. - * @since Android 1.0 */ protected void removeRange(int start, int end) { Iterator it = listIterator(start); @@ -681,7 +669,7 @@ public abstract class AbstractList extends AbstractCollection implements /** * Replaces the element at the specified location in this list with the * specified object. - * + * * @param location * the index at which to put the specified object. * @param object @@ -695,7 +683,6 @@ public abstract class AbstractList extends AbstractCollection implements * if an object cannot be added to this list. * @throws IndexOutOfBoundsException * if {@code location < 0 || >= size()} - * @since Android 1.0 */ public E set(int location, E object) { throw new UnsupportedOperationException(); @@ -733,7 +720,7 @@ public abstract class AbstractList extends AbstractCollection implements *

          * All methods will throw a ConcurrentModificationException if the modCount * of the original list is not equal to the expected value. - * + * * @param start * start index of the subList (inclusive). * @param end @@ -744,7 +731,6 @@ public abstract class AbstractList extends AbstractCollection implements * if (start < 0 || end > size()) * @throws IllegalArgumentException * if (start > end) - * @since Android 1.0 */ public List subList(int start, int end) { if (0 <= start && end <= size()) { diff --git a/libcore/luni/src/main/java/java/util/AbstractMap.java b/libcore/luni/src/main/java/java/util/AbstractMap.java index 00266a71a..b687e7264 100644 --- a/libcore/luni/src/main/java/java/util/AbstractMap.java +++ b/libcore/luni/src/main/java/java/util/AbstractMap.java @@ -17,17 +17,12 @@ package java.util; -// BEGIN android-added -// copied from newer harmony -import java.util.Map.Entry; -// END android-added - /** * This class is an abstract implementation of the {@code Map} interface. This * implementation does not support adding. A subclass must implement the * abstract method entrySet(). - * - * @since Android 1.0 + * + * @since 1.2 */ public abstract class AbstractMap implements Map { @@ -38,8 +33,6 @@ public abstract class AbstractMap implements Map { /** * Constructs a new instance of this {@code AbstractMap}. - * - * @since Android 1.0 */ protected AbstractMap() { super(); @@ -47,12 +40,11 @@ public abstract class AbstractMap implements Map { /** * Removes all elements from this map, leaving it empty. - * + * * @throws UnsupportedOperationException * if removing from this map is not supported. * @see #isEmpty() * @see #size() - * @since Android 1.0 */ public void clear() { entrySet().clear(); @@ -60,12 +52,11 @@ public abstract class AbstractMap implements Map { /** * Returns whether this map contains the specified key. - * + * * @param key * the key to search for. * @return {@code true} if this map contains the specified key, * {@code false} otherwise. - * @since Android 1.0 */ public boolean containsKey(Object key) { Iterator> it = entrySet().iterator(); @@ -87,12 +78,11 @@ public abstract class AbstractMap implements Map { /** * Returns whether this map contains the specified value. - * + * * @param value * the value to search for. * @return {@code true} if this map contains the specified value, * {@code false} otherwise. - * @since Android 1.0 */ public boolean containsValue(Object value) { Iterator> it = entrySet().iterator(); @@ -116,23 +106,21 @@ public abstract class AbstractMap implements Map { * Returns a set containing all of the mappings in this map. Each mapping is * an instance of {@link Map.Entry}. As the set is backed by this map, * changes in one will be reflected in the other. - * + * * @return a set of the mappings. - * @since Android 1.0 */ public abstract Set> entrySet(); /** * Compares the specified object to this instance, and returns {@code true} * if the specified object is a map and both maps contain the same mappings. - * + * * @param object * the object to compare with this object. * @return boolean {@code true} if the object is the same as this object, * and {@code false} if it is different from this object. * @see #hashCode() * @see #entrySet() - * @since Android 1.0 */ @Override public boolean equals(Object object) { @@ -145,19 +133,21 @@ public abstract class AbstractMap implements Map { return false; } - // BEGIN android-changed - // copied from newer version of harmony Iterator> it = entrySet().iterator(); - while (it.hasNext()) { - Entry entry = it.next(); - K key = entry.getKey(); - V value = entry.getValue(); - Object obj = map.get(key); - if( null != obj && (!obj.equals(value)) || null == obj && obj != value) { - return false; + + try { + while (it.hasNext()) { + Entry entry = it.next(); + K key = entry.getKey(); + V value = entry.getValue(); + Object obj = map.get(key); + if( null != obj && (!obj.equals(value)) || null == obj && obj != value) { + return false; + } } + } catch (ClassCastException cce) { + return false; } - // END android-changed return true; } return false; @@ -165,12 +155,11 @@ public abstract class AbstractMap implements Map { /** * Returns the value of the mapping with the specified key. - * + * * @param key * the key. * @return the value of the mapping with the specified key, or {@code null} * if no mapping for the specified key is found. - * @since Android 1.0 */ public V get(Object key) { Iterator> it = entrySet().iterator(); @@ -195,10 +184,9 @@ public abstract class AbstractMap implements Map { /** * Returns the hash code for this object. Objects which are equal must * return the same value for this method. - * + * * @return the hash code of this object. * @see #equals(Object) - * @since Android 1.0 */ @Override public int hashCode() { @@ -212,11 +200,10 @@ public abstract class AbstractMap implements Map { /** * Returns whether this map is empty. - * + * * @return {@code true} if this map has no elements, {@code false} * otherwise. * @see #size() - * @since Android 1.0 */ public boolean isEmpty() { return size() == 0; @@ -226,9 +213,8 @@ public abstract class AbstractMap implements Map { * Returns a set of the keys contained in this map. The set is backed by * this map so changes to one are reflected by the other. The returned set * does not support adding. - * + * * @return a set of the keys. - * @since Android 1.0 */ public Set keySet() { if (keySet == null) { @@ -269,7 +255,7 @@ public abstract class AbstractMap implements Map { /** * Maps the specified key to the specified value. - * + * * @param key * the key. * @param value @@ -286,7 +272,6 @@ public abstract class AbstractMap implements Map { * @throws NullPointerException * if the key or value is {@code null} and this Map does not * support {@code null} keys or values. - * @since Android 1.0 */ public V put(K key, V value) { throw new UnsupportedOperationException(); @@ -294,7 +279,7 @@ public abstract class AbstractMap implements Map { /** * Copies every mapping in the specified map to this map. - * + * * @param map * the map to copy mappings from. * @throws UnsupportedOperationException @@ -307,7 +292,6 @@ public abstract class AbstractMap implements Map { * @throws NullPointerException * if a key or value is {@code null} and this map does not * support {@code null} keys or values. - * @since Android 1.0 */ public void putAll(Map map) { for (Map.Entry entry : map.entrySet()) { @@ -317,14 +301,13 @@ public abstract class AbstractMap implements Map { /** * Removes a mapping with the specified key from this Map. - * + * * @param key * the key of the mapping to remove. * @return the value of the removed mapping or {@code null} if no mapping * for the specified key was found. * @throws UnsupportedOperationException * if removing from this map is not supported. - * @since Android 1.0 */ public V remove(Object key) { Iterator> it = entrySet().iterator(); @@ -350,9 +333,8 @@ public abstract class AbstractMap implements Map { /** * Returns the number of elements in this map. - * + * * @return the number of elements in this map. - * @since Android 1.0 */ public int size() { return entrySet().size(); @@ -360,9 +342,8 @@ public abstract class AbstractMap implements Map { /** * Returns the string representation of this map. - * + * * @return the string representation of this map. - * @since Android 1.0 */ @Override public String toString() { @@ -407,16 +388,13 @@ public abstract class AbstractMap implements Map { * "wrapper object" over the iterator of map's entrySet(). The {@code size} * method wraps the map's size method and the {@code contains} method wraps * the map's containsValue method. - *

          *

          * The collection is created when this method is called for the first time * and returned in response to all subsequent calls. This method may return * different collections when multiple concurrent calls occur to this * method, since no synchronization is performed. - *

          - * + * * @return a collection of the values contained in this map. - * @since Android 1.0 */ public Collection values() { if (valuesCollection == null) { @@ -458,12 +436,11 @@ public abstract class AbstractMap implements Map { /** * Returns a new instance of the same class as this instance, whose slots * have been filled in with the values of the slots of this instance. - * + * * @return a shallow copy of this object. * @throws CloneNotSupportedException * if the receiver's class does not implement the interface * {@code Cloneable}. - * @since Android 1.0 */ @Override @SuppressWarnings("unchecked") diff --git a/libcore/luni/src/main/java/java/util/AbstractQueue.java b/libcore/luni/src/main/java/java/util/AbstractQueue.java index 0b9bdf1ba..4b4fccf79 100644 --- a/libcore/luni/src/main/java/java/util/AbstractQueue.java +++ b/libcore/luni/src/main/java/java/util/AbstractQueue.java @@ -22,18 +22,15 @@ package java.util; * {@code element} are based on {@code offer, poll}, and {@code peek} except * that they throw exceptions to indicate some error instead of returning true * or false. - * + * * @param * the type of the element in the collection. - * @since Android 1.0 */ public abstract class AbstractQueue extends AbstractCollection implements Queue { /** * Constructor to be used by subclasses. - * - * @since Android 1.0 */ protected AbstractQueue() { super(); @@ -47,7 +44,6 @@ public abstract class AbstractQueue extends AbstractCollection implements * @return {@code true} if the operation succeeds, otherwise {@code false}. * @throws IllegalStateException * if the element is not allowed to be added to the queue. - * @since Android 1.0 */ @Override public boolean add(E o) { @@ -76,7 +72,6 @@ public abstract class AbstractQueue extends AbstractCollection implements * @throws IllegalArgumentException * If the collection to be added to the queue is the queue * itself. - * @since Android 1.0 */ @Override public boolean addAll(Collection c) { @@ -95,7 +90,6 @@ public abstract class AbstractQueue extends AbstractCollection implements * @return the element at the head of the queue. * @throws NoSuchElementException * if the queue is empty. - * @since Android 1.0 */ public E remove() { E o = poll(); @@ -111,7 +105,6 @@ public abstract class AbstractQueue extends AbstractCollection implements * @return the element at the head of the queue. * @throws NoSuchElementException * if the queue is empty. - * @since Android 1.0 */ public E element() { E o = peek(); @@ -123,8 +116,6 @@ public abstract class AbstractQueue extends AbstractCollection implements /** * Removes all elements of the queue, leaving it empty. - * - * @since Android 1.0 */ @Override public void clear() { diff --git a/libcore/luni/src/main/java/java/util/AbstractSequentialList.java b/libcore/luni/src/main/java/java/util/AbstractSequentialList.java index 4ca8f7b7c..dcdecaa3d 100644 --- a/libcore/luni/src/main/java/java/util/AbstractSequentialList.java +++ b/libcore/luni/src/main/java/java/util/AbstractSequentialList.java @@ -17,20 +17,17 @@ package java.util; - /** * AbstractSequentialList is an abstract implementation of the List interface. * This implementation does not support adding. A subclass must implement the * abstract method listIterator(). - * - * @since Android 1.0 + * + * @since 1.2 */ public abstract class AbstractSequentialList extends AbstractList { /** * Constructs a new instance of this AbstractSequentialList. - * - * @since Android 1.0 */ protected AbstractSequentialList() { super(); @@ -45,24 +42,11 @@ public abstract class AbstractSequentialList extends AbstractList { public boolean addAll(int location, Collection collection) { ListIterator it = listIterator(location); Iterator colIt = collection.iterator(); - // BEGIN android-removed - // int next = it.nextIndex(); - // while (colIt.hasNext()) { - // it.add(colIt.next()); - // it.previous(); - // } - // return next != it.nextIndex(); - // END android-removed - - // BEGIN android-added - // BUG: previous/next inconsistant. We care for list - // modification NOT iterator modification. - int size = this.size(); + int next = it.nextIndex(); while (colIt.hasNext()) { it.add(colIt.next()); } - return size != this.size(); - // END android-added + return next != it.nextIndex(); } @Override @@ -97,6 +81,9 @@ public abstract class AbstractSequentialList extends AbstractList { @Override public E set(int location, E object) { ListIterator it = listIterator(location); + if (!it.hasNext()) { + throw new IndexOutOfBoundsException(); + } E result = it.next(); it.set(object); return result; diff --git a/libcore/luni/src/main/java/java/util/AbstractSet.java b/libcore/luni/src/main/java/java/util/AbstractSet.java index a19b1f724..e6ef0bbe2 100644 --- a/libcore/luni/src/main/java/java/util/AbstractSet.java +++ b/libcore/luni/src/main/java/java/util/AbstractSet.java @@ -22,15 +22,13 @@ package java.util; * implementation does not support adding. A subclass must implement the * abstract methods iterator() and size(). * - * @since Android 1.0 + * @since 1.2 */ public abstract class AbstractSet extends AbstractCollection implements Set { /** * Constructs a new instance of this AbstractSet. - * - * @since Android 1.0 */ protected AbstractSet() { super(); @@ -46,7 +44,6 @@ public abstract class AbstractSet extends AbstractCollection implements * @return {@code true} if the specified object is equal to this set, * {@code false} otherwise * @see #hashCode - * @since Android 1.0 */ @Override public boolean equals(Object object) { @@ -55,14 +52,12 @@ public abstract class AbstractSet extends AbstractCollection implements } if (object instanceof Set) { Set s = (Set) object; - // BEGIN android-changed - // copied from a newer version of harmony + try { return size() == s.size() && containsAll(s); } catch (ClassCastException cce) { return false; } - // END android-changed } return false; } @@ -74,7 +69,6 @@ public abstract class AbstractSet extends AbstractCollection implements * * @return the hash code of this set. * @see #equals - * @since Android 1.0 */ @Override public int hashCode() { @@ -95,9 +89,8 @@ public abstract class AbstractSet extends AbstractCollection implements * the collection of objects to remove. * @return {@code true} if this collection was modified, {@code false} * otherwise. - * @exception UnsupportedOperationException - * when removing from this collection is not supported. - * @since Android 1.0 + * @throws UnsupportedOperationException + * if removing from this collection is not supported. */ @Override public boolean removeAll(Collection collection) { diff --git a/libcore/luni/src/main/java/java/util/ArrayList.java b/libcore/luni/src/main/java/java/util/ArrayList.java index 78f969084..b8c705633 100644 --- a/libcore/luni/src/main/java/java/util/ArrayList.java +++ b/libcore/luni/src/main/java/java/util/ArrayList.java @@ -17,7 +17,6 @@ package java.util; - import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; @@ -29,11 +28,11 @@ import java.lang.reflect.Array; * ArrayList is an implementation of {@link List}, backed by an array. All * optional operations adding, removing, and replacing are supported. The * elements can be any objects. - * - * @since Android 1.0 + * + * @since 1.2 */ -public class ArrayList extends AbstractList implements List, Cloneable, - Serializable, RandomAccess { +public class ArrayList extends AbstractList implements List, + Cloneable, Serializable, RandomAccess { private static final long serialVersionUID = 8683452581122892189L; @@ -43,53 +42,54 @@ public class ArrayList extends AbstractList implements List, Cloneable, // END android-added private transient int firstIndex; - + private transient int lastIndex; private transient E[] array; /** * Constructs a new instance of {@code ArrayList} with zero capacity. - * - * @since Android 1.0 */ public ArrayList() { + // BEGIN android-changed + // default capacity is zero, not ten this(0); + // END android-changed } /** * Constructs a new instance of {@code ArrayList} with the specified * capacity. - * + * * @param capacity * the initial capacity of this {@code ArrayList}. - * @since Android 1.0 */ public ArrayList(int capacity) { - firstIndex = lastIndex = 0; - try { - array = newElementArray(capacity); - } catch (NegativeArraySizeException e) { + if (capacity < 0) { throw new IllegalArgumentException(); } + firstIndex = lastIndex = 0; + array = newElementArray(capacity); } /** * Constructs a new instance of {@code ArrayList} containing the elements of * the specified collection. The initial size of the {@code ArrayList} will * be 10% higher than the size of the specified collection. - * + * * @param collection * the collection of elements to add. - * @since Android 1.0 */ public ArrayList(Collection collection) { - int size = collection.size(); - firstIndex = lastIndex = 0; + firstIndex = 0; + Object[] objects = collection.toArray(); + int size = objects.length; array = newElementArray(size + (size / 10)); - addAll(collection); + System.arraycopy(objects, 0, array, 0, size); + lastIndex = size; + modCount = 1; } - + @SuppressWarnings("unchecked") private E[] newElementArray(int size) { // BEGIN android-added @@ -98,7 +98,7 @@ public class ArrayList extends AbstractList implements List, Cloneable, } // END android-added - return (E[])new Object[size]; + return (E[]) new Object[size]; } /** @@ -106,20 +106,17 @@ public class ArrayList extends AbstractList implements List, Cloneable, * location. The object is inserted before any previous element at the * specified location. If the location is equal to the size of this * {@code ArrayList}, the object is added at the end. - * + * * @param location * the index at which to insert the object. * @param object * the object to add. * @throws IndexOutOfBoundsException * when {@code location < 0 || > size()} - * @since Android 1.0 */ @Override public void add(int location, E object) { - // BEGIN android-changed: slight performance improvement int size = lastIndex - firstIndex; - // END android-changed if (0 < location && location < size) { if (firstIndex == 0 && lastIndex == array.length) { growForInsert(location, 1); @@ -153,11 +150,10 @@ public class ArrayList extends AbstractList implements List, Cloneable, /** * Adds the specified object at the end of this {@code ArrayList}. - * + * * @param object * the object to add. * @return always true - * @since Android 1.0 */ @Override public boolean add(E object) { @@ -173,7 +169,7 @@ public class ArrayList extends AbstractList implements List, Cloneable, * Inserts the objects in the specified collection at the specified location * in this List. The objects are added in the order they are returned from * the collection's iterator. - * + * * @param location * the index at which to insert. * @param collection @@ -182,15 +178,22 @@ public class ArrayList extends AbstractList implements List, Cloneable, * otherwise. * @throws IndexOutOfBoundsException * when {@code location < 0 || > size()} - * @since Android 1.0 */ @Override public boolean addAll(int location, Collection collection) { - int size = size(); + int size = lastIndex - firstIndex; if (location < 0 || location > size) { throw new IndexOutOfBoundsException(); } - int growSize = collection.size(); + if (this == collection) { + collection = (ArrayList)clone(); + } + Object[] dumparray = collection.toArray(); + int growSize = dumparray.length; + if (growSize == 0) { + return false; + } + if (0 < location && location < size) { if (array.length - size < growSize) { growForInsert(location, growSize); @@ -222,52 +225,40 @@ public class ArrayList extends AbstractList implements List, Cloneable, lastIndex += growSize; } - if (growSize > 0) { - Iterator it = collection.iterator(); - int index = location + firstIndex; - int end = index + growSize; - while (index < end) { - array[index++] = it.next(); - } - modCount++; - return true; - } - return false; + System.arraycopy(dumparray, 0, this.array, location + firstIndex, + growSize); + modCount++; + return true; } /** * Adds the objects in the specified collection to this {@code ArrayList}. - * + * * @param collection * the collection of objects. * @return {@code true} if this {@code ArrayList} is modified, {@code false} * otherwise. - * @since Android 1.0 */ @Override public boolean addAll(Collection collection) { - int growSize = collection.size(); - if (growSize > 0) { - if (lastIndex > array.length - growSize) { - growAtEnd(growSize); - } - Iterator it = collection.iterator(); - int end = lastIndex + growSize; - while (lastIndex < end) { - array[lastIndex++] = it.next(); - } - modCount++; - return true; + Object[] dumpArray = collection.toArray(); + if (dumpArray.length == 0) { + return false; } - return false; + if (dumpArray.length > array.length - lastIndex) { + growAtEnd(dumpArray.length); + } + System.arraycopy(dumpArray, 0, this.array, lastIndex, dumpArray.length); + lastIndex += dumpArray.length; + modCount++; + return true; } /** * Removes all elements from this {@code ArrayList}, leaving it empty. - * + * * @see #isEmpty * @see #size - * @since Android 1.0 */ @Override public void clear() { @@ -281,10 +272,9 @@ public class ArrayList extends AbstractList implements List, Cloneable, /** * Returns a new {@code ArrayList} with the same elements, the same size and * the same capacity as this {@code ArrayList}. - * + * * @return a shallow copy of this {@code ArrayList} * @see java.lang.Cloneable - * @since Android 1.0 */ @Override @SuppressWarnings("unchecked") @@ -300,12 +290,11 @@ public class ArrayList extends AbstractList implements List, Cloneable, /** * Searches this {@code ArrayList} for the specified object. - * + * * @param object * the object to search for. * @return {@code true} if {@code object} is an element of this * {@code ArrayList}, {@code false} otherwise - * @since Android 1.0 */ @Override public boolean contains(Object object) { @@ -328,10 +317,9 @@ public class ArrayList extends AbstractList implements List, Cloneable, /** * Ensures that after this operation the {@code ArrayList} can hold the * specified number of elements without further growing. - * + * * @param minimumCapacity * the minimum capacity asked for. - * @since Android 1.0 */ public void ensureCapacity(int minimumCapacity) { if (array.length < minimumCapacity) { @@ -356,7 +344,7 @@ public class ArrayList extends AbstractList implements List, Cloneable, } private void growAtEnd(int required) { - int size = size(); + int size = lastIndex - firstIndex; if (firstIndex >= required - (array.length - lastIndex)) { int newLast = lastIndex - firstIndex; if (size > 0) { @@ -385,8 +373,8 @@ public class ArrayList extends AbstractList implements List, Cloneable, } private void growAtFront(int required) { - int size = size(); - if (array.length - lastIndex >= required) { + int size = lastIndex - firstIndex; + if (array.length - lastIndex + firstIndex >= required) { int newFirst = array.length - size; if (size > 0) { System.arraycopy(array, firstIndex, array, newFirst, size); @@ -416,7 +404,8 @@ public class ArrayList extends AbstractList implements List, Cloneable, } private void growForInsert(int location, int required) { - int size = size(), increment = size / 2; + int size = lastIndex - firstIndex; + int increment = size / 2; if (required > increment) { increment = required; } @@ -480,20 +469,17 @@ public class ArrayList extends AbstractList implements List, Cloneable, /** * Removes the object at the specified location from this list. - * + * * @param location * the index of the object to remove. * @return the removed object. * @throws IndexOutOfBoundsException * when {@code location < 0 || >= size()} - * @since Android 1.0 */ @Override public E remove(int location) { E result; - // BEGIN android-changed: slight performance improvement int size = lastIndex - firstIndex; - // END android-changed if (0 <= location && location < size) { if (location == size - 1) { result = array[--lastIndex]; @@ -514,6 +500,9 @@ public class ArrayList extends AbstractList implements List, Cloneable, array[--lastIndex] = null; } } + if (firstIndex == lastIndex) { + firstIndex = lastIndex = 0; + } } else { throw new IndexOutOfBoundsException(); } @@ -522,41 +511,34 @@ public class ArrayList extends AbstractList implements List, Cloneable, return result; } - // BEGIN android-added - /* - * The remove(Object) implementation from AbstractCollection creates - * a new Iterator on every remove and ends up calling remove(int). - */ @Override public boolean remove(Object object) { - int index = indexOf(object); - if (index >= 0) { - remove(index); + int location = indexOf(object); + if (location >= 0) { + remove(location); return true; } return false; } - // END android-added /** * Removes the objects in the specified range from the start to the end, but * not including the end index. - * + * * @param start * the index at which to start removing. * @param end * the index one after the end of the range to remove. * @throws IndexOutOfBoundsException * when {@code start < 0, start > end} or {@code end > size()} - * @since Android 1.0 */ @Override protected void removeRange(int start, int end) { - if (start >= 0 && start <= end && end <= size()) { + if (start >= 0 && start <= end && end <= (lastIndex - firstIndex)) { if (start == end) { return; } - int size = size(); + int size = lastIndex - firstIndex; if (end == size) { Arrays.fill(array, firstIndex + start, lastIndex, null); lastIndex = firstIndex + start; @@ -579,7 +561,7 @@ public class ArrayList extends AbstractList implements List, Cloneable, /** * Replaces the element at the specified location in this {@code ArrayList} * with the specified object. - * + * * @param location * the index at which to put the specified object. * @param object @@ -587,13 +569,10 @@ public class ArrayList extends AbstractList implements List, Cloneable, * @return the previous element at the index. * @throws IndexOutOfBoundsException * when {@code location < 0 || >= size()} - * @since Android 1.0 */ @Override public E set(int location, E object) { - // BEGIN android-changed: slight performance improvement if (0 <= location && location < (lastIndex - firstIndex)) { - // END android-changed E result = array[firstIndex + location]; array[firstIndex + location] = object; return result; @@ -603,9 +582,8 @@ public class ArrayList extends AbstractList implements List, Cloneable, /** * Returns the number of elements in this {@code ArrayList}. - * + * * @return the number of elements in this {@code ArrayList}. - * @since Android 1.0 */ @Override public int size() { @@ -615,13 +593,12 @@ public class ArrayList extends AbstractList implements List, Cloneable, /** * Returns a new array containing all elements contained in this * {@code ArrayList}. - * + * * @return an array of the elements from this {@code ArrayList} - * @since Android 1.0 */ @Override public Object[] toArray() { - int size = size(); + int size = lastIndex - firstIndex; Object[] result = new Object[size]; System.arraycopy(array, firstIndex, result, 0, size); return result; @@ -634,19 +611,18 @@ public class ArrayList extends AbstractList implements List, Cloneable, * type is created. If the specified array is used and is larger than this * {@code ArrayList}, the array element following the collection elements * is set to null. - * + * * @param contents * the array. * @return an array of the elements from this {@code ArrayList}. * @throws ArrayStoreException * when the type of an element in this {@code ArrayList} cannot * be stored in the type of the specified array. - * @since Android 1.0 */ @Override @SuppressWarnings("unchecked") public T[] toArray(T[] contents) { - int size = size(); + int size = lastIndex - firstIndex; if (size > contents.length) { Class ct = contents.getClass().getComponentType(); contents = (T[]) Array.newInstance(ct, size); @@ -661,17 +637,17 @@ public class ArrayList extends AbstractList implements List, Cloneable, /** * Sets the capacity of this {@code ArrayList} to be the same as the current * size. - * + * * @see #size - * @since Android 1.0 */ public void trimToSize() { - int size = size(); + int size = lastIndex - firstIndex; E[] newArray = newElementArray(size); System.arraycopy(array, firstIndex, newArray, 0, size); array = newArray; firstIndex = 0; lastIndex = array.length; + modCount = 0; } private static final ObjectStreamField[] serialPersistentFields = { new ObjectStreamField( @@ -679,7 +655,7 @@ public class ArrayList extends AbstractList implements List, Cloneable, private void writeObject(ObjectOutputStream stream) throws IOException { ObjectOutputStream.PutField fields = stream.putFields(); - fields.put("size", size()); //$NON-NLS-1$ + fields.put("size", lastIndex - firstIndex); //$NON-NLS-1$ stream.writeFields(); stream.writeInt(array.length); Iterator it = iterator(); @@ -695,7 +671,7 @@ public class ArrayList extends AbstractList implements List, Cloneable, lastIndex = fields.get("size", 0); //$NON-NLS-1$ array = newElementArray(stream.readInt()); for (int i = 0; i < lastIndex; i++) { - array[i] = (E)stream.readObject(); + array[i] = (E) stream.readObject(); } } } diff --git a/libcore/luni/src/main/java/java/util/Arrays.java b/libcore/luni/src/main/java/java/util/Arrays.java index d47994525..4fc1e852e 100644 --- a/libcore/luni/src/main/java/java/util/Arrays.java +++ b/libcore/luni/src/main/java/java/util/Arrays.java @@ -22,11 +22,16 @@ import java.lang.reflect.Array; /** * {@code Arrays} contains static methods which operate on arrays. - * - * @since Android 1.0 + * + * @since 1.2 */ public class Arrays { + // BEGIN android-removed + /* Specifies when to switch to insertion sort */ + // private static final int SIMPLE_LENGTH = 7; + // END android-removed + private static class ArrayList extends AbstractList implements List, Serializable, RandomAccess { @@ -152,11 +157,10 @@ public class Arrays { * {@code List} cannot be modified, i.e. adding and removing are unsupported, but * the elements can be set. Setting an element modifies the underlying * array. - * + * * @param array * the array. * @return a {@code List} of the elements of the specified array. - * @since Android 1.0 */ public static List asList(T... array) { return new ArrayList(array); @@ -167,19 +171,18 @@ public class Arrays { * ascending sorted array. Searching in an unsorted array has an undefined * result. It's also undefined which element is found if there are multiple * occurrences of the same element. - * + * * @param array * the sorted {@code byte} array to search. * @param value * the {@code byte} element to find. * @return the non-negative index of the element, or a negative index which * is {@code -index - 1} where the element would be inserted. - * @since Android 1.0 */ public static int binarySearch(byte[] array, byte value) { int low = 0, mid = -1, high = array.length - 1; while (low <= high) { - mid = (low + high) >> 1; + mid = (low + high) >>> 1; if (value > array[mid]) { low = mid + 1; } else if (value == array[mid]) { @@ -200,19 +203,18 @@ public class Arrays { * ascending sorted array. Searching in an unsorted array has an undefined * result. It's also undefined which element is found if there are multiple * occurrences of the same element. - * + * * @param array * the sorted {@code char} array to search. * @param value * the {@code char} element to find. * @return the non-negative index of the element, or a negative index which * is {@code -index - 1} where the element would be inserted. - * @since Android 1.0 */ public static int binarySearch(char[] array, char value) { int low = 0, mid = -1, high = array.length - 1; while (low <= high) { - mid = (low + high) >> 1; + mid = (low + high) >>> 1; if (value > array[mid]) { low = mid + 1; } else if (value == array[mid]) { @@ -232,20 +234,19 @@ public class Arrays { * ascending sorted array. Searching in an unsorted array has an undefined * result. It's also undefined which element is found if there are multiple * occurrences of the same element. - * + * * @param array * the sorted {@code double} array to search. * @param value * the {@code double} element to find. * @return the non-negative index of the element, or a negative index which * is {@code -index - 1} where the element would be inserted. - * @since Android 1.0 */ public static int binarySearch(double[] array, double value) { long longBits = Double.doubleToLongBits(value); int low = 0, mid = -1, high = array.length - 1; while (low <= high) { - mid = (low + high) >> 1; + mid = (low + high) >>> 1; if (lessThan(array[mid], value)) { low = mid + 1; } else if (longBits == Double.doubleToLongBits(array[mid])) { @@ -265,20 +266,19 @@ public class Arrays { * ascending sorted array. Searching in an unsorted array has an undefined * result. It's also undefined which element is found if there are multiple * occurrences of the same element. - * + * * @param array * the sorted {@code float} array to search. * @param value * the {@code float} element to find. * @return the non-negative index of the element, or a negative index which * is {@code -index - 1} where the element would be inserted. - * @since Android 1.0 */ public static int binarySearch(float[] array, float value) { int intBits = Float.floatToIntBits(value); int low = 0, mid = -1, high = array.length - 1; while (low <= high) { - mid = (low + high) >> 1; + mid = (low + high) >>> 1; if (lessThan(array[mid], value)) { low = mid + 1; } else if (intBits == Float.floatToIntBits(array[mid])) { @@ -298,19 +298,18 @@ public class Arrays { * ascending sorted array. Searching in an unsorted array has an undefined * result. It's also undefined which element is found if there are multiple * occurrences of the same element. - * + * * @param array * the sorted {@code int} array to search. * @param value * the {@code int} element to find. * @return the non-negative index of the element, or a negative index which * is {@code -index - 1} where the element would be inserted. - * @since Android 1.0 */ public static int binarySearch(int[] array, int value) { int low = 0, mid = -1, high = array.length - 1; while (low <= high) { - mid = (low + high) >> 1; + mid = (low + high) >>> 1; if (value > array[mid]) { low = mid + 1; } else if (value == array[mid]) { @@ -330,19 +329,18 @@ public class Arrays { * ascending sorted array. Searching in an unsorted array has an undefined * result. It's also undefined which element is found if there are multiple * occurrences of the same element. - * + * * @param array * the sorted {@code long} array to search. * @param value * the {@code long} element to find. * @return the non-negative index of the element, or a negative index which * is {@code -index - 1} where the element would be inserted. - * @since Android 1.0 */ public static int binarySearch(long[] array, long value) { int low = 0, mid = -1, high = array.length - 1; while (low <= high) { - mid = (low + high) >> 1; + mid = (low + high) >>> 1; if (value > array[mid]) { low = mid + 1; } else if (value == array[mid]) { @@ -362,28 +360,27 @@ public class Arrays { * ascending sorted array. Searching in an unsorted array has an undefined * result. It's also undefined which element is found if there are multiple * occurrences of the same element. - * + * * @param array * the sorted {@code Object} array to search. * @param object * the {@code Object} element to find. * @return the non-negative index of the element, or a negative index which * is {@code -index - 1} where the element would be inserted. - * @exception ClassCastException + * @throws ClassCastException * if an element in the array or the search element does not * implement {@code Comparable}, or cannot be compared to each other. - * @since Android 1.0 */ @SuppressWarnings("unchecked") public static int binarySearch(Object[] array, Object object) { if (array.length == 0) { return -1; } - Comparable key = (Comparable) object; + int low = 0, mid = 0, high = array.length - 1, result = 0; while (low <= high) { - mid = (low + high) >> 1; - if ((result = key.compareTo(array[mid])) > 0) { + mid = (low + high) >>> 1; + if ((result = ((Comparable)array[mid]).compareTo(object)) < 0){ low = mid + 1; } else if (result == 0) { return mid; @@ -391,7 +388,7 @@ public class Arrays { high = mid - 1; } } - return -mid - (result <= 0 ? 1 : 2); + return -mid - (result >= 0 ? 1 : 2); } /** @@ -400,7 +397,7 @@ public class Arrays { * Searching in an unsorted array has an undefined result. It's also * undefined which element is found if there are multiple occurrences of the * same element. - * + * * @param array * the sorted array to search * @param object @@ -409,10 +406,9 @@ public class Arrays { * the {@code Comparator} sued to compare the elements. * @return the non-negative index of the element, or a negative index which * is {@code -index - 1} where the element would be inserted. - * @exception ClassCastException - * if an element in the array cannot be compared to the search element + * @throws ClassCastException + * if an element in the array cannot be compared to the search element * using the {@code Comparator}. - * @since Android 1.0 */ public static int binarySearch(T[] array, T object, Comparator comparator) { @@ -422,7 +418,7 @@ public class Arrays { int low = 0, mid = 0, high = array.length - 1, result = 0; while (low <= high) { - mid = (low + high) >> 1; + mid = (low + high) >>> 1; if ((result = comparator.compare(array[mid], object)) < 0) { low = mid + 1; } else if (result == 0) { @@ -439,19 +435,18 @@ public class Arrays { * ascending sorted array. Searching in an unsorted array has an undefined * result. It's also undefined which element is found if there are multiple * occurrences of the same element. - * + * * @param array * the sorted {@code short} array to search. * @param value * the {@code short} element to find. * @return the non-negative index of the element, or a negative index which * is {@code -index - 1} where the element would be inserted. - * @since Android 1.0 */ public static int binarySearch(short[] array, short value) { int low = 0, mid = -1, high = array.length - 1; while (low <= high) { - mid = (low + high) >> 1; + mid = (low + high) >>> 1; if (value > array[mid]) { low = mid + 1; } else if (value == array[mid]) { @@ -468,12 +463,11 @@ public class Arrays { /** * Fills the specified array with the specified element. - * + * * @param array * the {@code byte} array to fill. * @param value * the {@code byte} element. - * @since Android 1.0 */ public static void fill(byte[] array, byte value) { for (int i = 0; i < array.length; i++) { @@ -483,7 +477,7 @@ public class Arrays { /** * Fills the specified range in the array with the specified element. - * + * * @param array * the {@code byte} array to fill. * @param start @@ -492,11 +486,10 @@ public class Arrays { * the last + 1 index to fill. * @param value * the {@code byte} element. - * @exception IllegalArgumentException + * @throws IllegalArgumentException * if {@code start > end}. - * @exception ArrayIndexOutOfBoundsException + * @throws ArrayIndexOutOfBoundsException * if {@code start < 0} or {@code end > array.length}. - * @since Android 1.0 */ public static void fill(byte[] array, int start, int end, byte value) { // Check for null first @@ -514,12 +507,11 @@ public class Arrays { /** * Fills the specified array with the specified element. - * + * * @param array * the {@code short} array to fill. * @param value * the {@code short} element. - * @since Android 1.0 */ public static void fill(short[] array, short value) { for (int i = 0; i < array.length; i++) { @@ -529,7 +521,7 @@ public class Arrays { /** * Fills the specified range in the array with the specified element. - * + * * @param array * the {@code short} array to fill. * @param start @@ -538,11 +530,10 @@ public class Arrays { * the last + 1 index to fill. * @param value * the {@code short} element. - * @exception IllegalArgumentException + * @throws IllegalArgumentException * if {@code start > end}. - * @exception ArrayIndexOutOfBoundsException + * @throws ArrayIndexOutOfBoundsException * if {@code start < 0} or {@code end > array.length}. - * @since Android 1.0 */ public static void fill(short[] array, int start, int end, short value) { // Check for null first @@ -560,12 +551,11 @@ public class Arrays { /** * Fills the specified array with the specified element. - * + * * @param array * the {@code char} array to fill. * @param value * the {@code char} element. - * @since Android 1.0 */ public static void fill(char[] array, char value) { for (int i = 0; i < array.length; i++) { @@ -575,7 +565,7 @@ public class Arrays { /** * Fills the specified range in the array with the specified element. - * + * * @param array * the {@code char} array to fill. * @param start @@ -584,11 +574,10 @@ public class Arrays { * the last + 1 index to fill. * @param value * the {@code char} element. - * @exception IllegalArgumentException + * @throws IllegalArgumentException * if {@code start > end}. - * @exception ArrayIndexOutOfBoundsException + * @throws ArrayIndexOutOfBoundsException * if {@code start < 0} or {@code end > array.length}. - * @since Android 1.0 */ public static void fill(char[] array, int start, int end, char value) { // Check for null first @@ -606,12 +595,11 @@ public class Arrays { /** * Fills the specified array with the specified element. - * + * * @param array * the {@code int} array to fill. * @param value * the {@code int} element. - * @since Android 1.0 */ public static void fill(int[] array, int value) { for (int i = 0; i < array.length; i++) { @@ -621,7 +609,7 @@ public class Arrays { /** * Fills the specified range in the array with the specified element. - * + * * @param array * the {@code int} array to fill. * @param start @@ -630,11 +618,10 @@ public class Arrays { * the last + 1 index to fill. * @param value * the {@code int} element. - * @exception IllegalArgumentException + * @throws IllegalArgumentException * if {@code start > end}. - * @exception ArrayIndexOutOfBoundsException + * @throws ArrayIndexOutOfBoundsException * if {@code start < 0} or {@code end > array.length}. - * @since Android 1.0 */ public static void fill(int[] array, int start, int end, int value) { // Check for null first @@ -652,12 +639,11 @@ public class Arrays { /** * Fills the specified array with the specified element. - * + * * @param array * the {@code long} array to fill. * @param value * the {@code long} element. - * @since Android 1.0 */ public static void fill(long[] array, long value) { for (int i = 0; i < array.length; i++) { @@ -667,7 +653,7 @@ public class Arrays { /** * Fills the specified range in the array with the specified element. - * + * * @param array * the {@code long} array to fill. * @param start @@ -676,11 +662,10 @@ public class Arrays { * the last + 1 index to fill. * @param value * the {@code long} element. - * @exception IllegalArgumentException + * @throws IllegalArgumentException * if {@code start > end}. - * @exception ArrayIndexOutOfBoundsException + * @throws ArrayIndexOutOfBoundsException * if {@code start < 0} or {@code end > array.length}. - * @since Android 1.0 */ public static void fill(long[] array, int start, int end, long value) { // Check for null first @@ -698,12 +683,11 @@ public class Arrays { /** * Fills the specified array with the specified element. - * + * * @param array * the {@code float} array to fill. * @param value * the {@code float} element. - * @since Android 1.0 */ public static void fill(float[] array, float value) { for (int i = 0; i < array.length; i++) { @@ -713,7 +697,7 @@ public class Arrays { /** * Fills the specified range in the array with the specified element. - * + * * @param array * the {@code float} array to fill. * @param start @@ -722,11 +706,10 @@ public class Arrays { * the last + 1 index to fill. * @param value * the {@code float} element. - * @exception IllegalArgumentException + * @throws IllegalArgumentException * if {@code start > end}. - * @exception ArrayIndexOutOfBoundsException + * @throws ArrayIndexOutOfBoundsException * if {@code start < 0} or {@code end > array.length}. - * @since Android 1.0 */ public static void fill(float[] array, int start, int end, float value) { // Check for null first @@ -744,12 +727,11 @@ public class Arrays { /** * Fills the specified array with the specified element. - * + * * @param array * the {@code double} array to fill. * @param value * the {@code double} element. - * @since Android 1.0 */ public static void fill(double[] array, double value) { for (int i = 0; i < array.length; i++) { @@ -759,7 +741,7 @@ public class Arrays { /** * Fills the specified range in the array with the specified element. - * + * * @param array * the {@code double} array to fill. * @param start @@ -768,11 +750,10 @@ public class Arrays { * the last + 1 index to fill. * @param value * the {@code double} element. - * @exception IllegalArgumentException + * @throws IllegalArgumentException * if {@code start > end}. - * @exception ArrayIndexOutOfBoundsException + * @throws ArrayIndexOutOfBoundsException * if {@code start < 0} or {@code end > array.length}. - * @since Android 1.0 */ public static void fill(double[] array, int start, int end, double value) { // Check for null first @@ -790,12 +771,11 @@ public class Arrays { /** * Fills the specified array with the specified element. - * + * * @param array * the {@code boolean} array to fill. * @param value * the {@code boolean} element. - * @since Android 1.0 */ public static void fill(boolean[] array, boolean value) { for (int i = 0; i < array.length; i++) { @@ -805,7 +785,7 @@ public class Arrays { /** * Fills the specified range in the array with the specified element. - * + * * @param array * the {@code boolean} array to fill. * @param start @@ -814,11 +794,10 @@ public class Arrays { * the last + 1 index to fill. * @param value * the {@code boolean} element. - * @exception IllegalArgumentException + * @throws IllegalArgumentException * if {@code start > end}. - * @exception ArrayIndexOutOfBoundsException + * @throws ArrayIndexOutOfBoundsException * if {@code start < 0} or {@code end > array.length}. - * @since Android 1.0 */ public static void fill(boolean[] array, int start, int end, boolean value) { // Check for null first @@ -836,12 +815,11 @@ public class Arrays { /** * Fills the specified array with the specified element. - * + * * @param array * the {@code Object} array to fill. * @param value * the {@code Object} element. - * @since Android 1.0 */ public static void fill(Object[] array, Object value) { for (int i = 0; i < array.length; i++) { @@ -851,7 +829,7 @@ public class Arrays { /** * Fills the specified range in the array with the specified element. - * + * * @param array * the {@code Object} array to fill. * @param start @@ -860,11 +838,10 @@ public class Arrays { * the last + 1 index to fill. * @param value * the {@code Object} element. - * @exception IllegalArgumentException + * @throws IllegalArgumentException * if {@code start > end}. - * @exception ArrayIndexOutOfBoundsException + * @throws ArrayIndexOutOfBoundsException * if {@code start < 0} or {@code end > array.length}. - * @since Android 1.0 */ public static void fill(Object[] array, int start, int end, Object value) { // Check for null first @@ -882,7 +859,7 @@ public class Arrays { /** * Returns a hash code based on the contents of the given array. For any two - * {@code boolean} arrays {@code a} and {@code b}, if + * {@code boolean} arrays {@code a} and {@code b}, if * {@code Arrays.equals(a, b)} returns {@code true}, it means * that the return value of {@code Arrays.hashCode(a)} equals {@code Arrays.hashCode(b)}. *

          @@ -891,11 +868,10 @@ public class Arrays { * containing a sequence of {@link Boolean}} instances representing the * elements of array in the same order. If the array is {@code null}, the return * value is 0. - * + * * @param array * the array whose hash code to compute. * @return the hash code for {@code array}. - * @since Android 1.0 */ public static int hashCode(boolean[] array) { if (array == null) { @@ -911,7 +887,7 @@ public class Arrays { /** * Returns a hash code based on the contents of the given array. For any two - * not-null {@code int} arrays {@code a} and {@code b}, if + * not-null {@code int} arrays {@code a} and {@code b}, if * {@code Arrays.equals(a, b)} returns {@code true}, it means * that the return value of {@code Arrays.hashCode(a)} equals {@code Arrays.hashCode(b)}. *

          @@ -920,11 +896,10 @@ public class Arrays { * containing a sequence of {@link Integer}} instances representing the * elements of array in the same order. If the array is {@code null}, the return * value is 0. - * + * * @param array * the array whose hash code to compute. * @return the hash code for {@code array}. - * @since Android 1.0 */ public static int hashCode(int[] array) { if (array == null) { @@ -940,7 +915,7 @@ public class Arrays { /** * Returns a hash code based on the contents of the given array. For any two - * {@code short} arrays {@code a} and {@code b}, if + * {@code short} arrays {@code a} and {@code b}, if * {@code Arrays.equals(a, b)} returns {@code true}, it means * that the return value of {@code Arrays.hashCode(a)} equals {@code Arrays.hashCode(b)}. *

          @@ -949,11 +924,10 @@ public class Arrays { * containing a sequence of {@link Short}} instances representing the * elements of array in the same order. If the array is {@code null}, the return * value is 0. - * + * * @param array * the array whose hash code to compute. * @return the hash code for {@code array}. - * @since Android 1.0 */ public static int hashCode(short[] array) { if (array == null) { @@ -969,7 +943,7 @@ public class Arrays { /** * Returns a hash code based on the contents of the given array. For any two - * {@code char} arrays {@code a} and {@code b}, if + * {@code char} arrays {@code a} and {@code b}, if * {@code Arrays.equals(a, b)} returns {@code true}, it means * that the return value of {@code Arrays.hashCode(a)} equals {@code Arrays.hashCode(b)}. *

          @@ -978,11 +952,10 @@ public class Arrays { * containing a sequence of {@link Character}} instances representing the * elements of array in the same order. If the array is {@code null}, the return * value is 0. - * + * * @param array * the array whose hash code to compute. * @return the hash code for {@code array}. - * @since Android 1.0 */ public static int hashCode(char[] array) { if (array == null) { @@ -998,7 +971,7 @@ public class Arrays { /** * Returns a hash code based on the contents of the given array. For any two - * {@code byte} arrays {@code a} and {@code b}, if + * {@code byte} arrays {@code a} and {@code b}, if * {@code Arrays.equals(a, b)} returns {@code true}, it means * that the return value of {@code Arrays.hashCode(a)} equals {@code Arrays.hashCode(b)}. *

          @@ -1007,11 +980,10 @@ public class Arrays { * containing a sequence of {@link Byte}} instances representing the * elements of array in the same order. If the array is {@code null}, the return * value is 0. - * + * * @param array * the array whose hash code to compute. * @return the hash code for {@code array}. - * @since Android 1.0 */ public static int hashCode(byte[] array) { if (array == null) { @@ -1027,7 +999,7 @@ public class Arrays { /** * Returns a hash code based on the contents of the given array. For any two - * {@code long} arrays {@code a} and {@code b}, if + * {@code long} arrays {@code a} and {@code b}, if * {@code Arrays.equals(a, b)} returns {@code true}, it means * that the return value of {@code Arrays.hashCode(a)} equals {@code Arrays.hashCode(b)}. *

          @@ -1036,11 +1008,10 @@ public class Arrays { * containing a sequence of {@link Long}} instances representing the * elements of array in the same order. If the array is {@code null}, the return * value is 0. - * + * * @param array * the array whose hash code to compute. * @return the hash code for {@code array}. - * @since Android 1.0 */ public static int hashCode(long[] array) { if (array == null) { @@ -1060,7 +1031,7 @@ public class Arrays { /** * Returns a hash code based on the contents of the given array. For any two - * {@code float} arrays {@code a} and {@code b}, if + * {@code float} arrays {@code a} and {@code b}, if * {@code Arrays.equals(a, b)} returns {@code true}, it means * that the return value of {@code Arrays.hashCode(a)} equals {@code Arrays.hashCode(b)}. *

          @@ -1069,11 +1040,10 @@ public class Arrays { * containing a sequence of {@link Float}} instances representing the * elements of array in the same order. If the array is {@code null}, the return * value is 0. - * + * * @param array * the array whose hash code to compute. * @return the hash code for {@code array}. - * @since Android 1.0 */ public static int hashCode(float[] array) { if (array == null) { @@ -1092,7 +1062,7 @@ public class Arrays { /** * Returns a hash code based on the contents of the given array. For any two - * {@code double} arrays {@code a} and {@code b}, if + * {@code double} arrays {@code a} and {@code b}, if * {@code Arrays.equals(a, b)} returns {@code true}, it means * that the return value of {@code Arrays.hashCode(a)} equals {@code Arrays.hashCode(b)}. *

          @@ -1101,11 +1071,10 @@ public class Arrays { * containing a sequence of {@link Double}} instances representing the * elements of array in the same order. If the array is {@code null}, the return * value is 0. - * + * * @param array * the array whose hash code to compute. * @return the hash code for {@code array}. - * @since Android 1.0 */ public static int hashCode(double[] array) { if (array == null) { @@ -1131,19 +1100,18 @@ public class Arrays { * method on an array that contains itself as an element, either directly or * indirectly. *

          - * For any two arrays {@code a} and {@code b}, if + * For any two arrays {@code a} and {@code b}, if * {@code Arrays.equals(a, b)} returns {@code true}, it means - * that the return value of {@code Arrays.hashCode(a)} equals + * that the return value of {@code Arrays.hashCode(a)} equals * {@code Arrays.hashCode(b)}. *

          * The value returned by this method is the same value as the method * Arrays.asList(array).hashCode(). If the array is {@code null}, the return value * is 0. - * + * * @param array * the array whose hash code to compute. * @return the hash code for {@code array}. - * @since Android 1.0 */ public static int hashCode(Object[] array) { if (array == null) { @@ -1170,7 +1138,7 @@ public class Arrays { * this method on an array that contains itself as an element, either * directly or indirectly. *

          - * For any two arrays {@code a} and {@code b}, if + * For any two arrays {@code a} and {@code b}, if * {@code Arrays.deepEquals(a, b)} returns {@code true}, it * means that the return value of {@code Arrays.deepHashCode(a)} equals * {@code Arrays.deepHashCode(b)}. @@ -1185,11 +1153,10 @@ public class Arrays { * an array of a reference type. The value returned by this method is the * same value as the method {@code Arrays.asList(array).hashCode()}. If the array is * {@code null}, the return value is 0. - * + * * @param array * the array whose hash code to compute. * @return the hash code for {@code array}. - * @since Android 1.0 */ public static int deepHashCode(Object[] array) { if (array == null) { @@ -1247,7 +1214,7 @@ public class Arrays { /** * Compares the two arrays. - * + * * @param array1 * the first {@code byte} array. * @param array2 @@ -1255,7 +1222,6 @@ public class Arrays { * @return {@code true} if both arrays are {@code null} or if the arrays have the * same length and the elements at each index in the two arrays are * equal, {@code false} otherwise. - * @since Android 1.0 */ public static boolean equals(byte[] array1, byte[] array2) { if (array1 == array2) { @@ -1274,7 +1240,7 @@ public class Arrays { /** * Compares the two arrays. - * + * * @param array1 * the first {@code short} array. * @param array2 @@ -1282,7 +1248,6 @@ public class Arrays { * @return {@code true} if both arrays are {@code null} or if the arrays have the * same length and the elements at each index in the two arrays are * equal, {@code false} otherwise. - * @since Android 1.0 */ public static boolean equals(short[] array1, short[] array2) { if (array1 == array2) { @@ -1301,7 +1266,7 @@ public class Arrays { /** * Compares the two arrays. - * + * * @param array1 * the first {@code char} array. * @param array2 @@ -1309,7 +1274,6 @@ public class Arrays { * @return {@code true} if both arrays are {@code null} or if the arrays have the * same length and the elements at each index in the two arrays are * equal, {@code false} otherwise. - * @since Android 1.0 */ public static boolean equals(char[] array1, char[] array2) { if (array1 == array2) { @@ -1328,7 +1292,7 @@ public class Arrays { /** * Compares the two arrays. - * + * * @param array1 * the first {@code int} array. * @param array2 @@ -1336,7 +1300,6 @@ public class Arrays { * @return {@code true} if both arrays are {@code null} or if the arrays have the * same length and the elements at each index in the two arrays are * equal, {@code false} otherwise. - * @since Android 1.0 */ public static boolean equals(int[] array1, int[] array2) { if (array1 == array2) { @@ -1355,7 +1318,7 @@ public class Arrays { /** * Compares the two arrays. - * + * * @param array1 * the first {@code long} array. * @param array2 @@ -1363,7 +1326,6 @@ public class Arrays { * @return {@code true} if both arrays are {@code null} or if the arrays have the * same length and the elements at each index in the two arrays are * equal, {@code false} otherwise. - * @since Android 1.0 */ public static boolean equals(long[] array1, long[] array2) { if (array1 == array2) { @@ -1383,7 +1345,7 @@ public class Arrays { /** * Compares the two arrays. The values are compared in the same manner as * {@code Float.equals()}. - * + * * @param array1 * the first {@code float} array. * @param array2 @@ -1392,7 +1354,6 @@ public class Arrays { * same length and the elements at each index in the two arrays are * equal, {@code false} otherwise. * @see Float#equals(Object) - * @since Android 1.0 */ public static boolean equals(float[] array1, float[] array2) { if (array1 == array2) { @@ -1413,7 +1374,7 @@ public class Arrays { /** * Compares the two arrays. The values are compared in the same manner as * {@code Double.equals()}. - * + * * @param array1 * the first {@code double} array. * @param array2 @@ -1422,7 +1383,6 @@ public class Arrays { * same length and the elements at each index in the two arrays are * equal, {@code false} otherwise. * @see Double#equals(Object) - * @since Android 1.0 */ public static boolean equals(double[] array1, double[] array2) { if (array1 == array2) { @@ -1442,7 +1402,7 @@ public class Arrays { /** * Compares the two arrays. - * + * * @param array1 * the first {@code boolean} array. * @param array2 @@ -1450,7 +1410,6 @@ public class Arrays { * @return {@code true} if both arrays are {@code null} or if the arrays have the * same length and the elements at each index in the two arrays are * equal, {@code false} otherwise. - * @since Android 1.0 */ public static boolean equals(boolean[] array1, boolean[] array2) { if (array1 == array2) { @@ -1469,7 +1428,7 @@ public class Arrays { /** * Compares the two arrays. - * + * * @param array1 * the first {@code Object} array. * @param array2 @@ -1477,7 +1436,6 @@ public class Arrays { * @return {@code true} if both arrays are {@code null} or if the arrays have the * same length and the elements at each index in the two arrays are * equal according to {@code equals()}, {@code false} otherwise. - * @since Android 1.0 */ public static boolean equals(Object[] array1, Object[] array2) { if (array1 == array2) { @@ -1522,7 +1480,7 @@ public class Arrays { *

          * If either of the given arrays contain themselves as elements, the * behavior of this method is uncertain. - * + * * @param array1 * the first {@code Object} array. * @param array2 @@ -1530,7 +1488,6 @@ public class Arrays { * @return {@code true} if both arrays are {@code null} or if the arrays have the * same length and the elements at each index in the two arrays are * equal according to {@code equals()}, {@code false} otherwise. - * @since Android 1.0 */ public static boolean deepEquals(Object[] array1, Object[] array2) { if (array1 == array2) { @@ -1603,41 +1560,58 @@ public class Arrays { } private static boolean lessThan(double double1, double double2) { - long d1, d2; - long NaNbits = Double.doubleToLongBits(Double.NaN); - if ((d1 = Double.doubleToLongBits(double1)) == NaNbits) { + // A slightly specialized version of + // Double.compare(double1, double2) < 0. + + // Non-zero and non-NaN checking. + if (double1 < double2) { + return true; + } + if (double1 > double2) { return false; } - if ((d2 = Double.doubleToLongBits(double2)) == NaNbits) { - return true; + if (double1 == double2 && 0.0d != double1) { + return false; } - if (double1 == double2) { - if (d1 == d2) { - return false; - } - // check for -0 - return d1 < d2; + + // NaNs are equal to other NaNs and larger than any other double. + if (Double.isNaN(double1)) { + return false; + } else if (Double.isNaN(double2)) { + return true; } - return double1 < double2; + + // Deal with +0.0 and -0.0. + long d1 = Double.doubleToRawLongBits(double1); + long d2 = Double.doubleToRawLongBits(double2); + return d1 < d2; } private static boolean lessThan(float float1, float float2) { - int f1, f2; - int NaNbits = Float.floatToIntBits(Float.NaN); - if ((f1 = Float.floatToIntBits(float1)) == NaNbits) { + // A slightly specialized version of Float.compare(float1, float2) < 0. + + // Non-zero and non-NaN checking. + if (float1 < float2) { + return true; + } + if (float1 > float2) { return false; } - if ((f2 = Float.floatToIntBits(float2)) == NaNbits) { - return true; + if (float1 == float2 && 0.0f != float1) { + return false; } - if (float1 == float2) { - if (f1 == f2) { - return false; - } - // check for -0 - return f1 < f2; + + // NaNs are equal to other NaNs and larger than any other float + if (Float.isNaN(float1)) { + return false; + } else if (Float.isNaN(float2)) { + return true; } - return float1 < float2; + + // Deal with +0.0 and -0.0 + int f1 = Float.floatToRawIntBits(float1); + int f2 = Float.floatToRawIntBits(float2); + return f1 < f2; } private static int med3(byte[] array, int a, int b, int c) { @@ -1684,10 +1658,9 @@ public class Arrays { /** * Sorts the specified array in ascending numerical order. - * + * * @param array * the {@code byte} array to be sorted. - * @since Android 1.0 */ public static void sort(byte[] array) { sort(0, array.length, array); @@ -1695,18 +1668,17 @@ public class Arrays { /** * Sorts the specified range in the array in ascending numerical order. - * + * * @param array * the {@code byte} array to be sorted. * @param start * the start index to sort. * @param end * the last + 1 index to sort. - * @exception IllegalArgumentException + * @throws IllegalArgumentException * if {@code start > end}. - * @exception ArrayIndexOutOfBoundsException + * @throws ArrayIndexOutOfBoundsException * if {@code start < 0} or {@code end > array.length}. - * @since Android 1.0 */ public static void sort(byte[] array, int start, int end) { if (array == null) { @@ -1718,8 +1690,8 @@ public class Arrays { private static void checkBounds(int arrLength, int start, int end) { if (start > end) { - throw new IllegalArgumentException("fromIndex(" + start //$NON-NLS-1$ - + ") > toIndex(" + end + ")"); //$NON-NLS-1$ //$NON-NLS-2$ + throw new IllegalArgumentException("start(" + start //$NON-NLS-1$ + + ") > end(" + end + ")"); //$NON-NLS-1$ //$NON-NLS-2$ } if (start < 0 || end > arrLength) { throw new ArrayIndexOutOfBoundsException(); @@ -1806,10 +1778,9 @@ public class Arrays { /** * Sorts the specified array in ascending numerical order. - * + * * @param array * the {@code char} array to be sorted. - * @since Android 1.0 */ public static void sort(char[] array) { sort(0, array.length, array); @@ -1817,18 +1788,17 @@ public class Arrays { /** * Sorts the specified range in the array in ascending numerical order. - * + * * @param array * the {@code char} array to be sorted. * @param start * the start index to sort. * @param end * the last + 1 index to sort. - * @exception IllegalArgumentException + * @throws IllegalArgumentException * if {@code start > end}. - * @exception ArrayIndexOutOfBoundsException + * @throws ArrayIndexOutOfBoundsException * if {@code start < 0} or {@code end > array.length}. - * @since Android 1.0 */ public static void sort(char[] array, int start, int end) { if (array == null) { @@ -1918,11 +1888,10 @@ public class Arrays { /** * Sorts the specified array in ascending numerical order. - * + * * @param array * the {@code double} array to be sorted. * @see #sort(double[], int, int) - * @since Android 1.0 */ public static void sort(double[] array) { sort(0, array.length, array); @@ -1931,19 +1900,18 @@ public class Arrays { /** * Sorts the specified range in the array in ascending numerical order. The * values are sorted according to the order imposed by {@code Double.compareTo()}. - * + * * @param array * the {@code double} array to be sorted. * @param start * the start index to sort. * @param end * the last + 1 index to sort. - * @exception IllegalArgumentException + * @throws IllegalArgumentException * if {@code start > end}. - * @exception ArrayIndexOutOfBoundsException + * @throws ArrayIndexOutOfBoundsException * if {@code start < 0} or {@code end > array.length}. * @see Double#compareTo(Double) - * @since Android 1.0 */ public static void sort(double[] array, int start, int end) { if (array == null) { @@ -2033,11 +2001,10 @@ public class Arrays { /** * Sorts the specified array in ascending numerical order. - * + * * @param array * the {@code float} array to be sorted. * @see #sort(float[], int, int) - * @since Android 1.0 */ public static void sort(float[] array) { sort(0, array.length, array); @@ -2046,19 +2013,18 @@ public class Arrays { /** * Sorts the specified range in the array in ascending numerical order. The * values are sorted according to the order imposed by {@code Float.compareTo()}. - * + * * @param array * the {@code float} array to be sorted. * @param start * the start index to sort. * @param end * the last + 1 index to sort. - * @exception IllegalArgumentException + * @throws IllegalArgumentException * if {@code start > end}. - * @exception ArrayIndexOutOfBoundsException + * @throws ArrayIndexOutOfBoundsException * if {@code start < 0} or {@code end > array.length}. * @see Float#compareTo(Float) - * @since Android 1.0 */ public static void sort(float[] array, int start, int end) { if (array == null) { @@ -2148,10 +2114,9 @@ public class Arrays { /** * Sorts the specified array in ascending numerical order. - * + * * @param array * the {@code int} array to be sorted. - * @since Android 1.0 */ public static void sort(int[] array) { sort(0, array.length, array); @@ -2159,18 +2124,17 @@ public class Arrays { /** * Sorts the specified range in the array in ascending numerical order. - * + * * @param array * the {@code int} array to be sorted. * @param start * the start index to sort. * @param end * the last + 1 index to sort. - * @exception IllegalArgumentException + * @throws IllegalArgumentException * if {@code start > end}. - * @exception ArrayIndexOutOfBoundsException + * @throws ArrayIndexOutOfBoundsException * if {@code start < 0} or {@code end > array.length}. - * @since Android 1.0 */ public static void sort(int[] array, int start, int end) { if (array == null) { @@ -2260,10 +2224,9 @@ public class Arrays { /** * Sorts the specified array in ascending numerical order. - * + * * @param array * the {@code long} array to be sorted. - * @since Android 1.0 */ public static void sort(long[] array) { sort(0, array.length, array); @@ -2271,18 +2234,17 @@ public class Arrays { /** * Sorts the specified range in the array in ascending numerical order. - * + * * @param array * the {@code long} array to be sorted. * @param start * the start index to sort. * @param end * the last + 1 index to sort. - * @exception IllegalArgumentException + * @throws IllegalArgumentException * if {@code start > end}. - * @exception ArrayIndexOutOfBoundsException + * @throws ArrayIndexOutOfBoundsException * if {@code start < 0} or {@code end > array.length}. - * @since Android 1.0 */ public static void sort(long[] array, int start, int end) { if (array == null) { @@ -2370,7 +2332,7 @@ public class Arrays { } } -// BEGIN android-changed +// BEGIN android-note /* *

          If this platform has an optimizing VM, check whether ComparableTimSort @@ -2405,19 +2367,22 @@ public class Arrays { // sort(a, fromIndex, toIndex, NATURAL_ORDER); // } +// END android-note + /** * Sorts the specified array in ascending natural order. - * + * * @param array * the {@code Object} array to be sorted. - * @exception ClassCastException + * @throws ClassCastException * if an element in the array does not implement {@code Comparable} * or if some elements cannot be compared to each other. * @see #sort(Object[], int, int) - * @since Android 1.0 */ public static void sort(Object[] array) { + // BEGIN android-changed ComparableTimSort.sort(array); + // END android-changed } /** @@ -2425,31 +2390,76 @@ public class Arrays { * elements must implement the {@code Comparable} interface and must be * comparable to each other without a {@code ClassCastException} being * thrown. - * + * * @param array * the {@code Object} array to be sorted. * @param start * the start index to sort. * @param end * the last + 1 index to sort. - * @exception ClassCastException + * @throws ClassCastException * if an element in the array does not implement {@code Comparable} * or some elements cannot be compared to each other. - * @exception IllegalArgumentException + * @throws IllegalArgumentException * if {@code start > end}. - * @exception ArrayIndexOutOfBoundsException + * @throws ArrayIndexOutOfBoundsException * if {@code start < 0} or {@code end > array.length}. - * @since Android 1.0 */ public static void sort(Object[] array, int start, int end) { + // BEGIN android-changed ComparableTimSort.sort(array, start, end); + // END android-changed } + // BEGIN android-removed + /* + private static void sort(int start, int end, Object[] array) { + ... + } + private static void swap(int a, int b, Object[] arr) { + ... + } + private static void mergeSort(Object[] in, Object[] out, int start, + int end) { + ... + } + private static void mergeSort(Object[] in, Object[] out, int start, + int end, Comparator c) { + ... + } + private static int find(Object[] arr, Comparable val, int bnd, int l, int r) { + ... + } + private static int find(Object[] arr, Object val, int bnd, int l, int r, + Comparator c) { + ... + } + private static int medChar(int a, int b, int c, String[] arr, int id) { + ... + } + private static int charAt(String str, int i) { + ... + } + private static void copySwap(Object[] src, int from, Object[] dst, int to, + int len) { + ... + } + private static void stableStringSort(String[] arr, int start, + int end) { + ... + } + private static void stableStringSort(String[] arr, String[] src, + String[] dst, int start, int end, int chId) { + ... + } + */ + // END android-removed + /** * Sorts the specified range in the array using the specified {@code Comparator}. * All elements must be comparable to each other without a * {@code ClassCastException} being thrown. - * + * * @param array * the {@code Object} array to be sorted. * @param start @@ -2458,45 +2468,44 @@ public class Arrays { * the last + 1 index to sort. * @param comparator * the {@code Comparator}. - * @exception ClassCastException + * @throws ClassCastException * if elements in the array cannot be compared to each other * using the {@code Comparator}. - * @exception IllegalArgumentException + * @throws IllegalArgumentException * if {@code start > end}. - * @exception ArrayIndexOutOfBoundsException + * @throws ArrayIndexOutOfBoundsException * if {@code start < 0} or {@code end > array.length}. - * @since Android 1.0 */ public static void sort(T[] array, int start, int end, Comparator comparator) { + // BEGIN android-changed TimSort.sort(array, start, end, comparator); + // END android-changed } /** * Sorts the specified array using the specified {@code Comparator}. All elements * must be comparable to each other without a {@code ClassCastException} being thrown. - * + * * @param array * the {@code Object} array to be sorted. * @param comparator * the {@code Comparator}. - * @exception ClassCastException + * @throws ClassCastException * if elements in the array cannot be compared to each other * using the {@code Comparator}. - * @since Android 1.0 */ public static void sort(T[] array, Comparator comparator) { + // BEGIN android-changed TimSort.sort(array, comparator); + // END android-changed } -// END android-changed - /** * Sorts the specified array in ascending numerical order. - * + * * @param array * the {@code short} array to be sorted. - * @since Android 1.0 */ public static void sort(short[] array) { sort(0, array.length, array); @@ -2504,18 +2513,17 @@ public class Arrays { /** * Sorts the specified range in the array in ascending numerical order. - * + * * @param array * the {@code short} array to be sorted. * @param start * the start index to sort. * @param end * the last + 1 index to sort. - * @exception IllegalArgumentException + * @throws IllegalArgumentException * if {@code start > end}. - * @exception ArrayIndexOutOfBoundsException + * @throws ArrayIndexOutOfBoundsException * if {@code start < 0} or {@code end > array.length}. - * @since Android 1.0 */ public static void sort(short[] array, int start, int end) { if (array == null) { @@ -2604,18 +2612,16 @@ public class Arrays { } /** - *

          * Creates a {@code String} representation of the {@code boolean[]} passed. * The result is surrounded by brackets ({@code "[]"}), each * element is converted to a {@code String} via the * {@link String#valueOf(boolean)} and separated by {@code ", "}. * If the array is {@code null}, then {@code "null"} is returned. - *

          - * + * * @param array * the {@code boolean} array to convert. * @return the {@code String} representation of {@code array}. - * @since Android 1.0 + * @since 1.5 */ public static String toString(boolean[] array) { if (array == null) { @@ -2636,18 +2642,16 @@ public class Arrays { } /** - *

          * Creates a {@code String} representation of the {@code byte[]} passed. The * result is surrounded by brackets ({@code "[]"}), each element * is converted to a {@code String} via the {@link String#valueOf(int)} and * separated by {@code ", "}. If the array is {@code null}, then * {@code "null"} is returned. - *

          - * + * * @param array * the {@code byte} array to convert. * @return the {@code String} representation of {@code array}. - * @since Android 1.0 + * @since 1.5 */ public static String toString(byte[] array) { if (array == null) { @@ -2668,18 +2672,16 @@ public class Arrays { } /** - *

          * Creates a {@code String} representation of the {@code char[]} passed. The * result is surrounded by brackets ({@code "[]"}), each element * is converted to a {@code String} via the {@link String#valueOf(char)} and * separated by {@code ", "}. If the array is {@code null}, then * {@code "null"} is returned. - *

          - * + * * @param array * the {@code char} array to convert. * @return the {@code String} representation of {@code array}. - * @since Android 1.0 + * @since 1.5 */ public static String toString(char[] array) { if (array == null) { @@ -2700,18 +2702,16 @@ public class Arrays { } /** - *

          * Creates a {@code String} representation of the {@code double[]} passed. * The result is surrounded by brackets ({@code "[]"}), each * element is converted to a {@code String} via the * {@link String#valueOf(double)} and separated by {@code ", "}. * If the array is {@code null}, then {@code "null"} is returned. - *

          - * + * * @param array * the {@code double} array to convert. * @return the {@code String} representation of {@code array}. - * @since Android 1.0 + * @since 1.5 */ public static String toString(double[] array) { if (array == null) { @@ -2732,18 +2732,16 @@ public class Arrays { } /** - *

          * Creates a {@code String} representation of the {@code float[]} passed. * The result is surrounded by brackets ({@code "[]"}), each * element is converted to a {@code String} via the * {@link String#valueOf(float)} and separated by {@code ", "}. * If the array is {@code null}, then {@code "null"} is returned. - *

          - * + * * @param array * the {@code float} array to convert. * @return the {@code String} representation of {@code array}. - * @since Android 1.0 + * @since 1.5 */ public static String toString(float[] array) { if (array == null) { @@ -2764,18 +2762,16 @@ public class Arrays { } /** - *

          * Creates a {@code String} representation of the {@code int[]} passed. The * result is surrounded by brackets ({@code "[]"}), each element * is converted to a {@code String} via the {@link String#valueOf(int)} and * separated by {@code ", "}. If the array is {@code null}, then * {@code "null"} is returned. - *

          - * + * * @param array * the {@code int} array to convert. * @return the {@code String} representation of {@code array}. - * @since Android 1.0 + * @since 1.5 */ public static String toString(int[] array) { if (array == null) { @@ -2796,18 +2792,16 @@ public class Arrays { } /** - *

          * Creates a {@code String} representation of the {@code long[]} passed. The * result is surrounded by brackets ({@code "[]"}), each element * is converted to a {@code String} via the {@link String#valueOf(long)} and * separated by {@code ", "}. If the array is {@code null}, then * {@code "null"} is returned. - *

          - * + * * @param array * the {@code long} array to convert. * @return the {@code String} representation of {@code array}. - * @since Android 1.0 + * @since 1.5 */ public static String toString(long[] array) { if (array == null) { @@ -2828,18 +2822,16 @@ public class Arrays { } /** - *

          * Creates a {@code String} representation of the {@code short[]} passed. * The result is surrounded by brackets ({@code "[]"}), each * element is converted to a {@code String} via the * {@link String#valueOf(int)} and separated by {@code ", "}. If * the array is {@code null}, then {@code "null"} is returned. - *

          - * + * * @param array * the {@code short} array to convert. * @return the {@code String} representation of {@code array}. - * @since Android 1.0 + * @since 1.5 */ public static String toString(short[] array) { if (array == null) { @@ -2860,18 +2852,16 @@ public class Arrays { } /** - *

          * Creates a {@code String} representation of the {@code Object[]} passed. * The result is surrounded by brackets ({@code "[]"}), each * element is converted to a {@code String} via the * {@link String#valueOf(Object)} and separated by {@code ", "}. * If the array is {@code null}, then {@code "null"} is returned. - *

          - * + * * @param array * the {@code Object} array to convert. * @return the {@code String} representation of {@code array}. - * @since Android 1.0 + * @since 1.5 */ public static String toString(Object[] array) { if (array == null) { @@ -2892,11 +2882,9 @@ public class Arrays { } /** - *

          * Creates a "deep" {@code String} representation of the * {@code Object[]} passed, such that if the array contains other arrays, * the {@code String} representation of those arrays is generated as well. - *

          *

          * If any of the elements are primitive arrays, the generation is delegated * to the other {@code toString} methods in this class. If any element @@ -2904,12 +2892,11 @@ public class Arrays { * as {@code "[...]"}. If an element is an {@code Object[]}, then its * representation is generated by a recursive call to this method. All other * elements are converted via the {@link String#valueOf(Object)} method. - *

          - * + * * @param array * the {@code Object} array to convert. * @return the {@code String} representation of {@code array}. - * @since Android 1.0 + * @since 1.5 */ public static String deepToString(Object[] array) { // Special case null to prevent NPE @@ -2923,10 +2910,8 @@ public class Arrays { } /** - *

          * Implementation method used by {@link #deepToString(Object[])}. - *

          - * + * * @param array * the {@code Object[]} to dive into. * @param origArrays @@ -3010,11 +2995,9 @@ public class Arrays { } /** - *

          * Utility method used to assist the implementation of * {@link #deepToString(Object[])}. - *

          - * + * * @param origArrays * An array of Object[] references. * @param array diff --git a/libcore/luni/src/main/java/java/util/BitSet.java b/libcore/luni/src/main/java/java/util/BitSet.java index aa60be067..4e01cbf5a 100644 --- a/libcore/luni/src/main/java/java/util/BitSet.java +++ b/libcore/luni/src/main/java/java/util/BitSet.java @@ -17,6 +17,8 @@ package java.util; +import java.io.IOException; +import java.io.ObjectInputStream; import java.io.Serializable; import org.apache.harmony.luni.util.Msg; @@ -26,20 +28,43 @@ import org.apache.harmony.luni.util.Msg; * {@code BitSet} can be on(1) or off(0). A {@code BitSet} is created with a * given size and grows if this size is exceeded. Growth is always rounded to a * 64 bit boundary. - * - * @since Android 1.0 */ public class BitSet implements Serializable, Cloneable { private static final long serialVersionUID = 7997698588986878753L; - // Size in bits of the data type being used in the bits array - private static final int ELM_SIZE = 64; + private static final int OFFSET = 6; + + private static final int ELM_SIZE = 1 << OFFSET; + + private static final int RIGHT_BITS = ELM_SIZE - 1; + + private static final long[] TWO_N_ARRAY = new long[] { 0x1L, 0x2L, 0x4L, + 0x8L, 0x10L, 0x20L, 0x40L, 0x80L, 0x100L, 0x200L, 0x400L, 0x800L, + 0x1000L, 0x2000L, 0x4000L, 0x8000L, 0x10000L, 0x20000L, 0x40000L, + 0x80000L, 0x100000L, 0x200000L, 0x400000L, 0x800000L, 0x1000000L, + 0x2000000L, 0x4000000L, 0x8000000L, 0x10000000L, 0x20000000L, + 0x40000000L, 0x80000000L, 0x100000000L, 0x200000000L, 0x400000000L, + 0x800000000L, 0x1000000000L, 0x2000000000L, 0x4000000000L, + 0x8000000000L, 0x10000000000L, 0x20000000000L, 0x40000000000L, + 0x80000000000L, 0x100000000000L, 0x200000000000L, 0x400000000000L, + 0x800000000000L, 0x1000000000000L, 0x2000000000000L, + 0x4000000000000L, 0x8000000000000L, 0x10000000000000L, + 0x20000000000000L, 0x40000000000000L, 0x80000000000000L, + 0x100000000000000L, 0x200000000000000L, 0x400000000000000L, + 0x800000000000000L, 0x1000000000000000L, 0x2000000000000000L, + 0x4000000000000000L, 0x8000000000000000L }; private long[] bits; + private transient boolean needClear; + + private transient int actualArrayLength; + + private transient boolean isLengthActual; + /** * Create a new {@code BitSet} with size equal to 64 bits. - * + * * @see #clear(int) * @see #set(int) * @see #clear() @@ -47,17 +72,18 @@ public class BitSet implements Serializable, Cloneable { * @see #set(int, boolean) * @see #set(int, int) * @see #set(int, int, boolean) - * @since Android 1.0 */ public BitSet() { - this(64); + bits = new long[1]; + actualArrayLength = 0; + isLengthActual = true; } /** * Create a new {@code BitSet} with size equal to nbits. If nbits is not a * multiple of 64, then create a {@code BitSet} with size nbits rounded to * the next closest multiple of 64. - * + * * @param nbits * the size of the bit set. * @throws NegativeArraySizeException @@ -69,31 +95,34 @@ public class BitSet implements Serializable, Cloneable { * @see #set(int, boolean) * @see #set(int, int) * @see #set(int, int, boolean) - * @since Android 1.0 */ public BitSet(int nbits) { - if (nbits >= 0) { - bits = new long[(nbits / ELM_SIZE) + (nbits % ELM_SIZE > 0 ? 1 : 0)]; - } else { + if (nbits < 0) { throw new NegativeArraySizeException(); } + bits = new long[(nbits >> OFFSET) + ((nbits & RIGHT_BITS) > 0 ? 1 : 0)]; + actualArrayLength = 0; + isLengthActual = true; } /** * Private constructor called from get(int, int) method - * + * * @param bits * the size of the bit set */ - private BitSet(long[] bits) { + private BitSet(long[] bits, boolean needClear, int actualArrayLength, + boolean isLengthActual) { this.bits = bits; + this.needClear = needClear; + this.actualArrayLength = actualArrayLength; + this.isLengthActual = isLengthActual; } /** * Creates a copy of this {@code BitSet}. - * + * * @return a copy of this {@code BitSet}. - * @since Android 1.0 */ @Override public Object clone() { @@ -110,13 +139,12 @@ public class BitSet implements Serializable, Cloneable { * Compares the argument to this {@code BitSet} and returns whether they are * equal. The object must be an instance of {@code BitSet} with the same * bits set. - * + * * @param obj * the {@code BitSet} object to compare. * @return a {@code boolean} indicating whether or not this {@code BitSet} and * {@code obj} are equal. * @see #hashCode - * @since Android 1.0 */ @Override public boolean equals(Object obj) { @@ -125,10 +153,13 @@ public class BitSet implements Serializable, Cloneable { } if (obj instanceof BitSet) { long[] bsBits = ((BitSet) obj).bits; - int length1 = bits.length, length2 = bsBits.length; + int length1 = this.actualArrayLength, length2 = ((BitSet) obj).actualArrayLength; + if (this.isLengthActual && ((BitSet) obj).isLengthActual + && length1 != length2) { + return false; + } // If one of the BitSets is larger than the other, check to see if - // any of - // its extra bits are set. If so return false. + // any of its extra bits are set. If so return false. if (length1 <= length2) { for (int i = 0; i < length1; i++) { if (bits[i] != bsBits[i]) { @@ -160,35 +191,29 @@ public class BitSet implements Serializable, Cloneable { /** * Increase the size of the internal array to accommodate {@code pos} bits. * The new array max index will be a multiple of 64. - * - * @param pos + * + * @param len * the index the new array needs to be able to access. - * @since Android 1.0 */ - private void growBits(int pos) { - pos++; // Inc to get correct bit count - long[] tempBits = new long[(pos / ELM_SIZE) - + (pos % ELM_SIZE > 0 ? 1 : 0)]; - System.arraycopy(bits, 0, tempBits, 0, bits.length); + private final void growLength(int len) { + long[] tempBits = new long[Math.max(len, bits.length * 2)]; + System.arraycopy(bits, 0, tempBits, 0, this.actualArrayLength); bits = tempBits; } /** * Computes the hash code for this {@code BitSet}. If two {@code BitSet}s are equal * the have to return the same result for {@code hashCode()}. - * + * * @return the {@code int} representing the hash code for this bit * set. * @see #equals * @see java.util.Hashtable - * @since Android 1.0 */ @Override public int hashCode() { long x = 1234; - // for (int i = 0, length = bits.length; i < length; i+=2) - // x ^= (bits[i] + ((long)bits[i+1] << 32)) * (i/2 + 1); - for (int i = 0, length = bits.length; i < length; i++) { + for (int i = 0, length = actualArrayLength; i < length; i++) { x ^= bits[i] * (i + 1); } return (int) ((x >> 32) ^ x); @@ -197,7 +222,7 @@ public class BitSet implements Serializable, Cloneable { /** * Retrieves the bit at index {@code pos}. Grows the {@code BitSet} if * {@code pos > size}. - * + * * @param pos * the index of the bit to be retrieved. * @return {@code true} if the bit at {@code pos} is set, @@ -211,89 +236,98 @@ public class BitSet implements Serializable, Cloneable { * @see #set(int, boolean) * @see #set(int, int) * @see #set(int, int, boolean) - * @since Android 1.0 */ public boolean get(int pos) { - if (pos >= 0) { - if (pos < bits.length * ELM_SIZE) { - return (bits[pos / ELM_SIZE] & (1L << (pos % ELM_SIZE))) != 0; - } - return false; + if (pos < 0) { + // Negative index specified + throw new IndexOutOfBoundsException(Msg.getString("K0006")); //$NON-NLS-1$ } - // Negative index specified - throw new IndexOutOfBoundsException(Msg.getString("K0006")); //$NON-NLS-1$ + + int arrayPos = pos >> OFFSET; + if (arrayPos < actualArrayLength) { + return (bits[arrayPos] & TWO_N_ARRAY[pos & RIGHT_BITS]) != 0; + } + return false; } /** * Retrieves the bits starting from {@code pos1} to {@code pos2} and returns * back a new bitset made of these bits. Grows the {@code BitSet} if * {@code pos2 > size}. - * + * * @param pos1 - * beginning position. + * inclusive beginning position. * @param pos2 - * ending position. + * exclusive ending position. * @return new bitset of the range specified. * @throws IndexOutOfBoundsException * if {@code pos1} or {@code pos2} is negative, or if * {@code pos2} is smaller than {@code pos1}. * @see #get(int) - * @since Android 1.0 */ public BitSet get(int pos1, int pos2) { - if (pos1 >= 0 && pos2 >= 0 && pos2 >= pos1) { - int last = (bits.length * ELM_SIZE); - if (pos1 >= last || pos1 == pos2) { - return new BitSet(0); - } - if (pos2 > last) { - pos2 = last; - } + if (pos1 < 0 || pos2 < 0 || pos2 < pos1) { + throw new IndexOutOfBoundsException(Msg.getString("K0006")); //$NON-NLS-1$ + } - int idx1 = pos1 / ELM_SIZE; - int idx2 = (pos2 - 1) / ELM_SIZE; - long factor1 = (~0L) << (pos1 % ELM_SIZE); - long factor2 = (~0L) >>> (ELM_SIZE - (pos2 % ELM_SIZE)); + int last = actualArrayLength << OFFSET; + if (pos1 >= last || pos1 == pos2) { + return new BitSet(0); + } + if (pos2 > last) { + pos2 = last; + } - if (idx1 == idx2) { - long result = (bits[idx1] & (factor1 & factor2)) >>> (pos1 % ELM_SIZE); - return new BitSet(new long[] { result }); - } - long[] newbits = new long[idx2 - idx1 + 1]; - // first fill in the first and last indexes in the new bitset - newbits[0] = bits[idx1] & factor1; - newbits[newbits.length - 1] = bits[idx2] & factor2; - - // fill in the in between elements of the new bitset - for (int i = 1; i < idx2 - idx1; i++) { - newbits[i] = bits[idx1 + i]; + int idx1 = pos1 >> OFFSET; + int idx2 = (pos2 - 1) >> OFFSET; + long factor1 = (~0L) << (pos1 & RIGHT_BITS); + long factor2 = (~0L) >>> (ELM_SIZE - (pos2 & RIGHT_BITS)); + + if (idx1 == idx2) { + long result = (bits[idx1] & (factor1 & factor2)) >>> (pos1 % ELM_SIZE); + if (result == 0) { + return new BitSet(0); } + return new BitSet(new long[] { result }, needClear, 1, true); + } + long[] newbits = new long[idx2 - idx1 + 1]; + // first fill in the first and last indexes in the new bitset + newbits[0] = bits[idx1] & factor1; + newbits[newbits.length - 1] = bits[idx2] & factor2; + + // fill in the in between elements of the new bitset + for (int i = 1; i < idx2 - idx1; i++) { + newbits[i] = bits[idx1 + i]; + } - // shift all the elements in the new bitset to the right by pos1 - // % ELM_SIZE - int numBitsToShift = pos1 % ELM_SIZE; - if (numBitsToShift != 0) { - for (int i = 0; i < newbits.length; i++) { - // shift the current element to the right regardless of - // sign - newbits[i] = newbits[i] >>> (numBitsToShift); - - // apply the last x bits of newbits[i+1] to the current - // element - if (i != newbits.length - 1) { - newbits[i] |= newbits[i + 1] << (ELM_SIZE - (numBitsToShift)); - } + // shift all the elements in the new bitset to the right by pos1 + // % ELM_SIZE + int numBitsToShift = pos1 & RIGHT_BITS; + int actualLen = newbits.length; + if (numBitsToShift != 0) { + for (int i = 0; i < newbits.length; i++) { + // shift the current element to the right regardless of + // sign + newbits[i] = newbits[i] >>> (numBitsToShift); + + // apply the last x bits of newbits[i+1] to the current + // element + if (i != newbits.length - 1) { + newbits[i] |= newbits[i + 1] << (ELM_SIZE - (numBitsToShift)); + } + if (newbits[i] != 0) { + actualLen = i + 1; } } - return new BitSet(newbits); } - throw new IndexOutOfBoundsException(Msg.getString("K0006")); //$NON-NLS-1$ + return new BitSet(newbits, needClear, actualLen, + newbits[actualLen - 1] != 0); } /** * Sets the bit at index {@code pos} to 1. Grows the {@code BitSet} if * {@code pos > size}. - * + * * @param pos * the index of the bit to set. * @throws IndexOutOfBoundsException @@ -301,23 +335,28 @@ public class BitSet implements Serializable, Cloneable { * @see #clear(int) * @see #clear() * @see #clear(int, int) - * @since Android 1.0 */ public void set(int pos) { - if (pos >= 0) { - if (pos >= bits.length * ELM_SIZE) { - growBits(pos); - } - bits[pos / ELM_SIZE] |= 1L << (pos % ELM_SIZE); - } else { + if (pos < 0) { throw new IndexOutOfBoundsException(Msg.getString("K0006")); //$NON-NLS-1$ } + + int len = (pos >> OFFSET) + 1; + if (len > bits.length) { + growLength(len); + } + bits[len - 1] |= TWO_N_ARRAY[pos & RIGHT_BITS]; + if (len > actualArrayLength) { + actualArrayLength = len; + isLengthActual = true; + } + needClear(); } /** * Sets the bit at index {@code pos} to {@code val}. Grows the * {@code BitSet} if {@code pos > size}. - * + * * @param pos * the index of the bit to set. * @param val @@ -325,7 +364,6 @@ public class BitSet implements Serializable, Cloneable { * @throws IndexOutOfBoundsException * if {@code pos} is negative. * @see #set(int) - * @since Android 1.0 */ public void set(int pos, boolean val) { if (val) { @@ -338,60 +376,68 @@ public class BitSet implements Serializable, Cloneable { /** * Sets the bits starting from {@code pos1} to {@code pos2}. Grows the * {@code BitSet} if {@code pos2 > size}. - * + * * @param pos1 - * beginning position. + * inclusive beginning position. * @param pos2 - * ending position. + * exclusive ending position. * @throws IndexOutOfBoundsException * if {@code pos1} or {@code pos2} is negative, or if * {@code pos2} is smaller than {@code pos1}. * @see #set(int) - * @since Android 1.0 */ public void set(int pos1, int pos2) { - if (pos1 >= 0 && pos2 >= 0 && pos2 >= pos1) { - if (pos1 == pos2) { - return; - } - if (pos2 >= bits.length * ELM_SIZE) { - growBits(pos2); - } + if (pos1 < 0 || pos2 < 0 || pos2 < pos1) { + throw new IndexOutOfBoundsException(Msg.getString("K0006")); //$NON-NLS-1$ + } - int idx1 = pos1 / ELM_SIZE; - int idx2 = (pos2 - 1) / ELM_SIZE; - long factor1 = (~0L) << (pos1 % ELM_SIZE); - long factor2 = (~0L) >>> (ELM_SIZE - (pos2 % ELM_SIZE)); + if (pos1 == pos2) { + return; + } + int len2 = ((pos2 - 1) >> OFFSET) + 1; + if (len2 > bits.length) { + growLength(len2); + } - if (idx1 == idx2) { - bits[idx1] |= (factor1 & factor2); - } else { - bits[idx1] |= factor1; - bits[idx2] |= factor2; - for (int i = idx1 + 1; i < idx2; i++) { - bits[i] |= (~0L); - } - } + int idx1 = pos1 >> OFFSET; + int idx2 = (pos2 - 1) >> OFFSET; + long factor1 = (~0L) << (pos1 & RIGHT_BITS); + long factor2 = (~0L) >>> (ELM_SIZE - (pos2 & RIGHT_BITS)); + + if (idx1 == idx2) { + bits[idx1] |= (factor1 & factor2); } else { - throw new IndexOutOfBoundsException(Msg.getString("K0006")); //$NON-NLS-1$ + bits[idx1] |= factor1; + bits[idx2] |= factor2; + for (int i = idx1 + 1; i < idx2; i++) { + bits[i] |= (~0L); + } + } + if (idx2 + 1 > actualArrayLength) { + actualArrayLength = idx2 + 1; + isLengthActual = true; } + needClear(); + } + + private void needClear() { + this.needClear = true; } /** * Sets the bits starting from {@code pos1} to {@code pos2} to the given * {@code val}. Grows the {@code BitSet} if {@code pos2 > size}. - * + * * @param pos1 - * beginning position. + * inclusive beginning position. * @param pos2 - * ending position. + * exclusive ending position. * @param val * value to set these bits. * @throws IndexOutOfBoundsException * if {@code pos1} or {@code pos2} is negative, or if * {@code pos2} is smaller than {@code pos1}. * @see #set(int,int) - * @since Android 1.0 */ public void set(int pos1, int pos2, boolean val) { if (val) { @@ -403,159 +449,183 @@ public class BitSet implements Serializable, Cloneable { /** * Clears all the bits in this {@code BitSet}. - * + * * @see #clear(int) * @see #clear(int, int) - * @since Android 1.0 */ public void clear() { - for (int i = 0; i < bits.length; i++) { - bits[i] = 0L; + if (needClear) { + for (int i = 0; i < bits.length; i++) { + bits[i] = 0L; + } + actualArrayLength = 0; + isLengthActual = true; + needClear = false; } } /** * Clears the bit at index {@code pos}. Grows the {@code BitSet} if * {@code pos > size}. - * + * * @param pos * the index of the bit to clear. * @throws IndexOutOfBoundsException * if {@code pos} is negative. * @see #clear(int, int) - * @since Android 1.0 */ public void clear(int pos) { - if (pos >= 0) { - if (pos < bits.length * ELM_SIZE) { - bits[pos / ELM_SIZE] &= ~(1L << (pos % ELM_SIZE)); - } - } else { + if (pos < 0) { // Negative index specified throw new IndexOutOfBoundsException(Msg.getString("K0006")); //$NON-NLS-1$ } + + if (!needClear) { + return; + } + int arrayPos = pos >> OFFSET; + if (arrayPos < actualArrayLength) { + bits[arrayPos] &= ~(TWO_N_ARRAY[pos & RIGHT_BITS]); + if (bits[actualArrayLength - 1] == 0) { + isLengthActual = false; + } + } } /** * Clears the bits starting from {@code pos1} to {@code pos2}. Grows the * {@code BitSet} if {@code pos2 > size}. - * + * * @param pos1 - * beginning position. + * inclusive beginning position. * @param pos2 - * ending position. + * exclusive ending position. * @throws IndexOutOfBoundsException * if {@code pos1} or {@code pos2} is negative, or if * {@code pos2} is smaller than {@code pos1}. * @see #clear(int) - * @since Android 1.0 */ public void clear(int pos1, int pos2) { - if (pos1 >= 0 && pos2 >= 0 && pos2 >= pos1) { - int last = (bits.length * ELM_SIZE); - if (pos1 >= last || pos1 == pos2) { - return; - } - if (pos2 > last) { - pos2 = last; - } + if (pos1 < 0 || pos2 < 0 || pos2 < pos1) { + throw new IndexOutOfBoundsException(Msg.getString("K0006")); //$NON-NLS-1$ + } - int idx1 = pos1 / ELM_SIZE; - int idx2 = (pos2 - 1) / ELM_SIZE; - long factor1 = (~0L) << (pos1 % ELM_SIZE); - long factor2 = (~0L) >>> (ELM_SIZE - (pos2 % ELM_SIZE)); + if (!needClear) { + return; + } + int last = (actualArrayLength << OFFSET); + if (pos1 >= last || pos1 == pos2) { + return; + } + if (pos2 > last) { + pos2 = last; + } - if (idx1 == idx2) { - bits[idx1] &= ~(factor1 & factor2); - } else { - bits[idx1] &= ~factor1; - bits[idx2] &= ~factor2; - for (int i = idx1 + 1; i < idx2; i++) { - bits[i] = 0L; - } - } + int idx1 = pos1 >> OFFSET; + int idx2 = (pos2 - 1) >> OFFSET; + long factor1 = (~0L) << (pos1 & RIGHT_BITS); + long factor2 = (~0L) >>> (ELM_SIZE - (pos2 & RIGHT_BITS)); + + if (idx1 == idx2) { + bits[idx1] &= ~(factor1 & factor2); } else { - throw new IndexOutOfBoundsException(Msg.getString("K0006")); //$NON-NLS-1$ + bits[idx1] &= ~factor1; + bits[idx2] &= ~factor2; + for (int i = idx1 + 1; i < idx2; i++) { + bits[i] = 0L; + } + } + if ((actualArrayLength > 0) && (bits[actualArrayLength - 1] == 0)) { + isLengthActual = false; } } /** * Flips the bit at index {@code pos}. Grows the {@code BitSet} if * {@code pos > size}. - * + * * @param pos * the index of the bit to flip. * @throws IndexOutOfBoundsException * if {@code pos} is negative. * @see #flip(int, int) - * @since Android 1.0 */ public void flip(int pos) { - if (pos >= 0) { - if (pos >= bits.length * ELM_SIZE) { - growBits(pos); - } - bits[pos / ELM_SIZE] ^= 1L << (pos % ELM_SIZE); - } else { + if (pos < 0) { throw new IndexOutOfBoundsException(Msg.getString("K0006")); //$NON-NLS-1$ } + + int len = (pos >> OFFSET) + 1; + if (len > bits.length) { + growLength(len); + } + bits[len - 1] ^= TWO_N_ARRAY[pos & RIGHT_BITS]; + if (len > actualArrayLength) { + actualArrayLength = len; + } + isLengthActual = !((actualArrayLength > 0) && (bits[actualArrayLength - 1] == 0)); + needClear(); } /** * Flips the bits starting from {@code pos1} to {@code pos2}. Grows the * {@code BitSet} if {@code pos2 > size}. - * + * * @param pos1 - * beginning position. + * inclusive beginning position. * @param pos2 - * ending position. + * exclusive ending position. * @throws IndexOutOfBoundsException * if {@code pos1} or {@code pos2} is negative, or if * {@code pos2} is smaller than {@code pos1}. * @see #flip(int) - * @since Android 1.0 */ public void flip(int pos1, int pos2) { - if (pos1 >= 0 && pos2 >= 0 && pos2 >= pos1) { - if (pos1 == pos2) { - return; - } - if (pos2 >= bits.length * ELM_SIZE) { - growBits(pos2); - } + if (pos1 < 0 || pos2 < 0 || pos2 < pos1) { + throw new IndexOutOfBoundsException(Msg.getString("K0006")); //$NON-NLS-1$ + } + + if (pos1 == pos2) { + return; + } + int len2 = ((pos2 - 1) >> OFFSET) + 1; + if (len2 > bits.length) { + growLength(len2); + } - int idx1 = pos1 / ELM_SIZE; - int idx2 = (pos2 - 1) / ELM_SIZE; - long factor1 = (~0L) << (pos1 % ELM_SIZE); - long factor2 = (~0L) >>> (ELM_SIZE - (pos2 % ELM_SIZE)); + int idx1 = pos1 >> OFFSET; + int idx2 = (pos2 - 1) >> OFFSET; + long factor1 = (~0L) << (pos1 & RIGHT_BITS); + long factor2 = (~0L) >>> (ELM_SIZE - (pos2 & RIGHT_BITS)); - if (idx1 == idx2) { - bits[idx1] ^= (factor1 & factor2); - } else { - bits[idx1] ^= factor1; - bits[idx2] ^= factor2; - for (int i = idx1 + 1; i < idx2; i++) { - bits[i] ^= (~0L); - } - } + if (idx1 == idx2) { + bits[idx1] ^= (factor1 & factor2); } else { - throw new IndexOutOfBoundsException(Msg.getString("K0006")); //$NON-NLS-1$ + bits[idx1] ^= factor1; + bits[idx2] ^= factor2; + for (int i = idx1 + 1; i < idx2; i++) { + bits[i] ^= (~0L); + } } + if (len2 > actualArrayLength) { + actualArrayLength = len2; + } + isLengthActual = !((actualArrayLength > 0) && (bits[actualArrayLength - 1] == 0)); + needClear(); } /** * Checks if these two {@code BitSet}s have at least one bit set to true in the same * position. - * + * * @param bs * {@code BitSet} used to calculate the intersection. * @return {@code true} if bs intersects with this {@code BitSet}, * {@code false} otherwise. - * @since Android 1.0 */ public boolean intersects(BitSet bs) { long[] bsBits = bs.bits; - int length1 = bits.length, length2 = bsBits.length; + int length1 = actualArrayLength, length2 = bs.actualArrayLength; if (length1 <= length2) { for (int i = 0; i < length1; i++) { @@ -575,19 +645,20 @@ public class BitSet implements Serializable, Cloneable { } /** - * Performs the logical AND of this {@code BitSet} with another + * Performs the logical AND of this {@code BitSet} with another * {@code BitSet}. The values of this {@code BitSet} are changed accordingly. - * + * * @param bs * {@code BitSet} to AND with. * @see #or * @see #xor - * @since Android 1.0 */ - public void and(BitSet bs) { long[] bsBits = bs.bits; - int length1 = bits.length, length2 = bsBits.length; + if (!needClear) { + return; + } + int length1 = actualArrayLength, length2 = bs.actualArrayLength; if (length1 <= length2) { for (int i = 0; i < length1; i++) { bits[i] &= bsBits[i]; @@ -599,113 +670,155 @@ public class BitSet implements Serializable, Cloneable { for (int i = length2; i < length1; i++) { bits[i] = 0; } + actualArrayLength = length2; } + isLengthActual = !((actualArrayLength > 0) && (bits[actualArrayLength - 1] == 0)); } /** * Clears all bits in the receiver which are also set in the parameter * {@code BitSet}. The values of this {@code BitSet} are changed accordingly. - * + * * @param bs * {@code BitSet} to ANDNOT with. - * @since Android 1.0 */ public void andNot(BitSet bs) { long[] bsBits = bs.bits; - int range = bits.length < bsBits.length ? bits.length : bsBits.length; + if (!needClear) { + return; + } + int range = actualArrayLength < bs.actualArrayLength ? actualArrayLength + : bs.actualArrayLength; for (int i = 0; i < range; i++) { bits[i] &= ~bsBits[i]; } + + if (actualArrayLength < range) { + actualArrayLength = range; + } + isLengthActual = !((actualArrayLength > 0) && (bits[actualArrayLength - 1] == 0)); } /** * Performs the logical OR of this {@code BitSet} with another {@code BitSet}. * The values of this {@code BitSet} are changed accordingly. - * + * * @param bs * {@code BitSet} to OR with. * @see #xor * @see #and - * @since Android 1.0 */ public void or(BitSet bs) { - int nbits = bs.length(); - int length = nbits / ELM_SIZE + (nbits % ELM_SIZE > 0 ? 1 : 0); - if (length > bits.length) { - growBits(nbits - 1); - } - long[] bsBits = bs.bits; - for (int i = 0; i < length; i++) { - bits[i] |= bsBits[i]; + int bsActualLen = bs.getActualArrayLength(); + if (bsActualLen > bits.length) { + long[] tempBits = new long[bsActualLen]; + System.arraycopy(bs.bits, 0, tempBits, 0, bs.actualArrayLength); + for (int i = 0; i < actualArrayLength; i++) { + tempBits[i] |= bits[i]; + } + bits = tempBits; + actualArrayLength = bsActualLen; + isLengthActual = true; + } else { + long[] bsBits = bs.bits; + for (int i = 0; i < bsActualLen; i++) { + bits[i] |= bsBits[i]; + } + if (bsActualLen > actualArrayLength) { + actualArrayLength = bsActualLen; + isLengthActual = true; + } } + needClear(); } /** * Performs the logical XOR of this {@code BitSet} with another {@code BitSet}. * The values of this {@code BitSet} are changed accordingly. - * + * * @param bs * {@code BitSet} to XOR with. * @see #or * @see #and - * @since Android 1.0 */ public void xor(BitSet bs) { - int nbits = bs.length(); - int length = nbits / ELM_SIZE + (nbits % ELM_SIZE > 0 ? 1 : 0); - if (length > bits.length) { - growBits(nbits - 1); - } - long[] bsBits = bs.bits; - for (int i = 0; i < length; i++) { - bits[i] ^= bsBits[i]; + int bsActualLen = bs.getActualArrayLength(); + if (bsActualLen > bits.length) { + long[] tempBits = new long[bsActualLen]; + System.arraycopy(bs.bits, 0, tempBits, 0, bs.actualArrayLength); + for (int i = 0; i < actualArrayLength; i++) { + tempBits[i] ^= bits[i]; + } + bits = tempBits; + actualArrayLength = bsActualLen; + isLengthActual = !((actualArrayLength > 0) && (bits[actualArrayLength - 1] == 0)); + } else { + long[] bsBits = bs.bits; + for (int i = 0; i < bsActualLen; i++) { + bits[i] ^= bsBits[i]; + } + if (bsActualLen > actualArrayLength) { + actualArrayLength = bsActualLen; + isLengthActual = true; + } } - + needClear(); } /** * Returns the number of bits this {@code BitSet} has. - * + * * @return the number of bits contained in this {@code BitSet}. * @see #length - * @since Android 1.0 */ public int size() { - return bits.length * ELM_SIZE; + return bits.length << OFFSET; } /** * Returns the number of bits up to and including the highest bit set. - * + * * @return the length of the {@code BitSet}. - * @since Android 1.0 */ public int length() { - int idx = bits.length - 1; + int idx = actualArrayLength - 1; while (idx >= 0 && bits[idx] == 0) { --idx; } + actualArrayLength = idx + 1; if (idx == -1) { return 0; } int i = ELM_SIZE - 1; long val = bits[idx]; - while ((val & (1L << i)) == 0 && i > 0) { + while ((val & (TWO_N_ARRAY[i])) == 0 && i > 0) { i--; } - return idx * ELM_SIZE + i + 1; + return (idx << OFFSET) + i + 1; + } + + private final int getActualArrayLength() { + if (isLengthActual) { + return actualArrayLength; + } + int idx = actualArrayLength - 1; + while (idx >= 0 && bits[idx] == 0) { + --idx; + } + actualArrayLength = idx + 1; + isLengthActual = true; + return actualArrayLength; } /** * Returns a string containing a concise, human-readable description of the * receiver. - * + * * @return a comma delimited list of the indices of all bits that are set. - * @since Android 1.0 */ @Override public String toString() { - StringBuffer sb = new StringBuffer(bits.length / 2); + StringBuilder sb = new StringBuilder(bits.length / 2); int bitCount = 0; sb.append('{'); boolean comma = false; @@ -715,7 +828,7 @@ public class BitSet implements Serializable, Cloneable { continue; } for (int j = 0; j < ELM_SIZE; j++) { - if (((bits[i] & (1L << j)) != 0)) { + if (((bits[i] & (TWO_N_ARRAY[j])) != 0)) { if (comma) { sb.append(", "); //$NON-NLS-1$ } @@ -731,131 +844,152 @@ public class BitSet implements Serializable, Cloneable { /** * Returns the position of the first bit that is {@code true} on or after {@code pos}. - * + * * @param pos * the starting position (inclusive). * @return -1 if there is no bits that are set to {@code true} on or after {@code pos}. - * @since Android 1.0 */ public int nextSetBit(int pos) { - if (pos >= 0) { - if (pos >= bits.length * ELM_SIZE) { - return -1; - } - - int idx = pos / ELM_SIZE; - // first check in the same bit set element - if (bits[idx] != 0L) { - for (int j = pos % ELM_SIZE; j < ELM_SIZE; j++) { - if (((bits[idx] & (1L << j)) != 0)) { - return idx * ELM_SIZE + j; - } - } + if (pos < 0) { + throw new IndexOutOfBoundsException(Msg.getString("K0006")); //$NON-NLS-1$ + } - } - idx++; - while (idx < bits.length && bits[idx] == 0L) { - idx++; - } - if (idx == bits.length) { - return -1; - } + if (pos >= actualArrayLength << OFFSET) { + return -1; + } - // we know for sure there is a bit set to true in this element - // since the bitset value is not 0L - for (int j = 0; j < ELM_SIZE; j++) { - if (((bits[idx] & (1L << j)) != 0)) { - return idx * ELM_SIZE + j; + int idx = pos >> OFFSET; + // first check in the same bit set element + if (bits[idx] != 0L) { + for (int j = pos & RIGHT_BITS; j < ELM_SIZE; j++) { + if (((bits[idx] & (TWO_N_ARRAY[j])) != 0)) { + return (idx << OFFSET) + j; } } + } + idx++; + while (idx < actualArrayLength && bits[idx] == 0L) { + idx++; + } + if (idx == actualArrayLength) { return -1; } - throw new IndexOutOfBoundsException(Msg.getString("K0006")); //$NON-NLS-1$ + + // we know for sure there is a bit set to true in this element + // since the bitset value is not 0L + for (int j = 0; j < ELM_SIZE; j++) { + if (((bits[idx] & (TWO_N_ARRAY[j])) != 0)) { + return (idx << OFFSET) + j; + } + } + + return -1; } /** * Returns the position of the first bit that is {@code false} on or after {@code pos}. - * + * * @param pos * the starting position (inclusive). * @return the position of the next bit set to {@code false}, even if it is further * than this {@code BitSet}'s size. - * @since Android 1.0 */ public int nextClearBit(int pos) { - if (pos >= 0) { - int bssize = bits.length * ELM_SIZE; - if (pos >= bssize) { - return pos; - } - - int idx = pos / ELM_SIZE; - // first check in the same bit set element - if (bits[idx] != (~0L)) { - for (int j = pos % ELM_SIZE; j < ELM_SIZE; j++) { - if (((bits[idx] & (1L << j)) == 0)) { - return idx * ELM_SIZE + j; - } - } + if (pos < 0) { + throw new IndexOutOfBoundsException(Msg.getString("K0006")); //$NON-NLS-1$ + } - } - idx++; - while (idx < bits.length && bits[idx] == (~0L)) { - idx++; - } - if (idx == bits.length) { - return bssize; - } + int length = actualArrayLength; + int bssize = length << OFFSET; + if (pos >= bssize) { + return pos; + } - // we know for sure there is a bit set to true in this element - // since the bitset value is not 0L - for (int j = 0; j < ELM_SIZE; j++) { - if (((bits[idx] & (1L << j)) == 0)) { + int idx = pos >> OFFSET; + // first check in the same bit set element + if (bits[idx] != (~0L)) { + for (int j = pos % ELM_SIZE; j < ELM_SIZE; j++) { + if (((bits[idx] & (TWO_N_ARRAY[j])) == 0)) { return idx * ELM_SIZE + j; } } - + } + idx++; + while (idx < length && bits[idx] == (~0L)) { + idx++; + } + if (idx == length) { return bssize; } - throw new IndexOutOfBoundsException(Msg.getString("K0006")); //$NON-NLS-1$ + + // we know for sure there is a bit set to true in this element + // since the bitset value is not 0L + for (int j = 0; j < ELM_SIZE; j++) { + if (((bits[idx] & (TWO_N_ARRAY[j])) == 0)) { + return (idx << OFFSET) + j; + } + } + + return bssize; } /** * Returns true if all the bits in this {@code BitSet} are set to false. - * + * * @return {@code true} if the {@code BitSet} is empty, * {@code false} otherwise. - * @since Android 1.0 */ public boolean isEmpty() { - for (int idx = 0; idx < bits.length; idx++) { + if (!needClear) { + return true; + } + int length = bits.length; + for (int idx = 0; idx < length; idx++) { if (bits[idx] != 0L) { return false; } } - return true; } /** * Returns the number of bits that are {@code true} in this {@code BitSet}. - * + * * @return the number of {@code true} bits in the set. - * @since Android 1.0 */ public int cardinality() { + if (!needClear) { + return 0; + } int count = 0; - for (int idx = 0; idx < bits.length; idx++) { - long temp = bits[idx]; - if (temp != 0L) { - for (int i = 0; i < ELM_SIZE; i++) { - if ((temp & (1L << i)) != 0L) { - count++; - } - } - } + int length = bits.length; + // FIXME: need to test performance, if still not satisfied, change it to + // 256-bits table based + for (int idx = 0; idx < length; idx++) { + count += pop(bits[idx] & 0xffffffffL); + count += pop(bits[idx] >>> 32); } return count; } + + private final int pop(long x) { + // BEGIN android-note + // delegate to Integer.bitCount(i); consider using native code + // END android-note + x = x - ((x >>> 1) & 0x55555555); + x = (x & 0x33333333) + ((x >>> 2) & 0x33333333); + x = (x + (x >>> 4)) & 0x0f0f0f0f; + x = x + (x >>> 8); + x = x + (x >>> 16); + return (int) x & 0x0000003f; + } + + private void readObject(ObjectInputStream ois) throws IOException, + ClassNotFoundException { + ois.defaultReadObject(); + this.isLengthActual = false; + this.actualArrayLength = bits.length; + this.needClear = this.getActualArrayLength() != 0; + } } diff --git a/libcore/luni/src/main/java/java/util/Calendar.java b/libcore/luni/src/main/java/java/util/Calendar.java index 98840b224..0ac574cb8 100644 --- a/libcore/luni/src/main/java/java/util/Calendar.java +++ b/libcore/luni/src/main/java/java/util/Calendar.java @@ -14,27 +14,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/** -******************************************************************************* -* Copyright (C) 1996-2008, International Business Machines Corporation and * -* others. All Rights Reserved. * -******************************************************************************* -*/ package java.util; - import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectStreamField; import java.io.Serializable; -// BEGIN android-note -// The class javadoc and some of the method descriptions are copied from ICU4J -// source files. Changes have been made to the copied descriptions. -// The icu license header was added to this file. -// END android-note /** * {@code Calendar} is an abstract base class for converting between a * {@code Date} object and a set of integer fields such as @@ -42,22 +30,22 @@ import java.io.Serializable; * {@code HOUR}, and so on. (A {@code Date} object represents a * specific instant in time with millisecond precision. See {@link Date} for * information about the {@code Date} class.) - * + * *

          * Subclasses of {@code Calendar} interpret a {@code Date} * according to the rules of a specific calendar system. - * + * *

          * Like other locale-sensitive classes, {@code Calendar} provides a class * method, {@code getInstance}, for getting a default instance of * this class for general use. {@code Calendar}'s {@code getInstance} method * returns a calendar whose locale is based on system settings and whose time fields * have been initialized with the current date and time:

          - * + * *
          Calendar rightNow = Calendar.getInstance()
          - * + * *
          - * + * *

          * A {@code Calendar} object can produce all the time field values needed * to implement the date-time formatting for a particular language and calendar @@ -68,7 +56,7 @@ import java.io.Serializable; * Other values are defined by the concrete subclass, such as {@code ERA} * and {@code YEAR}. See individual field documentation and subclass * documentation for details. - * + * *

          * When a {@code Calendar} is lenient, it accepts a wider * range of field values than it produces. For example, a lenient @@ -79,14 +67,14 @@ import java.io.Serializable; * by {@code get()}, they normalize them. For example, a * {@code GregorianCalendar} always produces {@code DAY_OF_MONTH} * values between 1 and the length of the month. - * + * *

          * {@code Calendar} defines a locale-specific seven day week using two * parameters: the first day of the week and the minimal days in first week * (from 1 to 7). These numbers are taken from the locale resource data when a * {@code Calendar} is constructed. They may also be specified explicitly * through the API. - * + * *

          * When setting or getting the {@code WEEK_OF_MONTH} or * {@code WEEK_OF_YEAR} fields, {@code Calendar} must determine @@ -99,76 +87,74 @@ import java.io.Serializable; * be different. For example, a specific {@code Calendar} subclass may * designate the week before week 1 of a year as week n of the * previous year. - * + * *

          * When computing a {@code Date} from time fields, two special * circumstances may arise: there may be insufficient information to compute the * {@code Date} (such as only year and month but no day in the month), or * there may be inconsistent information (such as "Tuesday, July 15, 1996" -- * July 15, 1996 is actually a Monday). - * + * *

          * Insufficient information. The calendar will use default * information to specify the missing fields. This may vary by calendar; for the * Gregorian calendar, the default for a field is the same as that of the start * of the epoch: i.e., YEAR = 1970, MONTH = JANUARY, DATE = 1, etc. - * + * *

          * Inconsistent information. If fields conflict, the calendar * will give preference to fields set more recently. For example, when * determining the day, the calendar will look for one of the following * combinations of fields. The most recent combination, as determined by the * most recently set single field, will be used. - * + * *

          - * + * *
            * MONTH + DAY_OF_MONTH
            * MONTH + WEEK_OF_MONTH + DAY_OF_WEEK
            * MONTH + DAY_OF_WEEK_IN_MONTH + DAY_OF_WEEK
            * DAY_OF_YEAR
            * DAY_OF_WEEK + WEEK_OF_YEAR
          - * + * *
          - * + * * For the time of day: - * + * *
          - * + * *
            * HOUR_OF_DAY
            * AM_PM + HOUR
          - * + * *
          - * + * *

          * Note: There are certain possible ambiguities in * interpretation of certain singular times, which are resolved in the following * ways: *

            *
          1. 24:00:00 "belongs" to the following day. That is, 23:59 on Dec 31, 1969 - * < 24:00 on Jan 1, 1970 < 24:01:00 on Jan 1, 1970 form a sequence of - * three consecutive minutes in time. - * + * < 24:00 on Jan 1, 1970 < 24:01:00 on Jan 1, 1970 form a sequence of + * three consecutive minutes in time. + * *
          2. Although historically not precise, midnight also belongs to "am", and * noon belongs to "pm", so on the same day, we have 12:00 am (midnight) < 12:01 am, * and 12:00 pm (noon) < 12:01 pm *
          - * + * *

          * The date or time format strings are not part of the definition of a calendar, * as those must be modifiable or overridable by the user at runtime. Use * {@link java.text.DateFormat} to format dates. - * + * *

          * Field manipulation methods - *

          - * + * *

          * {@code Calendar} fields can be changed using three methods: * {@code set()}, {@code add()}, and {@code roll()}. - *

          - * + * *

          * {@code set(f, value)} changes field {@code f} * to {@code value}. In addition, it sets an internal member variable to @@ -182,8 +168,7 @@ import java.io.Serializable; * the calendar system. In addition, {@code get(f)} will not necessarily * return {@code value} after the fields have been recomputed. The * specifics are determined by the concrete calendar class. - *

          - * + * *

          * Example: Consider a {@code GregorianCalendar} originally * set to August 31, 1999. Calling set(Calendar.MONTH, @@ -193,15 +178,13 @@ import java.io.Serializable; * then called. However, a call to {@code set(Calendar.DAY_OF_MONTH, 30)} * before the call to {@code getTime()} sets the calendar to September * 30, 1999, since no recomputation occurs after {@code set()} itself. - *

          - * + * *

          * {@code add(f, delta)} adds {@code delta} to * field {@code f}. This is equivalent to calling set(f, * get(f) + delta) * with two adjustments: - *

          - * + * *
          *

          * Add rule 1. The value of field {@code f} after the @@ -210,8 +193,7 @@ import java.io.Serializable; * {@code f}. Overflow occurs when a field value exceeds its range and, * as a result, the next larger field is incremented or decremented and the * field value is adjusted back into its range. - *

          - * + * *

          * Add rule 2. If a smaller field is expected to be invariant, * but   it is impossible for it to be equal to its prior value because of @@ -221,14 +203,12 @@ import java.io.Serializable; * smaller field than {@code DAY_OF_MONTH}. No adjustment is made to * smaller fields that are not expected to be invariant. The calendar system * determines what fields are expected to be invariant. - *

          *
          - * + * *

          * In addition, unlike {@code set()}, {@code add()} forces an * immediate recomputation of the calendar's milliseconds and all fields. - *

          - * + * *

          * Example: Consider a {@code GregorianCalendar} originally * set to August 31, 1999. Calling {@code add(Calendar.MONTH, 13)} sets @@ -240,22 +220,19 @@ import java.io.Serializable; * Although it is a smaller field, {@code DAY_OF_WEEK} is not adjusted by * rule 2, since it is expected to change when the month changes in a * {@code GregorianCalendar}. - *

          - * + * *

          * {@code roll(f, delta)} adds {@code delta} to * field {@code f} without changing larger fields. This is equivalent to * calling {@code add(f, delta)} with the following adjustment: - *

          - * + * *
          *

          * Roll rule. Larger fields are unchanged after the call. A * larger field represents a larger unit of time. {@code DAY_OF_MONTH} is * a larger field than {@code HOUR}. - *

          *
          - * + * *

          * Example: Consider a {@code GregorianCalendar} originally * set to August 31, 1999. Calling roll(Calendar.MONTH, @@ -265,8 +242,7 @@ import java.io.Serializable; * the {@code DAY_OF_MONTH} cannot be 31 in the month April. Add rule 2 * sets it to the closest possible value, 30. Finally, the roll rule * maintains the {@code YEAR} field value of 1999. - *

          - * + * *

          * Example: Consider a {@code GregorianCalendar} originally * set to Sunday June 6, 1999. Calling @@ -279,8 +255,7 @@ import java.io.Serializable; * According to add rule 2, the {@code DAY_OF_WEEK}, an invariant when * changing the {@code WEEK_OF_MONTH}, is set to Tuesday, the closest * possible value to Sunday (where Sunday is the first day of the week). - *

          - * + * *

          * Usage model. To motivate the behavior of {@code add()} * and {@code roll()}, consider a user interface component with @@ -294,8 +269,7 @@ import java.io.Serializable; * {@code add()} or {@code roll()}, depending on whether larger * fields should be affected, the user interface can behave as most users will * intuitively expect. - *

          - * + * *

          * Note: You should always use {@code roll} and {@code add} rather than * attempting to perform arithmetic operations directly on the fields of a @@ -304,11 +278,10 @@ import java.io.Serializable; * during non-leap years. The subclasses' add and roll * methods will take this into account, while simple arithmetic manipulations * may give invalid results. - * + * * @see Date * @see GregorianCalendar * @see TimeZone - * @since Android 1.0 */ public abstract class Calendar implements Serializable, Cloneable, Comparable { @@ -318,38 +291,28 @@ public abstract class Calendar implements Serializable, Cloneable, /** * Set to {@code true} when the calendar fields have been set from the time, set to * {@code false} when a field is changed and the fields must be recomputed. - * - * @since Android 1.0 */ protected boolean areFieldsSet; /** * An integer array of calendar fields. The length is {@code FIELD_COUNT}. - * - * @since Android 1.0 */ protected int[] fields; /** * A boolean array. Each element indicates if the corresponding field has * been set. The length is {@code FIELD_COUNT}. - * - * @since Android 1.0 */ protected boolean[] isSet; /** * Set to {@code true} when the time has been set, set to {@code false} when a field is * changed and the time must be recomputed. - * - * @since Android 1.0 */ protected boolean isTimeSet; /** * The time in milliseconds since January 1, 1970. - * - * @since Android 1.0 */ protected long time; @@ -365,100 +328,75 @@ public abstract class Calendar implements Serializable, Cloneable, private TimeZone zone; - // BEGIN android-changed /** * Value of the {@code MONTH} field indicating the first month of the * year. - * - * @since Android 1.0 */ public static final int JANUARY = 0; /** * Value of the {@code MONTH} field indicating the second month of * the year. - * - * @since Android 1.0 */ public static final int FEBRUARY = 1; /** * Value of the {@code MONTH} field indicating the third month of the * year. - * - * @since Android 1.0 */ public static final int MARCH = 2; /** * Value of the {@code MONTH} field indicating the fourth month of * the year. - * - * @since Android 1.0 */ public static final int APRIL = 3; /** * Value of the {@code MONTH} field indicating the fifth month of the * year. - * - * @since Android 1.0 */ public static final int MAY = 4; /** * Value of the {@code MONTH} field indicating the sixth month of the * year. - * - * @since Android 1.0 */ public static final int JUNE = 5; /** * Value of the {@code MONTH} field indicating the seventh month of * the year. - * - * @since Android 1.0 */ public static final int JULY = 6; /** * Value of the {@code MONTH} field indicating the eighth month of * the year. - * - * @since Android 1.0 */ public static final int AUGUST = 7; /** * Value of the {@code MONTH} field indicating the ninth month of the * year. - * - * @since Android 1.0 */ public static final int SEPTEMBER = 8; /** * Value of the {@code MONTH} field indicating the tenth month of the * year. - * - * @since Android 1.0 */ public static final int OCTOBER = 9; /** * Value of the {@code MONTH} field indicating the eleventh month of * the year. - * - * @since Android 1.0 */ public static final int NOVEMBER = 10; /** * Value of the {@code MONTH} field indicating the twelfth month of * the year. - * - * @since Android 1.0 */ public static final int DECEMBER = 11; @@ -466,57 +404,41 @@ public abstract class Calendar implements Serializable, Cloneable, * Value of the {@code MONTH} field indicating the thirteenth month * of the year. Although {@code GregorianCalendar} does not use this * value, lunar calendars do. - * - * @since Android 1.0 */ public static final int UNDECIMBER = 12; /** * Value of the {@code DAY_OF_WEEK} field indicating Sunday. - * - * @since Android 1.0 */ public static final int SUNDAY = 1; /** * Value of the {@code DAY_OF_WEEK} field indicating Monday. - * - * @since Android 1.0 */ public static final int MONDAY = 2; /** * Value of the {@code DAY_OF_WEEK} field indicating Tuesday. - * - * @since Android 1.0 */ public static final int TUESDAY = 3; /** * Value of the {@code DAY_OF_WEEK} field indicating Wednesday. - * - * @since Android 1.0 */ public static final int WEDNESDAY = 4; /** * Value of the {@code DAY_OF_WEEK} field indicating Thursday. - * - * @since Android 1.0 */ public static final int THURSDAY = 5; /** * Value of the {@code DAY_OF_WEEK} field indicating Friday. - * - * @since Android 1.0 */ public static final int FRIDAY = 6; /** * Value of the {@code DAY_OF_WEEK} field indicating Saturday. - * - * @since Android 1.0 */ public static final int SATURDAY = 7; @@ -524,19 +446,15 @@ public abstract class Calendar implements Serializable, Cloneable, * Field number for {@code get} and {@code set} indicating the * era, e.g., AD or BC in the Julian calendar. This is a calendar-specific * value; see subclass documentation. - * + * * @see GregorianCalendar#AD * @see GregorianCalendar#BC - * - * @since Android 1.0 */ public static final int ERA = 0; /** * Field number for {@code get} and {@code set} indicating the * year. This is a calendar-specific value; see subclass documentation. - * - * @since Android 1.0 */ public static final int YEAR = 1; @@ -545,7 +463,7 @@ public abstract class Calendar implements Serializable, Cloneable, * month. This is a calendar-specific value. The first month of the year is * {@code JANUARY}; the last depends on the number of months in a * year. - * + * * @see #JANUARY * @see #FEBRUARY * @see #MARCH @@ -559,8 +477,6 @@ public abstract class Calendar implements Serializable, Cloneable, * @see #NOVEMBER * @see #DECEMBER * @see #UNDECIMBER - * - * @since Android 1.0 */ public static final int MONTH = 2; @@ -571,11 +487,9 @@ public abstract class Calendar implements Serializable, Cloneable, * {@code getMinimalDaysInFirstWeek()}, has value 1. Subclasses * define the value of {@code WEEK_OF_YEAR} for days before the first * week of the year. - * + * * @see #getFirstDayOfWeek * @see #getMinimalDaysInFirstWeek - * - * @since Android 1.0 */ public static final int WEEK_OF_YEAR = 3; @@ -586,11 +500,9 @@ public abstract class Calendar implements Serializable, Cloneable, * {@code getMinimalDaysInFirstWeek()}, has value 1. Subclasses * define the value of {@code WEEK_OF_MONTH} for days before the * first week of the month. - * + * * @see #getFirstDayOfWeek * @see #getMinimalDaysInFirstWeek - * - * @since Android 1.0 */ public static final int WEEK_OF_MONTH = 4; @@ -598,10 +510,8 @@ public abstract class Calendar implements Serializable, Cloneable, * Field number for {@code get} and {@code set} indicating the * day of the month. This is a synonym for {@code DAY_OF_MONTH}. The * first day of the month has value 1. - * + * * @see #DAY_OF_MONTH - * - * @since Android 1.0 */ public static final int DATE = 5; @@ -609,10 +519,8 @@ public abstract class Calendar implements Serializable, Cloneable, * Field number for {@code get} and {@code set} indicating the * day of the month. This is a synonym for {@code DATE}. The first * day of the month has value 1. - * + * * @see #DATE - * - * @since Android 1.0 */ public static final int DAY_OF_MONTH = 5; @@ -620,8 +528,6 @@ public abstract class Calendar implements Serializable, Cloneable, * Field number for {@code get} and {@code set} indicating the * day number within the current year. The first day of the year has value * 1. - * - * @since Android 1.0 */ public static final int DAY_OF_YEAR = 6; @@ -631,7 +537,7 @@ public abstract class Calendar implements Serializable, Cloneable, * {@code MONDAY}, {@code TUESDAY}, {@code WEDNESDAY}, * {@code THURSDAY}, {@code FRIDAY}, and * {@code SATURDAY}. - * + * * @see #SUNDAY * @see #MONDAY * @see #TUESDAY @@ -639,8 +545,6 @@ public abstract class Calendar implements Serializable, Cloneable, * @see #THURSDAY * @see #FRIDAY * @see #SATURDAY - * - * @since Android 1.0 */ public static final int DAY_OF_WEEK = 7; @@ -664,11 +568,9 @@ public abstract class Calendar implements Serializable, Cloneable, * within the month than positive values. For example, if a month has 31 * days, {@code DAY_OF_WEEK_IN_MONTH -1} will overlap * {@code DAY_OF_WEEK_IN_MONTH 5} and the end of {@code 4}. - * + * * @see #DAY_OF_WEEK * @see #WEEK_OF_MONTH - * - * @since Android 1.0 */ public static final int DAY_OF_WEEK_IN_MONTH = 8; @@ -676,12 +578,10 @@ public abstract class Calendar implements Serializable, Cloneable, * Field number for {@code get} and {@code set} indicating * whether the {@code HOUR} is before or after noon. E.g., at * 10:04:15.250 PM the {@code AM_PM} is {@code PM}. - * + * * @see #AM * @see #PM * @see #HOUR - * - * @since Android 1.0 */ public static final int AM_PM = 9; @@ -689,11 +589,9 @@ public abstract class Calendar implements Serializable, Cloneable, * Field number for {@code get} and {@code set} indicating the * hour of the morning or afternoon. {@code HOUR} is used for the * 12-hour clock. E.g., at 10:04:15.250 PM the {@code HOUR} is 10. - * + * * @see #AM_PM * @see #HOUR_OF_DAY - * - * @since Android 1.0 */ public static final int HOUR = 10; @@ -701,10 +599,8 @@ public abstract class Calendar implements Serializable, Cloneable, * Field number for {@code get} and {@code set} indicating the * hour of the day. {@code HOUR_OF_DAY} is used for the 24-hour * clock. E.g., at 10:04:15.250 PM the {@code HOUR_OF_DAY} is 22. - * + * * @see #HOUR - * - * @since Android 1.0 */ public static final int HOUR_OF_DAY = 11; @@ -712,8 +608,6 @@ public abstract class Calendar implements Serializable, Cloneable, * Field number for {@code get} and {@code set} indicating the * minute within the hour. E.g., at 10:04:15.250 PM the {@code MINUTE} * is 4. - * - * @since Android 1.0 */ public static final int MINUTE = 12; @@ -721,8 +615,6 @@ public abstract class Calendar implements Serializable, Cloneable, * Field number for {@code get} and {@code set} indicating the * second within the minute. E.g., at 10:04:15.250 PM the * {@code SECOND} is 15. - * - * @since Android 1.0 */ public static final int SECOND = 13; @@ -730,61 +622,46 @@ public abstract class Calendar implements Serializable, Cloneable, * Field number for {@code get} and {@code set} indicating the * millisecond within the second. E.g., at 10:04:15.250 PM the * {@code MILLISECOND} is 250. - * - * @since Android 1.0 */ public static final int MILLISECOND = 14; /** * Field number for {@code get} and {@code set} indicating the * raw offset from GMT in milliseconds. - * - * @since Android 1.0 */ public static final int ZONE_OFFSET = 15; /** * Field number for {@code get} and {@code set} indicating the * daylight savings offset in milliseconds. - * - * @since Android 1.0 */ public static final int DST_OFFSET = 16; /** * This is the total number of fields in this calendar. - * - * @since Android 1.0 */ public static final int FIELD_COUNT = 17; /** * Value of the {@code AM_PM} field indicating the period of the day * from midnight to just before noon. - * - * @since Android 1.0 */ public static final int AM = 0; /** * Value of the {@code AM_PM} field indicating the period of the day * from noon to just before midnight. - * - * @since Android 1.0 */ public static final int PM = 1; - // END android-changed private static String[] fieldNames = { "ERA=", "YEAR=", "MONTH=", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - "WEEK_OF_YEAR=", "WEEK_OF_MONTH=", "DAY_OF_MONTH=", "DAY_OF_YEAR=", //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ - "DAY_OF_WEEK=", "DAY_OF_WEEK_IN_MONTH=", "AM_PM=", "HOUR=", //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ //$NON-NLS-4$ + "WEEK_OF_YEAR=", "WEEK_OF_MONTH=", "DAY_OF_MONTH=", "DAY_OF_YEAR=", //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + "DAY_OF_WEEK=", "DAY_OF_WEEK_IN_MONTH=", "AM_PM=", "HOUR=", //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ //$NON-NLS-4$ "HOUR_OF_DAY", "MINUTE=", "SECOND=", "MILLISECOND=", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ "ZONE_OFFSET=", "DST_OFFSET=" }; //$NON-NLS-1$ //$NON-NLS-2$ /** * Constructs a {@code Calendar} instance using the default {@code TimeZone} and {@code Locale}. - * - * @since Android 1.0 */ protected Calendar() { this(TimeZone.getDefault(), Locale.getDefault()); @@ -800,30 +677,38 @@ public abstract class Calendar implements Serializable, Cloneable, /** * Constructs a {@code Calendar} instance using the specified {@code TimeZone} and {@code Locale}. - * + * * @param timezone * the timezone. * @param locale * the locale. - * - * @since Android 1.0 */ protected Calendar(TimeZone timezone, Locale locale) { this(timezone); + // BEGIN android-changed + // com.ibm.icu.util.Calendar icuCalendar = com.ibm.icu.util.Calendar + // .getInstance(com.ibm.icu.util.SimpleTimeZone + // .getTimeZone(timezone.getID()), locale); + // setFirstDayOfWeek(icuCalendar.getFirstDayOfWeek()); + // setMinimalDaysInFirstWeek(icuCalendar.getMinimalDaysInFirstWeek()); ResourceBundle bundle = Locale.getBundle("Locale", locale); //$NON-NLS-1$ setFirstDayOfWeek(((Integer) bundle.getObject("First_Day")).intValue()); //$NON-NLS-1$ setMinimalDaysInFirstWeek(((Integer) bundle.getObject("Minimal_Days")) //$NON-NLS-1$ .intValue()); + // END android-changed } + /** * Adds the specified amount to a {@code Calendar} field. - * + * * @param field * the {@code Calendar} field to modify. * @param value * the amount to add to the field. - * @since Android 1.0 + * @throws IllegalArgumentException + * if {@code field} is {@code DST_OFFSET} or {@code + * ZONE_OFFSET}. */ abstract public void add(int field, int value); @@ -831,11 +716,13 @@ public abstract class Calendar implements Serializable, Cloneable, * Returns whether the {@code Date} specified by this {@code Calendar} instance is after the {@code Date} * specified by the parameter. The comparison is not dependent on the time * zones of the {@code Calendar}. - * + * * @param calendar * the {@code Calendar} instance to compare. * @return {@code true} when this Calendar is after calendar, {@code false} otherwise. - * @since Android 1.0 + * @throws IllegalArgumentException + * if the time is not set and the time cannot be computed + * from the current field values. */ public boolean after(Object calendar) { if (!(calendar instanceof Calendar)) { @@ -848,11 +735,13 @@ public abstract class Calendar implements Serializable, Cloneable, * Returns whether the {@code Date} specified by this {@code Calendar} instance is before the * {@code Date} specified by the parameter. The comparison is not dependent on the * time zones of the {@code Calendar}. - * + * * @param calendar * the {@code Calendar} instance to compare. * @return {@code true} when this Calendar is before calendar, {@code false} otherwise. - * @since Android 1.0 + * @throws IllegalArgumentException + * if the time is not set and the time cannot be computed + * from the current field values. */ public boolean before(Object calendar) { if (!(calendar instanceof Calendar)) { @@ -864,8 +753,6 @@ public abstract class Calendar implements Serializable, Cloneable, /** * Clears all of the fields of this {@code Calendar}. All fields are initialized to * zero. - * - * @since Android 1.0 */ public final void clear() { for (int i = 0; i < FIELD_COUNT; i++) { @@ -877,10 +764,9 @@ public abstract class Calendar implements Serializable, Cloneable, /** * Clears the specified field to zero and sets the isSet flag to {@code false}. - * + * * @param field * the field to clear. - * @since Android 1.0 */ public final void clear(int field) { fields[field] = 0; @@ -890,11 +776,10 @@ public abstract class Calendar implements Serializable, Cloneable, /** * Returns a new {@code Calendar} with the same properties. - * + * * @return a shallow copy of this {@code Calendar}. - * + * * @see java.lang.Cloneable - * @since Android 1.0 */ @Override public Object clone() { @@ -912,11 +797,10 @@ public abstract class Calendar implements Serializable, Cloneable, /** * Computes the time from the fields if the time has not already been set. * Computes the fields from the time if the fields are not already set. - * - * @exception IllegalArgumentException - * when the time is not set and the time cannot be computed + * + * @throws IllegalArgumentException + * if the time is not set and the time cannot be computed * from the current field values. - * @since Android 1.0 */ protected void complete() { if (!isTimeSet) { @@ -931,18 +815,15 @@ public abstract class Calendar implements Serializable, Cloneable, /** * Computes the {@code Calendar} fields from {@code time}. - * - * @since Android 1.0 */ protected abstract void computeFields(); /** * Computes {@code time} from the Calendar fields. - * - * @exception IllegalArgumentException - * when the time cannot be computed from the current field + * + * @throws IllegalArgumentException + * if the time cannot be computed from the current field * values. - * @since Android 1.0 */ protected abstract void computeTime(); @@ -950,12 +831,11 @@ public abstract class Calendar implements Serializable, Cloneable, * Compares the specified object to this {@code Calendar} and returns whether they are * equal. The object must be an instance of {@code Calendar} and have the same * properties. - * + * * @param object * the object to compare with this object. * @return {@code true} if the specified object is equal to this {@code Calendar}, {@code false} * otherwise. - * @since Android 1.0 */ @Override public boolean equals(Object object) { @@ -977,18 +857,17 @@ public abstract class Calendar implements Serializable, Cloneable, /** * Gets the value of the specified field after computing the field values by * calling {@code complete()} first. - * + * * @param field * the field to get. * @return the value of the specified field. - * - * @exception IllegalArgumentException - * when the fields are not set, the time is not set, and the + * + * @throws IllegalArgumentException + * if the fields are not set, the time is not set, and the * time cannot be computed from the current field values. - * @exception ArrayIndexOutOfBoundsException + * @throws ArrayIndexOutOfBoundsException * if the field is not inside the range of possible fields. * The range is starting at 0 up to {@code FIELD_COUNT}. - * @since Android 1.0 */ public int get(int field) { complete(); @@ -997,11 +876,10 @@ public abstract class Calendar implements Serializable, Cloneable, /** * Gets the maximum value of the specified field for the current date. - * + * * @param field * the field. * @return the maximum value of the specified field. - * @since Android 1.0 */ public int getActualMaximum(int field) { int value, next; @@ -1023,11 +901,10 @@ public abstract class Calendar implements Serializable, Cloneable, /** * Gets the minimum value of the specified field for the current date. - * + * * @param field * the field. * @return the minimum value of the specified field. - * @since Android 1.0 */ public int getActualMinimum(int field) { int value, next; @@ -1049,9 +926,8 @@ public abstract class Calendar implements Serializable, Cloneable, /** * Gets the list of installed {@code Locale}s which support {@code Calendar}. - * + * * @return an array of {@code Locale}. - * @since Android 1.0 */ public static synchronized Locale[] getAvailableLocales() { return Locale.getAvailableLocales(); @@ -1059,9 +935,8 @@ public abstract class Calendar implements Serializable, Cloneable, /** * Gets the first day of the week for this {@code Calendar}. - * + * * @return the first day of the week. - * @since Android 1.0 */ public int getFirstDayOfWeek() { return firstDayOfWeek; @@ -1071,21 +946,19 @@ public abstract class Calendar implements Serializable, Cloneable, * Gets the greatest minimum value of the specified field. This is the * biggest value that {@code getActualMinimum} can return for any possible * time. - * + * * @param field * the field. * @return the greatest minimum value of the specified field. - * @since Android 1.0 */ abstract public int getGreatestMinimum(int field); /** * Constructs a new instance of the {@code Calendar} subclass appropriate for the * default {@code Locale}. - * + * * @return a {@code Calendar} subclass instance set to the current date and time in * the default {@code Timezone}. - * @since Android 1.0 */ public static synchronized Calendar getInstance() { return new GregorianCalendar(); @@ -1094,11 +967,10 @@ public abstract class Calendar implements Serializable, Cloneable, /** * Constructs a new instance of the {@code Calendar} subclass appropriate for the * specified {@code Locale}. - * + * * @param locale * the locale to use. * @return a {@code Calendar} subclass instance set to the current date and time. - * @since Android 1.0 */ public static synchronized Calendar getInstance(Locale locale) { return new GregorianCalendar(locale); @@ -1107,12 +979,11 @@ public abstract class Calendar implements Serializable, Cloneable, /** * Constructs a new instance of the {@code Calendar} subclass appropriate for the * default {@code Locale}, using the specified {@code TimeZone}. - * + * * @param timezone * the {@code TimeZone} to use. * @return a {@code Calendar} subclass instance set to the current date and time in * the specified timezone. - * @since Android 1.0 */ public static synchronized Calendar getInstance(TimeZone timezone) { return new GregorianCalendar(timezone); @@ -1121,14 +992,13 @@ public abstract class Calendar implements Serializable, Cloneable, /** * Constructs a new instance of the {@code Calendar} subclass appropriate for the * specified {@code Locale}. - * + * * @param timezone * the {@code TimeZone} to use. * @param locale * the {@code Locale} to use. * @return a {@code Calendar} subclass instance set to the current date and time in * the specified timezone. - * @since Android 1.0 */ public static synchronized Calendar getInstance(TimeZone timezone, Locale locale) { @@ -1139,30 +1009,27 @@ public abstract class Calendar implements Serializable, Cloneable, * Gets the smallest maximum value of the specified field. This is the * smallest value that {@code getActualMaximum()} can return for any * possible time. - * + * * @param field * the field number. * @return the smallest maximum value of the specified field. - * @since Android 1.0 */ abstract public int getLeastMaximum(int field); /** * Gets the greatest maximum value of the specified field. This returns the * biggest value that {@code get} can return for the specified field. - * + * * @param field * the field. * @return the greatest maximum value of the specified field. - * @since Android 1.0 */ abstract public int getMaximum(int field); /** * Gets the minimal days in the first week of the year. - * + * * @return the minimal days in the first week of the year. - * @since Android 1.0 */ public int getMinimalDaysInFirstWeek() { return minimalDaysInFirstWeek; @@ -1171,23 +1038,21 @@ public abstract class Calendar implements Serializable, Cloneable, /** * Gets the smallest minimum value of the specified field. this returns the * smallest value thet {@code get} can return for the specified field. - * + * * @param field * the field number. * @return the smallest minimum value of the specified field. - * @since Android 1.0 */ abstract public int getMinimum(int field); /** * Gets the time of this {@code Calendar} as a {@code Date} object. - * + * * @return a new {@code Date} initialized to the time of this {@code Calendar}. - * - * @exception IllegalArgumentException - * when the time is not set and the time cannot be computed + * + * @throws IllegalArgumentException + * if the time is not set and the time cannot be computed * from the current field values. - * @since Android 1.0 */ public final Date getTime() { return new Date(getTimeInMillis()); @@ -1195,13 +1060,12 @@ public abstract class Calendar implements Serializable, Cloneable, /** * Computes the time from the fields if required and returns the time. - * + * * @return the time of this {@code Calendar}. - * - * @exception IllegalArgumentException - * when the time is not set and the time cannot be computed + * + * @throws IllegalArgumentException + * if the time is not set and the time cannot be computed * from the current field values. - * @since Android 1.0 */ public long getTimeInMillis() { if (!isTimeSet) { @@ -1213,9 +1077,8 @@ public abstract class Calendar implements Serializable, Cloneable, /** * Gets the timezone of this {@code Calendar}. - * + * * @return the {@code TimeZone} used by this {@code Calendar}. - * @since Android 1.0 */ public TimeZone getTimeZone() { return zone; @@ -1224,11 +1087,10 @@ public abstract class Calendar implements Serializable, Cloneable, /** * Returns an integer hash code for the receiver. Objects which are equal * return the same value for this method. - * + * * @return the receiver's hash. - * + * * @see #equals - * @since Android 1.0 */ @Override public int hashCode() { @@ -1238,11 +1100,10 @@ public abstract class Calendar implements Serializable, Cloneable, /** * Gets the value of the specified field without recomputing. - * + * * @param field * the field. * @return the value of the specified field. - * @since Android 1.0 */ protected final int internalGet(int field) { return fields[field]; @@ -1251,9 +1112,8 @@ public abstract class Calendar implements Serializable, Cloneable, /** * Returns if this {@code Calendar} accepts field values which are outside the valid * range for the field. - * + * * @return {@code true} if this {@code Calendar} is lenient, {@code false} otherwise. - * @since Android 1.0 */ public boolean isLenient() { return lenient; @@ -1261,11 +1121,10 @@ public abstract class Calendar implements Serializable, Cloneable, /** * Returns whether the specified field is set. - * + * * @param field * a {@code Calendar} field number. * @return {@code true} if the specified field is set, {@code false} otherwise. - * @since Android 1.0 */ public final boolean isSet(int field) { return isSet[field]; @@ -1276,12 +1135,11 @@ public abstract class Calendar implements Serializable, Cloneable, * the field when it goes beyond the maximum or minimum value for the * current date. Other fields will be adjusted as required to maintain a * consistent date. - * + * * @param field * the field to roll. * @param value * the amount to add. - * @since Android 1.0 */ public void roll(int field, int value) { boolean increment = value >= 0; @@ -1296,23 +1154,21 @@ public abstract class Calendar implements Serializable, Cloneable, * field when it goes beyond the maximum or minimum value for the current * date. Other fields will be adjusted as required to maintain a consistent * date. - * + * * @param field * the number indicating the field to roll. * @param increment * {@code true} to increment the field, {@code false} to decrement. - * @since Android 1.0 */ abstract public void roll(int field, boolean increment); /** * Sets a field to the specified value. - * + * * @param field * the code indicating the {@code Calendar} field to modify. * @param value * the value. - * @since Android 1.0 */ public void set(int field, int value) { fields[field] = value; @@ -1332,14 +1188,13 @@ public abstract class Calendar implements Serializable, Cloneable, /** * Sets the year, month and day of the month fields. Other fields are not * changed. - * + * * @param year * the year. * @param month * the month. * @param day * the day of the month. - * @since Android 1.0 */ public final void set(int year, int month, int day) { set(YEAR, year); @@ -1350,7 +1205,7 @@ public abstract class Calendar implements Serializable, Cloneable, /** * Sets the year, month, day of the month, hour of day and minute fields. * Other fields are not changed. - * + * * @param year * the year. * @param month @@ -1361,7 +1216,6 @@ public abstract class Calendar implements Serializable, Cloneable, * the hour of day. * @param minute * the minute. - * @since Android 1.0 */ public final void set(int year, int month, int day, int hourOfDay, int minute) { @@ -1373,7 +1227,7 @@ public abstract class Calendar implements Serializable, Cloneable, /** * Sets the year, month, day of the month, hour of day, minute and second * fields. Other fields are not changed. - * + * * @param year * the year. * @param month @@ -1386,7 +1240,6 @@ public abstract class Calendar implements Serializable, Cloneable, * the minute. * @param second * the second. - * @since Android 1.0 */ public final void set(int year, int month, int day, int hourOfDay, int minute, int second) { @@ -1396,10 +1249,9 @@ public abstract class Calendar implements Serializable, Cloneable, /** * Sets the first day of the week for this {@code Calendar}. - * + * * @param value * a {@code Calendar} day of the week. - * @since Android 1.0 */ public void setFirstDayOfWeek(int value) { firstDayOfWeek = value; @@ -1408,10 +1260,9 @@ public abstract class Calendar implements Serializable, Cloneable, /** * Sets this {@code Calendar} to accept field values which are outside the valid * range for the field. - * + * * @param value * a boolean value. - * @since Android 1.0 */ public void setLenient(boolean value) { lenient = value; @@ -1419,10 +1270,9 @@ public abstract class Calendar implements Serializable, Cloneable, /** * Sets the minimal days in the first week of the year. - * + * * @param value * the minimal days in the first week of the year. - * @since Android 1.0 */ public void setMinimalDaysInFirstWeek(int value) { minimalDaysInFirstWeek = value; @@ -1430,10 +1280,9 @@ public abstract class Calendar implements Serializable, Cloneable, /** * Sets the time of this {@code Calendar}. - * + * * @param date * a {@code Date} object. - * @since Android 1.0 */ public final void setTime(Date date) { setTimeInMillis(date.getTime()); @@ -1441,43 +1290,45 @@ public abstract class Calendar implements Serializable, Cloneable, /** * Sets the time of this {@code Calendar}. - * + * * @param milliseconds * the time as the number of milliseconds since Jan. 1, 1970. - * @since Android 1.0 */ public void setTimeInMillis(long milliseconds) { - time = milliseconds; - isTimeSet = true; - areFieldsSet = false; - complete(); + if (!isTimeSet || !areFieldsSet || time != milliseconds) { + time = milliseconds; + isTimeSet = true; + areFieldsSet = false; + complete(); + } } /** * Sets the {@code TimeZone} used by this Calendar. - * + * * @param timezone * a {@code TimeZone}. - * @since Android 1.0 */ public void setTimeZone(TimeZone timezone) { zone = timezone; + areFieldsSet = false; } /** * Returns the string representation of this {@code Calendar}. - * + * * @return the string representation of this {@code Calendar}. - * @since Android 1.0 */ @Override + @SuppressWarnings("nls") public String toString() { - StringBuffer result = new StringBuffer(getClass().getName() + "[time=" //$NON-NLS-1$ - + (isTimeSet ? String.valueOf(time) : "?") + ",areFieldsSet=" //$NON-NLS-1$//$NON-NLS-2$ - + areFieldsSet + - // ",areAllFieldsSet=" + areAllFieldsSet + - ",lenient=" + lenient + ",zone=" + zone + ",firstDayOfWeek=" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - + firstDayOfWeek + ",minimalDaysInFirstWeek=" //$NON-NLS-1$ + StringBuilder result = new StringBuilder(getClass().getName() + "[time=" + + (isTimeSet ? String.valueOf(time) : "?") + + ",areFieldsSet=" + + areFieldsSet + + // ",areAllFieldsSet=" + areAllFieldsSet + + ",lenient=" + lenient + ",zone=" + zone + ",firstDayOfWeek=" + + firstDayOfWeek + ",minimalDaysInFirstWeek=" + minimalDaysInFirstWeek); for (int i = 0; i < FIELD_COUNT; i++) { result.append(','); @@ -1496,7 +1347,7 @@ public abstract class Calendar implements Serializable, Cloneable, /** * Compares the times of the two {@code Calendar}, which represent the milliseconds * from the January 1, 1970 00:00:00.000 GMT (Gregorian). - * + * * @param anotherCalendar * another calendar that this one is compared with. * @return 0 if the times of the two {@code Calendar}s are equal, -1 if the time of @@ -1507,7 +1358,6 @@ public abstract class Calendar implements Serializable, Cloneable, * @throws IllegalArgumentException * if the argument does not include a valid time * value. - * @since Android 1.0 */ public int compareTo(Calendar anotherCalendar) { if (null == anotherCalendar) { @@ -1524,6 +1374,7 @@ public abstract class Calendar implements Serializable, Cloneable, return -1; } + @SuppressWarnings("nls") private static final ObjectStreamField[] serialPersistentFields = { new ObjectStreamField("areFieldsSet", Boolean.TYPE), //$NON-NLS-1$ new ObjectStreamField("fields", int[].class), //$NON-NLS-1$ @@ -1537,6 +1388,7 @@ public abstract class Calendar implements Serializable, Cloneable, new ObjectStreamField("time", Long.TYPE), //$NON-NLS-1$ new ObjectStreamField("zone", TimeZone.class), }; //$NON-NLS-1$ + @SuppressWarnings("nls") private void writeObject(ObjectOutputStream stream) throws IOException { complete(); ObjectOutputStream.PutField putFields = stream.putFields(); @@ -1554,6 +1406,7 @@ public abstract class Calendar implements Serializable, Cloneable, stream.writeFields(); } + @SuppressWarnings("nls") private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { ObjectInputStream.GetField readFields = stream.readFields(); diff --git a/libcore/luni/src/main/java/java/util/Collection.java b/libcore/luni/src/main/java/java/util/Collection.java index e26dba621..6447e394a 100644 --- a/libcore/luni/src/main/java/java/util/Collection.java +++ b/libcore/luni/src/main/java/java/util/Collection.java @@ -22,7 +22,7 @@ package java.util; * {@code Collection} is the root of the collection hierarchy. It defines operations on * data collections and the behavior that they will have in all implementations * of {@code Collection}s. - * + * * All direct or indirect implementations of {@code Collection} should implement at * least two constuctors. One with no parameters which creates an empty * collection and one with a parameter of type {@code Collection}. This second @@ -30,58 +30,55 @@ package java.util; * initial collection but with the same elements. Implementations of {@code Collection} * cannot be forced to implement these two constructors but at least all * implementations under {@code java.util} do. - * + * * Methods that change the content of a collection throw an * {@code UnsupportedOperationException} if the underlying collection does not * support that operation, though it's not mandatory to throw such an {@code Exception} * in cases where the requested operation would not change the collection. In * these cases it's up to the implementation whether it throws an * {@code UnsupportedOperationException} or not. - * + * * Methods marked with (optional) can throw an * {@code UnsupportedOperationException} if the underlying collection doesn't * support that method. - * - * @since Android 1.0 */ public interface Collection extends Iterable { /** * Attempts to add {@code object} to the contents of this * {@code Collection} (optional). - * + * * After this method finishes successfully it is guaranteed that the object * is contained in the collection. - * + * * If the collection was modified it returns {@code true}, {@code false} if * no changes were made. - * + * * An implementation of {@code Collection} may narrow the set of accepted * objects, but it has to specify this in the documentation. If the object * to be added does not meet this restriction, then an * {@code IllegalArgumentException} is thrown. - * + * * If a collection does not yet contain an object that is to be added and * adding the object fails, this method must throw an appropriate * unchecked Exception. Returning false is not permitted in this case * because it would violate the postcondition that the element will be part * of the collection after this method finishes. - * + * * @param object * the object to add. * @return {@code true} if this {@code Collection} is * modified, {@code false} otherwise. - * - * @exception UnsupportedOperationException - * when adding to this {@code Collection} is not supported. - * @exception ClassCastException - * when the class of the object is inappropriate for this + * + * @throws UnsupportedOperationException + * if adding to this {@code Collection} is not supported. + * @throws ClassCastException + * if the class of the object is inappropriate for this * collection. - * @exception IllegalArgumentException - * when the object cannot be added to this {@code Collection}. - * @exception NullPointerException - * when null elements cannot be added to the {@code Collection}. - * @since Android 1.0 + * @throws IllegalArgumentException + * if the object cannot be added to this {@code Collection}. + * @throws NullPointerException + * if null elements cannot be added to the {@code Collection}. */ public boolean add(E object); @@ -90,35 +87,33 @@ public interface Collection extends Iterable { * to the contents of this {@code Collection} (optional). If the passed {@code Collection} * is changed during the process of adding elements to this {@code Collection}, the * behavior is not defined. - * + * * @param collection * the {@code Collection} of objects. * @return {@code true} if this {@code Collection} is modified, {@code false} * otherwise. - * @exception UnsupportedOperationException - * when adding to this {@code Collection} is not supported. - * @exception ClassCastException - * when the class of an object is inappropriate for this + * @throws UnsupportedOperationException + * if adding to this {@code Collection} is not supported. + * @throws ClassCastException + * if the class of an object is inappropriate for this * {@code Collection}. - * @exception IllegalArgumentException - * when an object cannot be added to this {@code Collection}. - * @exception NullPointerException - * when {@code collection} is {@code null}, or if it + * @throws IllegalArgumentException + * if an object cannot be added to this {@code Collection}. + * @throws NullPointerException + * if {@code collection} is {@code null}, or if it * contains {@code null} elements and this {@code Collection} does * not support such elements. - * @since Android 1.0 */ public boolean addAll(Collection collection); /** * Removes all elements from this {@code Collection}, leaving it empty (optional). - * - * @exception UnsupportedOperationException - * when removing from this {@code Collection} is not supported. - * + * + * @throws UnsupportedOperationException + * if removing from this {@code Collection} is not supported. + * * @see #isEmpty * @see #size - * @since Android 1.0 */ public void clear(); @@ -127,18 +122,17 @@ public interface Collection extends Iterable { * {@code true} if and only if at least one element {@code elem} in this * {@code Collection} meets following requirement: * {@code (object==null ? elem==null : object.equals(elem))}. - * + * * @param object * the object to search for. * @return {@code true} if object is an element of this {@code Collection}, * {@code false} otherwise. - * @exception ClassCastException + * @throws ClassCastException * if the object to look for isn't of the correct * type. - * @exception NullPointerException + * @throws NullPointerException * if the object to look for is {@code null} and this * {@code Collection} doesn't support {@code null} elements. - * @since Android 1.0 */ public boolean contains(Object object); @@ -147,56 +141,52 @@ public interface Collection extends Iterable { * specified {@code Collection}. If an elemenet {@code elem} is contained several * times in the specified {@code Collection}, the method returns {@code true} even * if {@code elem} is contained only once in this {@code Collection}. - * + * * @param collection * the collection of objects. * @return {@code true} if all objects in the specified {@code Collection} are * elements of this {@code Collection}, {@code false} otherwise. - * @exception ClassCastException + * @throws ClassCastException * if one or more elements of {@code collection} isn't of the * correct type. - * @exception NullPointerException + * @throws NullPointerException * if {@code collection} contains at least one {@code null} * element and this {@code Collection} doesn't support {@code null} * elements. - * @exception NullPointerException + * @throws NullPointerException * if {@code collection} is {@code null}. - * @since Android 1.0 */ public boolean containsAll(Collection collection); /** * Compares the argument to the receiver, and returns true if they represent * the same object using a class specific comparison. - * + * * @param object * the object to compare with this object. * @return {@code true} if the object is the same as this object and * {@code false} if it is different from this object. * @see #hashCode - * @since Android 1.0 */ public boolean equals(Object object); /** * Returns an integer hash code for the receiver. Objects which are equal * return the same value for this method. - * + * * @return the receiver's hash. - * + * * @see #equals - * @since Android 1.0 */ public int hashCode(); /** * Returns if this {@code Collection} contains no elements. - * + * * @return {@code true} if this {@code Collection} has no elements, {@code false} * otherwise. - * + * * @see #size - * @since Android 1.0 */ public boolean isEmpty(); @@ -205,9 +195,8 @@ public interface Collection extends Iterable { * objects contained by this {@code Collection}. The order in which the elements are * returned by the iterator is not defined. Only if the instance of the * {@code Collection} has a defined order the elements are returned in that order. - * + * * @return an iterator for accessing the {@code Collection} contents. - * @since Android 1.0 */ public Iterator iterator(); @@ -215,19 +204,18 @@ public interface Collection extends Iterable { * Removes one instance of the specified object from this {@code Collection} if one * is contained (optional). The element {@code elem} that is removed * complies with {@code (object==null ? elem==null : object.equals(elem)}. - * + * * @param object * the object to remove. * @return {@code true} if this {@code Collection} is modified, {@code false} * otherwise. - * @exception UnsupportedOperationException - * when removing from this {@code Collection} is not supported. - * @exception ClassCastException - * when the object passed is not of the correct type. - * @exception NullPointerException + * @throws UnsupportedOperationException + * if removing from this {@code Collection} is not supported. + * @throws ClassCastException + * if the object passed is not of the correct type. + * @throws NullPointerException * if {@code object} is {@code null} and this {@code Collection} * doesn't support {@code null} elements. - * @since Android 1.0 */ public boolean remove(Object object); @@ -236,24 +224,23 @@ public interface Collection extends Iterable { * specified {@code Collection} (optional). After this method returns none of the * elements in the passed {@code Collection} can be found in this {@code Collection} * anymore. - * + * * @param collection * the collection of objects to remove. * @return {@code true} if this {@code Collection} is modified, {@code false} * otherwise. - * - * @exception UnsupportedOperationException - * when removing from this {@code Collection} is not supported. - * @exception ClassCastException + * + * @throws UnsupportedOperationException + * if removing from this {@code Collection} is not supported. + * @throws ClassCastException * if one or more elements of {@code collection} * isn't of the correct type. - * @exception NullPointerException + * @throws NullPointerException * if {@code collection} contains at least one * {@code null} element and this {@code Collection} doesn't support * {@code null} elements. - * @exception NullPointerException + * @throws NullPointerException * if {@code collection} is {@code null}. - * @since Android 1.0 */ public boolean removeAll(Collection collection); @@ -262,48 +249,45 @@ public interface Collection extends Iterable { * {@code Collection} passed (optional). After this method returns this {@code Collection} * will only contain elements that also can be found in the {@code Collection} * passed to this method. - * + * * @param collection * the collection of objects to retain. * @return {@code true} if this {@code Collection} is modified, {@code false} * otherwise. - * @exception UnsupportedOperationException - * when removing from this {@code Collection} is not supported. - * @exception ClassCastException + * @throws UnsupportedOperationException + * if removing from this {@code Collection} is not supported. + * @throws ClassCastException * if one or more elements of {@code collection} * isn't of the correct type. - * @exception NullPointerException + * @throws NullPointerException * if {@code collection} contains at least one * {@code null} element and this {@code Collection} doesn't support * {@code null} elements. - * @exception NullPointerException + * @throws NullPointerException * if {@code collection} is {@code null}. - * @since Android 1.0 */ public boolean retainAll(Collection collection); /** * Returns a count of how many objects this {@code Collection} contains. - * + * * @return how many objects this {@code Collection} contains, or Integer.MAX_VALUE * if there are more than Integer.MAX_VALUE elements in this * {@code Collection}. - * @since Android 1.0 */ public int size(); /** * Returns a new array containing all elements contained in this {@code Collection}. - * + * * If the implementation has ordered elements it will return the element * array in the same order as an iterator would return them. - * + * * The array returned does not reflect any changes of the {@code Collection}. A new * array is created even if the underlying data structure is already an * array. - * + * * @return an array of the elements from this {@code Collection}. - * @since Android 1.0 */ public Object[] toArray(); @@ -313,21 +297,20 @@ public interface Collection extends Iterable { * array is used, otherwise an array of the same type is created. If the * specified array is used and is larger than this {@code Collection}, the array * element following the {@code Collection} elements is set to null. - * + * * If the implementation has ordered elements it will return the element * array in the same order as an iterator would return them. - * + * * {@code toArray(new Object[0])} behaves exactly the same way as * {@code toArray()} does. - * + * * @param array * the array. * @return an array of the elements from this {@code Collection}. - * - * @exception ArrayStoreException - * when the type of an element in this {@code Collection} cannot be + * + * @throws ArrayStoreException + * if the type of an element in this {@code Collection} cannot be * stored in the type of the specified array. - * @since Android 1.0 */ public T[] toArray(T[] array); } diff --git a/libcore/luni/src/main/java/java/util/Collections.java b/libcore/luni/src/main/java/java/util/Collections.java index 5905bc2ad..767d98b18 100644 --- a/libcore/luni/src/main/java/java/util/Collections.java +++ b/libcore/luni/src/main/java/java/util/Collections.java @@ -27,8 +27,8 @@ import org.apache.harmony.luni.internal.nls.Messages; /** * {@code Collections} contains static methods which operate on * {@code Collection} classes. - * - * @since Android 1.0 + * + * @since 1.2 */ public class Collections { @@ -69,7 +69,7 @@ public class Collections { @SuppressWarnings("unchecked") private static final class EmptyList extends AbstractList implements - Serializable { + RandomAccess, Serializable { private static final long serialVersionUID = 8842843931221139166L; @Override @@ -171,24 +171,18 @@ public class Collections { /** * An empty immutable instance of {@link List}. - * - * @since Android 1.0 */ @SuppressWarnings("unchecked") public static final List EMPTY_LIST = new EmptyList(); /** * An empty immutable instance of {@link Set}. - * - * @since Android 1.0 */ @SuppressWarnings("unchecked") public static final Set EMPTY_SET = new EmptySet(); /** * An empty immutable instance of {@link Map}. - * - * @since Android 1.0 */ @SuppressWarnings("unchecked") public static final Map EMPTY_MAP = new EmptyMap(); @@ -532,9 +526,9 @@ public class Collections { * problems. This is necessary since RandomAccess API was introduced * only in 1.4. *

          - * + * * @return SynchronizedList - * + * * @see SynchronizedList#readResolve() */ private Object writeReplace() { @@ -591,15 +585,51 @@ public class Collections { } public int indexOf(Object object) { + final int size; + final Object[] array; synchronized (mutex) { - return list.indexOf(object); + size = list.size(); + array = new Object[size]; + list.toArray(array); + } + if (null != object) + for (int i = 0; i < size; i++) { + if (object.equals(array[i])) { + return i; + } + } + else { + for (int i = 0; i < size; i++) { + if (null == array[i]) { + return i; + } + } } + return -1; } public int lastIndexOf(Object object) { + final int size; + final Object[] array; synchronized (mutex) { - return list.lastIndexOf(object); + size = list.size(); + array = new Object[size]; + list.toArray(array); + } + if (null != object) + for (int i = size - 1; i >= 0; i--) { + if (object.equals(array[i])) { + return i; + } + } + else { + for (int i = size - 1; i >= 0; i--) { + if (null == array[i]) { + return i; + } + } } + return -1; } public ListIterator listIterator() { @@ -646,10 +676,10 @@ public class Collections { * replaced with SynchronizedList instances during serialization for * compliance with JREs before 1.4. *

          - * + * * @return a SynchronizedList instance if the underlying list implements * RandomAccess interface, or this same object if not. - * + * * @see SynchronizedRandomAccessList#writeReplace() */ private Object readResolve() { @@ -1033,9 +1063,9 @@ public class Collections { * problems. This is necessary since RandomAccess API was introduced * only in 1.4. *

          - * + * * @return UnmodifiableList - * + * * @see UnmodifiableList#readResolve() */ private Object writeReplace() { @@ -1150,11 +1180,11 @@ public class Collections { * replaced with UnmodifiableList instances during serialization for * compliance with JREs before 1.4. *

          - * + * * @return an UnmodifiableList instance if the underlying list * implements RandomAccess interface, or this same object if * not. - * + * * @see UnmodifiableRandomAccessList#writeReplace() */ private Object readResolve() { @@ -1429,13 +1459,13 @@ public class Collections { /** * Performs a binary search for the specified element in the specified - * sorted List. The List needs to be already sorted in natural sorting + * sorted list. The list needs to be already sorted in natural sorting * order. Searching in an unsorted array has an undefined result. It's also * undefined which element is found if there are multiple occurrences of the * same element. - * + * * @param list - * the sorted List to search. + * the sorted list to search. * @param object * the element to find. * @return the non-negative index of the element, or a negative index which @@ -1443,7 +1473,6 @@ public class Collections { * @throws ClassCastException * if an element in the List or the search element does not * implement Comparable, or cannot be compared to each other. - * @since Android 1.0 */ @SuppressWarnings("unchecked") public static int binarySearch( @@ -1455,12 +1484,12 @@ public class Collections { return -1; } - Comparable key = (Comparable) object; + if (!(list instanceof RandomAccess)) { - ListIterator it = (ListIterator) list.listIterator(); + ListIterator> it = list.listIterator(); while (it.hasNext()) { int result; - if ((result = key.compareTo(it.next())) <= 0) { + if ((result = -it.next().compareTo(object)) <= 0) { if (result == 0) { return it.previousIndex(); } @@ -1473,7 +1502,7 @@ public class Collections { int low = 0, mid = list.size(), high = mid - 1, result = -1; while (low <= high) { mid = (low + high) >> 1; - if ((result = key.compareTo((T) list.get(mid))) > 0) { + if ((result = -list.get(mid).compareTo(object)) > 0) { low = mid + 1; } else if (result == 0) { return mid; @@ -1486,26 +1515,28 @@ public class Collections { /** * Performs a binary search for the specified element in the specified - * sorted List using the specified Comparator. The List needs to be already + * sorted list using the specified comparator. The list needs to be already * sorted according to the comparator passed. Searching in an unsorted array * has an undefined result. It's also undefined which element is found if * there are multiple occurrences of the same element. - * + * * @param list * the sorted List to search. * @param object * the element to find. * @param comparator - * the Comparator. If the comparator is {@code null} then the + * the comparator. If the comparator is {@code null} then the * search uses the objects' natural ordering. * @return the non-negative index of the element, or a negative index which * is the {@code -index - 1} where the element would be inserted. * @throws ClassCastException * when an element in the list and the searched element cannot - * be compared to each other using the Comparator. - * @since Android 1.0 + * be compared to each other using the comparator. */ @SuppressWarnings("unchecked") + // BEGIN android-note + // removed "@param The element type", which is rejected by apicheck + // END android-note public static int binarySearch(List list, T object, Comparator comparator) { if (comparator == null) { @@ -1516,7 +1547,7 @@ public class Collections { ListIterator it = list.listIterator(); while (it.hasNext()) { int result; - if ((result = comparator.compare(object, it.next())) <= 0) { + if ((result = -comparator.compare(it.next(), object)) <= 0) { if (result == 0) { return it.previousIndex(); } @@ -1529,7 +1560,7 @@ public class Collections { int low = 0, mid = list.size(), high = mid - 1, result = -1; while (low <= high) { mid = (low + high) >> 1; - if ((result = comparator.compare(object, list.get(mid))) > 0) { + if ((result = -comparator.compare(list.get(mid),object)) > 0) { low = mid + 1; } else if (result == 0) { return mid; @@ -1545,17 +1576,16 @@ public class Collections { * end both lists will have the same objects at the same index. If the * destination array is larger than the source list, the elements in the * destination list with {@code index >= source.size()} will be unchanged. - * + * * @param destination * the list whose elements are set from the source list. * @param source * the list with the elements to be copied into the destination. * @throws IndexOutOfBoundsException - * when the destination List is smaller than the source List. + * when the destination list is smaller than the source list. * @throws UnsupportedOperationException * when replacing an element in the destination list is not * supported. - * @since Android 1.0 */ public static void copy(List destination, List source) { @@ -1576,11 +1606,10 @@ public class Collections { /** * Returns an {@code Enumeration} on the specified collection. - * + * * @param collection * the collection to enumerate. * @return an Enumeration. - * @since Android 1.0 */ public static Enumeration enumeration(Collection collection) { final Collection c = collection; @@ -1598,15 +1627,14 @@ public class Collections { } /** - * Fills the specified List with the specified element. - * + * Fills the specified list with the specified element. + * * @param list - * the List to fill. + * the list to fill. * @param object * the element to fill the list with. * @throws UnsupportedOperationException * when replacing an element in the List is not supported. - * @since Android 1.0 */ public static void fill(List list, T object) { ListIterator it = list.listIterator(); @@ -1617,16 +1645,15 @@ public class Collections { } /** - * Searches the specified Collection for the maximum element. - * + * Searches the specified collection for the maximum element. + * * @param collection - * the Collection to search. + * the collection to search. * @return the maximum element in the Collection. * @throws ClassCastException - * when an element in the Collection does not implement + * when an element in the collection does not implement * {@code Comparable} or elements cannot be compared to each * other. - * @since Android 1.0 */ public static > T max( Collection collection) { @@ -1642,18 +1669,17 @@ public class Collections { } /** - * Searches the specified Collection for the maximum element using the - * specified Comparator. - * + * Searches the specified collection for the maximum element using the + * specified comparator. + * * @param collection - * the Collection to search. + * the collection to search. * @param comparator - * the Comparator. + * the comparator. * @return the maximum element in the Collection. * @throws ClassCastException - * when elements in the Collection cannot be compared to each + * when elements in the collection cannot be compared to each * other using the {@code Comparator}. - * @since Android 1.0 */ public static T max(Collection collection, Comparator comparator) { @@ -1669,16 +1695,15 @@ public class Collections { } /** - * Searches the specified Collection for the minimum element. - * + * Searches the specified collection for the minimum element. + * * @param collection - * the Collection to search. - * @return the minimum element in the Collection. + * the collection to search. + * @return the minimum element in the collection. * @throws ClassCastException - * when an element in the Collection does not implement + * when an element in the collection does not implement * {@code Comparable} or elements cannot be compared to each * other. - * @since Android 1.0 */ public static > T min( Collection collection) { @@ -1694,18 +1719,17 @@ public class Collections { } /** - * Searches the specified Collection for the minimum element using the - * specified Comparator. - * + * Searches the specified collection for the minimum element using the + * specified comparator. + * * @param collection - * the Collection to search. + * the collection to search. * @param comparator - * the Comparator. - * @return the minimum element in the Collection. + * the comparator. + * @return the minimum element in the collection. * @throws ClassCastException - * when elements in the Collection cannot be compared to each + * when elements in the collection cannot be compared to each * other using the {@code Comparator}. - * @since Android 1.0 */ public static T min(Collection collection, Comparator comparator) { @@ -1721,17 +1745,16 @@ public class Collections { } /** - * Returns a List containing the specified number of the specified element. + * Returns a list containing the specified number of the specified element. * The list cannot be modified. The list is serializable. - * + * * @param length * the size of the returned list. * @param object * the element to be added {@code length} times to a list. - * @return a List containing {@code length} copies of the element. + * @return a list containing {@code length} copies of the element. * @throws IllegalArgumentException * when {@code length < 0}. - * @since Android 1.0 */ public static List nCopies(final int length, T object) { return new CopiesList(length, object); @@ -1740,12 +1763,11 @@ public class Collections { /** * Modifies the specified {@code List} by reversing the order of the * elements. - * + * * @param list - * the List to reverse. + * the list to reverse. * @throws UnsupportedOperationException * when replacing an element in the List is not supported. - * @since Android 1.0 */ @SuppressWarnings("unchecked") public static void reverse(List list) { @@ -1762,14 +1784,13 @@ public class Collections { } /** - * A Comparator which reverses the natural order of the elements. The + * A comparator which reverses the natural order of the elements. The * {@code Comparator} that's returned is {@link Serializable}. - * + * * @return a {@code Comparator} instance. * @see Comparator * @see Comparable * @see Serializable - * @since Android 1.0 */ public static Comparator reverseOrder() { return new ReverseComparator(); @@ -1782,13 +1803,12 @@ public class Collections { *

          * The {@code Comparator} that's returned is {@link Serializable} if the * {@code Comparator} passed is serializable or {@code null}. - *

          - * + * * @param c * the {@code Comparator} to reverse or {@code null}. * @return a {@code Comparator} instance. * @see Comparator - * @since Android 1.0 + * @since 1.5 */ public static Comparator reverseOrder(Comparator c) { if (c == null) { @@ -1798,38 +1818,35 @@ public class Collections { } /** - * Moves every element of the List to a random new position in the list. - * + * Moves every element of the list to a random new position in the list. + * * @param list * the List to shuffle. - * + * * @throws UnsupportedOperationException * when replacing an element in the List is not supported. - * @since Android 1.0 */ public static void shuffle(List list) { shuffle(list, new Random()); } /** - * Moves every element of the List to a random new position in the list + * Moves every element of the list to a random new position in the list * using the specified random number generator. - * + * * @param list - * the List to shuffle. + * the list to shuffle. * @param random * the random number generator. - * * @throws UnsupportedOperationException - * when replacing an element in the List is not supported. - * @since Android 1.0 + * when replacing an element in the list is not supported. */ @SuppressWarnings("unchecked") public static void shuffle(List list, Random random) { if (!(list instanceof RandomAccess)) { Object[] array = list.toArray(); for (int i = array.length - 1; i > 0; i--) { - int index = random.nextInt() % (i + 1); + int index = random.nextInt(i + 1); if (index < 0) { index = -index; } @@ -1848,7 +1865,7 @@ public class Collections { } else { List rawList = (List) list; for (int i = rawList.size() - 1; i > 0; i--) { - int index = random.nextInt() % (i + 1); + int index = random.nextInt(i + 1); if (index < 0) { index = -index; } @@ -1858,26 +1875,24 @@ public class Collections { } /** - * Returns a Set containing the specified element. The set cannot be + * Returns a set containing the specified element. The set cannot be * modified. The set is serializable. - * + * * @param object * the element. - * @return a Set containing the element. - * @since Android 1.0 + * @return a set containing the element. */ public static Set singleton(E object) { return new SingletonSet(object); } /** - * Returns a List containing the specified element. The list cannot be + * Returns a list containing the specified element. The list cannot be * modified. The list is serializable. - * + * * @param object * the element. - * @return a List containing the element. - * @since Android 1.0 + * @return a list containing the element. */ public static List singletonList(E object) { return new SingletonList(object); @@ -1886,28 +1901,26 @@ public class Collections { /** * Returns a Map containing the specified key and value. The map cannot be * modified. The map is serializable. - * + * * @param key * the key. * @param value * the value. * @return a Map containing the key and value. - * @since Android 1.0 */ public static Map singletonMap(K key, V value) { return new SingletonMap(key, value); } /** - * Sorts the specified List in ascending natural order. The algorithm is + * Sorts the specified list in ascending natural order. The algorithm is * stable which means equal elements don't get reordered. - * + * * @param list - * the List to be sorted. + * the list to be sorted. * @throws ClassCastException * when an element in the List does not implement Comparable or * elements cannot be compared to each other. - * @since Android 1.0 */ @SuppressWarnings("unchecked") public static > void sort(List list) { @@ -1922,17 +1935,16 @@ public class Collections { } /** - * Sorts the specified List using the specified Comparator. The algorithm is + * Sorts the specified list using the specified comparator. The algorithm is * stable which means equal elements don't get reordered. - * + * * @param list - * the List to be sorted. + * the list to be sorted. * @param comparator - * the Comparator. + * the comparator. * @throws ClassCastException - * when elements in the List cannot be compared to each other - * using the Comparator. - * @since Android 1.0 + * when elements in the list cannot be compared to each other + * using the comparator. */ @SuppressWarnings("unchecked") public static void sort(List list, Comparator comparator) { @@ -1947,26 +1959,30 @@ public class Collections { } /** - * Swaps the elements of List {@code list} at indices {@code index1} and + * Swaps the elements of list {@code list} at indices {@code index1} and * {@code index2}. - * + * * @param list - * the List to manipulate. + * the list to manipulate. * @param index1 * position of the first element to swap with the element in * index2. * @param index2 * position of the other element. - * + * * @throws IndexOutOfBoundsException * if index1 or index2 is out of range of this list. - * @since Android 1.0 + * @since 1.4 */ @SuppressWarnings("unchecked") public static void swap(List list, int index1, int index2) { if (list == null) { throw new NullPointerException(); } + final int size = list.size(); + if (index1 < 0 || index1 >= size || index2 < 0 || index2 >= size) { + throw new IndexOutOfBoundsException(); + } if (index1 == index2) { return; } @@ -1978,20 +1994,18 @@ public class Collections { * Replaces all occurrences of Object {@code obj} in {@code list} with * {@code newObj}. If the {@code obj} is {@code null}, then all * occurrences of {@code null} are replaced with {@code newObj}. - * + * * @param list - * the List to modify. + * the list to modify. * @param obj - * the Object to find and replace occurrences of. + * the object to find and replace occurrences of. * @param obj2 - * the Object to replace all occurrences of {@code obj} in + * the object to replace all occurrences of {@code obj} in * {@code list}. * @return true, if at least one occurrence of {@code obj} has been found in * {@code list}. - * * @throws UnsupportedOperationException * if the list does not support setting elements. - * @since Android 1.0 */ public static boolean replaceAll(List list, T obj, T obj2) { int index; @@ -2005,19 +2019,17 @@ public class Collections { } /** - * Rotates the elements in List {@code list} by the distance {@code dist} + * Rotates the elements in {@code list} by the distance {@code dist} *

          * e.g. for a given list with elements [1, 2, 3, 4, 5, 6, 7, 8, 9, 0], * calling rotate(list, 3) or rotate(list, -7) would modify the list to look * like this: [8, 9, 0, 1, 2, 3, 4, 5, 6, 7] - *

          - * + * * @param lst * the list whose elements are to be rotated. * @param dist * is the distance the list is rotated. This can be any valid * integer. Negative values rotate the list backwards. - * @since Android 1.0 */ @SuppressWarnings("unchecked") public static void rotate(List lst, int dist) { @@ -2069,14 +2081,13 @@ public class Collections { * index of the first occurrence. *

          * -1 is returned if the {@code sublist} does not exist in {@code list}. - * + * * @param list * the List to search {@code sublist} in. * @param sublist * the List to search in {@code list}. * @return the beginning index of the first occurrence of {@code sublist} in * {@code list}, or -1. - * @since Android 1.0 */ public static int indexOfSubList(List list, List sublist) { int size = list.size(); @@ -2137,14 +2148,13 @@ public class Collections { * index of the last occurrence. *

          * -1 is returned if the {@code sublist} does not exist in {@code list}. - * + * * @param list - * the List to search {@code sublist} in. + * the list to search {@code sublist} in. * @param sublist - * the List to search in {@code list}. + * the list to search in {@code list}. * @return the beginning index of the last occurrence of {@code sublist} in * {@code list}, or -1. - * @since Android 1.0 */ public static int lastIndexOfSubList(List list, List sublist) { int sublistSize = sublist.size(); @@ -2198,14 +2208,13 @@ public class Collections { } /** - * Returns an {@code ArrayList} with all the elements in the - * {@code enumeration}. The elements in the returned ArrayList are in the + * Returns an {@code ArrayList} with all the elements in the {@code + * enumeration}. The elements in the returned {@code ArrayList} are in the * same order as in the {@code enumeration}. - * + * * @param enumeration * the source {@link Enumeration}. * @return an {@code ArrayList} from {@code enumeration}. - * @since Android 1.0 */ public static ArrayList list(Enumeration enumeration) { ArrayList list = new ArrayList(); @@ -2216,13 +2225,12 @@ public class Collections { } /** - * Returns a wrapper on the specified Collection which synchronizes all - * access to the Collection. - * + * Returns a wrapper on the specified collection which synchronizes all + * access to the collection. + * * @param collection * the Collection to wrap in a synchronized collection. * @return a synchronized Collection. - * @since Android 1.0 */ public static Collection synchronizedCollection( Collection collection) { @@ -2235,11 +2243,10 @@ public class Collections { /** * Returns a wrapper on the specified List which synchronizes all access to * the List. - * + * * @param list * the List to wrap in a synchronized list. * @return a synchronized List. - * @since Android 1.0 */ public static List synchronizedList(List list) { if (list == null) { @@ -2252,13 +2259,12 @@ public class Collections { } /** - * Returns a wrapper on the specified Map which synchronizes all access to - * the Map. - * + * Returns a wrapper on the specified map which synchronizes all access to + * the map. + * * @param map - * the Map to wrap in a synchronized map. + * the map to wrap in a synchronized map. * @return a synchronized Map. - * @since Android 1.0 */ public static Map synchronizedMap(Map map) { if (map == null) { @@ -2268,13 +2274,12 @@ public class Collections { } /** - * Returns a wrapper on the specified Set which synchronizes all access to - * the Set. - * + * Returns a wrapper on the specified set which synchronizes all access to + * the set. + * * @param set - * the Set to wrap in a synchronized set. - * @return a synchronized Set. - * @since Android 1.0 + * the set to wrap in a synchronized set. + * @return a synchronized set. */ public static Set synchronizedSet(Set set) { if (set == null) { @@ -2284,13 +2289,12 @@ public class Collections { } /** - * Returns a wrapper on the specified SortedMap which synchronizes all - * access to the SortedMap. - * + * Returns a wrapper on the specified sorted map which synchronizes all + * access to the sorted map. + * * @param map - * the SortedMap to wrap in a synchronized sorted map. - * @return a synchronized SortedMap. - * @since Android 1.0 + * the sorted map to wrap in a synchronized sorted map. + * @return a synchronized sorted map. */ public static SortedMap synchronizedSortedMap( SortedMap map) { @@ -2301,13 +2305,12 @@ public class Collections { } /** - * Returns a wrapper on the specified SortedSet which synchronizes all - * access to the SortedSet. - * + * Returns a wrapper on the specified sorted set which synchronizes all + * access to the sorted set. + * * @param set - * the SortedSet to wrap in a synchronized sorted set. - * @return a synchronized SortedSet. - * @since Android 1.0 + * the sorted set to wrap in a synchronized sorted set. + * @return a synchronized sorted set. */ public static SortedSet synchronizedSortedSet(SortedSet set) { if (set == null) { @@ -2317,14 +2320,13 @@ public class Collections { } /** - * Returns a wrapper on the specified Collection which throws an + * Returns a wrapper on the specified collection which throws an * {@code UnsupportedOperationException} whenever an attempt is made to - * modify the Collection. - * + * modify the collection. + * * @param collection - * the Collection to wrap in an unmodifiable collection. - * @return an unmodifiable Collection. - * @since Android 1.0 + * the collection to wrap in an unmodifiable collection. + * @return an unmodifiable collection. */ @SuppressWarnings("unchecked") public static Collection unmodifiableCollection( @@ -2336,14 +2338,13 @@ public class Collections { } /** - * Returns a wrapper on the specified List which throws an + * Returns a wrapper on the specified list which throws an * {@code UnsupportedOperationException} whenever an attempt is made to - * modify the List. - * + * modify the list. + * * @param list - * the List to wrap in an unmodifiable list. + * the list to wrap in an unmodifiable list. * @return an unmodifiable List. - * @since Android 1.0 */ @SuppressWarnings("unchecked") public static List unmodifiableList(List list) { @@ -2357,14 +2358,13 @@ public class Collections { } /** - * Returns a wrapper on the specified Map which throws an + * Returns a wrapper on the specified map which throws an * {@code UnsupportedOperationException} whenever an attempt is made to - * modify the Map. - * + * modify the map. + * * @param map - * the Map to wrap in an unmodifiable map. - * @return a unmodifiable Map. - * @since Android 1.0 + * the map to wrap in an unmodifiable map. + * @return a unmodifiable map. */ @SuppressWarnings("unchecked") public static Map unmodifiableMap( @@ -2376,14 +2376,13 @@ public class Collections { } /** - * Returns a wrapper on the specified Set which throws an + * Returns a wrapper on the specified set which throws an * {@code UnsupportedOperationException} whenever an attempt is made to - * modify the Set. - * + * modify the set. + * * @param set - * the Set to wrap in an unmodifiable set. - * @return a unmodifiable Set. - * @since Android 1.0 + * the set to wrap in an unmodifiable set. + * @return a unmodifiable set */ @SuppressWarnings("unchecked") public static Set unmodifiableSet(Set set) { @@ -2394,14 +2393,13 @@ public class Collections { } /** - * Returns a wrapper on the specified SortedMap which throws an + * Returns a wrapper on the specified sorted map which throws an * {@code UnsupportedOperationException} whenever an attempt is made to - * modify the SortedMap. - * + * modify the sorted map. + * * @param map - * the SortedMap to wrap in an unmodifiable sorted map. - * @return a unmodifiable SortedMap. - * @since Android 1.0 + * the sorted map to wrap in an unmodifiable sorted map. + * @return a unmodifiable sorted map */ @SuppressWarnings("unchecked") public static SortedMap unmodifiableSortedMap( @@ -2413,14 +2411,13 @@ public class Collections { } /** - * Returns a wrapper on the specified SortedSet which throws an + * Returns a wrapper on the specified sorted set which throws an * {@code UnsupportedOperationException} whenever an attempt is made to - * modify the SortedSet. - * + * modify the sorted set. + * * @param set - * the SortedSet to wrap in an unmodifiable sorted set. - * @return a unmodifiable SortedSet. - * @since Android 1.0 + * the sorted set to wrap in an unmodifiable sorted set. + * @return a unmodifiable sorted set. */ public static SortedSet unmodifiableSortedSet(SortedSet set) { if (set == null) { @@ -2433,8 +2430,7 @@ public class Collections { * Returns the number of elements in the {@code Collection} that match the * {@code Object} passed. If the {@code Object} is {@code null}, then the * number of {@code null} elements is returned. - *

          - * + * * @param c * the {@code Collection} to search. * @param o @@ -2442,7 +2438,7 @@ public class Collections { * @return the number of matching elements. * @throws NullPointerException * if the {@code Collection} parameter is {@code null}. - * @since Android 1.0 + * @since 1.5 */ public static int frequency(Collection c, Object o) { if (c == null) { @@ -2464,10 +2460,10 @@ public class Collections { /** * Returns a type-safe empty, immutable {@link List}. - * + * * @return an empty {@link List}. + * @since 1.5 * @see #EMPTY_LIST - * @since Android 1.0 */ @SuppressWarnings("unchecked") public static final List emptyList() { @@ -2476,10 +2472,10 @@ public class Collections { /** * Returns a type-safe empty, immutable {@link Set}. - * + * * @return an empty {@link Set}. + * @since 1.5 * @see #EMPTY_SET - * @since Android 1.0 */ @SuppressWarnings("unchecked") public static final Set emptySet() { @@ -2488,10 +2484,10 @@ public class Collections { /** * Returns a type-safe empty, immutable {@link Map}. - * + * * @return an empty {@link Map}. + * @since 1.5 * @see #EMPTY_MAP - * @since Android 1.0 */ @SuppressWarnings("unchecked") public static final Map emptyMap() { @@ -2503,13 +2499,12 @@ public class Collections { * to insert an element of the wrong type into this collection throws a * {@code ClassCastException}. At creation time the types in {@code c} are * not checked for correct type. - * + * * @param c * the collection to be wrapped in a typesafe collection. * @param type * the type of the elements permitted to insert. * @return a typesafe collection. - * @since Android 1.0 */ public static Collection checkedCollection(Collection c, Class type) { @@ -2521,7 +2516,7 @@ public class Collections { * insert an element of the wrong type into this map throws a * {@code ClassCastException}. At creation time the types in {@code m} are * not checked for correct type. - * + * * @param m * the map to be wrapped in a typesafe map. * @param keyType @@ -2529,7 +2524,6 @@ public class Collections { * @param valueType * the type of the values permitted to insert. * @return a typesafe map. - * @since Android 1.0 */ public static Map checkedMap(Map m, Class keyType, Class valueType) { @@ -2541,13 +2535,12 @@ public class Collections { * insert an element of the wrong type into this list throws a * {@code ClassCastException}. At creation time the types in {@code list} * are not checked for correct type. - * + * * @param list * the list to be wrapped in a typesafe list. * @param type * the type of the elements permitted to insert. * @return a typesafe list. - * @since Android 1.0 */ public static List checkedList(List list, Class type) { if (list instanceof RandomAccess) { @@ -2561,13 +2554,12 @@ public class Collections { * insert an element of the wrong type into this set throws a * {@code ClassCastException}. At creation time the types in {@code s} are * not checked for correct type. - * + * * @param s * the set to be wrapped in a typesafe set. * @param type * the type of the elements permitted to insert. * @return a typesafe set. - * @since Android 1.0 */ public static Set checkedSet(Set s, Class type) { return new CheckedSet(s, type); @@ -2578,7 +2570,7 @@ public class Collections { * to insert an element of the wrong type into this sorted map throws a * {@code ClassCastException}. At creation time the types in {@code m} are * not checked for correct type. - * + * * @param m * the sorted map to be wrapped in a typesafe sorted map. * @param keyType @@ -2586,7 +2578,6 @@ public class Collections { * @param valueType * the type of the values permitted to insert. * @return a typesafe sorted map. - * @since Android 1.0 */ public static SortedMap checkedSortedMap(SortedMap m, Class keyType, Class valueType) { @@ -2598,13 +2589,12 @@ public class Collections { * to insert an element of the wrong type into this sorted set throws a * {@code ClassCastException}. At creation time the types in {@code s} are * not checked for correct type. - * + * * @param s * the sorted set to be wrapped in a typesafe sorted set. * @param type * the type of the elements permitted to insert. * @return a typesafe sorted set. - * @since Android 1.0 */ public static SortedSet checkedSortedSet(SortedSet s, Class type) { @@ -2613,7 +2603,7 @@ public class Collections { /** * Adds all the specified elements to the specified collection. - * + * * @param c * the collection the elements are to be inserted into. * @param a @@ -2628,7 +2618,6 @@ public class Collections { * @throws IllegalArgumentException * if at least one of the elements can't be inserted into the * collection. - * @since Android 1.0 */ public static boolean addAll(Collection c, T... a) { boolean modified = false; @@ -2640,7 +2629,7 @@ public class Collections { /** * Returns whether the specified collections have no elements in common. - * + * * @param c1 * the first collection. * @param c2 @@ -2649,7 +2638,6 @@ public class Collections { * {@code false} otherwise. * @throws NullPointerException * if one of the collections is {@code null}. - * @since Android 1.0 */ public static boolean disjoint(Collection c1, Collection c2) { if ((c1 instanceof Set) && !(c2 instanceof Set) @@ -2670,7 +2658,7 @@ public class Collections { /** * Checks if specified object is instance of specified class. Used for a * dynamically typesafe view of the collections. - * + * * @param obj - * object is to be checked * @param type - @@ -2701,7 +2689,7 @@ public class Collections { /** * Constructs a dynamically typesafe view of the specified collection. - * + * * @param c - * the collection for which an unmodifiable view is to be * constructed. @@ -2844,7 +2832,7 @@ public class Collections { /** * Constructs a dynamically typesafe view of the specified ListIterator. - * + * * @param i - * the listIterator for which a dynamically typesafe view to * be constructed. @@ -2930,7 +2918,7 @@ public class Collections { /** * Constructs a dynamically typesafe view of the specified list. - * + * * @param l - * the list for which a dynamically typesafe view is to be * constructed. @@ -3049,7 +3037,7 @@ public class Collections { /** * Constructs a dynamically typesafe view of the specified * randomAccessList. - * + * * @param l - * the randomAccessList for which a dynamically typesafe view * is to be constructed. @@ -3069,7 +3057,7 @@ public class Collections { /** * Constructs a dynamically typesafe view of the specified set. - * + * * @param s - * the set for which a dynamically typesafe view is to be * constructed. @@ -3111,7 +3099,7 @@ public class Collections { /** * Constructs a dynamically typesafe view of the specified map. - * + * * @param m - * the map for which a dynamically typesafe view is to be * constructed. @@ -3262,10 +3250,12 @@ public class Collections { /** * Constructs a dynamically typesafe view of the specified map * entry. - * - * @param e - + * + * @param e * the map entry for which a dynamically typesafe view is * to be constructed. + * @param valueType + * the type of the value */ public CheckedEntry(Map.Entry e, Class valueType) { if (e == null) { diff --git a/libcore/luni/src/main/java/java/util/ComparableTimSort.java b/libcore/luni/src/main/java/java/util/ComparableTimSort.java index 882add113..cda4b12bb 100644 --- a/libcore/luni/src/main/java/java/util/ComparableTimSort.java +++ b/libcore/luni/src/main/java/java/util/ComparableTimSort.java @@ -150,7 +150,7 @@ class ComparableTimSort { // If array is small, do a "mini-TimSort" with no merges if (nRemaining < MIN_MERGE) { - int initRunLen = countRunAndMakeAscending(a, lo, nRemaining); + int initRunLen = countRunAndMakeAscending(a, lo, hi); binarySort(a, lo, hi, lo + initRunLen); return; } diff --git a/libcore/luni/src/main/java/java/util/Comparator.java b/libcore/luni/src/main/java/java/util/Comparator.java index 7b0944899..2d6f598b3 100644 --- a/libcore/luni/src/main/java/java/util/Comparator.java +++ b/libcore/luni/src/main/java/java/util/Comparator.java @@ -25,8 +25,8 @@ package java.util; * method has to return zero for each pair of elements (a,b) where a.equals(b) * holds true. It is recommended that a {@code Comparator} implements * {@link java.io.Serializable}. - * - * @since Android 1.0 + * + * @since 1.2 */ public interface Comparator { /** @@ -42,15 +42,15 @@ public interface Comparator { * follow {@code compare(a,c) > 0} for all possible combinations of {@code * (a,b,c)} * - * + * * @param object1 * an {@code Object}. * @param object2 * a second {@code Object} to compare with {@code object1}. * @return an integer < 0 if {@code object1} is less than {@code object2}, 0 if they are * equal, and > 0 if {@code object1} is greater than {@code object2}. - * @exception ClassCastException - * when objects are not of the correct type. + * @throws ClassCastException + * if objects are not of the correct type. */ public int compare(T object1, T object2); @@ -61,7 +61,7 @@ public interface Comparator { *

          * A {@code Comparator} never needs to override this method, but may choose so for * performance reasons. - * + * * @param object * the {@code Object} to compare with this comparator. * @return boolean {@code true} if specified {@code Object} is the same as this diff --git a/libcore/luni/src/main/java/java/util/Currency.java b/libcore/luni/src/main/java/java/util/Currency.java index 8ac0e6eff..79de49d35 100644 --- a/libcore/luni/src/main/java/java/util/Currency.java +++ b/libcore/luni/src/main/java/java/util/Currency.java @@ -17,15 +17,15 @@ package java.util; -import java.io.Serializable; - +// BEGIN android-added import org.apache.harmony.luni.util.Msg; +// END android-added + +import java.io.Serializable; /** * This class represents a currency as identified in the ISO 4217 currency * codes. - * - * @since Android 1.0 */ public final class Currency implements Serializable { @@ -35,9 +35,11 @@ public final class Currency implements Serializable { private String currencyCode; + // BEGIN android-added private static String currencyVars = "EURO, HK, PREEURO"; //$NON-NLS-1$ private transient int defaultFractionDigits; + // END android-added /** * @param currencyCode @@ -49,20 +51,20 @@ public final class Currency implements Serializable { /** * Returns the {@code Currency} instance for this currency code. *

          - * + * * @param currencyCode * the currency code. * @return the {@code Currency} instance for this currency code. - * + * * @throws IllegalArgumentException * if the currency code is not a supported ISO 4217 currency * code. - * @since Android 1.0 */ public static Currency getInstance(String currencyCode) { Currency currency = codesToCurrencies.get(currencyCode); if (currency == null) { + // BEGIN android-added ResourceBundle bundle = Locale.getBundle( "ISO4CurrenciesToDigits", Locale.getDefault()); //$NON-NLS-1$ currency = new Currency(currencyCode); @@ -77,6 +79,7 @@ public final class Currency implements Serializable { } currency.defaultFractionDigits = Integer .parseInt(defaultFractionDigits); + // END android-added codesToCurrencies.put(currencyCode, currency); } @@ -85,16 +88,26 @@ public final class Currency implements Serializable { /** * Returns the {@code Currency} instance for this {@code Locale}'s country. - * + * * @param locale * the {@code Locale} of a country. * @return the {@code Currency} used in the country defined by the locale parameter. - * + * * @throws IllegalArgumentException * if the locale's country is not a supported ISO 3166 Country. - * @since Android 1.0 */ public static Currency getInstance(Locale locale) { + // BEGIN android-changed + // com.ibm.icu.util.Currency currency = null; + // try { + // currency = com.ibm.icu.util.Currency.getInstance(locale); + // } catch (IllegalArgumentException e) { + // return null; + // } + // if (currency == null) { + // throw new IllegalArgumentException(locale.getCountry()); + // } + // String currencyCode = currency.getCurrencyCode(); String country = locale.getCountry(); String variant = locale.getVariant(); if (!variant.equals("") && currencyVars.indexOf(variant) > -1) { //$NON-NLS-1$ @@ -110,6 +123,7 @@ public final class Currency implements Serializable { throw new IllegalArgumentException(Msg.getString( "K0323", locale.toString())); //$NON-NLS-1$ } + // END android-changed if (currencyCode.equals("None")) { //$NON-NLS-1$ return null; @@ -120,9 +134,8 @@ public final class Currency implements Serializable { /** * Returns this {@code Currency}'s ISO 4217 currency code. - * + * * @return this {@code Currency}'s ISO 4217 currency code. - * @since Android 1.0 */ public String getCurrencyCode() { return currencyCode; @@ -133,9 +146,8 @@ public final class Currency implements Serializable { * if the default locale is the US, the symbol of the US dollar is "$". For * other locales it may be "US$". If no symbol can be determined, the ISO * 4217 currency code of the US dollar is returned. - * + * * @return the symbol for this {@code Currency} in the default {@code Locale}. - * @since Android 1.0 */ public String getSymbol() { return getSymbol(Locale.getDefault()); @@ -157,18 +169,19 @@ public final class Currency implements Serializable { * symbol for this currency in this bundle, then the * ISO 4217 currency code is returned. *

          - * + * * @param locale * the locale for which the currency symbol should be returned. * @return the representation of this {@code Currency}'s symbol in the specified * locale. - * @since Android 1.0 */ public String getSymbol(Locale locale) { if (locale.getCountry().equals("")) { //$NON-NLS-1$ return currencyCode; } + // BEGIN android-changed + // return com.ibm.icu.util.Currency.getInstance(currencyCode).getSymbol(locale); // check in the Locale bundle first, if the local has the same currency ResourceBundle bundle = Locale.getBundle("Locale", locale); //$NON-NLS-1$ if (((String) bundle.getObject("IntCurrencySymbol")) //$NON-NLS-1$ @@ -197,6 +210,7 @@ public final class Currency implements Serializable { return result; } return currencyCode; + // END android-changed } /** @@ -204,19 +218,20 @@ public final class Currency implements Serializable { * instance, the default number of fraction digits for the US dollar is 2. * For the Japanese Yen the number is 0. In the case of pseudo-currencies, * such as IMF Special Drawing Rights, -1 is returned. - * + * * @return the default number of fraction digits for this currency. - * @since Android 1.0 */ public int getDefaultFractionDigits() { + // BEGIN android-changed + // return com.ibm.icu.util.Currency.getInstance(currencyCode).getDefaultFractionDigits(); return defaultFractionDigits; + // END android-changed } /** * Returns this currency's ISO 4217 currency code. - * + * * @return this currency's ISO 4217 currency code. - * @since Android 1.0 */ @Override public String toString() { diff --git a/libcore/luni/src/main/java/java/util/Date.java b/libcore/luni/src/main/java/java/util/Date.java index 172f68214..80610d378 100644 --- a/libcore/luni/src/main/java/java/util/Date.java +++ b/libcore/luni/src/main/java/java/util/Date.java @@ -29,13 +29,12 @@ import org.apache.harmony.luni.internal.nls.Messages; /** * {@code Date} represents a specific moment in time, to the millisecond. - * + * * @see System#currentTimeMillis * @see Calendar * @see GregorianCalendar * @see SimpleTimeZone * @see TimeZone - * @since Android 1.0 */ public class Date implements Serializable, Cloneable, Comparable { @@ -46,10 +45,15 @@ public class Date implements Serializable, Cloneable, Comparable { private transient long milliseconds; + private static String[] dayOfWeekNames = { "Sun", "Mon", "Tue", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + "Wed", "Thu", "Fri", "Sat" }; //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + + private static String[] monthNames = { "Jan", "Feb", "Mar", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + "Apr", "May", "Jun", "Jul", //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + "Aug", "Sep", "Oct", "Nov", "Dec"}; //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ + /** * Initializes this {@code Date} instance to the current date and time. - * - * @since Android 1.0 */ public Date() { this(System.currentTimeMillis()); @@ -58,17 +62,16 @@ public class Date implements Serializable, Cloneable, Comparable { /** * Constructs a new {@code Date} initialized to midnight in the default {@code TimeZone} on * the specified date. - * + * * @param year * the year, 0 is 1900. * @param month * the month, 0 - 11. * @param day * the day of the month, 1 - 31. - * + * * @deprecated use * {@link GregorianCalendar#GregorianCalendar(int, int, int)} - * @since Android 1.0 */ @Deprecated public Date(int year, int month, int day) { @@ -80,7 +83,7 @@ public class Date implements Serializable, Cloneable, Comparable { /** * Constructs a new {@code Date} initialized to the specified date and time in the * default {@code TimeZone}. - * + * * @param year * the year, 0 is 1900. * @param month @@ -91,10 +94,9 @@ public class Date implements Serializable, Cloneable, Comparable { * the hour of day, 0 - 23. * @param minute * the minute of the hour, 0 - 59. - * + * * @deprecated use * {@link GregorianCalendar#GregorianCalendar(int, int, int, int, int)} - * @since Android 1.0 */ @Deprecated public Date(int year, int month, int day, int hour, int minute) { @@ -106,7 +108,7 @@ public class Date implements Serializable, Cloneable, Comparable { /** * Constructs a new {@code Date} initialized to the specified date and time in the * default {@code TimeZone}. - * + * * @param year * the year, 0 is 1900. * @param month @@ -119,10 +121,9 @@ public class Date implements Serializable, Cloneable, Comparable { * the minute of the hour, 0 - 59. * @param second * the second of the minute, 0 - 59. - * + * * @deprecated use * {@link GregorianCalendar#GregorianCalendar(int, int, int, int, int, int)} - * @since Android 1.0 */ @Deprecated public Date(int year, int month, int day, int hour, int minute, int second) { @@ -134,24 +135,22 @@ public class Date implements Serializable, Cloneable, Comparable { /** * Initializes this {@code Date} instance using the specified millisecond value. The * value is the number of milliseconds since Jan. 1, 1970 GMT. - * + * * @param milliseconds * the number of milliseconds since Jan. 1, 1970 GMT. - * @since Android 1.0 */ public Date(long milliseconds) { - this.setTime(milliseconds); + this.milliseconds = milliseconds; } /** * Constructs a new {@code Date} initialized to the date and time parsed from the * specified String. - * + * * @param string * the String to parse. - * + * * @deprecated use {@link DateFormat} - * @since Android 1.0 */ @Deprecated public Date(String string) { @@ -160,12 +159,11 @@ public class Date implements Serializable, Cloneable, Comparable { /** * Returns if this {@code Date} is after the specified Date. - * + * * @param date * a Date instance to compare. * @return {@code true} if this {@code Date} is after the specified {@code Date}, * {@code false} otherwise. - * @since Android 1.0 */ public boolean after(Date date) { return milliseconds > date.milliseconds; @@ -173,12 +171,11 @@ public class Date implements Serializable, Cloneable, Comparable { /** * Returns if this {@code Date} is before the specified Date. - * + * * @param date * a {@code Date} instance to compare. * @return {@code true} if this {@code Date} is before the specified {@code Date}, * {@code false} otherwise. - * @since Android 1.0 */ public boolean before(Date date) { return milliseconds < date.milliseconds; @@ -186,11 +183,10 @@ public class Date implements Serializable, Cloneable, Comparable { /** * Returns a new {@code Date} with the same millisecond value as this {@code Date}. - * + * * @return a shallow copy of this {@code Date}. - * + * * @see java.lang.Cloneable - * @since Android 1.0 */ @Override public Object clone() { @@ -204,12 +200,11 @@ public class Date implements Serializable, Cloneable, Comparable { /** * Compare the receiver to the specified {@code Date} to determine the relative * ordering. - * + * * @param date * a {@code Date} to compare against. * @return an {@code int < 0} if this {@code Date} is less than the specified {@code Date}, {@code 0} if * they are equal, and an {@code int > 0} if this {@code Date} is greater. - * @since Android 1.0 */ public int compareTo(Date date) { if (milliseconds < date.milliseconds) { @@ -225,14 +220,13 @@ public class Date implements Serializable, Cloneable, Comparable { * Compares the specified object to this {@code Date} and returns if they are equal. * To be equal, the object must be an instance of {@code Date} and have the same millisecond * value. - * + * * @param object * the object to compare with this object. * @return {@code true} if the specified object is equal to this {@code Date}, {@code false} * otherwise. - * + * * @see #hashCode - * @since Android 1.0 */ @Override public boolean equals(Object object) { @@ -242,11 +236,10 @@ public class Date implements Serializable, Cloneable, Comparable { /** * Returns the gregorian calendar day of the month for this {@code Date} object. - * + * * @return the day of the month. - * + * * @deprecated use {@code Calendar.get(Calendar.DATE)} - * @since Android 1.0 */ @Deprecated public int getDate() { @@ -255,11 +248,10 @@ public class Date implements Serializable, Cloneable, Comparable { /** * Returns the gregorian calendar day of the week for this {@code Date} object. - * + * * @return the day of the week. - * + * * @deprecated use {@code Calendar.get(Calendar.DAY_OF_WEEK)} - * @since Android 1.0 */ @Deprecated public int getDay() { @@ -268,11 +260,10 @@ public class Date implements Serializable, Cloneable, Comparable { /** * Returns the gregorian calendar hour of the day for this {@code Date} object. - * + * * @return the hour of the day. - * + * * @deprecated use {@code Calendar.get(Calendar.HOUR_OF_DAY)} - * @since Android 1.0 */ @Deprecated public int getHours() { @@ -281,11 +272,10 @@ public class Date implements Serializable, Cloneable, Comparable { /** * Returns the gregorian calendar minute of the hour for this {@code Date} object. - * + * * @return the minutes. - * + * * @deprecated use {@code Calendar.get(Calendar.MINUTE)} - * @since Android 1.0 */ @Deprecated public int getMinutes() { @@ -294,11 +284,10 @@ public class Date implements Serializable, Cloneable, Comparable { /** * Returns the gregorian calendar month for this {@code Date} object. - * + * * @return the month. - * + * * @deprecated use {@code Calendar.get(Calendar.MONTH)} - * @since Android 1.0 */ @Deprecated public int getMonth() { @@ -307,11 +296,10 @@ public class Date implements Serializable, Cloneable, Comparable { /** * Returns the gregorian calendar second of the minute for this {@code Date} object. - * + * * @return the seconds. - * + * * @deprecated use {@code Calendar.get(Calendar.SECOND)} - * @since Android 1.0 */ @Deprecated public int getSeconds() { @@ -321,9 +309,8 @@ public class Date implements Serializable, Cloneable, Comparable { /** * Returns this {@code Date} as a millisecond value. The value is the number of * milliseconds since Jan. 1, 1970, midnight GMT. - * + * * @return the number of milliseconds since Jan. 1, 1970, midnight GMT. - * @since Android 1.0 */ public long getTime() { return milliseconds; @@ -331,12 +318,11 @@ public class Date implements Serializable, Cloneable, Comparable { /** * Returns the timezone offset in minutes of the default {@code TimeZone}. - * + * * @return the timezone offset in minutes of the default {@code TimeZone}. - * + * * @deprecated use * {@code (Calendar.get(Calendar.ZONE_OFFSET) + Calendar.get(Calendar.DST_OFFSET)) / 60000} - * @since Android 1.0 */ @Deprecated public int getTimezoneOffset() { @@ -346,11 +332,10 @@ public class Date implements Serializable, Cloneable, Comparable { /** * Returns the gregorian calendar year since 1900 for this {@code Date} object. - * + * * @return the year - 1900. - * + * * @deprecated use {@code Calendar.get(Calendar.YEAR) - 1900} - * @since Android 1.0 */ @Deprecated public int getYear() { @@ -360,11 +345,10 @@ public class Date implements Serializable, Cloneable, Comparable { /** * Returns an integer hash code for the receiver. Objects which are equal * return the same value for this method. - * + * * @return this {@code Date}'s hash. - * + * * @see #equals - * @since Android 1.0 */ @Override public int hashCode() { @@ -384,13 +368,12 @@ public class Date implements Serializable, Cloneable, Comparable { * Returns the millisecond value of the date and time parsed from the * specified {@code String}. Many date/time formats are recognized, including IETF * standard syntax, i.e. Tue, 22 Jun 1999 12:16:00 GMT-0500 - * + * * @param string * the String to parse. * @return the millisecond value parsed from the String. - * + * * @deprecated use {@link DateFormat} - * @since Android 1.0 */ @Deprecated public static long parse(String string) { @@ -404,10 +387,10 @@ public class Date implements Serializable, Cloneable, Comparable { int commentLevel = 0; int offset = 0, length = string.length(), state = 0; int year = -1, month = -1, date = -1; - int hour = -1, minute = -1, second = -1, zoneOffset = 0; + int hour = -1, minute = -1, second = -1, zoneOffset = 0, minutesOffset = 0; boolean zone = false; final int PAD = 0, LETTERS = 1, NUMBERS = 2; - StringBuffer buffer = new StringBuffer(); + StringBuilder buffer = new StringBuilder(); while (offset <= length) { char next = offset < length ? string.charAt(offset) : '\r'; @@ -442,6 +425,14 @@ public class Date implements Serializable, Cloneable, Comparable { if (sign == '+' || sign == '-') { if (zoneOffset == 0) { zone = true; + if (next == ':') { + minutesOffset = sign == '-' ? -Integer + .parseInt(string.substring(offset, + offset + 2)) : Integer + .parseInt(string.substring(offset, + offset + 2)); + offset += 2; + } zoneOffset = sign == '-' ? -digit : digit; sign = 0; } else { @@ -557,6 +548,7 @@ public class Date implements Serializable, Cloneable, Comparable { } else if (year < 100) { year += 1900; } + minute -= minutesOffset; if (zone) { if (zoneOffset >= 24 || zoneOffset <= -24) { hour -= zoneOffset / 100; @@ -574,12 +566,11 @@ public class Date implements Serializable, Cloneable, Comparable { /** * Sets the gregorian calendar day of the month for this {@code Date} object. - * + * * @param day * the day of the month. - * + * * @deprecated use {@code Calendar.set(Calendar.DATE, day)} - * @since Android 1.0 */ @Deprecated public void setDate(int day) { @@ -590,12 +581,11 @@ public class Date implements Serializable, Cloneable, Comparable { /** * Sets the gregorian calendar hour of the day for this {@code Date} object. - * + * * @param hour * the hour of the day. - * + * * @deprecated use {@code Calendar.set(Calendar.HOUR_OF_DAY, hour)} - * @since Android 1.0 */ @Deprecated public void setHours(int hour) { @@ -606,12 +596,11 @@ public class Date implements Serializable, Cloneable, Comparable { /** * Sets the gregorian calendar minute of the hour for this {@code Date} object. - * + * * @param minute * the minutes. - * + * * @deprecated use {@code Calendar.set(Calendar.MINUTE, minute)} - * @since Android 1.0 */ @Deprecated public void setMinutes(int minute) { @@ -622,12 +611,11 @@ public class Date implements Serializable, Cloneable, Comparable { /** * Sets the gregorian calendar month for this {@code Date} object. - * + * * @param month * the month. - * + * * @deprecated use {@code Calendar.set(Calendar.MONTH, month)} - * @since Android 1.0 */ @Deprecated public void setMonth(int month) { @@ -638,12 +626,11 @@ public class Date implements Serializable, Cloneable, Comparable { /** * Sets the gregorian calendar second of the minute for this {@code Date} object. - * + * * @param second * the seconds. - * + * * @deprecated use {@code Calendar.set(Calendar.SECOND, second)} - * @since Android 1.0 */ @Deprecated public void setSeconds(int second) { @@ -655,10 +642,9 @@ public class Date implements Serializable, Cloneable, Comparable { /** * Sets this {@code Date} to the specified millisecond value. The value is the * number of milliseconds since Jan. 1, 1970 GMT. - * + * * @param milliseconds * the number of milliseconds since Jan. 1, 1970 GMT. - * @since Android 1.0 */ public void setTime(long milliseconds) { this.milliseconds = milliseconds; @@ -666,12 +652,11 @@ public class Date implements Serializable, Cloneable, Comparable { /** * Sets the gregorian calendar year since 1900 for this {@code Date} object. - * + * * @param year * the year since 1900. - * + * * @deprecated use {@code Calendar.set(Calendar.YEAR, year + 1900)} - * @since Android 1.0 */ @Deprecated public void setYear(int year) { @@ -683,11 +668,10 @@ public class Date implements Serializable, Cloneable, Comparable { /** * Returns the string representation of this {@code Date} in GMT in the format: 22 * Jun 1999 13:02:00 GMT - * + * * @return the string representation of this {@code Date} in GMT. - * + * * @deprecated use {@link DateFormat} - * @since Android 1.0 */ @Deprecated public String toGMTString() { @@ -705,11 +689,10 @@ public class Date implements Serializable, Cloneable, Comparable { /** * Returns the string representation of this {@code Date} for the default {@code Locale}. - * + * * @return the string representation of this {@code Date} for the default {@code Locale}. - * + * * @deprecated use {@link DateFormat} - * @since Android 1.0 */ @Deprecated public String toLocaleString() { @@ -719,20 +702,29 @@ public class Date implements Serializable, Cloneable, Comparable { /** * Returns the string representation of this {@code Date} in the format: Tue Jun 22 * 13:07:00 GMT 1999 - * + * * @return the string representation of this {@code Date}. - * @since Android 1.0 */ @Override public String toString() { - return new SimpleDateFormat("E MMM dd HH:mm:ss z ", Locale.US) //$NON-NLS-1$ - .format(this) - + new GregorianCalendar(milliseconds).get(Calendar.YEAR); + Calendar cal = new GregorianCalendar(milliseconds); + return dayOfWeekNames[cal.get(Calendar.DAY_OF_WEEK) - 1] + " " + monthNames[cal.get(Calendar.MONTH)]//$NON-NLS-1$ + + " " + toTwoDigits(cal.get(Calendar.DAY_OF_MONTH)) + " " + toTwoDigits(cal.get(Calendar.HOUR_OF_DAY))//$NON-NLS-1$ //$NON-NLS-2$ + + ":" + toTwoDigits(cal.get(Calendar.MINUTE)) + ":" + toTwoDigits(cal.get(Calendar.SECOND))//$NON-NLS-1$ //$NON-NLS-2$ + + " " + cal.getTimeZone().getID() + " " + cal.get(Calendar.YEAR);//$NON-NLS-1$ //$NON-NLS-2$ + } + + private String toTwoDigits(int digit) { + if(digit >= 10) { + return "" + digit;//$NON-NLS-1$ + } else { + return "0" + digit;//$NON-NLS-1$ + } } /** * Returns the millisecond value of the specified date and time in GMT. - * + * * @param year * the year, 0 is 1900. * @param month @@ -746,12 +738,11 @@ public class Date implements Serializable, Cloneable, Comparable { * @param second * the second of the minute, 0 - 59. * @return the date and time in GMT in milliseconds. - * + * * @deprecated use: * Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("GMT")); * cal.set(year + 1900, month, day, hour, minute, second); * cal.getTime().getTime(); - * @since Android 1.0 */ @Deprecated public static long UTC(int year, int month, int day, int hour, int minute, diff --git a/libcore/luni/src/main/java/java/util/Dictionary.java b/libcore/luni/src/main/java/java/util/Dictionary.java index 236ddf039..c29b11bf1 100644 --- a/libcore/luni/src/main/java/java/util/Dictionary.java +++ b/libcore/luni/src/main/java/java/util/Dictionary.java @@ -25,14 +25,11 @@ package java.util; * associate keys with values, such as {@code Hashtable}. * * @see Hashtable - * @since Android 1.0 + * @since 1.0 */ -public abstract class Dictionary { - +public abstract class Dictionary { /** * Constructs a new instance of this class. - * - * @since Android 1.0 */ public Dictionary() { super(); @@ -45,7 +42,6 @@ public abstract class Dictionary { * @see #keys * @see #size * @see Enumeration - * @since Android 1.0 */ public abstract Enumeration elements(); @@ -57,7 +53,6 @@ public abstract class Dictionary { * @return the value associated with {@code key}, or {@code null} if the * specified key does not exist. * @see #put - * @since Android 1.0 */ public abstract V get(Object key); @@ -67,7 +62,6 @@ public abstract class Dictionary { * @return {@code true} if this dictionary has no key/value pairs, * {@code false} otherwise. * @see #size - * @since Android 1.0 */ public abstract boolean isEmpty(); @@ -78,7 +72,6 @@ public abstract class Dictionary { * @see #elements * @see #size * @see Enumeration - * @since Android 1.0 */ public abstract Enumeration keys(); @@ -96,7 +89,6 @@ public abstract class Dictionary { * @see #elements * @see #get * @see #keys - * @since Android 1.0 */ public abstract V put(K key, V value); @@ -110,7 +102,6 @@ public abstract class Dictionary { * {@code key} was not known to this dictionary. * @see #get * @see #put - * @since Android 1.0 */ public abstract V remove(Object key); @@ -120,7 +111,6 @@ public abstract class Dictionary { * @return the number of key/value pairs in this dictionary. * @see #elements * @see #keys - * @since Android 1.0 */ public abstract int size(); } diff --git a/libcore/luni/src/main/java/java/util/DuplicateFormatFlagsException.java b/libcore/luni/src/main/java/java/util/DuplicateFormatFlagsException.java index 908aa36d9..3330dcb97 100644 --- a/libcore/luni/src/main/java/java/util/DuplicateFormatFlagsException.java +++ b/libcore/luni/src/main/java/java/util/DuplicateFormatFlagsException.java @@ -19,12 +19,11 @@ package java.util; /** * The unchecked exception will be thrown out if there are duplicate flags given * out in the format specifier. - * + * * @see java.lang.RuntimeException - * @since Android 1.0 */ public class DuplicateFormatFlagsException extends IllegalFormatException { - + private static final long serialVersionUID = 18890531L; private String flags; diff --git a/libcore/luni/src/main/java/java/util/EmptyStackException.java b/libcore/luni/src/main/java/java/util/EmptyStackException.java index 73d33baf6..d8a48cce2 100644 --- a/libcore/luni/src/main/java/java/util/EmptyStackException.java +++ b/libcore/luni/src/main/java/java/util/EmptyStackException.java @@ -21,9 +21,8 @@ package java.util; /** * An {@code EmptyStackException} is thrown if the pop/peek method of a stack is * executed on an empty stack. - * + * * @see java.lang.RuntimeException - * @since Android 1.0 */ public class EmptyStackException extends RuntimeException { diff --git a/libcore/luni/src/main/java/java/util/EnumMap.java b/libcore/luni/src/main/java/java/util/EnumMap.java index 1351069a6..975fdad94 100644 --- a/libcore/luni/src/main/java/java/util/EnumMap.java +++ b/libcore/luni/src/main/java/java/util/EnumMap.java @@ -24,11 +24,14 @@ import java.lang.reflect.Array; /** * An {@code Map} specialized for use with {@code Enum} types as keys. - * - * @since Android 1.0 */ public class EnumMap, V> extends AbstractMap implements - Map, Serializable, Cloneable { + Serializable, Cloneable, Map { + + // BEGIN android-changed + // added implements Map for apicheck + // END android-changed + private static final long serialVersionUID = 458661240069192865L; private Class keyType; @@ -57,6 +60,7 @@ public class EnumMap, V> extends AbstractMap implements ordinal = ((Enum) theKey).ordinal(); } + @SuppressWarnings("unchecked") @Override public boolean equals(Object object) { if (!enumMap.hasMapping[ordinal]) { @@ -83,18 +87,21 @@ public class EnumMap, V> extends AbstractMap implements : enumMap.values[ordinal].hashCode()); } + @SuppressWarnings("unchecked") @Override public KT getKey() { checkEntryStatus(); return (KT) enumMap.keys[ordinal]; } + @SuppressWarnings("unchecked") @Override public VT getValue() { checkEntryStatus(); return (VT) enumMap.values[ordinal]; } + @SuppressWarnings("unchecked") @Override public VT setValue(VT value) { checkEntryStatus(); @@ -285,6 +292,7 @@ public class EnumMap, V> extends AbstractMap implements super(value, em); } + @SuppressWarnings("unchecked") @Override public E next() { if (!hasNext()) { @@ -354,6 +362,7 @@ public class EnumMap, V> extends AbstractMap implements return toArray(entryArray); } + @SuppressWarnings("unchecked") @Override public Object[] toArray(Object[] array) { int size = enumMap.size(); @@ -378,10 +387,11 @@ public class EnumMap, V> extends AbstractMap implements /** * Constructs an empty {@code EnumMap} using the given key type. - * + * * @param keyType * the class object giving the type of the keys used by this {@code EnumMap}. - * @since Android 1.0 + * @throws NullPointerException + * if {@code keyType} is {@code null}. */ public EnumMap(Class keyType) { initialization(keyType); @@ -390,10 +400,11 @@ public class EnumMap, V> extends AbstractMap implements /** * Constructs an {@code EnumMap} using the same key type as the given {@code EnumMap} and * initially containing the same mappings. - * + * * @param map * the {@code EnumMap} from which this {@code EnumMap} is initialized. - * @since Android 1.0 + * @throws NullPointerException + * if {@code map} is {@code null}. */ public EnumMap(EnumMap map) { initialization(map); @@ -402,16 +413,18 @@ public class EnumMap, V> extends AbstractMap implements /** * Constructs an {@code EnumMap} initialized from the given map. If the given map * is an {@code EnumMap} instance, this constructor behaves in the exactly the same - * way as {@link EnumMap#EnumMap(EnumMap)}}. Otherwise, the given map + * way as {@link EnumMap#EnumMap(EnumMap)}}. Otherwise, the given map * should contain at least one mapping. - * + * * @param map * the map from which this {@code EnumMap} is initialized. * @throws IllegalArgumentException * if {@code map} is not an {@code EnumMap} instance and does not contain * any mappings. - * @since Android 1.0 + * @throws NullPointerException + * if {@code map} is {@code null}. */ + @SuppressWarnings("unchecked") public EnumMap(Map map) { if (map instanceof EnumMap) { initialization((EnumMap) map); @@ -433,10 +446,9 @@ public class EnumMap, V> extends AbstractMap implements /** * Removes all elements from this {@code EnumMap}, leaving it empty. - * + * * @see #isEmpty() * @see #size() - * @since Android 1.0 */ @Override public void clear() { @@ -447,10 +459,10 @@ public class EnumMap, V> extends AbstractMap implements /** * Returns a shallow copy of this {@code EnumMap}. - * + * * @return a shallow copy of this {@code EnumMap}. - * @since Android 1.0 */ + @SuppressWarnings("unchecked") @Override public EnumMap clone() { try { @@ -464,12 +476,11 @@ public class EnumMap, V> extends AbstractMap implements /** * Returns whether this {@code EnumMap} contains the specified key. - * + * * @param key * the key to search for. * @return {@code true} if this {@code EnumMap} contains the specified key, * {@code false} otherwise. - * @since Android 1.0 */ @Override public boolean containsKey(Object key) { @@ -482,12 +493,11 @@ public class EnumMap, V> extends AbstractMap implements /** * Returns whether this {@code EnumMap} contains the specified value. - * + * * @param value * the value to search for. * @return {@code true} if this {@code EnumMap} contains the specified value, * {@code false} otherwise. - * @since Android 1.0 */ @Override public boolean containsValue(Object value) { @@ -511,9 +521,11 @@ public class EnumMap, V> extends AbstractMap implements * Returns a {@code Set} containing all of the mappings in this {@code EnumMap}. Each mapping is * an instance of {@link Map.Entry}. As the {@code Set} is backed by this {@code EnumMap}, * changes in one will be reflected in the other. - * + *

          + * The order of the entries in the set will be the order that the enum keys + * were declared in. + * * @return a {@code Set} of the mappings. - * @since Android 1.0 */ @Override public Set> entrySet() { @@ -526,15 +538,15 @@ public class EnumMap, V> extends AbstractMap implements /** * Compares the argument to the receiver, and returns {@code true} if the * specified {@code Object} is an {@code EnumMap} and both {@code EnumMap}s contain the same mappings. - * + * * @param object * the {@code Object} to compare with this {@code EnumMap}. * @return boolean {@code true} if {@code object} is the same as this {@code EnumMap}, * {@code false} otherwise. * @see #hashCode() * @see #entrySet() - * @since Android 1.0 */ + @SuppressWarnings("unchecked") @Override public boolean equals(Object object) { if (this == object) { @@ -553,12 +565,11 @@ public class EnumMap, V> extends AbstractMap implements /** * Returns the value of the mapping with the specified key. - * + * * @param key * the key. * @return the value of the mapping with the specified key, or {@code null} * if no mapping for the specified key is found. - * @since Android 1.0 */ @Override @SuppressWarnings("unchecked") @@ -574,9 +585,11 @@ public class EnumMap, V> extends AbstractMap implements * Returns a set of the keys contained in this {@code EnumMap}. The {@code Set} is backed by * this {@code EnumMap} so changes to one are reflected in the other. The {@code Set} does not * support adding. - * + *

          + * The order of the set will be the order that the enum keys were declared + * in. + * * @return a {@code Set} of the keys. - * @since Android 1.0 */ @Override public Set keySet() { @@ -588,7 +601,7 @@ public class EnumMap, V> extends AbstractMap implements /** * Maps the specified key to the specified value. - * + * * @param key * the key. * @param value @@ -605,7 +618,6 @@ public class EnumMap, V> extends AbstractMap implements * @throws NullPointerException * if the key or value is {@code null} and this {@code EnumMap} does not * support {@code null} keys or values. - * @since Android 1.0 */ @Override @SuppressWarnings("unchecked") @@ -615,7 +627,7 @@ public class EnumMap, V> extends AbstractMap implements /** * Copies every mapping in the specified {@code Map} to this {@code EnumMap}. - * + * * @param map * the {@code Map} to copy mappings from. * @throws UnsupportedOperationException @@ -628,7 +640,6 @@ public class EnumMap, V> extends AbstractMap implements * @throws NullPointerException * if a key or value is {@code null} and this {@code EnumMap} does not * support {@code null} keys or values. - * @since Android 1.0 */ @Override @SuppressWarnings("unchecked") @@ -638,14 +649,13 @@ public class EnumMap, V> extends AbstractMap implements /** * Removes a mapping with the specified key from this {@code EnumMap}. - * + * * @param key * the key of the mapping to remove. * @return the value of the removed mapping or {@code null} if no mapping * for the specified key was found. * @throws UnsupportedOperationException * if removing from this {@code EnumMap} is not supported. - * @since Android 1.0 */ @Override @SuppressWarnings("unchecked") @@ -665,9 +675,8 @@ public class EnumMap, V> extends AbstractMap implements /** * Returns the number of elements in this {@code EnumMap}. - * + * * @return the number of elements in this {@code EnumMap}. - * @since Android 1.0 */ @Override public int size() { @@ -680,9 +689,11 @@ public class EnumMap, V> extends AbstractMap implements * {@link Map#values()}. The {@code Collection}'s {@code Iterator} will return the values * in the their corresponding keys' natural order (the {@code Enum} constants are * declared in this order). - * + *

          + * The order of the values in the collection will be the order that their + * corresponding enum keys were declared in. + * * @return a collection of the values contained in this {@code EnumMap}. - * @since Android 1.0 */ @Override public Collection values() { @@ -692,6 +703,7 @@ public class EnumMap, V> extends AbstractMap implements return valuesCollection; } + @SuppressWarnings("unchecked") private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); diff --git a/libcore/luni/src/main/java/java/util/EnumSet.java b/libcore/luni/src/main/java/java/util/EnumSet.java index 4e44f2e79..d49d90044 100644 --- a/libcore/luni/src/main/java/java/util/EnumSet.java +++ b/libcore/luni/src/main/java/java/util/EnumSet.java @@ -23,8 +23,6 @@ import java.io.Serializable; /** * An EnumSet is a specialized Set to be used with enums as keys. - * - * @since Android 1.0 */ public abstract class EnumSet> extends AbstractSet implements Cloneable, Serializable { @@ -37,7 +35,7 @@ public abstract class EnumSet> extends AbstractSet static /*package*/ LangAccess LANG_BOOTSTRAP = null; // END android-added - private static final long serialVersionUID = 4782406773684236311L; + private static final long serialVersionUID = 1009687484059888093L; final Class elementClass; @@ -48,14 +46,13 @@ public abstract class EnumSet> extends AbstractSet /** * Creates an empty enum set. The permitted elements are of type * Class<E>. - * + * * @param elementType * the class object for the elements contained. * @return an empty enum set, with permitted elements of type {@code * elementType}. * @throws ClassCastException * if the specified element type is not and enum type. - * @since Android 1.0 */ public static > EnumSet noneOf(Class elementType) { if (!elementType.isEnum()) { @@ -73,13 +70,12 @@ public abstract class EnumSet> extends AbstractSet /** * Creates an enum set filled with all the enum elements of the specified * {@code elementType}. - * + * * @param elementType * the class object for the elements contained. * @return an enum set with elements solely from the specified element type. * @throws ClassCastException * if the specified element type is not and enum type. - * @since Android 1.0 */ public static > EnumSet allOf(Class elementType) { EnumSet set = noneOf(elementType); @@ -91,13 +87,12 @@ public abstract class EnumSet> extends AbstractSet * Creates an enum set. All the contained elements are of type * Class<E>, and the contained elements are the same as those * contained in {@code s}. - * + * * @param s * the enum set from which to copy. * @return an enum set with all the elements from the specified enum set. * @throws ClassCastException * if the specified element type is not and enum type. - * @since Android 1.0 */ public static > EnumSet copyOf(EnumSet s) { EnumSet set = EnumSet.noneOf(s.elementClass); @@ -109,14 +104,15 @@ public abstract class EnumSet> extends AbstractSet * Creates an enum set. The contained elements are the same as those * contained in collection {@code c}. If c is an enum set, invoking this * method is the same as invoking {@link #copyOf(EnumSet)}. - * + * * @param c * the collection from which to copy. if it is not an enum set, * it must not be empty. * @return an enum set with all the elements from the specified collection. * @throws IllegalArgumentException * if c is not an enum set and contains no elements at all. - * @since Android 1.0 + * @throws NullPointerException + * if {@code c} is {@code null}. */ public static > EnumSet copyOf(Collection c) { if (c instanceof EnumSet) { @@ -138,12 +134,13 @@ public abstract class EnumSet> extends AbstractSet /** * Creates an enum set. All the contained elements complement those from the * specified enum set. - * + * * @param s * the specified enum set. * @return an enum set with all the elements complementary to those from the * specified enum set. - * @since Android 1.0 + * @throws NullPointerException + * if {@code s} is {@code null}. */ public static > EnumSet complementOf(EnumSet s) { EnumSet set = EnumSet.noneOf(s.elementClass); @@ -159,11 +156,12 @@ public abstract class EnumSet> extends AbstractSet * six overloadings of the method. They accept from one to five elements * respectively. The sixth one receives an arbitrary number of elements, and * runs slower than those that only receive a fixed number of elements. - * + * * @param e * the element to be initially contained. * @return an enum set containing the specified element. - * @since Android 1.0 + * @throws NullPointerException + * if {@code e} is {@code null}. */ public static > EnumSet of(E e) { EnumSet set = EnumSet.noneOf(e.getDeclaringClass()); @@ -172,17 +170,18 @@ public abstract class EnumSet> extends AbstractSet } /** - * Creates a new enum set, containing only the specified element. There are + * Creates a new enum set, containing only the specified elements. There are * six overloadings of the method. They accept from one to five elements * respectively. The sixth one receives an arbitrary number of elements, and * runs slower than those that only receive a fixed number of elements. - * + * * @param e1 * the initially contained element. * @param e2 * another initially contained element. - * @return an enum set containing the specified element. - * @since Android 1.0 + * @return an enum set containing the specified elements. + * @throws NullPointerException + * if any of the specified elements is {@code null}. */ public static > EnumSet of(E e1, E e2) { EnumSet set = of(e1); @@ -191,19 +190,20 @@ public abstract class EnumSet> extends AbstractSet } /** - * Creates a new enum set, containing only the specified element. There are + * Creates a new enum set, containing only the specified elements. There are * six overloadings of the method. They accept from one to five elements * respectively. The sixth one receives an arbitrary number of elements, and * runs slower than those that only receive a fixed number of elements. - * + * * @param e1 * the initially contained element. * @param e2 * another initially contained element. * @param e3 * another initially contained element. - * @return an enum set containing the specified element. - * @since Android 1.0 + * @return an enum set containing the specified elements. + * @throws NullPointerException + * if any of the specified elements is {@code null}. */ public static > EnumSet of(E e1, E e2, E e3) { EnumSet set = of(e1, e2); @@ -212,11 +212,11 @@ public abstract class EnumSet> extends AbstractSet } /** - * Creates a new enum set, containing only the specified element. There are + * Creates a new enum set, containing only the specified elements. There are * six overloadings of the method. They accept from one to five elements * respectively. The sixth one receives an arbitrary number of elements, and * runs slower than those that only receive a fixed number of elements. - * + * * @param e1 * the initially contained element. * @param e2 @@ -225,8 +225,9 @@ public abstract class EnumSet> extends AbstractSet * another initially contained element. * @param e4 * another initially contained element. - * @return an enum set containing the specified element. - * @since Android 1.0 + * @return an enum set containing the specified elements. + * @throws NullPointerException + * if any of the specified elements is {@code null}. */ public static > EnumSet of(E e1, E e2, E e3, E e4) { EnumSet set = of(e1, e2, e3); @@ -235,11 +236,11 @@ public abstract class EnumSet> extends AbstractSet } /** - * Creates a new enum set, containing only the specified element. There are + * Creates a new enum set, containing only the specified elements. There are * six overloadings of the method. They accept from one to five elements * respectively. The sixth one receives an arbitrary number of elements, and * runs slower than those that only receive a fixed number of elements. - * + * * @param e1 * the initially contained element. * @param e2 @@ -250,8 +251,9 @@ public abstract class EnumSet> extends AbstractSet * another initially contained element. * @param e5 * another initially contained element. - * @return an enum set containing the specified element. - * @since Android 1.0 + * @return an enum set containing the specified elements. + * @throws NullPointerException + * if any of the specified elements is {@code null}. */ public static > EnumSet of(E e1, E e2, E e3, E e4, E e5) { EnumSet set = of(e1, e2, e3, e4); @@ -263,13 +265,14 @@ public abstract class EnumSet> extends AbstractSet * Creates a new enum set, containing only the specified elements. It can * receive an arbitrary number of elements, and runs slower than those only * receiving a fixed number of elements. - * + * * @param start * the first initially contained element. * @param others * the other initially contained elements. * @return an enum set containing the specified elements. - * @since Android 1.0 + * @throws NullPointerException + * if any of the specified elements is {@code null}. */ public static > EnumSet of(E start, E... others) { EnumSet set = of(start); @@ -283,15 +286,16 @@ public abstract class EnumSet> extends AbstractSet * Creates an enum set containing all the elements within the range defined * by {@code start} and {@code end} (inclusive). All the elements must be in * order. - * + * * @param start * the element used to define the beginning of the range. * @param end * the element used to define the end of the range. * @return an enum set with elements in the range from start to end. + * @throws NullPointerException + * if any one of {@code start} or {@code end} is {@code null}. * @throws IllegalArgumentException * if {@code start} is behind {@code end}. - * @since Android 1.0 */ public static > EnumSet range(E start, E end) { if (start.compareTo(end) > 0) { @@ -307,10 +311,9 @@ public abstract class EnumSet> extends AbstractSet /** * Creates a new enum set with the same elements as those contained in this * enum set. - * + * * @return a new enum set with the same elements as those contained in this * enum set. - * @since Android 1.0 */ @SuppressWarnings("unchecked") @Override diff --git a/libcore/luni/src/main/java/java/util/Enumeration.java b/libcore/luni/src/main/java/java/util/Enumeration.java index d60bdd27a..8b8f7bd96 100644 --- a/libcore/luni/src/main/java/java/util/Enumeration.java +++ b/libcore/luni/src/main/java/java/util/Enumeration.java @@ -17,18 +17,16 @@ package java.util; - /** * An Enumeration is used to sequence over a collection of objects. *

          - * Preferably an Iterator should be used. Iterator replaces the Enumeration - * interface and adds a way to remove elements from a collection. - *

          - * + * Preferably an {@link Iterator} should be used. {@code Iterator} replaces the + * enumeration interface and adds a way to remove elements from a collection. + * * @see Hashtable * @see Properties * @see Vector - * @since Android 1.0 + * @version 1.0 */ public interface Enumeration { @@ -37,7 +35,6 @@ public interface Enumeration { * * @return {@code true} if there are more elements, {@code false} otherwise. * @see #nextElement - * @since Android 1.0 */ public boolean hasMoreElements(); @@ -48,7 +45,6 @@ public interface Enumeration { * @throws NoSuchElementException * if there are no more elements. * @see #hasMoreElements - * @since Android 1.0 */ public E nextElement(); } diff --git a/libcore/luni/src/main/java/java/util/EventListener.java b/libcore/luni/src/main/java/java/util/EventListener.java index 9ff487669..b100f0c21 100644 --- a/libcore/luni/src/main/java/java/util/EventListener.java +++ b/libcore/luni/src/main/java/java/util/EventListener.java @@ -17,13 +17,11 @@ package java.util; - /** * EventListener is the superclass of all event listener interfaces. - * + * * @see EventObject - * @since Android 1.0 */ public interface EventListener { - /*empty*/ + /* empty */ } diff --git a/libcore/luni/src/main/java/java/util/EventListenerProxy.java b/libcore/luni/src/main/java/java/util/EventListenerProxy.java index a165385d9..1e023f6cf 100644 --- a/libcore/luni/src/main/java/java/util/EventListenerProxy.java +++ b/libcore/luni/src/main/java/java/util/EventListenerProxy.java @@ -20,8 +20,6 @@ package java.util; /** * This abstract class provides a simple wrapper for objects of type {@code EventListener}. - * - * @since Android 1.0 */ public abstract class EventListenerProxy implements EventListener { @@ -32,7 +30,6 @@ public abstract class EventListenerProxy implements EventListener { * * @param listener * the listener wrapped by this proxy. - * @since Android 1.0 */ public EventListenerProxy(EventListener listener) { super(); @@ -43,7 +40,6 @@ public abstract class EventListenerProxy implements EventListener { * Returns the wrapped {@code EventListener}. * * @return the wrapped {@code EventListener}. - * @since Android 1.0 */ public EventListener getListener() { return listener; diff --git a/libcore/luni/src/main/java/java/util/EventObject.java b/libcore/luni/src/main/java/java/util/EventObject.java index 61e530a40..76da7e299 100644 --- a/libcore/luni/src/main/java/java/util/EventObject.java +++ b/libcore/luni/src/main/java/java/util/EventObject.java @@ -25,8 +25,6 @@ import java.io.Serializable; * add event specific information. * * @see EventListener - * - * @since Android 1.0 */ public class EventObject implements Serializable { @@ -34,8 +32,6 @@ public class EventObject implements Serializable { /** * The event source. - * - * @since Android 1.0 */ protected transient Object source; @@ -44,7 +40,6 @@ public class EventObject implements Serializable { * * @param source * the object which fired the event. - * @since Android 1.0 */ public EventObject(Object source) { if (source != null) { @@ -58,7 +53,6 @@ public class EventObject implements Serializable { * Returns the event source. * * @return the object which fired the event. - * @since Android 1.0 */ public Object getSource() { return source; @@ -68,7 +62,6 @@ public class EventObject implements Serializable { * Returns the string representation of this {@code EventObject}. * * @return the string representation of this {@code EventObject}. - * @since Android 1.0 */ @Override public String toString() { diff --git a/libcore/luni/src/main/java/java/util/FormatFlagsConversionMismatchException.java b/libcore/luni/src/main/java/java/util/FormatFlagsConversionMismatchException.java index aa0c967e5..e4f39d23b 100644 --- a/libcore/luni/src/main/java/java/util/FormatFlagsConversionMismatchException.java +++ b/libcore/luni/src/main/java/java/util/FormatFlagsConversionMismatchException.java @@ -23,7 +23,6 @@ import java.io.Serializable; * conversion and the flags are incompatible. * * @see java.lang.RuntimeException - * @since Android 1.0 */ public class FormatFlagsConversionMismatchException extends IllegalFormatException implements Serializable { diff --git a/libcore/luni/src/main/java/java/util/Formattable.java b/libcore/luni/src/main/java/java/util/Formattable.java index 2564c04ce..15b66b57e 100644 --- a/libcore/luni/src/main/java/java/util/Formattable.java +++ b/libcore/luni/src/main/java/java/util/Formattable.java @@ -20,16 +20,15 @@ package java.util; * Classes that handle custom formatting for the 's' specifier of {@code Formatter} * should implement the {@code Formattable} interface. It gives basic control over * formatting objects. - * + * * @see Formatter - * @since Android 1.0 */ public interface Formattable { /** * Formats the object using the specified {@code Formatter}. - * + * * @param formatter * the {@code Formatter} to use. * @param flags @@ -54,7 +53,6 @@ public interface Formattable { * is -1, then maximum length is not enforced. * @throws IllegalFormatException * if any of the parameters is not supported. - * @since Android 1.0 */ void formatTo(Formatter formatter, int flags, int width, int precision) throws IllegalFormatException; diff --git a/libcore/luni/src/main/java/java/util/FormattableFlags.java b/libcore/luni/src/main/java/java/util/FormattableFlags.java index b5078e44c..b8d44d4d8 100644 --- a/libcore/luni/src/main/java/java/util/FormattableFlags.java +++ b/libcore/luni/src/main/java/java/util/FormattableFlags.java @@ -22,7 +22,6 @@ package java.util; * flags must be done by the implementations. * * @see Formattable - * @since Android 1.0 */ public class FormattableFlags { @@ -37,8 +36,6 @@ public class FormattableFlags { * right-justified. * * The flag corresponds to '-' ('\u002d') in the format specifier. - * - * @since Android 1.0 */ public static final int LEFT_JUSTIFY = 1; @@ -48,8 +45,6 @@ public class FormattableFlags { * effect as {@code String.toUpperCase(java.util.Locale)}. * * This flag corresponds to {@code '^' ('\u005e')} in the format specifier. - * - * @since Android 1.0 */ public static final int UPPERCASE = 2; @@ -58,8 +53,6 @@ public class FormattableFlags { * of the alternate form is determined by the {@code Formattable}. * * This flag corresponds to {@code '#' ('\u0023')} in the format specifier. - * - * @since Android 1.0 */ public static final int ALTERNATE = 4; } diff --git a/libcore/luni/src/main/java/java/util/Formatter.java b/libcore/luni/src/main/java/java/util/Formatter.java index a6f14f0ec..d1dd4177c 100644 --- a/libcore/luni/src/main/java/java/util/Formatter.java +++ b/libcore/luni/src/main/java/java/util/Formatter.java @@ -28,6 +28,7 @@ import java.io.PrintStream; import java.io.UnsupportedEncodingException; import java.math.BigDecimal; import java.math.BigInteger; +import java.math.MathContext; import java.nio.CharBuffer; import java.nio.charset.Charset; import java.security.AccessController; @@ -37,63 +38,59 @@ import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.text.NumberFormat; -// BEGIN android-note -// Added quite extensive class documentation. Almost none was present before. -// END android-note - /** *

          The {@code Formatter} class is a String-formatting utility that is designed - * to work like the {@code printf} function of the C programming language. + * to work like the {@code printf} function of the C programming language. * Its key methods are the {@code format} methods which create a formatted - * {@code String} by replacing a set of placeholders (format tokens) with formatted + * {@code String} by replacing a set of placeholders (format tokens) with formatted * values. The style used to format each value is determined by the format * token used. For example, the call
          * {@code format("My decimal value is %d and my String is %s.", 3, "Hello");}
          * returns the {@code String}
          - * {@code My decimal value is 3 and my String is Hello.}

          - * - *

          The format token consists of a percent sign, optionally followed - * by flags and precision arguments, and then a single character that + * {@code My decimal value is 3 and my String is Hello.} + * + *

          The format token consists of a percent sign, optionally followed + * by flags and precision arguments, and then a single character that * indicates the type of value * being formatted. If the type is a time/date, then the type character - * {@code t} is followed by an additional character that indicates how the - * date is to be formatted. The two characters {@code <$} immediately + * {@code t} is followed by an additional character that indicates how the + * date is to be formatted. The two characters {@code <$} immediately * following the % sign indicate that the previous value should be used again * instead of moving on to the next value argument. A number {@code n} * and a dollar sign immediately following the % sign make n the next argument - * to be used.

          - * - *

          The available choices are the following:

          - * + * to be used. + * + *

          The available choices are the following: + * * * * * * - * - * - * - * - * + * + * + * + * + * * - * - * - * - * - * + * + * + * + * + * * - * - * - * + * + * + * * - * + * * - * - * - * + * + * + * * - * + * * * * * - * - * - * + * + * + * * - * + * * * * * - * - * - * + * + * + * * - * + * * - * - * - * + * + * + * * - * + * * - * - * - * + * + * + * * - * + * * * * * - * - * - * + * + * + * * - * + * * - * - * - * + * + * + * * - * + * * - * - * - * + * + * + * * - * + * * - * - * - * + * + * + * * - * + * * - * - * - * + * + * + * * - * + * * - * - * - * + * + * + * * - * + * * * * * - * - * - * + * + * + * * - * + * * - * + * * - * + * notation, where the precision indicates the number of significant digits. + * * - * + * * - * + * * - * + * notation , where the precision indicates the maximum number of significant digits. + * * - * + * * - * + * * - * + * notation, where the precision indicates the number of significant digits. + * * - * + * * * * * - * + * * - * + * requires it. + * * - * + * * * * * - * - * - * + * + * + * * - * + * * * * * - * - * - * + * + * + * * - * + * * - * - * - * + * + * - * + * * - * - * - * + * + * + * * - * + * * - * - * - * + * + * + * * - * + * * - * - * - * + * + * + * * - * + * * - * - * - * + * + * + * * - * + * * - * - * - * + * + * + * * - * + * * - * - * - * + * + * + * * - * + * * - * - * - * + * + * + * * - * + * * - * + * * - * + * without a leading zero + * * - * + * * - * - * - * + * + * + * * - * + * * - * - * - * + * + * + * * - * + * * - * - * - * + * + * + * * - * + * * - * - * - * + * + * + * * - * + * * - * + * * - * + * (1 January 1970 00:00:00 UTC) + * * - * + * * - * - * - * + * + * + * * - * + * * * * * - * - * - * + * + * + * * - * + * * - * - * - * + * + * + * * - * + * * - * - * - * + * + * + * * - * + * * * * * - * - * - * + * + * + * * - * - *
          * Text value types
          {@code s}String{@code format("%s, %s", "hello", "Hello");}{@code hello, Hello}
          {@code s}String{@code format("%s, %s", "hello", "Hello");}{@code hello, Hello}
          {@code S}, {@code s}String to capitals{@code format("%S, %S", "hello", "Hello");}{@code HELLO, HELLO}
          {@code S}, {@code s}String to capitals{@code format("%S, %S", "hello", "Hello");}{@code HELLO, HELLO}
          {@code c}Character{@code format("%c, %c", 'd', 0x65);}{@code c}Character{@code format("%c, %c", 'd', 0x65);}{@code d, e}
          {@code C}Character to capitals{@code format("%C, %C", 'd', 0x65);}{@code C}Character to capitals{@code format("%C, %C", 'd', 0x65);}{@code D, E}
          * Text option flags
          The value between the @@ -101,33 +98,33 @@ import java.text.NumberFormat; * characters of the formatted value
          {@code -}Left justify (width value is required){@code format("%-3C, %3C", 'd', 0x65);}{@code -}Left justify (width value is required){@code format("%-3C, %3C", 'd', 0x65);}{@code D , E}
          * Integer types
          {@code d}int, formatted as decimal{@code format("%d, %d"1$, 35, 0x10);}{@code d}int, formatted as decimal{@code format("%d, %d"1$, 35, 0x10);}{@code 35, 16}
          {@code o}int, formatted as octal{@code format("%o, %o", 8, 010);}{@code o}int, formatted as octal{@code format("%o, %o", 8, 010);}{@code 10, 10}
          {@code X}, {@code x}int, formatted as hexidecimal{@code format("%x, %X", 10, 10);}{@code X}, {@code x}int, formatted as hexidecimal{@code format("%x, %X", 10, 10);}{@code a, A}
          * Integer option flags
          The value between the @@ -135,43 +132,43 @@ import java.text.NumberFormat; * characters of the formatted value
          {@code +}lead with the number's sign{@code format("%+d, %+4d", 5, 5);}{@code +}lead with the number's sign{@code format("%+d, %+4d", 5, 5);}{@code +5, +5}
          {@code -}Left justify (width value is required){@code format("%-6dx", 5);}{@code -}Left justify (width value is required){@code format("%-6dx", 5);}{@code 5 x}
          {@code #}Print the leading characters that indicate - * hexidecimal or octal (for use only with hex and octal types) {@code format("%#o", 010);}{@code #}Print the leading characters that indicate + * hexidecimal or octal (for use only with hex and octal types) {@code format("%#o", 010);}{@code 010}
          {@code }A space indicates that non-negative numbers - * should have a leading space. {@code format("x% d% 5d", 4, 4);}{@code }A space indicates that non-negative numbers + * should have a leading space. {@code format("x% d% 5d", 4, 4);}{@code x 4 4}
          {@code 0}Pad the number with leading zeros (width value is required){@code format("%07d, %03d", 4, 5555);}{@code 0}Pad the number with leading zeros (width value is required){@code format("%07d, %03d", 4, 5555);}{@code 0000004, 5555}
          {@code (}Put parentheses around negative numbers (decimal only){@code format("%(d, %(d, %(6d", 12, -12, -12);}{@code (}Put parentheses around negative numbers (decimal only){@code format("%(d, %(d, %(6d", 12, -12, -12);}{@code 12, (12), (12)}
          * Float types
          A value immediately following the % symbol @@ -180,229 +177,223 @@ import java.text.NumberFormat; * gives the precision (6 by default).
          {@code f}float (or double) formatted as a decimal, where - * the precision indicates the number of digits after the decimal.{@code format("%f %<.1f %<1.5f %<10f %<6.0f", 123.456f);}{@code f}float (or double) formatted as a decimal, where + * the precision indicates the number of digits after the decimal.{@code format("%f %<.1f %<1.5f %<10f %<6.0f", 123.456f);}{@code 123.456001 123.5 123.45600 123.456001 123}
          {@code E}, {@code e}{@code E}, {@code e}float (or double) formatted in decimal exponential - * notation, where the precision indicates the number of significant digits.{@code format("%E %<.1e %<1.5E %<10E %<6.0E", 123.456f);}{@code format("%E %<.1e %<1.5E %<10E %<6.0E", 123.456f);}{@code 1.234560E+02 1.2e+02 1.23456E+02 1.234560E+02 1E+02}
          {@code G}, {@code g}{@code G}, {@code g}float (or double) formatted in decimal exponential - * notation , where the precision indicates the maximum number of significant digits.{@code format("%G %<.1g %<1.5G %<10G %<6.0G", 123.456f);}{@code format("%G %<.1g %<1.5G %<10G %<6.0G", 123.456f);}{@code 123.456 1e+02 123.46 123.456 1E+02}
          {@code A}, {@code a}{@code A}, {@code a}float (or double) formatted as a hexidecimal in exponential - * notation, where the precision indicates the number of significant digits.{@code format("%A %<.1a %<1.5A %<10A %<6.0A", 123.456f);}{@code format("%A %<.1a %<1.5A %<10A %<6.0A", 123.456f);}{@code 0X1.EDD2F2P6 0x1.fp6 0X1.EDD2FP6 0X1.EDD2F2P6 0X1.FP6}
          - * Float-type option flags
          See the Integer-type options. + * Float-type option flags
          See the Integer-type options. * The options for float-types are the * same as for integer types with one addition:
          {@code ,}{@code ,}Use a comma in place of a decimal if the locale - * requires it. {@code format(new Locale("fr"), "%,7.2f", 6.03f);}{@code format(new Locale("fr"), "%,7.2f", 6.03f);}{@code 6,03}
          * Date types
          {@code t}, {@code T}Date{@code format(new Locale("fr"), "%tB %TB", Calendar.getInstance(), Calendar.getInstance());}{@code t}, {@code T}Date{@code format(new Locale("fr"), "%tB %TB", Calendar.getInstance(), Calendar.getInstance());}{@code avril AVRIL}
          - * Date format precisions
          The format precision character + * Date format precisions
          The format precision character * follows the {@code t}.
          {@code A}, {@code a}The day of the week{@code format("%ta %tA", cal, cal);}{@code A}, {@code a}The day of the week{@code format("%ta %tA", cal, cal);}{@code Tue Tuesday}
          {@code b}, {@code B}, {@code h}The name of the month{@code format("%tb % + * {@code b}, {@code B}, {@code h}The name of the month{@code format("%tb % * {@code Apr April Apr}
          {@code C}The century{@code format("%tC\n", cal);}{@code C}The century{@code format("%tC\n", cal);}{@code 20}
          {@code d}, {@code e}The day of the month (with or without leading zeros){@code format("%td %te", cal, cal);}{@code d}, {@code e}The day of the month (with or without leading zeros){@code format("%td %te", cal, cal);}{@code 01 1}
          {@code F}The complete date formatted as YYYY-MM-DD{@code format("%tF", cal);}{@code F}The complete date formatted as YYYY-MM-DD{@code format("%tF", cal);}{@code 2008-04-01}
          {@code D}The complete date formatted as MM/DD/YY - * (not corrected for locale) {@code format(new Locale("en_US"), "%tD", cal);
          format(new Locale("en_UK"), " %tD", cal);}
          {@code D}The complete date formatted as MM/DD/YY + * (not corrected for locale) {@code format(new Locale("en_US"), "%tD", cal);
          format(new Locale("en_UK"), " %tD", cal);}
          {@code 04/01/08 04/01/08}
          {@code j}The number of the day (from the beginning of the year).{@code format("%tj\n", cal);}{@code j}The number of the day (from the beginning of the year).{@code format("%tj\n", cal);}{@code 092}
          {@code m}The number of the month{@code format("%tm\n", cal);}{@code m}The number of the month{@code format("%tm\n", cal);}{@code 04}
          {@code y}, {@code Y}The year{@code format("%ty %tY", cal, cal);}{@code y}, {@code Y}The year{@code format("%ty %tY", cal, cal);}{@code 08 2008}
          {@code H}, {@code I}, {@code k}, {@code l}{@code H}, {@code I}, {@code k}, {@code l}The hour of the day, in 12 or 24 hour format, with or - * without a leading zero{@code format("%tH %tI %tk %tl", cal, cal, cal, cal);}{@code format("%tH %tI %tk %tl", cal, cal, cal, cal);}{@code 16 04 16 4}
          {@code p}a.m. or p.m.{@code format("%tp %Tp", cal, cal);}{@code p}a.m. or p.m.{@code format("%tp %Tp", cal, cal);}{@code pm PM}
          {@code M}, {@code S}, {@code L}, {@code N}The minutes, seconds, milliseconds, and nanoseconds{@code format("%tM %tS %tL %tN", cal, cal, cal, cal);}{@code M}, {@code S}, {@code L}, {@code N}The minutes, seconds, milliseconds, and nanoseconds{@code format("%tM %tS %tL %tN", cal, cal, cal, cal);}{@code 08 17 359 359000000}
          {@code Z}, {@code z}The time zone: its abbreviation or offset from GMT{@code format("%tZ %tz", cal, cal);}{@code Z}, {@code z}The time zone: its abbreviation or offset from GMT{@code format("%tZ %tz", cal, cal);}{@code CEST +0100}
          {@code R}, {@code r}, {@code T}The complete time{@code format("%tR %tr %tT", cal, cal, cal);}{@code R}, {@code r}, {@code T}The complete time{@code format("%tR %tr %tT", cal, cal, cal);}{@code 16:15 04:15:32 PM 16:15:32}
          {@code s}, {@code Q}{@code s}, {@code Q}The number of seconds or milliseconds from "the epoch" - * (1 January 1970 00:00:00 UTC) {@code format("%ts %tQ", cal, cal);}{@code format("%ts %tQ", cal, cal);}{@code 1207059412 1207059412656}
          {@code c}The complete time and date{@code format("%tc", cal);}{@code c}The complete time and date{@code format("%tc", cal);}{@code Tue Apr 01 16:19:17 CEST 2008}
          * Other data types
          {@code B}, {@code b}Boolean{@code format("%b, %B", true, false);}{@code B}, {@code b}Boolean{@code format("%b, %B", true, false);}{@code true, FALSE}
          {@code H}, {@code h}Hashcode{@code format("%h, %H", obj, obj);}{@code H}, {@code h}Hashcode{@code format("%h, %H", obj, obj);}{@code 190d11, 190D11}
          {@code n}line separator{@code format("first%nsecond", "???");}{@code n}line separator{@code format("first%nsecond", "???");}{@code first
          second}
          * Escape sequences
          {@code %}Escape the % character{@code format("%d%%, %d", 50, 60);}{@code %}Escape the % character{@code format("%d%%, %d", 50, 60);}{@code 50%, 60}
          - * + * + * + * *

          An instance of Formatter can be created to write the formatted - * output to standard types of output streams. Its functionality can - * also be accessed through the format methods of an output stream + * output to standard types of output streams. Its functionality can + * also be accessed through the format methods of an output stream * or of {@code String}:
          * {@code System.out.println(String.format("%ty\n", cal));}
          - * {@code System.out.format("%ty\n", cal);}

          - * - *

          The class is not multi-threaded safe. The user is responsible for + * {@code System.out.format("%ty\n", cal);} + * + *

          The class is not multi-threaded safe. The user is responsible for * maintaining a thread-safe design if a {@code Formatter} is - * accessed by multiple threads.

          - * - * @since Android 1.0 + * accessed by multiple threads. + * + * @since 1.5 */ public final class Formatter implements Closeable, Flushable { - // BEGIN android-changed /** * The enumeration giving the available styles for formatting very large * decimal numbers. - * - * @since Android 1.0 - */ + */ public enum BigDecimalLayoutForm { /** * Use scientific style for BigDecimals. - * @since Android 1.0 */ - SCIENTIFIC, + SCIENTIFIC, /** * Use normal decimal/float style for BigDecimals. - * @since Android 1.0 */ DECIMAL_FLOAT } - // END android-changed private Appendable out; @@ -414,29 +405,26 @@ public final class Formatter implements Closeable, Flushable { /** * Constructs a {@code Formatter}. - * + * * The output is written to a {@code StringBuilder} which can be acquired by invoking * {@link #out()} and whose content can be obtained by calling * {@code toString()}. - * + * * The {@code Locale} for the {@code Formatter} is the default {@code Locale}. - * - * @since Android 1.0 */ public Formatter() { this(new StringBuilder(), Locale.getDefault()); } /** - * Constructs a {@code Formatter} whose output will be written to the + * Constructs a {@code Formatter} whose output will be written to the * specified {@code Appendable}. - * + * * The locale for the {@code Formatter} is the default {@code Locale}. - * + * * @param a * the output destination of the {@code Formatter}. If {@code a} is {@code null}, * then a {@code StringBuilder} will be used. - * @since Android 1.0 */ public Formatter(Appendable a) { this(a, Locale.getDefault()); @@ -444,32 +432,30 @@ public final class Formatter implements Closeable, Flushable { /** * Constructs a {@code Formatter} with the specified {@code Locale}. - * + * * The output is written to a {@code StringBuilder} which can be acquired by invoking * {@link #out()} and whose content can be obtained by calling * {@code toString()}. - * + * * @param l * the {@code Locale} of the {@code Formatter}. If {@code l} is {@code null}, * then no localization will be used. - * @since Android 1.0 */ public Formatter(Locale l) { this(new StringBuilder(), l); } /** - * Constructs a {@code Formatter} with the specified {@code Locale} - * and whose output will be written to the + * Constructs a {@code Formatter} with the specified {@code Locale} + * and whose output will be written to the * specified {@code Appendable}. - * + * * @param a * the output destination of the {@code Formatter}. If {@code a} is {@code null}, * then a {@code StringBuilder} will be used. * @param l * the {@code Locale} of the {@code Formatter}. If {@code l} is {@code null}, * then no localization will be used. - * @since Android 1.0 */ public Formatter(Appendable a, Locale l) { if (null == a) { @@ -482,17 +468,16 @@ public final class Formatter implements Closeable, Flushable { /** * Constructs a {@code Formatter} whose output is written to the specified file. - * + * * The charset of the {@code Formatter} is the default charset. - * + * * The {@code Locale} for the {@code Formatter} is the default {@code Locale}. - * + * * @param fileName * the filename of the file that is used as the output * destination for the {@code Formatter}. The file will be truncated to * zero size if the file exists, or else a new file will be * created. The output of the {@code Formatter} is buffered. - * * @throws FileNotFoundException * if the filename does not denote a normal and writable file, * or if a new file cannot be created, or if any error arises when @@ -500,7 +485,6 @@ public final class Formatter implements Closeable, Flushable { * @throws SecurityException * if there is a {@code SecurityManager} in place which denies permission * to write to the file in {@code checkWrite(file.getPath())}. - * @since Android 1.0 */ public Formatter(String fileName) throws FileNotFoundException { this(new File(fileName)); @@ -509,9 +493,9 @@ public final class Formatter implements Closeable, Flushable { /** * Constructs a {@code Formatter} whose output is written to the specified file. - * + * * The {@code Locale} for the {@code Formatter} is the default {@code Locale}. - * + * * @param fileName * the filename of the file that is used as the output * destination for the {@code Formatter}. The file will be truncated to @@ -519,7 +503,6 @@ public final class Formatter implements Closeable, Flushable { * created. The output of the {@code Formatter} is buffered. * @param csn * the name of the charset for the {@code Formatter}. - * * @throws FileNotFoundException * if the filename does not denote a normal and writable file, * or if a new file cannot be created, or if any error arises when @@ -529,7 +512,6 @@ public final class Formatter implements Closeable, Flushable { * to write to the file in {@code checkWrite(file.getPath())}. * @throws UnsupportedEncodingException * if the charset with the specified name is not supported. - * @since Android 1.0 */ public Formatter(String fileName, String csn) throws FileNotFoundException, UnsupportedEncodingException { @@ -537,9 +519,9 @@ public final class Formatter implements Closeable, Flushable { } /** - * Constructs a {@code Formatter} with the given {@code Locale} and charset, + * Constructs a {@code Formatter} with the given {@code Locale} and charset, * and whose output is written to the specified file. - * + * * @param fileName * the filename of the file that is used as the output * destination for the {@code Formatter}. The file will be truncated to @@ -550,7 +532,6 @@ public final class Formatter implements Closeable, Flushable { * @param l * the {@code Locale} of the {@code Formatter}. If {@code l} is {@code null}, * then no localization will be used. - * * @throws FileNotFoundException * if the filename does not denote a normal and writable file, * or if a new file cannot be created, or if any error arises when @@ -560,7 +541,6 @@ public final class Formatter implements Closeable, Flushable { * to write to the file in {@code checkWrite(file.getPath())}. * @throws UnsupportedEncodingException * if the charset with the specified name is not supported. - * @since Android 1.0 */ public Formatter(String fileName, String csn, Locale l) throws FileNotFoundException, UnsupportedEncodingException { @@ -570,17 +550,16 @@ public final class Formatter implements Closeable, Flushable { /** * Constructs a {@code Formatter} whose output is written to the specified {@code File}. - * + * * The charset of the {@code Formatter} is the default charset. - * + * * The {@code Locale} for the {@code Formatter} is the default {@code Locale}. - * + * * @param file * the {@code File} that is used as the output destination for the * {@code Formatter}. The {@code File} will be truncated to zero size if the {@code File} * exists, or else a new {@code File} will be created. The output of the * {@code Formatter} is buffered. - * * @throws FileNotFoundException * if the {@code File} is not a normal and writable {@code File}, or if a * new {@code File} cannot be created, or if any error rises when opening or @@ -588,18 +567,17 @@ public final class Formatter implements Closeable, Flushable { * @throws SecurityException * if there is a {@code SecurityManager} in place which denies permission * to write to the {@code File} in {@code checkWrite(file.getPath())}. - * @since Android 1.0 */ public Formatter(File file) throws FileNotFoundException { this(new FileOutputStream(file)); } /** - * Constructs a {@code Formatter} with the given charset, + * Constructs a {@code Formatter} with the given charset, * and whose output is written to the specified {@code File}. - * + * * The {@code Locale} for the {@code Formatter} is the default {@code Locale}. - * + * * @param file * the {@code File} that is used as the output destination for the * {@code Formatter}. The {@code File} will be truncated to zero size if the {@code File} @@ -616,7 +594,6 @@ public final class Formatter implements Closeable, Flushable { * to write to the {@code File} in {@code checkWrite(file.getPath())}. * @throws UnsupportedEncodingException * if the charset with the specified name is not supported. - * @since Android 1.0 */ public Formatter(File file, String csn) throws FileNotFoundException, UnsupportedEncodingException { @@ -624,9 +601,9 @@ public final class Formatter implements Closeable, Flushable { } /** - * Constructs a {@code Formatter} with the given {@code Locale} and charset, + * Constructs a {@code Formatter} with the given {@code Locale} and charset, * and whose output is written to the specified {@code File}. - * + * * @param file * the {@code File} that is used as the output destination for the * {@code Formatter}. The {@code File} will be truncated to zero size if the {@code File} @@ -646,7 +623,6 @@ public final class Formatter implements Closeable, Flushable { * to write to the {@code File} in {@code checkWrite(file.getPath())}. * @throws UnsupportedEncodingException * if the charset with the specified name is not supported. - * @since Android 1.0 */ public Formatter(File file, String csn, Locale l) throws FileNotFoundException, UnsupportedEncodingException { @@ -654,9 +630,9 @@ public final class Formatter implements Closeable, Flushable { try { fout = new FileOutputStream(file); OutputStreamWriter writer = new OutputStreamWriter(fout, csn); - // BEGIN android-modified + // BEGIN android-changed out = new BufferedWriter(writer, 8192); - // END android-modified + // END android-changed } catch (RuntimeException e) { closeOutputStream(fout); throw e; @@ -670,37 +646,35 @@ public final class Formatter implements Closeable, Flushable { /** * Constructs a {@code Formatter} whose output is written to the specified {@code OutputStream}. - * + * * The charset of the {@code Formatter} is the default charset. - * + * * The {@code Locale} for the {@code Formatter} is the default {@code Locale}. - * + * * @param os * the stream to be used as the destination of the {@code Formatter}. - * @since Android 1.0 */ public Formatter(OutputStream os) { OutputStreamWriter writer = new OutputStreamWriter(os, Charset .defaultCharset()); - // BEGIN android-modified + // BEGIN android-changed out = new BufferedWriter(writer, 8192); - // END android-modified + // END android-changed locale = Locale.getDefault(); } /** - * Constructs a {@code Formatter} with the given charset, + * Constructs a {@code Formatter} with the given charset, * and whose output is written to the specified {@code OutputStream}. - * + * * The {@code Locale} for the {@code Formatter} is the default {@code Locale}. - * + * * @param os * the stream to be used as the destination of the {@code Formatter}. * @param csn * the name of the charset for the {@code Formatter}. * @throws UnsupportedEncodingException * if the charset with the specified name is not supported. - * @since Android 1.0 */ public Formatter(OutputStream os, String csn) throws UnsupportedEncodingException { @@ -709,9 +683,9 @@ public final class Formatter implements Closeable, Flushable { } /** - * Constructs a {@code Formatter} with the given {@code Locale} and charset, + * Constructs a {@code Formatter} with the given {@code Locale} and charset, * and whose output is written to the specified {@code OutputStream}. - * + * * @param os * the stream to be used as the destination of the {@code Formatter}. * @param csn @@ -721,31 +695,29 @@ public final class Formatter implements Closeable, Flushable { * then no localization will be used. * @throws UnsupportedEncodingException * if the charset with the specified name is not supported. - * @since Android 1.0 */ public Formatter(OutputStream os, String csn, Locale l) throws UnsupportedEncodingException { OutputStreamWriter writer = new OutputStreamWriter(os, csn); - // BEGIN android-modified + // BEGIN android-changed out = new BufferedWriter(writer, 8192); - // END android-modified + // END android-changed locale = l; } /** * Constructs a {@code Formatter} whose output is written to the specified {@code PrintStream}. - * + * * The charset of the {@code Formatter} is the default charset. - * + * * The {@code Locale} for the {@code Formatter} is the default {@code Locale}. - * + * * @param ps * the {@code PrintStream} used as destination of the {@code Formatter}. If - * {@code ps} is {@code null}, then a {@ code NullPointerExcepiton} will + * {@code ps} is {@code null}, then a {@code NullPointerException} will * be raised. - * @since Android 1.0 */ public Formatter(PrintStream ps) { if (null == ps) { @@ -763,11 +735,10 @@ public final class Formatter implements Closeable, Flushable { /** * Returns the {@code Locale} of the {@code Formatter}. - * + * * @return the {@code Locale} for the {@code Formatter} or {@code null} for no {@code Locale}. * @throws FormatterClosedException * if the {@code Formatter} has been closed. - * @since Android 1.0 */ public Locale locale() { checkClosed(); @@ -776,11 +747,10 @@ public final class Formatter implements Closeable, Flushable { /** * Returns the output destination of the {@code Formatter}. - * + * * @return the output destination of the {@code Formatter}. * @throws FormatterClosedException * if the {@code Formatter} has been closed. - * @since Android 1.0 */ public Appendable out() { checkClosed(); @@ -790,12 +760,11 @@ public final class Formatter implements Closeable, Flushable { /** * Returns the content by calling the {@code toString()} method of the output * destination. - * + * * @return the content by calling the {@code toString()} method of the output * destination. * @throws FormatterClosedException * if the {@code Formatter} has been closed. - * @since Android 1.0 */ @Override public String toString() { @@ -805,11 +774,10 @@ public final class Formatter implements Closeable, Flushable { /** * Flushes the {@code Formatter}. If the output destination is {@link Flushable}, - * then the method {@ code flush()} will be called on that destination. - * + * then the method {@code flush()} will be called on that destination. + * * @throws FormatterClosedException * if the {@code Formatter} has been closed. - * @since Android 1.0 */ public void flush() { checkClosed(); @@ -825,14 +793,12 @@ public final class Formatter implements Closeable, Flushable { /** * Closes the {@code Formatter}. If the output destination is {@link Closeable}, * then the method {@code close()} will be called on that destination. - * + * * If the {@code Formatter} has been closed, then calling the this method will have no * effect. - * + * * Any method but the {@link #ioException()} that is called after the * {@code Formatter} has been closed will raise a {@code FormatterClosedException}. - * - * @since Android 1.0 */ public void close() { closed = true; @@ -850,10 +816,9 @@ public final class Formatter implements Closeable, Flushable { * Returns the last {@code IOException} thrown by the {@code Formatter}'s output * destination. If the {@code append()} method of the destination does not throw * {@code IOException}s, the {@code ioException()} method will always return {@code null}. - * + * * @return the last {@code IOException} thrown by the {@code Formatter}'s output * destination. - * @since Android 1.0 */ public IOException ioException() { return lastIOException; @@ -861,7 +826,7 @@ public final class Formatter implements Closeable, Flushable { /** * Writes a formatted string to the output destination of the {@code Formatter}. - * + * * @param format * a format string. * @param args @@ -875,23 +840,22 @@ public final class Formatter implements Closeable, Flushable { * the format string, or any other illegal situation. * @throws FormatterClosedException * if the {@code Formatter} has been closed. - * @since Android 1.0 */ public Formatter format(String format, Object... args) { return format(locale, format, args); } -// BEGIN android-changed + // BEGIN android-added /** * Cached transformer. Improves performance when format() is called multiple * times. */ private Transformer transformer; -// END android-changed + // END android-added /** * Writes a formatted string to the output destination of the {@code Formatter}. - * + * * @param l * the {@code Locale} used in the method. If {@code locale} is * {@code null}, then no localization will be applied. This @@ -910,7 +874,6 @@ public final class Formatter implements Closeable, Flushable { * the format string, or any other illegal situation. * @throws FormatterClosedException * if the {@code Formatter} has been closed. - * @since Android 1.0 */ public Formatter format(Locale l, String format, Object... args) { checkClosed(); @@ -1275,7 +1238,7 @@ public final class Formatter implements Closeable, Flushable { break; } case 'n': { - result = transfromFromLineSeparator(); + result = transformFromLineSeparator(); break; } case 't': @@ -1499,7 +1462,7 @@ public final class Formatter implements Closeable, Flushable { * Transforms line separator to a formatted string. Any flag, the width * or the precision is illegal. */ - private String transfromFromLineSeparator() { + private String transformFromLineSeparator() { if (formatToken.isPrecisionSet()) { throw new IllegalFormatPrecisionException(formatToken .getPrecision()); @@ -2121,9 +2084,18 @@ public final class Formatter implements Closeable, Flushable { boolean requireScientificRepresentation = true; double d = ((Number) argument).doubleValue(); d = Math.abs(d); - long l = Math.round(d); + if (Double.isInfinite(d)) { + precision = formatToken.getPrecision(); + precision--; + formatToken.setPrecision(precision); + transform_e(); + return; + } + BigDecimal b = new BigDecimal(d, new MathContext(precision)); + d = b.doubleValue(); + long l = b.longValue(); - if (l >= 1) { + if (d >= 1 && d < Math.pow(10, precision)) { if (l < Math.pow(10, precision)) { requireScientificRepresentation = false; precision -= String.valueOf(l).length(); @@ -2137,19 +2109,20 @@ public final class Formatter implements Closeable, Flushable { } } else { - l = Math.round(d * Math.pow(10, 4)); - if (l >= 1) { + l = b.movePointRight(4).longValue(); + b.movePointLeft(4); + if (d >= Math.pow(10, -4) && d < 1) { requireScientificRepresentation = false; precision += 4 - String.valueOf(l).length(); - l = Math.round(d * Math.pow(10, precision + 1)); + l = b.movePointRight(precision + 1).longValue(); + b.movePointLeft(precision + 1); if (String.valueOf(l).length() <= formatToken .getPrecision()) { precision++; } - l = Math.round(d * Math.pow(10, precision)); - if (l < Math.pow(10, precision - 4)) { - requireScientificRepresentation = true; - } else { + l = b.movePointRight(precision).longValue(); + b.movePointLeft(precision); + if (l >= Math.pow(10, precision - 4)) { formatToken.setPrecision(precision); } } @@ -2461,9 +2434,10 @@ public final class Formatter implements Closeable, Flushable { private void transform_Z() { TimeZone timeZone = calendar.getTimeZone(); - result - .append(timeZone.getDisplayName(true, TimeZone.SHORT, - locale)); + result.append(timeZone + .getDisplayName( + timeZone.inDaylightTime(calendar.getTime()), + TimeZone.SHORT, locale)); } private void transform_z() { diff --git a/libcore/luni/src/main/java/java/util/FormatterClosedException.java b/libcore/luni/src/main/java/java/util/FormatterClosedException.java index 63d1052fe..e7e57ba04 100644 --- a/libcore/luni/src/main/java/java/util/FormatterClosedException.java +++ b/libcore/luni/src/main/java/java/util/FormatterClosedException.java @@ -23,7 +23,6 @@ import java.io.Serializable; * closed. * * @see java.lang.RuntimeException - * @since Android 1.0 */ public class FormatterClosedException extends IllegalStateException implements Serializable { diff --git a/libcore/luni/src/main/java/java/util/GregorianCalendar.java b/libcore/luni/src/main/java/java/util/GregorianCalendar.java index 32368ec50..d8cd5568f 100644 --- a/libcore/luni/src/main/java/java/util/GregorianCalendar.java +++ b/libcore/luni/src/main/java/java/util/GregorianCalendar.java @@ -14,12 +14,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/** -******************************************************************************* -* Copyright (C) 1996-2008, International Business Machines Corporation and * -* others. All Rights Reserved. * -******************************************************************************* -*/ package java.util; @@ -27,24 +21,19 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; -// BEGIN android-note -// The class javadoc and some of the method descriptions are copied from ICU4J -// source files. Changes have been made to the copied descriptions. -// The icu license header was added to this file. -// END android-note /** * {@code GregorianCalendar} is a concrete subclass of {@link Calendar} * and provides the standard calendar used by most of the world. - * + * *

          * The standard (Gregorian) calendar has 2 eras, BC and AD. - * + * *

          * This implementation handles a single discontinuity, which corresponds by * default to the date the Gregorian calendar was instituted (October 15, 1582 * in some countries, later in others). The cutover date may be changed by the * caller by calling {@code setGregorianChange()}. - * + * *

          * Historically, in those countries which adopted the Gregorian calendar first, * October 4, 1582 was thus followed by October 15, 1582. This calendar models @@ -53,7 +42,7 @@ import java.io.ObjectOutputStream; * the Julian calendar is the leap year rule. The Julian calendar specifies leap * years every four years, whereas the Gregorian calendar omits century years * which are not divisible by 400. - * + * *

          * {@code GregorianCalendar} implements proleptic Gregorian * and Julian calendars. That is, dates are computed by extrapolating the @@ -64,13 +53,13 @@ import java.io.ObjectOutputStream; * 4 AD onward, when modern Julian calendar rules were adopted. Before this * date, leap year rules were applied irregularly, and before 45 BC the Julian * calendar did not even exist. - * + * *

          * Prior to the institution of the Gregorian calendar, New Year's Day was March * 25. To avoid confusion, this calendar always uses January 1. A manual * adjustment may be made if desired for dates that are prior to the Gregorian * changeover and which fall between January 1 and March 24. - * + * *

          * Values calculated for the {@code WEEK_OF_YEAR} field range from 1 to * 53. Week 1 for a year is the earliest seven day period starting on @@ -80,7 +69,7 @@ import java.io.ObjectOutputStream; * {@code getFirstDayOfWeek()}, and the day of the week of January 1. * Weeks between week 1 of one year and week 1 of the following year are * numbered sequentially from 2 to 52 or 53 (as needed). - * + * *

          * For example, January 1, 1998 was a Thursday. If * {@code getFirstDayOfWeek()} is {@code MONDAY} and @@ -90,7 +79,7 @@ import java.io.ObjectOutputStream; * {@code getFirstDayOfWeek()} is {@code SUNDAY}, then week 1 of * 1998 starts on January 4, 1998, and ends on January 10, 1998; the first three * days of 1998 then are part of week 53 of 1997. - * + * *

          * Values calculated for the {@code WEEK_OF_MONTH} field range from 0 or * 1 to 4 or 5. Week 1 of a month (the days with WEEK_OF_MONTH = @@ -102,7 +91,7 @@ import java.io.ObjectOutputStream; * {@code getFirstDayOfWeek()}, and will not include days of the * previous month. Days of a month before week 1 have a * {@code WEEK_OF_MONTH} of 0. - * + * *

          * For example, if {@code getFirstDayOfWeek()} is {@code SUNDAY} * and {@code getMinimalDaysInFirstWeek()} is 4, then the first week of @@ -111,10 +100,10 @@ import java.io.ObjectOutputStream; * Saturday, January 3 have a {@code WEEK_OF_MONTH} of 0. If * {@code getMinimalDaysInFirstWeek()} is changed to 3, then January 1 * through January 3 have a {@code WEEK_OF_MONTH} of 1. - * + * *

          * Example:

          - * + * *
            * // get the supported ids for GMT-08:00 (Pacific Standard Time)
            * String[] ids = TimeZone.getAvailableIDs(-8 * 60 * 60 * 1000);
          @@ -186,13 +175,11 @@ import java.io.ObjectOutputStream;
            * System.out.println("DST_OFFSET: "
            *        + (calendar.get(Calendar.DST_OFFSET)/(60*60*1000))); // in hours
            * 
          - * + * *
          - * + * * @see Calendar * @see TimeZone - * - * @since Android 1.0 */ public class GregorianCalendar extends Calendar { @@ -200,18 +187,14 @@ public class GregorianCalendar extends Calendar { /** * Value for the BC era. - * - * @since Android 1.0 */ public static final int BC = 0; /** * Value for the AD era. - * - * @since Android 1.0 */ public static final int AD = 1; - + private static final long defaultGregorianCutover = -12219292800000l; private long gregorianCutover = defaultGregorianCutover; @@ -251,8 +234,6 @@ public class GregorianCalendar extends Calendar { /** * Constructs a new {@code GregorianCalendar} initialized to the current date and * time with the default {@code Locale} and {@code TimeZone}. - * - * @since Android 1.0 */ public GregorianCalendar() { this(TimeZone.getDefault(), Locale.getDefault()); @@ -261,14 +242,13 @@ public class GregorianCalendar extends Calendar { /** * Constructs a new {@code GregorianCalendar} initialized to midnight in the default * {@code TimeZone} and {@code Locale} on the specified date. - * + * * @param year * the year. * @param month * the month. * @param day * the day of the month. - * @since Android 1.0 */ public GregorianCalendar(int year, int month, int day) { super(TimeZone.getDefault(), Locale.getDefault()); @@ -278,7 +258,7 @@ public class GregorianCalendar extends Calendar { /** * Constructs a new {@code GregorianCalendar} initialized to the specified date and * time in the default {@code TimeZone} and {@code Locale}. - * + * * @param year * the year. * @param month @@ -289,7 +269,6 @@ public class GregorianCalendar extends Calendar { * the hour. * @param minute * the minute. - * @since Android 1.0 */ public GregorianCalendar(int year, int month, int day, int hour, int minute) { super(TimeZone.getDefault(), Locale.getDefault()); @@ -299,7 +278,7 @@ public class GregorianCalendar extends Calendar { /** * Constructs a new {@code GregorianCalendar} initialized to the specified date and * time in the default {@code TimeZone} and {@code Locale}. - * + * * @param year * the year. * @param month @@ -312,7 +291,6 @@ public class GregorianCalendar extends Calendar { * the minute. * @param second * the second. - * @since Android 1.0 */ public GregorianCalendar(int year, int month, int day, int hour, int minute, int second) { @@ -328,10 +306,9 @@ public class GregorianCalendar extends Calendar { /** * Constructs a new {@code GregorianCalendar} initialized to the current date and * time and using the specified {@code Locale} and the default {@code TimeZone}. - * + * * @param locale * the {@code Locale}. - * @since Android 1.0 */ public GregorianCalendar(Locale locale) { this(TimeZone.getDefault(), locale); @@ -340,10 +317,9 @@ public class GregorianCalendar extends Calendar { /** * Constructs a new {@code GregorianCalendar} initialized to the current date and * time and using the specified {@code TimeZone} and the default {@code Locale}. - * + * * @param timezone * the {@code TimeZone}. - * @since Android 1.0 */ public GregorianCalendar(TimeZone timezone) { this(timezone, Locale.getDefault()); @@ -352,12 +328,11 @@ public class GregorianCalendar extends Calendar { /** * Constructs a new {@code GregorianCalendar} initialized to the current date and * time and using the specified {@code TimeZone} and {@code Locale}. - * + * * @param timezone * the {@code TimeZone}. * @param locale * the {@code Locale}. - * @since Android 1.0 */ public GregorianCalendar(TimeZone timezone, Locale locale) { super(timezone, locale); @@ -372,15 +347,14 @@ public class GregorianCalendar extends Calendar { /** * Adds the specified amount to a {@code Calendar} field. - * + * * @param field * the {@code Calendar} field to modify. * @param value * the amount to add to the field. - * - * @exception IllegalArgumentException - * when the specified field is DST_OFFSET or ZONE_OFFSET. - * @since Android 1.0 + * + * @throws IllegalArgumentException + * if the specified field is DST_OFFSET or ZONE_OFFSET. */ @Override public void add(int field, int value) { @@ -477,9 +451,8 @@ public class GregorianCalendar extends Calendar { /** * Creates new instance of {@code GregorianCalendar} with the same properties. - * + * * @return a shallow copy of this {@code GregorianCalendar}. - * @since Android 1.0 */ @Override public Object clone() { @@ -508,7 +481,7 @@ public class GregorianCalendar extends Calendar { int dayOfYear = computeYearAndDay(days, timeVal + zoneOffset); fields[DAY_OF_YEAR] = dayOfYear; - if(fields[YEAR] == changeYear && gregorianCutover < timeVal + zoneOffset){ + if(fields[YEAR] == changeYear && gregorianCutover <= timeVal + zoneOffset){ dayOfYear += currentYearSkew; } int month = dayOfYear / 32; @@ -539,7 +512,7 @@ public class GregorianCalendar extends Calendar { dayOfYear = computeYearAndDay(days, timeVal - zoneOffset + dstOffset); fields[DAY_OF_YEAR] = dayOfYear; - if(fields[YEAR] == changeYear && gregorianCutover < timeVal - zoneOffset + dstOffset){ + if(fields[YEAR] == changeYear && gregorianCutover <= timeVal - zoneOffset + dstOffset){ dayOfYear += currentYearSkew; } month = dayOfYear / 32; @@ -621,7 +594,9 @@ public class GregorianCalendar extends Calendar { protected void computeFields() { int zoneOffset = getTimeZone().getRawOffset(); - fields[ZONE_OFFSET] = zoneOffset; + if(!isSet[ZONE_OFFSET]) { + fields[ZONE_OFFSET] = zoneOffset; + } int millis = (int) (time % 86400000); int savedMillis = millis; @@ -864,6 +839,9 @@ public class GregorianCalendar extends Calendar { - (days + daysInMonth(leapYear, month) - 3)) + fields[DAY_OF_WEEK_IN_MONTH] * 7; } + } else if (isSet[DAY_OF_WEEK]) { + int skew = mod7(days - 3 - (getFirstDayOfWeek() - 1)); + days += mod7(mod7(skew + dayOfWeek - (days - 3)) - skew); } } } else { @@ -967,7 +945,7 @@ public class GregorianCalendar extends Calendar { return (year - 1970) * 365 + ((year - 1972) / 4) - ((year - 2000) / 100) + ((year - 2000) / 400); } - + private int daysInMonth() { return daysInMonth(isLeapYear(fields[YEAR]), fields[MONTH]); } @@ -1003,16 +981,15 @@ public class GregorianCalendar extends Calendar { * Compares the specified {@code Object} to this {@code GregorianCalendar} and returns whether * they are equal. To be equal, the {@code Object} must be an instance of {@code GregorianCalendar} and * have the same properties. - * + * * @param object * the {@code Object} to compare with this {@code GregorianCalendar}. * @return {@code true} if {@code object} is equal to this * {@code GregorianCalendar}, {@code false} otherwise. - * @exception IllegalArgumentException - * when the time is not set and the time cannot be computed + * @throws IllegalArgumentException + * if the time is not set and the time cannot be computed * from the current field values. * @see #hashCode - * @since Android 1.0 */ @Override public boolean equals(Object object) { @@ -1023,11 +1000,10 @@ public class GregorianCalendar extends Calendar { /** * Gets the maximum value of the specified field for the current date. For * example, the maximum number of days in the current month. - * + * * @param field * the field. * @return the maximum value of the specified field. - * @since Android 1.0 */ @Override public int getActualMaximum(int field) { @@ -1095,11 +1071,10 @@ public class GregorianCalendar extends Calendar { * Gets the minimum value of the specified field for the current date. For * the gregorian calendar, this value is the same as * {@code getMinimum()}. - * + * * @param field * the field. * @return the minimum value of the specified field. - * @since Android 1.0 */ @Override public int getActualMinimum(int field) { @@ -1109,11 +1084,10 @@ public class GregorianCalendar extends Calendar { /** * Gets the greatest minimum value of the specified field. For the gregorian * calendar, this value is the same as {@code getMinimum()}. - * + * * @param field * the field. * @return the greatest minimum value of the specified field. - * @since Android 1.0 */ @Override public int getGreatestMinimum(int field) { @@ -1123,9 +1097,8 @@ public class GregorianCalendar extends Calendar { /** * Returns the gregorian change date of this calendar. This is the date on * which the gregorian calendar came into effect. - * + * * @return a {@code Date} which represents the gregorian change date. - * @since Android 1.0 */ public final Date getGregorianChange() { return new Date(gregorianCutover); @@ -1134,11 +1107,10 @@ public class GregorianCalendar extends Calendar { /** * Gets the smallest maximum value of the specified field. For example, 28 * for the day of month field. - * + * * @param field * the field. * @return the smallest maximum value of the specified field. - * @since Android 1.0 */ @Override public int getLeastMaximum(int field) { @@ -1158,11 +1130,10 @@ public class GregorianCalendar extends Calendar { /** * Gets the greatest maximum value of the specified field. For example, 31 * for the day of month field. - * + * * @param field * the field. * @return the greatest maximum value of the specified field. - * @since Android 1.0 */ @Override public int getMaximum(int field) { @@ -1171,11 +1142,10 @@ public class GregorianCalendar extends Calendar { /** * Gets the smallest minimum value of the specified field. - * + * * @param field * the field. * @return the smallest minimum value of the specified field. - * @since Android 1.0 */ @Override public int getMinimum(int field) { @@ -1234,11 +1204,10 @@ public class GregorianCalendar extends Calendar { /** * Returns an integer hash code for the receiver. Objects which are equal * return the same value for this method. - * + * * @return the receiver's hash. - * + * * @see #equals - * @since Android 1.0 */ @Override public int hashCode() { @@ -1248,12 +1217,11 @@ public class GregorianCalendar extends Calendar { /** * Returns whether the specified year is a leap year. - * + * * @param year * the year. * @return {@code true} if the specified year is a leap year, {@code false} * otherwise. - * @since Android 1.0 */ public boolean isLeapYear(int year) { if (year > changeYear) { @@ -1288,15 +1256,14 @@ public class GregorianCalendar extends Calendar { * field when it goes beyond the maximum or minimum value for the current * date. Other fields will be adjusted as required to maintain a consistent * date. - * + * * @param field * the field to roll. * @param value * the amount to add. - * - * @exception IllegalArgumentException - * when an invalid field is specified. - * @since Android 1.0 + * + * @throws IllegalArgumentException + * if an invalid field is specified. */ @Override public void roll(int field, int value) { @@ -1310,72 +1277,90 @@ public class GregorianCalendar extends Calendar { isCached = false; complete(); + int days, day, mod, maxWeeks, newWeek; int max = -1; switch (field) { - case YEAR: - max = maximums[field]; - break; - case WEEK_OF_YEAR: - case WEEK_OF_MONTH: - int days, - day; - if (field == WEEK_OF_YEAR) { - days = daysInYear(fields[YEAR]); - day = DAY_OF_YEAR; + case YEAR: + max = maximums[field]; + break; + case WEEK_OF_YEAR: + days = daysInYear(fields[YEAR]); + day = DAY_OF_YEAR; + mod = mod7(fields[DAY_OF_WEEK] - fields[day] + - (getFirstDayOfWeek() - 1)); + maxWeeks = (days - 1 + mod) / 7 + 1; + newWeek = mod(fields[field] - 1 + value, maxWeeks) + 1; + if (newWeek == maxWeeks) { + int addDays = (newWeek - fields[field]) * 7; + if (fields[day] > addDays && fields[day] + addDays > days) { + set(field, 1); } else { - days = daysInMonth(); - day = DATE; + set(field, newWeek - 1); } - int mod = mod7(fields[DAY_OF_WEEK] - fields[day] - - (getFirstDayOfWeek() - 1)); - int maxWeeks = (days - 1 + mod) / 7 + 1; - int newWeek = mod(fields[field] - 1 + value, maxWeeks) + 1; - if (newWeek == maxWeeks) { - if (fields[day] + (newWeek - fields[field]) * 7 > days) { - set(day, days); - } else { - set(field, newWeek); - } - } else if (newWeek == 1) { - int week = (fields[day] - ((fields[day] - 1) / 7 * 7) - 1 + mod) / 7 + 1; - if (week > 1) { - set(day, 1); - } else { - set(field, newWeek); - } + } else if (newWeek == 1) { + int week = (fields[day] - ((fields[day] - 1) / 7 * 7) - 1 + mod) / 7 + 1; + if (week > 1) { + set(field, 1); } else { set(field, newWeek); } - break; - case DATE: - max = daysInMonth(); - break; - case DAY_OF_YEAR: - max = daysInYear(fields[YEAR]); - break; - case DAY_OF_WEEK: - max = maximums[field]; - lastDateFieldSet = WEEK_OF_MONTH; - break; - case DAY_OF_WEEK_IN_MONTH: - max = (fields[DATE] + ((daysInMonth() - fields[DATE]) / 7 * 7) - 1) / 7 + 1; - break; - - case ERA: - case MONTH: - case AM_PM: - case HOUR: - case HOUR_OF_DAY: - case MINUTE: - case SECOND: - case MILLISECOND: - set(field, mod(fields[field] + value, maximums[field] + 1)); - if (field == MONTH && fields[DATE] > daysInMonth()) { - set(DATE, daysInMonth()); - } else if (field == AM_PM) { - lastTimeFieldSet = HOUR; + } else { + set(field, newWeek); + } + break; + case WEEK_OF_MONTH: + days = daysInMonth(); + day = DATE; + mod = mod7(fields[DAY_OF_WEEK] - fields[day] + - (getFirstDayOfWeek() - 1)); + maxWeeks = (days - 1 + mod) / 7 + 1; + newWeek = mod(fields[field] - 1 + value, maxWeeks) + 1; + if (newWeek == maxWeeks) { + if (fields[day] + (newWeek - fields[field]) * 7 > days) { + set(day, days); + } else { + set(field, newWeek); } - break; + } else if (newWeek == 1) { + int week = (fields[day] - ((fields[day] - 1) / 7 * 7) - 1 + mod) / 7 + 1; + if (week > 1) { + set(day, 1); + } else { + set(field, newWeek); + } + } else { + set(field, newWeek); + } + break; + case DATE: + max = daysInMonth(); + break; + case DAY_OF_YEAR: + max = daysInYear(fields[YEAR]); + break; + case DAY_OF_WEEK: + max = maximums[field]; + lastDateFieldSet = WEEK_OF_MONTH; + break; + case DAY_OF_WEEK_IN_MONTH: + max = (fields[DATE] + ((daysInMonth() - fields[DATE]) / 7 * 7) - 1) / 7 + 1; + break; + + case ERA: + case MONTH: + case AM_PM: + case HOUR: + case HOUR_OF_DAY: + case MINUTE: + case SECOND: + case MILLISECOND: + set(field, mod(fields[field] + value, maximums[field] + 1)); + if (field == MONTH && fields[DATE] > daysInMonth()) { + set(DATE, daysInMonth()); + } else if (field == AM_PM) { + lastTimeFieldSet = HOUR; + } + break; } if (max != -1) { set(field, mod(fields[field] - 1 + value, max) + 1); @@ -1389,15 +1374,14 @@ public class GregorianCalendar extends Calendar { * date. Other fields will be adjusted as required to maintain a consistent * date. For example, March 31 will roll to April 30 when rolling the month * field. - * + * * @param field * the field to roll. * @param increment * {@code true} to increment the field, {@code false} to * decrement. - * @exception IllegalArgumentException - * when an invalid field is specified. - * @since Android 1.0 + * @throws IllegalArgumentException + * if an invalid field is specified. */ @Override public void roll(int field, boolean increment) { @@ -1406,10 +1390,9 @@ public class GregorianCalendar extends Calendar { /** * Sets the gregorian change date of this calendar. - * + * * @param date * a {@code Date} which represents the gregorian change date. - * @since Android 1.0 */ public void setGregorianChange(Date date) { gregorianCutover = date.getTime(); diff --git a/libcore/luni/src/main/java/java/util/HashMap.java b/libcore/luni/src/main/java/java/util/HashMap.java index 594e8a801..28978d4be 100644 --- a/libcore/luni/src/main/java/java/util/HashMap.java +++ b/libcore/luni/src/main/java/java/util/HashMap.java @@ -1,10 +1,10 @@ /* * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with + * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at + * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * @@ -15,705 +15,657 @@ * limitations under the License. */ +// BEGIN android-note +// Completely different implementation from harmony. Runs much faster. +// BEGIN android-note + package java.util; import java.io.IOException; +import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; +import java.io.ObjectStreamField; import java.io.Serializable; /** * HashMap is an implementation of Map. All optional operations (adding and * removing) are supported. Keys and values can be any objects. - * - * @since Android 1.0 + * + * @param the type of keys maintained by this map + * @param the type of mapped values */ -public class HashMap extends AbstractMap implements Map, - Cloneable, Serializable { - private static final long serialVersionUID = 362498820763181265L; - - transient int elementCount; - - transient Entry[] elementData; - - final float loadFactor; - - int threshold; - - transient int modCount = 0; - - private static final int DEFAULT_SIZE = 16; - - static class Entry extends MapEntry { - final int origKeyHash; - - Entry next; - - Entry(K theKey, int hash) { - super(theKey, null); - this.origKeyHash = hash; - } - - Entry(K theKey, V theValue) { - super(theKey, theValue); - origKeyHash = (theKey == null ? 0 : theKey.hashCode()); - } - - @Override - @SuppressWarnings("unchecked") - public Object clone() { - Entry entry = (Entry) super.clone(); - if (next != null) { - entry.next = (Entry) next.clone(); - } - return entry; - } - } - - static class HashMapIterator implements Iterator { - private int position = 0; - - int expectedModCount; - - final MapEntry.Type type; - - boolean canRemove = false; - - Entry entry; - - Entry lastEntry; - - final HashMap associatedMap; - - HashMapIterator(MapEntry.Type value, HashMap hm) { - associatedMap = hm; - type = value; - expectedModCount = hm.modCount; - } - - public boolean hasNext() { - if (entry != null) { - return true; - } - // BEGIN android-changed - Entry[] elementData = associatedMap.elementData; - int length = elementData.length; - int newPosition = position; - boolean result = false; - - while (newPosition < length) { - if (elementData[newPosition] == null) { - newPosition++; - } else { - result = true; - break; - } - } - - position = newPosition; - return result; - // END android-changed - } - - void checkConcurrentMod() throws ConcurrentModificationException { - if (expectedModCount != associatedMap.modCount) { - throw new ConcurrentModificationException(); - } - } - - public E next() { - // BEGIN android-changed - // inline checkConcurrentMod() - if (expectedModCount != associatedMap.modCount) { - throw new ConcurrentModificationException(); - } - if (!hasNext()) { - throw new NoSuchElementException(); - } - - MapEntry result; - Entry _entry = entry; - if (_entry == null) { - result = lastEntry = associatedMap.elementData[position++]; - entry = lastEntry.next; - } else { - if (lastEntry.next != _entry) { - lastEntry = lastEntry.next; - } - result = _entry; - entry = _entry.next; - } - canRemove = true; - return type.get(result); - // END android-changed - } - - public void remove() { - checkConcurrentMod(); - if (!canRemove) { - throw new IllegalStateException(); - } - - canRemove = false; - associatedMap.modCount++; - if (lastEntry.next == entry) { - while (associatedMap.elementData[--position] == null) { - // Do nothing - } - associatedMap.elementData[position] = associatedMap.elementData[position].next; - entry = null; - } else { - lastEntry.next = entry; - } - associatedMap.elementCount--; - expectedModCount++; - } - } +public class HashMap extends AbstractMap + implements Cloneable, Serializable, Map { + /** + * Min capacity (other than zero) for a HashMap. Must be a power of two + * greater than 1 (and less than 1 << 30). + */ + private static final int MINIMUM_CAPACITY = 4; - static class HashMapEntrySet extends AbstractSet> { - private final HashMap associatedMap; + /** + * Max capacity for a HashMap. Must be a power of two >= MINIMUM_CAPACITY. + */ + private static final int MAXIMUM_CAPACITY = 1 << 30; - public HashMapEntrySet(HashMap hm) { - associatedMap = hm; - } + /** + * An empty table shared by all zero-capacity maps (typically from default + * constructor). It is never written to, and replaced on first put. Its size + * is set to half the minimum, so that the first resize will create a + * minimum-sized table. + */ + private static final Entry[] EMPTY_TABLE + = new HashMapEntry[MINIMUM_CAPACITY >>> 1]; - HashMap hashMap() { - return associatedMap; - } + /** + * The default load factor. Note that this implementation ignores the + * load factor, but cannot do away with it entirely because it's + * metioned in the API. + * + *

          Note that this constant has no impact on the behavior of the program, + * but it is emitted as part of the serialized form. The load factor of + * .75 is hardwired into the program, which uses cheap shifts in place of + * expensive division. + */ + static final float DEFAULT_LOAD_FACTOR = .75F; - @Override - public int size() { - return associatedMap.elementCount; - } + /** + * The hash table. If this hash map contains a mapping for null, it is + * not represented this hash table. + */ + transient HashMapEntry[] table; - @Override - public void clear() { - associatedMap.clear(); - } + /** + * The entry representing the null key, or null if there's no such mapping. + */ + transient HashMapEntry entryForNullKey; - @Override - public boolean remove(Object object) { - if (contains(object)) { - associatedMap.remove(((Map.Entry) object).getKey()); - return true; - } - return false; - } + /** + * The number of mappings in this hash map. + */ + transient int size; - @Override - public boolean contains(Object object) { - if (object instanceof Map.Entry) { - Object key = ((Map.Entry) object).getKey(); - Entry entry; - if (key == null) { - entry = associatedMap.findNullKeyEntry(); - } else { - int hash = key.hashCode(); - int index = (hash & 0x7FFFFFFF) % associatedMap.elementData.length; - entry = associatedMap.findNonNullKeyEntry(key, index, hash); - } - return object.equals(entry); - } - return false; - } + /** + * Incremented by "structural modifications" to allow (best effort) + * detection of concurrent modification. + */ + transient int modCount; - @Override - public Iterator> iterator() { - return new HashMapIterator, KT, VT>( - new MapEntry.Type, KT, VT>() { - public Map.Entry get(MapEntry entry) { - return entry; - } - }, associatedMap); - } - } + /** + * The table is rehashed when its size exceeds this threshold. + * The value of this field is generally .75 * capacity, except when + * the capacity is zero, as described in the EMPTY_TABLE declaration + * above. + */ + private transient int threshold; - @SuppressWarnings("unchecked") - Entry[] newElementArray(int s) { - return new Entry[s]; - } + // Views - lazily initialized + private transient Set keySet; + private transient Set> entrySet; + private transient Collection values; /** * Constructs a new empty {@code HashMap} instance. - * - * @since Android 1.0 */ + @SuppressWarnings("unchecked") public HashMap() { - this(DEFAULT_SIZE); + table = (HashMapEntry[]) EMPTY_TABLE; + threshold = -1; // Forces first put invocation to replace EMPTY_TABLE } /** * Constructs a new {@code HashMap} instance with the specified capacity. - * + * * @param capacity * the initial capacity of this hash map. * @throws IllegalArgumentException * when the capacity is less than zero. - * @since Android 1.0 */ public HashMap(int capacity) { - if (capacity >= 0) { - elementCount = 0; - elementData = newElementArray(capacity == 0 ? 1 : capacity); - loadFactor = 0.75f; // Default load factor of 0.75 - computeMaxSize(); + if (capacity < 0) { + throw new IllegalArgumentException("Capacity: " + capacity); + } + + if (capacity == 0) { + @SuppressWarnings("unchecked") + HashMapEntry[] tab = (HashMapEntry[]) EMPTY_TABLE; + table = tab; + threshold = -1; // Forces first put() to replace EMPTY_TABLE + return; + } + + if (capacity < MINIMUM_CAPACITY) { + capacity = MINIMUM_CAPACITY; + } else if (capacity > MAXIMUM_CAPACITY) { + capacity = MAXIMUM_CAPACITY; } else { - throw new IllegalArgumentException(); + capacity = roundUpToPowerOfTwo(capacity); } + makeTable(capacity); } /** * Constructs a new {@code HashMap} instance with the specified capacity and * load factor. - * + * * @param capacity * the initial capacity of this hash map. * @param loadFactor * the initial load factor. * @throws IllegalArgumentException * when the capacity is less than zero or the load factor is - * less or equal to zero. - * @since Android 1.0 + * less or equal to zero or NaN. */ public HashMap(int capacity, float loadFactor) { - if (capacity >= 0 && loadFactor > 0) { - elementCount = 0; - elementData = newElementArray(capacity == 0 ? 1 : capacity); - this.loadFactor = loadFactor; - computeMaxSize(); - } else { - throw new IllegalArgumentException(); + this(capacity); + + if (loadFactor <= 0 || Float.isNaN(loadFactor)) { + throw new IllegalArgumentException("Load factor: " + loadFactor); } + + /* + * Note that this implementation ignores loadFactor; it always uses + * a load facator of 3/4. This simplifies the code and generally + * improves performance. + */ } /** * Constructs a new {@code HashMap} instance containing the mappings from * the specified map. - * + * * @param map * the mappings to add. - * @since Android 1.0 */ public HashMap(Map map) { - this(map.size() < 6 ? 11 : map.size() * 2); - putAllImpl(map); + this(capacityForInitSize(map.size())); + constructorPutAll(map); } - // BEGIN android-changed /** - * Removes all mappings from this hash map, leaving it empty. - * - * @see #isEmpty - * @see #size - * @since Android 1.0 + * Inserts all of the elements of map into this HashMap in a manner + * suitable for use by constructors and pseudocostructors (i.e., clone, + * readObject). Also used by LinkedHashMap. */ - @Override - public void clear() { - internalClear(); + final void constructorPutAll(Map map) { + for (Entry e : map.entrySet()) { + constructorPut(e.getKey(), e.getValue()); + } } - void internalClear() { - if (elementCount > 0) { - elementCount = 0; - Arrays.fill(elementData, null); - modCount++; - } + /** + * Returns an appropriate capacity for the specified initial size. Does + * not round the result up to a power of two; the caller must do this! + * The returned value will be between 0 and MAXIMUM_CAPACITY (inclusive). + */ + static int capacityForInitSize(int size) { + int result = (size >> 1) + size; // Multiply by 3/2 to allow for growth + + // boolean expr is equivalent to result >= 0 && result result; try { - // BEGIN android-changed - // copied from newer version of harmony - HashMap map = (HashMap) super.clone(); - map.elementData = newElementArray(elementData.length); - map.internalClear(); - Entry entry; - for (int i = 0; i < elementData.length; i++) { - if ((entry = elementData[i]) != null){ - map.putImpl(entry.getKey(), entry.getValue()); - while (entry.next != null){ - entry = entry.next; - map.putImpl(entry.getKey(), entry.getValue()); - } - } - // END android-changed - } - return map; + result = (HashMap) super.clone(); } catch (CloneNotSupportedException e) { - return null; + throw new AssertionError(e); } - } - private void computeMaxSize() { - threshold = (int) (elementData.length * loadFactor); + // Restore clone to empty state, retaining our capacity and threshold + result.makeTable(table.length); + result.entryForNullKey = null; + result.size = 0; + result.keySet = null; + result.entrySet = null; + result.values = null; + + result.init(); // Give subclass a chance to initialize itself + result.constructorPutAll(this); // Calls method overridden in subclass!! + return result; } /** - * Returns whether this map contains the specified key. - * - * @param key - * the key to search for. - * @return {@code true} if this map contains the specified key, - * {@code false} otherwise. - * @since Android 1.0 + * This method is called from the pseudo-constructors (clone and readObject) + * prior to invoking constructorPut/constructorPutAll, which invoke the + * overriden constructorNewEntry method. Normally it is a VERY bad idea to + * invoke an overridden method from a pseudo-constructor (Effective Java + * Item 17). In this cases it is unavoidable, and the init method provides a + * workaround. */ - @Override - public boolean containsKey(Object key) { - Entry m; - if (key == null) { - m = findNullKeyEntry(); - } else { - int hash = key.hashCode(); - int index = (hash & 0x7FFFFFFF) % elementData.length; - m = findNonNullKeyEntry(key, index, hash); - } - return m != null; - } + void init() { } /** - * Returns whether this map contains the specified value. - * - * @param value - * the value to search for. - * @return {@code true} if this map contains the specified value, - * {@code false} otherwise. - * @since Android 1.0 - */ - @Override - public boolean containsValue(Object value) { - if (value != null) { - for (int i = elementData.length; --i >= 0;) { - Entry entry = elementData[i]; - while (entry != null) { - if (value.equals(entry.value)) { - return true; - } - entry = entry.next; - } - } - } else { - for (int i = elementData.length; --i >= 0;) { - Entry entry = elementData[i]; - while (entry != null) { - if (entry.value == null) { - return true; - } - entry = entry.next; - } - } - } - return false; + * Returns whether this map is empty. + * + * @return {@code true} if this map has no elements, {@code false} + * otherwise. + * @see #size() + */ + @Override public boolean isEmpty() { + return size == 0; } /** - * Returns a set containing all of the mappings in this map. Each mapping is - * an instance of {@link Map.Entry}. As the set is backed by this map, - * changes in one will be reflected in the other. - * - * @return a set of the mappings. - * @since Android 1.0 + * Returns the number of elements in this map. + * + * @return the number of elements in this map. */ - @Override - public Set> entrySet() { - return new HashMapEntrySet(this); + @Override public int size() { + return size; } /** * Returns the value of the mapping with the specified key. - * + * * @param key * the key. * @return the value of the mapping with the specified key, or {@code null} * if no mapping for the specified key is found. - * @since Android 1.0 */ - @Override public V get(Object key) { - Entry m; if (key == null) { - m = findNullKeyEntry(); - } else { - int hash = key.hashCode(); - int index = (hash & 0x7FFFFFFF) % elementData.length; - m = findNonNullKeyEntry(key, index, hash); - } - if (m != null) { - return m.value; + HashMapEntry e = entryForNullKey; + return e == null ? null : e.value; } - return null; - } - final Entry findNonNullKeyEntry(Object key, int index, int keyHash) { - Entry m = elementData[index]; - // BEGIN android-changed - // The VM can optimize String.equals but not Object.equals - if (key instanceof String) { - String keyString = (String) key; - while (m != null) { - if (m.origKeyHash == keyHash) { - if (keyString.equals(m.key)) { - return m; - } - } - m = m.next; - } - } else { - while (m != null) { - if (m.origKeyHash == keyHash) { - if (key.equals(m.key)) { - return m; - } - } - m = m.next; + // Doug Lea's supplemental secondaryHash function (inlined) + int hash = key.hashCode(); + hash ^= (hash >>> 20) ^ (hash >>> 12); + hash ^= (hash >>> 7) ^ (hash >>> 4); + + HashMapEntry[] tab = table; + for (HashMapEntry e = tab[hash & (tab.length - 1)]; + e != null; e = e.next) { + K eKey = e.key; + if (eKey == key || (e.hash == hash && key.equals(eKey))) { + return e.value; } } return null; - // END android-changed - } - - final Entry findNullKeyEntry() { - Entry m = elementData[0]; - while (m != null && m.key != null) - m = m.next; - return m; } /** - * Returns whether this map is empty. - * - * @return {@code true} if this map has no elements, {@code false} - * otherwise. - * @see #size() - * @since Android 1.0 + * Returns whether this map contains the specified key. + * + * @param key + * the key to search for. + * @return {@code true} if this map contains the specified key, + * {@code false} otherwise. */ - @Override - public boolean isEmpty() { - return elementCount == 0; - } - - /** - * Returns a set of the keys contained in this map. The set is backed by - * this map so changes to one are reflected by the other. The set does not - * support adding. - * - * @return a set of the keys. - * @since Android 1.0 - */ - @Override - public Set keySet() { - if (keySet == null) { - keySet = new AbstractSet() { - @Override - public boolean contains(Object object) { - return containsKey(object); - } + @Override public boolean containsKey(Object key) { + if (key == null) { + return entryForNullKey != null; + } - @Override - public int size() { - return HashMap.this.size(); - } + // Doug Lea's supplemental secondaryHash function (inlined) + int hash = key.hashCode(); + hash ^= (hash >>> 20) ^ (hash >>> 12); + hash ^= (hash >>> 7) ^ (hash >>> 4); - @Override - public void clear() { - HashMap.this.clear(); - } + HashMapEntry[] tab = table; + for (HashMapEntry e = tab[hash & (tab.length - 1)]; + e != null; e = e.next) { + K eKey = e.key; + if (eKey == key || (e.hash == hash && key.equals(eKey))) { + return true; + } + } + return false; + } - @Override - public boolean remove(Object key) { - Entry entry = HashMap.this.removeEntry(key); - return entry != null; + /** + * Returns whether this map contains the specified value. + * + * @param value + * the value to search for. + * @return {@code true} if this map contains the specified value, + * {@code false} otherwise. + */ + @Override public boolean containsValue(Object value) { + HashMapEntry[] tab = table; + int len = tab.length; + if (value == null) { + for (int i = 0; i < len; i++) { + for (HashMapEntry e = tab[i]; e != null; e = e.next) { + if (e.value == null) { + return true; + } } + } + return entryForNullKey != null && entryForNullKey.value == null; + } - @Override - public Iterator iterator() { - return new HashMapIterator( - new MapEntry.Type() { - public K get(MapEntry entry) { - return entry.key; - } - }, HashMap.this); + // value is non-null + for (int i = 0; i < len; i++) { + for (HashMapEntry e = tab[i]; e != null; e = e.next) { + if (value.equals(e.value)) { + return true; } - }; + } } - return keySet; + return entryForNullKey != null && value.equals(entryForNullKey.value); } /** * Maps the specified key to the specified value. - * + * * @param key * the key. * @param value * the value. * @return the value of any previous mapping with the specified key or * {@code null} if there was no such mapping. - * @since Android 1.0 */ - @Override - public V put(K key, V value) { - return putImpl(key, value); - } + @Override public V put(K key, V value) { + if (key == null) { + return putValueForNullKey(value); + } - private V putImpl(K key, V value) { - Entry entry; - if(key == null) { - entry = findNullKeyEntry(); - if (entry == null) { - modCount++; - if (++elementCount > threshold) { - rehash(); - } - entry = createHashedEntry(key, 0, 0); + int hash = secondaryHash(key.hashCode()); + HashMapEntry[] tab = table; + int index = hash & (tab.length - 1); + for (HashMapEntry e = tab[index]; e != null; e = e.next) { + if (e.hash == hash && key.equals(e.key)) { + preModify(e); + V oldValue = e.value; + e.value = value; + return oldValue; } + } + + // No entry for (non-null) key is present; create one + modCount++; + if (size++ > threshold) { + tab = doubleCapacity(); + index = hash & (tab.length - 1); + } + addNewEntry(key, value, hash, index); + return null; + } + + private V putValueForNullKey(V value) { + HashMapEntry entry = entryForNullKey; + if (entry == null) { + addNewEntryForNullKey(value); + size++; + modCount++; + return null; } else { - int hash = key.hashCode(); - int index = (hash & 0x7FFFFFFF) % elementData.length; - entry = findNonNullKeyEntry(key, index, hash); + preModify(entry); + V oldValue = entry.value; + entry.value = value; + return oldValue; + } + } + + /** + * Give LinkedHashMap a chance to take action when we modify an exisitng + * entry. + * + * @param e the entry we're about to modify. + */ + void preModify(HashMapEntry e) { } + + /** + * This method is just like put, except that it doesn't do things that + * are inappropriate or unnecessary for constructors and pseudo-constructors + * (i.e., clone, readObject). In particular, this method does not check to + * ensure that capacity is sufficient, and does not increment modCount. + */ + private void constructorPut(K key, V value) { + if (key == null) { + HashMapEntry entry = entryForNullKey; if (entry == null) { - modCount++; - if (++elementCount > threshold) { - rehash(); - index = (hash & 0x7FFFFFFF) % elementData.length; - } - entry = createHashedEntry(key, index, hash); + entryForNullKey = constructorNewEntry(null, value, 0, null); + size++; + } else { + entry.value = value; } + return; } - V result = entry.value; - entry.value = value; - return result; + int hash = secondaryHash(key.hashCode()); + HashMapEntry[] tab = table; + int index = hash & (tab.length - 1); + HashMapEntry first = tab[index]; + for (HashMapEntry e = first; e != null; e = e.next) { + if (e.hash == hash && key.equals(e.key)) { + e.value = value; + return; + } + } + + // No entry for (non-null) key is present; create one + tab[index] = constructorNewEntry(key, value, hash, first); + size++; } - Entry createEntry(K key, int index, V value) { - Entry entry = new Entry(key, value); - entry.next = elementData[index]; - elementData[index] = entry; - return entry; + /** + * Creates a new entry for the given key, value, hash, and index and + * inserts it into the hash table. This method is called by put + * (and indirectly, putAll), and overridden by LinkedHashMap. The hash + * must incorporate the secondary hash function. + */ + void addNewEntry(K key, V value, int hash, int index) { + table[index] = new HashMapEntry(key, value, hash, table[index]); } - Entry createHashedEntry(K key, int index, int hash) { - Entry entry = new Entry(key,hash); - entry.next = elementData[index]; - elementData[index] = entry; - return entry; + /** + * Creates a new entry for the null key, and the given value and + * inserts it into the hash table. This method is called by put + * (and indirectly, putAll), and overridden by LinkedHashMap. + */ + void addNewEntryForNullKey(V value) { + entryForNullKey = new HashMapEntry(null, value, 0, null); + } + + /** + * Like newEntry, but does not perform any activity that would be + * unnecessary or inappropriate for constructors. In this class, the + * two methods behave identically; in LinkedHashMap, they differ. + */ + HashMapEntry constructorNewEntry( + K key, V value, int hash, HashMapEntry first) { + return new HashMapEntry(key, value, hash, first); } /** * Copies all the mappings in the specified map to this map. These mappings * will replace all mappings that this map had for any of the keys currently * in the given map. - * + * * @param map * the map to copy mappings from. - * @since Android 1.0 */ - @Override - public void putAll(Map map) { - if (!map.isEmpty()) { - putAllImpl(map); - } + @Override public void putAll(Map map) { + ensureCapacity(map.size()); + super.putAll(map); } - private void putAllImpl(Map map) { - int capacity = elementCount + map.size(); - if (capacity > threshold) { - rehash(capacity); + /** + * Ensures that the hash table has sufficient capacity to store the + * specified number of mappings, with room to grow. If not, it increases the + * capacity as appropriate. Like doubleCapacity, this method moves existing + * entries to new buckets as appropriate. Unlike doubleCapacity, this method + * can grow the table by factors of 2^n for n > 1. Hopefully, a single call + * to this method will be faster than multiple calls to doubleCapacity. + * + *

          This method is called only by putAll. + */ + private void ensureCapacity(int numMappings) { + int newCapacity = roundUpToPowerOfTwo(capacityForInitSize(numMappings)); + HashMapEntry[] oldTable = table; + int oldCapacity = oldTable.length; + if (newCapacity <= oldCapacity) { + return; } - for (Map.Entry entry : map.entrySet()) { - putImpl(entry.getKey(), entry.getValue()); + if (newCapacity == oldCapacity << 1) { + doubleCapacity(); + return; } - } - - void rehash(int capacity) { - int length = (capacity == 0 ? 1 : capacity << 1); - Entry[] newData = newElementArray(length); - for (int i = 0; i < elementData.length; i++) { - Entry entry = elementData[i]; - while (entry != null) { - int index = (entry.origKeyHash & 0x7FFFFFFF) % length; - Entry next = entry.next; - entry.next = newData[index]; - newData[index] = entry; - entry = next; + // We're growing by at least 4x, rehash in the obvious way + HashMapEntry[] newTable = makeTable(newCapacity); + if (size != 0) { + int newMask = newCapacity - 1; + for (int i = 0; i < oldCapacity; i++) { + for (HashMapEntry e = oldTable[i]; e != null;) { + HashMapEntry oldNext = e.next; + int newIndex = e.hash & newMask; + HashMapEntry newNext = newTable[newIndex]; + newTable[newIndex] = e; + e.next = newNext; + e = oldNext; + } } } - elementData = newData; - computeMaxSize(); } - void rehash() { - rehash(elementData.length); + /** + * Allocate a table of the given capacity and set the threshold accordingly. + * @param newCapacity must be a power of two + */ + private HashMapEntry[] makeTable(int newCapacity) { + @SuppressWarnings("unchecked") HashMapEntry[] newTable + = (HashMapEntry[]) new HashMapEntry[newCapacity]; + table = newTable; + threshold = (newCapacity >> 1) + (newCapacity >> 2); // 3/4 capacity + return newTable; + } + + /** + * Doubles the capacity of the hash table. Existing entries are placed in + * the correct bucket on the enlarged table. If the current capacity is, + * MAXIMUM_CAPACITY, this method is a no-op. Returns the table, which + * will be new unless we were already at MAXIMUM_CAPACITY. + */ + private HashMapEntry[] doubleCapacity() { + HashMapEntry[] oldTable = table; + int oldCapacity = oldTable.length; + if (oldCapacity == MAXIMUM_CAPACITY) { + return oldTable; + } + int newCapacity = oldCapacity << 1; + HashMapEntry[] newTable = makeTable(newCapacity); + if (size == 0) { + return newTable; + } + + for (int j = 0; j < oldCapacity; j++) { + /* + * Rehash the bucket using the minimum number of field writes. + * This is the most subtle and delicate code in the class. + */ + HashMapEntry e = oldTable[j]; + if (e == null) { + continue; + } + int highBit = e.hash & oldCapacity; + HashMapEntry broken = null; + newTable[j | highBit] = e; + for (HashMapEntry n = e.next; n != null; e = n, n = n.next) { + int nextHighBit = n.hash & oldCapacity; + if (nextHighBit != highBit) { + if (broken == null) + newTable[j | nextHighBit] = n; + else + broken.next = n; + broken = e; + highBit = nextHighBit; + } + } + if (broken != null) + broken.next = null; + } + return newTable; } /** * Removes the mapping with the specified key from this map. - * + * * @param key * the key of the mapping to remove. * @return the value of the removed mapping or {@code null} if no mapping * for the specified key was found. - * @since Android 1.0 */ - @Override - public V remove(Object key) { - Entry entry = removeEntry(key); - if (entry != null) { - return entry.value; + @Override public V remove(Object key) { + if (key == null) { + return removeNullKey(); + } + int hash = secondaryHash(key.hashCode()); + HashMapEntry[] tab = table; + int index = hash & (tab.length - 1); + for (HashMapEntry e = tab[index], prev = null; + e != null; prev = e, e = e.next) { + if (e.hash == hash && key.equals(e.key)) { + if (prev == null) { + tab[index] = e.next; + } else { + prev.next = e.next; + } + modCount++; + size--; + postRemove(e); + return e.value; + } } return null; } - Entry removeEntry(Object key) { - int index = 0; - Entry entry; - Entry last = null; - if (key != null) { - int hash = key.hashCode(); - index = (hash & 0x7FFFFFFF) % elementData.length; - entry = elementData[index]; - while (entry != null && !(entry.origKeyHash == hash && key.equals(entry.key))) { - last = entry; - entry = entry.next; - } - } else { - entry = elementData[0]; - while (entry != null && entry.key != null) { - last = entry; - entry = entry.next; - } - } - if (entry == null) { + private V removeNullKey() { + HashMapEntry e = entryForNullKey; + if (e == null) { return null; } - if (last == null) { - elementData[index] = entry.next; - } else { - last.next = entry.next; - } + entryForNullKey = null; modCount++; - elementCount--; - return entry; + size--; + postRemove(e); + return e.value; } /** - * Returns the number of elements in this map. - * - * @return the number of elements in this map. - * @since Android 1.0 + * Subclass overrides this method to unlink entry. */ - @Override - public int size() { - return elementCount; + void postRemove(HashMapEntry e) { } + + /** + * Removes all mappings from this hash map, leaving it empty. + * + * @see #isEmpty + * @see #size + */ + @Override public void clear() { + if (size != 0) { + Arrays.fill(table, null); + entryForNullKey = null; + modCount++; + size = 0; + } + } + + /** + * Returns a set of the keys contained in this map. The set is backed by + * this map so changes to one are reflected by the other. The set does not + * support adding. + * + * @return a set of the keys. + */ + @Override public Set keySet() { + Set ks = keySet; + return (ks != null) ? ks : (keySet = new KeySet()); } /** @@ -734,69 +686,356 @@ public class HashMap extends AbstractMap implements Map, * different collections when multiple concurrent calls occur, since no * synchronization is performed. *

          - * + * * @return a collection of the values contained in this map. - * @since Android 1.0 - */ - @Override - public Collection values() { - if (valuesCollection == null) { - valuesCollection = new AbstractCollection() { - @Override - public boolean contains(Object object) { - return containsValue(object); - } + */ + @Override public Collection values() { + Collection vs = values; + return (vs != null) ? vs : (values = new Values()); + } - @Override - public int size() { - return HashMap.this.size(); - } + /** + * Returns a set containing all of the mappings in this map. Each mapping is + * an instance of {@link Map.Entry}. As the set is backed by this map, + * changes in one will be reflected in the other. + * + * @return a set of the mappings. + */ + public Set> entrySet() { + Set> es = entrySet; + return (es != null) ? es : (entrySet = new EntrySet()); + } + + static class HashMapEntry implements Entry { + final K key; + V value; + final int hash; + HashMapEntry next; + + HashMapEntry(K key, V value, int hash, HashMapEntry next) { + this.key = key; + this.value = value; + this.hash = hash; + this.next = next; + } + + public final K getKey() { + return key; + } - @Override - public void clear() { - HashMap.this.clear(); + public final V getValue() { + return value; + } + + public final V setValue(V value) { + V oldValue = this.value; + this.value = value; + return oldValue; + } + + @Override public final boolean equals(Object o) { + if (!(o instanceof Entry)) { + return false; + } + Entry e = (Entry) o; + return HashMap.equals(e.getKey(), key) + && HashMap.equals(e.getValue(), value); + } + + @Override public final int hashCode() { + return (key == null ? 0 : key.hashCode()) ^ + (value == null ? 0 : value.hashCode()); + } + + @Override public final String toString() { + return key + "=" + value; + } + } + + private abstract class HashIterator { + int nextIndex; + HashMapEntry nextEntry = entryForNullKey; + HashMapEntry lastEntryReturned; + int expectedModCount = modCount; + + HashIterator() { + if (nextEntry == null) { + HashMapEntry[] tab = table; + HashMapEntry next = null; + while (next == null && nextIndex < tab.length) { + next = tab[nextIndex++]; } + nextEntry = next; + } + } + + public boolean hasNext() { + return nextEntry != null; + } + + HashMapEntry nextEntry() { + if (modCount != expectedModCount) + throw new ConcurrentModificationException(); + if (nextEntry == null) + throw new NoSuchElementException(); + + HashMapEntry entryToReturn = nextEntry; + HashMapEntry[] tab = table; + HashMapEntry next = entryToReturn.next; + while (next == null && nextIndex < tab.length) { + next = tab[nextIndex++]; + } + nextEntry = next; + return lastEntryReturned = entryToReturn; + } + + public void remove() { + if (lastEntryReturned == null) + throw new IllegalStateException(); + if (modCount != expectedModCount) + throw new ConcurrentModificationException(); + HashMap.this.remove(lastEntryReturned.key); + lastEntryReturned = null; + expectedModCount = modCount; + } + } + + private final class KeyIterator extends HashIterator + implements Iterator { + public K next() { return nextEntry().key; } + } + + private final class ValueIterator extends HashIterator + implements Iterator { + public V next() { return nextEntry().value; } + } - @Override - public Iterator iterator() { - return new HashMapIterator( - new MapEntry.Type() { - public V get(MapEntry entry) { - return entry.value; - } - }, HashMap.this); + private final class EntryIterator extends HashIterator + implements Iterator> { + public Entry next() { return nextEntry(); } + } + + /** + * Returns true if this map contains the specified mapping. + */ + private boolean containsMapping(Object key, Object value) { + if (key == null) { + HashMapEntry e = entryForNullKey; + return e != null && equals(value, e.value); + } + + int hash = secondaryHash(key.hashCode()); + HashMapEntry[] tab = table; + int index = hash & (tab.length - 1); + for (HashMapEntry e = tab[index]; e != null; e = e.next) { + if (e.hash == hash && key.equals(e.key)) { + return equals(value, e.value); + } + } + return false; // No entry for key + } + + /** + * Removes the mapping from key to value and returns true if this mapping + * exists; otherwise, returns does nothing and returns false. + */ + private boolean removeMapping(Object key, Object value) { + if (key == null) { + HashMapEntry e = entryForNullKey; + if (e == null || !equals(value, e.value)) { + return false; + } + entryForNullKey = null; + modCount++; + size--; + postRemove(e); + return true; + } + + int hash = secondaryHash(key.hashCode()); + HashMapEntry[] tab = table; + int index = hash & (tab.length - 1); + for (HashMapEntry e = tab[index], prev = null; + e != null; prev = e, e = e.next) { + if (e.hash == hash && key.equals(e.key)) { + if (!equals(value, e.value)) { + return false; // Map has wrong value for key + } + if (prev == null) { + tab[index] = e.next; + } else { + prev.next = e.next; } - }; + modCount++; + size--; + postRemove(e); + return true; + } } - return valuesCollection; + return false; // No entry for key } + private static boolean equals(Object o1, Object o2) { + return o1 == o2 || (o1 != null && o1.equals(o2)); + } + + // Subclass (LinkedHashMap) overrrides these for correct iteration order + Iterator newKeyIterator() { return new KeyIterator(); } + Iterator newValueIterator() { return new ValueIterator(); } + Iterator> newEntryIterator() { return new EntryIterator(); } + + private final class KeySet extends AbstractSet { + public Iterator iterator() { + return newKeyIterator(); + } + public int size() { + return size; + } + public boolean isEmpty() { + return size == 0; + } + public boolean contains(Object o) { + return containsKey(o); + } + public boolean remove(Object o) { + int oldSize = size; + HashMap.this.remove(o); + return size != oldSize; + } + public void clear() { + HashMap.this.clear(); + } + } + + private final class Values extends AbstractCollection { + public Iterator iterator() { + return newValueIterator(); + } + public int size() { + return size; + } + public boolean isEmpty() { + return size == 0; + } + public boolean contains(Object o) { + return containsValue(o); + } + public void clear() { + HashMap.this.clear(); + } + } + + private final class EntrySet extends AbstractSet> { + public Iterator> iterator() { + return newEntryIterator(); + } + public boolean contains(Object o) { + if (!(o instanceof Entry)) + return false; + Entry e = (Entry) o; + return containsMapping(e.getKey(), e.getValue()); + } + public boolean remove(Object o) { + if (!(o instanceof Entry)) + return false; + Entry e = (Entry)o; + return removeMapping(e.getKey(), e.getValue()); + } + public int size() { + return size; + } + public boolean isEmpty() { + return size == 0; + } + public void clear() { + HashMap.this.clear(); + } + } + + /** + * Applies a supplemental hash function to a given hashCode, which defends + * against poor quality hash functions. This is critical because HashMap + * uses power-of-two length hash tables, that otherwise encounter collisions + * for hashCodes that do not differ in lower or upper bits. + */ + private static int secondaryHash(int h) { + // Doug Lea's supplemental hash function + h ^= (h >>> 20) ^ (h >>> 12); + return h ^ (h >>> 7) ^ (h >>> 4); + } + + /** + * Returns the smallest power of two >= its argument, with several caveats: + * If the argument is negative but not Integer.MIN_VALUE, the method returns + * zero. If the argument is > 2^30 or equal to Integer.MIN_VALUE, the method + * returns Integer.MIN_VALUE. If the argument is zero, the method returns + * zero. + */ + private static int roundUpToPowerOfTwo(int i) { + i--; // If input is a power of two, shift its high-order bit right + + // "Smear" the high-order bit all the way to the right + i |= i >>> 1; + i |= i >>> 2; + i |= i >>> 4; + i |= i >>> 8; + i |= i >>> 16; + + return i + 1; + } + + private static final long serialVersionUID = 362498820763181265L; + + /** + * Serializable fields. + * + * @serialField loadFactor float + * load factor for this HashMap + */ + private static final ObjectStreamField[] serialPersistentFields = { + new ObjectStreamField("loadFactor", Float.TYPE) + }; + private void writeObject(ObjectOutputStream stream) throws IOException { - stream.defaultWriteObject(); - stream.writeInt(elementData.length); - stream.writeInt(elementCount); - Iterator iterator = entrySet().iterator(); - while (iterator.hasNext()) { - Entry entry = (Entry) iterator.next(); - stream.writeObject(entry.key); - stream.writeObject(entry.value); - entry = entry.next; + // Emulate loadFactor field for other implementations to read + ObjectOutputStream.PutField fields = stream.putFields(); + fields.put("loadFactor", DEFAULT_LOAD_FACTOR); + stream.writeFields(); + + stream.writeInt(table.length); // Capacity + stream.writeInt(size); + for (Entry e : entrySet()) { + stream.writeObject(e.getKey()); + stream.writeObject(e.getValue()); } } - @SuppressWarnings("unchecked") private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); - int length = stream.readInt(); - elementData = newElementArray(length); - elementCount = stream.readInt(); - for (int i = elementCount; --i >= 0;) { - K key = (K) stream.readObject(); - int index = (null == key) ? 0 : (key.hashCode() & 0x7FFFFFFF) - % length; - createEntry(key, index, (V) stream.readObject()); + int capacity = stream.readInt(); + if (capacity < 0) { + throw new InvalidObjectException("Capacity: " + capacity); + } + if (capacity < MINIMUM_CAPACITY) { + capacity = MINIMUM_CAPACITY; + } else if (capacity > MAXIMUM_CAPACITY) { + capacity = MAXIMUM_CAPACITY; + } else { + capacity = roundUpToPowerOfTwo(capacity); + } + makeTable(capacity); + + int size = stream.readInt(); + if (size < 0) { + throw new InvalidObjectException("Size: " + size); } - } + init(); // Give subclass (LinkedHashMap) a chance to initialize itself + for (int i = 0; i < size; i++) { + @SuppressWarnings("unchecked") K key = (K) stream.readObject(); + @SuppressWarnings("unchecked") V val = (V) stream.readObject(); + constructorPut(key, val); + } + } } diff --git a/libcore/luni/src/main/java/java/util/HashSet.java b/libcore/luni/src/main/java/java/util/HashSet.java index fe89109b9..4c97ca53b 100644 --- a/libcore/luni/src/main/java/java/util/HashSet.java +++ b/libcore/luni/src/main/java/java/util/HashSet.java @@ -17,7 +17,6 @@ package java.util; - import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; @@ -26,8 +25,6 @@ import java.io.Serializable; /** * HashSet is an implementation of a Set. All optional operations (adding and * removing) are supported. The elements can be any objects. - * - * @since Android 1.0 */ public class HashSet extends AbstractSet implements Set, Cloneable, Serializable { @@ -38,8 +35,6 @@ public class HashSet extends AbstractSet implements Set, Cloneable, /** * Constructs a new empty instance of {@code HashSet}. - * - * @since Android 1.0 */ public HashSet() { this(new HashMap>()); @@ -50,7 +45,6 @@ public class HashSet extends AbstractSet implements Set, Cloneable, * * @param capacity * the initial capacity of this {@code HashSet}. - * @since Android 1.0 */ public HashSet(int capacity) { this(new HashMap>(capacity)); @@ -64,7 +58,6 @@ public class HashSet extends AbstractSet implements Set, Cloneable, * the initial capacity. * @param loadFactor * the initial load factor. - * @since Android 1.0 */ public HashSet(int capacity, float loadFactor) { this(new HashMap>(capacity, loadFactor)); @@ -76,10 +69,10 @@ public class HashSet extends AbstractSet implements Set, Cloneable, * * @param collection * the collection of elements to add. - * @since Android 1.0 */ public HashSet(Collection collection) { - this(new HashMap>(collection.size() < 6 ? 11 : collection.size() * 2)); + this(new HashMap>(collection.size() < 6 ? 11 : collection + .size() * 2)); for (E e : collection) { add(e); } @@ -96,7 +89,6 @@ public class HashSet extends AbstractSet implements Set, Cloneable, * the object to add. * @return {@code true} when this {@code HashSet} did not already contain * the object, {@code false} otherwise - * @since Android 1.0 */ @Override public boolean add(E object) { @@ -108,7 +100,6 @@ public class HashSet extends AbstractSet implements Set, Cloneable, * * @see #isEmpty * @see #size - * @since Android 1.0 */ @Override public void clear() { @@ -121,7 +112,6 @@ public class HashSet extends AbstractSet implements Set, Cloneable, * * @return a shallow copy of this {@code HashSet}. * @see java.lang.Cloneable - * @since Android 1.0 */ @Override @SuppressWarnings("unchecked") @@ -142,7 +132,6 @@ public class HashSet extends AbstractSet implements Set, Cloneable, * the object to search for. * @return {@code true} if {@code object} is an element of this * {@code HashSet}, {@code false} otherwise. - * @since Android 1.0 */ @Override public boolean contains(Object object) { @@ -155,7 +144,6 @@ public class HashSet extends AbstractSet implements Set, Cloneable, * @return {@code true} if this {@code HashSet} has no elements, * {@code false} otherwise. * @see #size - * @since Android 1.0 */ @Override public boolean isEmpty() { @@ -167,7 +155,6 @@ public class HashSet extends AbstractSet implements Set, Cloneable, * * @return an Iterator on the elements of this {@code HashSet}. * @see Iterator - * @since Android 1.0 */ @Override public Iterator iterator() { @@ -180,7 +167,6 @@ public class HashSet extends AbstractSet implements Set, Cloneable, * @param object * the object to remove. * @return {@code true} if the object was removed, {@code false} otherwise. - * @since Android 1.0 */ @Override public boolean remove(Object object) { @@ -191,7 +177,6 @@ public class HashSet extends AbstractSet implements Set, Cloneable, * Returns the number of elements in this {@code HashSet}. * * @return the number of elements in this {@code HashSet}. - * @since Android 1.0 */ @Override public int size() { @@ -200,15 +185,11 @@ public class HashSet extends AbstractSet implements Set, Cloneable, private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); - stream.writeInt(backingMap.elementData.length); - stream.writeFloat(backingMap.loadFactor); - stream.writeInt(backingMap.elementCount); - for (int i = backingMap.elementData.length; --i >= 0;) { - HashMap.Entry> entry = backingMap.elementData[i]; - while (entry != null) { - stream.writeObject(entry.key); - entry = entry.next; - } + stream.writeInt(backingMap.table.length); + stream.writeFloat(HashMap.DEFAULT_LOAD_FACTOR); + stream.writeInt(size()); + for (E e : this) { + stream.writeObject(e); } } @@ -221,7 +202,7 @@ public class HashSet extends AbstractSet implements Set, Cloneable, backingMap = createBackingMap(length, loadFactor); int elementCount = stream.readInt(); for (int i = elementCount; --i >= 0;) { - E key = (E)stream.readObject(); + E key = (E) stream.readObject(); backingMap.put(key, this); } } diff --git a/libcore/luni/src/main/java/java/util/Hashtable.java b/libcore/luni/src/main/java/java/util/Hashtable.java index abede2d47..ead0db36a 100644 --- a/libcore/luni/src/main/java/java/util/Hashtable.java +++ b/libcore/luni/src/main/java/java/util/Hashtable.java @@ -15,15 +15,19 @@ * limitations under the License. */ +// BEGIN android-note +// Completely different implementation from harmony. Runs much faster. +// BEGIN android-note + package java.util; import java.io.IOException; +import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; +import java.io.ObjectStreamField; import java.io.Serializable; -import org.apache.harmony.luni.internal.nls.Messages; - /** * Hashtable associates keys with values. Both keys and values cannot be null. * The size of the Hashtable is the number of key/value pairs it contains. The @@ -31,922 +35,1126 @@ import org.apache.harmony.luni.internal.nls.Messages; * factor is a float value which determines how full the Hashtable gets before * expanding the capacity. If the load factor of the Hashtable is exceeded, the * capacity is doubled. - * + * + * @param the type of keys maintained by this map + * @param the type of mapped values + * * @see Enumeration * @see java.io.Serializable * @see java.lang.Object#equals * @see java.lang.Object#hashCode - * @since Android 1.0 */ -public class Hashtable extends Dictionary implements Map, - Cloneable, Serializable { - - private static final long serialVersionUID = 1421746759512286392L; - - transient int elementCount; - - transient Entry[] elementData; - - private float loadFactor; - - private int threshold; - - transient int firstSlot; - - transient int lastSlot = -1; - - transient int modCount; - - private static final Enumeration EMPTY_ENUMERATION = new Enumeration() { - public boolean hasMoreElements() { - return false; - } - - public Object nextElement() { - throw new NoSuchElementException(); - } - }; - - private static Entry newEntry(K key, V value, int hash) { - return new Entry(key, value); - } - - private static class Entry extends MapEntry { - Entry next; - - final int hashcode; - - Entry(K theKey, V theValue) { - super(theKey, theValue); - hashcode = theKey.hashCode(); - } - - @Override - @SuppressWarnings("unchecked") - public Object clone() { - Entry entry = (Entry) super.clone(); - if (next != null) { - entry.next = (Entry) next.clone(); - } - return entry; - } - - @Override - public V setValue(V object) { - if (object == null) { - throw new NullPointerException(); - } - V result = value; - value = object; - return result; - } - - public int getKeyHash() { - return key.hashCode(); - } - - public boolean equalsKey(Object aKey, int hash) { - // BEGIN android-changed - // The VM can inline String.equals - return hashcode == aKey.hashCode() - && (key instanceof String - ? ((String) key).equals(aKey) : key.equals(aKey)); - // END android-changed - } - - @Override - public String toString() { - return key + "=" + value; //$NON-NLS-1$ - } - } - - private final class HashIterator implements Iterator { - private int position, expectedModCount; - - private final MapEntry.Type type; - - private Entry lastEntry; - - private int lastPosition; - - private boolean canRemove = false; - - HashIterator(MapEntry.Type value) { - type = value; - position = lastSlot; - expectedModCount = modCount; - } - - public boolean hasNext() { - if (lastEntry != null && lastEntry.next != null) { - return true; - } - while (position >= firstSlot) { - if (elementData[position] == null) { - position--; - } else { - return true; - } - } - return false; - } +public class Hashtable extends Dictionary + implements Map, Cloneable, Serializable { + /** + * Min capacity (other than zero) for a Hashtable. Must be a power of two + * greater than 1 (and less than 1 << 30). + */ + private static final int MINIMUM_CAPACITY = 4; - public E next() { - if (expectedModCount == modCount) { - if (lastEntry != null) { - lastEntry = lastEntry.next; - } - if (lastEntry == null) { - while (position >= firstSlot - && (lastEntry = elementData[position]) == null) { - position--; - } - if (lastEntry != null) { - lastPosition = position; - // decrement the position so we don't find the same slot - // next time - position--; - } - } - if (lastEntry != null) { - canRemove = true; - return type.get(lastEntry); - } - throw new NoSuchElementException(); - } - throw new ConcurrentModificationException(); - } + /** + * Max capacity for a Hashtable. Must be a power of two >= MINIMUM_CAPACITY. + */ + private static final int MAXIMUM_CAPACITY = 1 << 30; - public void remove() { - if (expectedModCount == modCount) { - if (canRemove) { - canRemove = false; - synchronized (Hashtable.this) { - boolean removed = false; - Entry entry = elementData[lastPosition]; - if (entry == lastEntry) { - elementData[lastPosition] = entry.next; - removed = true; - } else { - while (entry != null && entry.next != lastEntry) { - entry = entry.next; - } - if (entry != null) { - entry.next = lastEntry.next; - removed = true; - } - } - if (removed) { - modCount++; - elementCount--; - expectedModCount++; - return; - } - // the entry must have been (re)moved outside of the - // iterator - // but this condition wasn't caught by the modCount - // check - // throw ConcurrentModificationException() outside of - // synchronized block - } - } else { - throw new IllegalStateException(); - } - } - throw new ConcurrentModificationException(); - } - } + /** + * An empty table shared by all zero-capacity maps (typically from default + * constructor). It is never written to, and replaced on first put. Its size + * is set to half the minimum, so that the first resize will create a + * minimum-sized table. + */ + private static final Entry[] EMPTY_TABLE + = new HashtableEntry[MINIMUM_CAPACITY >>> 1]; - private final class HashEnumerator implements Enumeration { - boolean key; + /** + * The default load factor. Note that this implementation ignores the + * load factor, but cannot do away with it entirely because it's + * metioned in the API. + * + *

          Note that this constant has no impact on the behavior of the program, + * but it is emitted as part of the serialized form. The load factor of + * .75 is hardwired into the program, which uses cheap shifts in place of + * expensive division. + */ + private static final float DEFAULT_LOAD_FACTOR = .75F; - int start; + /** + * The hash table. + */ + private transient HashtableEntry[] table; - Entry entry; + /** + * The number of mappings in this hash map. + */ + private transient int size; - HashEnumerator(boolean isKey) { - key = isKey; - start = lastSlot + 1; - } + /** + * Incremented by "structural modifications" to allow (best effort) + * detection of concurrent modification. + */ + private transient int modCount; - public boolean hasMoreElements() { - if (entry != null) { - return true; - } - while (--start >= firstSlot) { - if (elementData[start] != null) { - entry = elementData[start]; - return true; - } - } - return false; - } + /** + * The table is rehashed when its size exceeds this threshold. + * The value of this field is generally .75 * capacity, except when + * the capacity is zero, as described in the EMPTY_TABLE declaration + * above. + */ + private transient int threshold; - @SuppressWarnings("unchecked") - public E nextElement() { - if (hasMoreElements()) { - Object result = key ? entry.key : entry.value; - entry = entry.next; - return (E) result; - } - throw new NoSuchElementException(); - } - } + // Views - lazily initialized + private transient Set keySet; + private transient Set> entrySet; + private transient Collection values; /** * Constructs a new {@code Hashtable} using the default capacity and load * factor. - * - * @since Android 1.0 */ + @SuppressWarnings("unchecked") public Hashtable() { - this(11); + table = (HashtableEntry[]) EMPTY_TABLE; + threshold = -1; // Forces first put invocation to replace EMPTY_TABLE } /** * Constructs a new {@code Hashtable} using the specified capacity and the * default load factor. - * + * * @param capacity * the initial capacity. - * @since Android 1.0 */ public Hashtable(int capacity) { - if (capacity >= 0) { - elementCount = 0; - elementData = newElementArray(capacity == 0 ? 1 : capacity); - firstSlot = elementData.length; - loadFactor = 0.75f; - computeMaxSize(); + if (capacity < 0) { + throw new IllegalArgumentException("Capacity: " + capacity); + } + + if (capacity == 0) { + @SuppressWarnings("unchecked") + HashtableEntry[] tab = (HashtableEntry[]) EMPTY_TABLE; + table = tab; + threshold = -1; // Forces first put() to replace EMPTY_TABLE + return; + } + + if (capacity < MINIMUM_CAPACITY) { + capacity = MINIMUM_CAPACITY; + } else if (capacity > MAXIMUM_CAPACITY) { + capacity = MAXIMUM_CAPACITY; } else { - throw new IllegalArgumentException(); + capacity = roundUpToPowerOfTwo(capacity); } + makeTable(capacity); } /** * Constructs a new {@code Hashtable} using the specified capacity and load * factor. - * + * * @param capacity * the initial capacity. * @param loadFactor * the initial load factor. - * @since Android 1.0 */ public Hashtable(int capacity, float loadFactor) { - if (capacity >= 0 && loadFactor > 0) { - elementCount = 0; - firstSlot = capacity; - elementData = newElementArray(capacity == 0 ? 1 : capacity); - this.loadFactor = loadFactor; - computeMaxSize(); - } else { - throw new IllegalArgumentException(); + this(capacity); + + if (loadFactor <= 0 || Float.isNaN(loadFactor)) { + throw new IllegalArgumentException("Load factor: " + loadFactor); } + + /* + * Note that this implementation ignores loadFactor; it always uses + * a load facator of 3/4. This simplifies the code and generally + * improves performance. + */ } /** * Constructs a new instance of {@code Hashtable} containing the mappings * from the specified map. - * + * * @param map * the mappings to add. - * @since Android 1.0 */ public Hashtable(Map map) { - this(map.size() < 6 ? 11 : (map.size() * 4 / 3) + 11); - putAll(map); + this(capacityForInitSize(map.size())); + constructorPutAll(map); } - @SuppressWarnings("unchecked") - private Entry[] newElementArray(int size) { - return new Entry[size]; + /** + * Inserts all of the elements of map into this Hashtable in a manner + * suitable for use by constructors and pseudocostructors (i.e., clone, + * readObject). + */ + private void constructorPutAll(Map map) { + for (Entry e : map.entrySet()) { + constructorPut(e.getKey(), e.getValue()); + } } /** - * Removes all key/value pairs from this {@code Hashtable}, leaving the - * size zero and the capacity unchanged. - * - * @see #isEmpty - * @see #size - * @since Android 1.0 + * Returns an appropriate capacity for the specified initial size. Does + * not round the result up to a power of two; the caller must do this! + * The returned value will be between 0 and MAXIMUM_CAPACITY (inclusive). */ - public synchronized void clear() { - elementCount = 0; - Arrays.fill(elementData, null); - modCount++; + private static int capacityForInitSize(int size) { + int result = (size >> 1) + size; // Multiply by 3/2 to allow for growth + + // boolean expr is equivalent to result >= 0 && result result; try { - Hashtable hashtable = (Hashtable) super.clone(); - hashtable.elementData = elementData.clone(); - Entry entry; - for (int i = elementData.length; --i >= 0;) { - if ((entry = elementData[i]) != null) { - hashtable.elementData[i] = (Entry) entry.clone(); - } - } - return hashtable; + result = (Hashtable) super.clone(); } catch (CloneNotSupportedException e) { - return null; + throw new AssertionError(e); } - } - private void computeMaxSize() { - threshold = (int) (elementData.length * loadFactor); + // Restore clone to empty state, retaining our capacity and threshold + result.makeTable(table.length); + result.size = 0; + result.keySet = null; + result.entrySet = null; + result.values = null; + + result.constructorPutAll(this); + return result; } /** - * Returns true if this {@code Hashtable} contains the specified object as - * the value of at least one of the key/value pairs. - * - * @param value - * the object to look for as a value in this {@code Hashtable}. - * @return {@code true} if object is a value in this {@code Hashtable}, + * Returns true if this {@code Hashtable} has no key/value pairs. + * + * @return {@code true} if this {@code Hashtable} has no key/value pairs, * {@code false} otherwise. - * @see #containsKey - * @see java.lang.Object#equals - * @since Android 1.0 + * @see #size */ - public synchronized boolean contains(Object value) { - if (value == null) { - throw new NullPointerException(); - } + public synchronized boolean isEmpty() { + return size == 0; + } - for (int i = elementData.length; --i >= 0;) { - Entry entry = elementData[i]; - while (entry != null) { - if (value.equals(entry.value)) { - return true; - } - entry = entry.next; + /** + * Returns the number of key/value pairs in this {@code Hashtable}. + * + * @return the number of key/value pairs in this {@code Hashtable}. + * @see #elements + * @see #keys + */ + public synchronized int size() { + return size; + } + + /** + * Returns the value associated with the specified key in this + * {@code Hashtable}. + * + * @param key + * the key of the value returned. + * @return the value associated with the specified key, or {@code null} if + * the specified key does not exist. + * @see #put + */ + public synchronized V get(Object key) { + // Doug Lea's supplemental secondaryHash function (inlined) + int hash = key.hashCode(); + hash ^= (hash >>> 20) ^ (hash >>> 12); + hash ^= (hash >>> 7) ^ (hash >>> 4); + + HashtableEntry[] tab = table; + for (HashtableEntry e = tab[hash & (tab.length - 1)]; + e != null; e = e.next) { + K eKey = e.key; + if (eKey == key || (e.hash == hash && key.equals(eKey))) { + return e.value; } } - return false; + return null; } /** * Returns true if this {@code Hashtable} contains the specified object as a * key of one of the key/value pairs. - * + * * @param key * the object to look for as a key in this {@code Hashtable}. * @return {@code true} if object is a key in this {@code Hashtable}, * {@code false} otherwise. * @see #contains * @see java.lang.Object#equals - * @since Android 1.0 */ public synchronized boolean containsKey(Object key) { - return getEntry(key) != null; + // Doug Lea's supplemental secondaryHash function (inlined) + int hash = key.hashCode(); + hash ^= (hash >>> 20) ^ (hash >>> 12); + hash ^= (hash >>> 7) ^ (hash >>> 4); + + HashtableEntry[] tab = table; + for (HashtableEntry e = tab[hash & (tab.length - 1)]; + e != null; e = e.next) { + K eKey = e.key; + if (eKey == key || (e.hash == hash && key.equals(eKey))) { + return true; + } + } + return false; } /** * Searches this {@code Hashtable} for the specified value. - * + * * @param value * the object to search for. * @return {@code true} if {@code value} is a value of this * {@code Hashtable}, {@code false} otherwise. - * @since Android 1.0 */ - public boolean containsValue(Object value) { - return contains(value); - } + public synchronized boolean containsValue(Object value) { + if (value == null) { + throw new NullPointerException(); + } - /** - * Returns an enumeration on the values of this {@code Hashtable}. The - * results of the Enumeration may be affected if the contents of this - * {@code Hashtable} are modified. - * - * @return an enumeration of the values of this {@code Hashtable}. - * @see #keys - * @see #size - * @see Enumeration - * @since Android 1.0 - */ - @Override - @SuppressWarnings("unchecked") - public synchronized Enumeration elements() { - if (elementCount == 0) { - return (Enumeration) EMPTY_ENUMERATION; + HashtableEntry[] tab = table; + int len = tab.length; + + for (int i = 0; i < len; i++) { + for (HashtableEntry e = tab[i]; e != null; e = e.next) { + if (value.equals(e.value)) { + return true; + } + } } - return new HashEnumerator(false); + return false; } /** - * Returns a set of the mappings contained in this {@code Hashtable}. Each - * element in the set is a {@link Map.Entry}. The set is backed by this - * {@code Hashtable} so changes to one are reflected by the other. The set - * does not support adding. - * - * @return a set of the mappings. - * @since Android 1.0 - */ - public Set> entrySet() { - return new Collections.SynchronizedSet>( - new AbstractSet>() { - @Override - public int size() { - synchronized (Hashtable.this) { - return elementCount; - } - } - - @Override - public void clear() { - Hashtable.this.clear(); - } - - @Override - @SuppressWarnings("unchecked") - public boolean remove(Object object) { - synchronized (Hashtable.this) { - if (contains(object)) { - Hashtable.this - .remove(((Map.Entry) object) - .getKey()); - return true; - } - return false; - } - } - - @Override - @SuppressWarnings("unchecked") - public boolean contains(Object object) { - synchronized (Hashtable.this) { - Entry entry = getEntry(((Map.Entry) object) - .getKey()); - return object.equals(entry); - } - } - - @Override - public Iterator> iterator() { - return new HashIterator>( - new MapEntry.Type, K, V>() { - public Map.Entry get( - MapEntry entry) { - return entry; - } - }); - } - }, this); + * Returns true if this {@code Hashtable} contains the specified object as + * the value of at least one of the key/value pairs. + * + * @param value + * the object to look for as a value in this {@code Hashtable}. + * @return {@code true} if object is a value in this {@code Hashtable}, + * {@code false} otherwise. + * @see #containsKey + * @see java.lang.Object#equals + */ + public boolean contains(Object value) { + return containsValue(value); } /** - * Compares this {@code Hashtable} with the specified object and indicates - * if they are equal. In order to be equal, {@code object} must be an - * instance of Map and contain the same key/value pairs. - * - * @param object - * the object to compare with this object. - * @return {@code true} if the specified object is equal to this Map, - * {@code false} otherwise. - * @see #hashCode - * @since Android 1.0 + * Associate the specified value with the specified key in this + * {@code Hashtable}. If the key already exists, the old value is replaced. + * The key and value cannot be null. + * + * @param key + * the key to add. + * @param value + * the value to add. + * @return the old value associated with the specified key, or {@code null} + * if the key did not exist. + * @see #elements + * @see #get + * @see #keys + * @see java.lang.Object#equals */ - @Override - public synchronized boolean equals(Object object) { - if (this == object) { - return true; + public synchronized V put(K key, V value) { + if (value == null) { + throw new NullPointerException(); } - if (object instanceof Map) { - Map map = (Map) object; - if (size() != map.size()) { - return false; + int hash = secondaryHash(key.hashCode()); + HashtableEntry[] tab = table; + int index = hash & (tab.length - 1); + HashtableEntry first = tab[index]; + for (HashtableEntry e = first; e != null; e = e.next) { + if (e.hash == hash && key.equals(e.key)) { + V oldValue = e.value; + e.value = value; + return oldValue; } + } - Set> entries = entrySet(); - for (Map.Entry e : map.entrySet()) { - if (!entries.contains(e)) { - return false; - } - } - return true; + // No entry for key is present; create one + modCount++; + if (size++ > threshold) { + rehash(); // Does nothing!! + tab = doubleCapacity(); + index = hash & (tab.length - 1); + first = tab[index]; } - return false; + tab[index] = new HashtableEntry(key, value, hash, first); + return null; } /** - * Returns the value associated with the specified key in this - * {@code Hashtable}. - * - * @param key - * the key of the value returned. - * @return the value associated with the specified key, or {@code null} if - * the specified key does not exist. - * @see #put - * @since Android 1.0 + * This method is just like put, except that it doesn't do things that + * are inappropriate or unnecessary for constructors and pseudo-constructors + * (i.e., clone, readObject). In particular, this method does not check to + * ensure that capacity is sufficient, and does not increment modCount. */ - @Override - public synchronized V get(Object key) { - int hash = key.hashCode(); - int index = (hash & 0x7FFFFFFF) % elementData.length; - Entry entry = elementData[index]; - while (entry != null) { - if (entry.equalsKey(key, hash)) { - return entry.value; - } - entry = entry.next; + private void constructorPut(K key, V value) { + if (value == null) { + throw new NullPointerException(); } - return null; - } - - Entry getEntry(Object key) { - int hash = key.hashCode(); - int index = (hash & 0x7FFFFFFF) % elementData.length; - Entry entry = elementData[index]; - while (entry != null) { - if (entry.equalsKey(key, hash)) { - return entry; + int hash = secondaryHash(key.hashCode()); + HashtableEntry[] tab = table; + int index = hash & (tab.length - 1); + HashtableEntry first = tab[index]; + for (HashtableEntry e = first; e != null; e = e.next) { + if (e.hash == hash && key.equals(e.key)) { + e.value = value; + return; } - entry = entry.next; } - return null; + + // No entry for key is present; create one + tab[index] = new HashtableEntry(key, value, hash, first); + size++; } - @Override - public synchronized int hashCode() { - int result = 0; - Iterator> it = entrySet().iterator(); - while (it.hasNext()) { - Map.Entry entry = it.next(); - Object key = entry.getKey(); - Object value = entry.getValue(); - int hash = (key != this ? key.hashCode() : 0) - ^ (value != this ? (value != null ? value.hashCode() : 0) - : 0); - result += hash; + /** + * Copies every mapping to this {@code Hashtable} from the specified map. + * + * @param map + * the map to copy mappings from. + */ + public synchronized void putAll(Map map) { + ensureCapacity(map.size()); + for (Entry e : map.entrySet()) { + put(e.getKey(), e.getValue()); } - return result; } /** - * Returns true if this {@code Hashtable} has no key/value pairs. - * - * @return {@code true} if this {@code Hashtable} has no key/value pairs, - * {@code false} otherwise. + * Ensures that the hash table has sufficient capacity to store the + * specified number of mappings, with room to grow. If not, it increases the + * capacity as appropriate. Like doubleCapacity, this method moves existing + * entries to new buckets as appropriate. Unlike doubleCapacity, this method + * can grow the table by factors of 2^n for n > 1. Hopefully, a single call + * to this method will be faster than multiple calls to doubleCapacity. + * + *

          This method is called only by putAll. + */ + private void ensureCapacity(int numMappings) { + int newCapacity = roundUpToPowerOfTwo(capacityForInitSize(numMappings)); + HashtableEntry[] oldTable = table; + int oldCapacity = oldTable.length; + if (newCapacity <= oldCapacity) { + return; + } + + rehash(); // Does nothing!! + + if (newCapacity == oldCapacity << 1) { + doubleCapacity(); + return; + } + + // We're growing by at least 4x, rehash in the obvious way + HashtableEntry[] newTable = makeTable(newCapacity); + if (size != 0) { + int newMask = newCapacity - 1; + for (int i = 0; i < oldCapacity; i++) { + for (HashtableEntry e = oldTable[i]; e != null;) { + HashtableEntry oldNext = e.next; + int newIndex = e.hash & newMask; + HashtableEntry newNext = newTable[newIndex]; + newTable[newIndex] = e; + e.next = newNext; + e = oldNext; + } + } + } + } + + /** + * Increases the capacity of this {@code Hashtable}. This method is called + * when the size of this {@code Hashtable} exceeds the load factor. + */ + protected void rehash() { + /* + * This method has no testable semantics, other than that it gets + * called from time to time. + */ + } + + /** + * Allocate a table of the given capacity and set the threshold accordingly. + * @param newCapacity must be a power of two + */ + private HashtableEntry[] makeTable(int newCapacity) { + @SuppressWarnings("unchecked") HashtableEntry[] newTable + = (HashtableEntry[]) new HashtableEntry[newCapacity]; + table = newTable; + threshold = (newCapacity >> 1) + (newCapacity >> 2); // 3/4 capacity + return newTable; + } + + /** + * Doubles the capacity of the hash table. Existing entries are placed in + * the correct bucket on the enlarged table. If the current capacity is, + * MAXIMUM_CAPACITY, this method is a no-op. Returns the table, which + * will be new unless we were already at MAXIMUM_CAPACITY. + */ + private HashtableEntry[] doubleCapacity() { + HashtableEntry[] oldTable = table; + int oldCapacity = oldTable.length; + if (oldCapacity == MAXIMUM_CAPACITY) { + return oldTable; + } + int newCapacity = oldCapacity << 1; + HashtableEntry[] newTable = makeTable(newCapacity); + if (size == 0) { + return newTable; + } + + for (int j = 0; j < oldCapacity; j++) { + /* + * Rehash the bucket using the minimum number of field writes. + * This is the most subtle and delicate code in the class. + */ + HashtableEntry e = oldTable[j]; + if (e == null) { + continue; + } + int highBit = e.hash & oldCapacity; + HashtableEntry broken = null; + newTable[j | highBit] = e; + for (HashtableEntry n = e.next; n != null; e = n, n = n.next) { + int nextHighBit = n.hash & oldCapacity; + if (nextHighBit != highBit) { + if (broken == null) + newTable[j | nextHighBit] = n; + else + broken.next = n; + broken = e; + highBit = nextHighBit; + } + } + if (broken != null) + broken.next = null; + } + return newTable; + } + + /** + * Removes the key/value pair with the specified key from this + * {@code Hashtable}. + * + * @param key + * the key to remove. + * @return the value associated with the specified key, or {@code null} if + * the specified key did not exist. + * @see #get + * @see #put + */ + public synchronized V remove(Object key) { + int hash = secondaryHash(key.hashCode()); + HashtableEntry[] tab = table; + int index = hash & (tab.length - 1); + for (HashtableEntry e = tab[index], prev = null; + e != null; prev = e, e = e.next) { + if (e.hash == hash && key.equals(e.key)) { + if (prev == null) { + tab[index] = e.next; + } else { + prev.next = e.next; + } + modCount++; + size--; + return e.value; + } + } + return null; + } + + /** + * Removes all key/value pairs from this {@code Hashtable}, leaving the + * size zero and the capacity unchanged. + * + * @see #isEmpty * @see #size - * @since Android 1.0 */ - @Override - public synchronized boolean isEmpty() { - return elementCount == 0; + public synchronized void clear() { + if (size != 0) { + Arrays.fill(table, null); + modCount++; + size = 0; + } + } + + /** + * Returns a set of the keys contained in this {@code Hashtable}. The set + * is backed by this {@code Hashtable} so changes to one are reflected by + * the other. The set does not support adding. + * + * @return a set of the keys. + */ + public synchronized Set keySet() { + Set ks = keySet; + return (ks != null) ? ks : (keySet = new KeySet()); + } + + /** + * Returns a collection of the values contained in this {@code Hashtable}. + * The collection is backed by this {@code Hashtable} so changes to one are + * reflected by the other. The collection does not support adding. + * + * @return a collection of the values. + */ + public synchronized Collection values() { + Collection vs = values; + return (vs != null) ? vs : (values = new Values()); } + /** + * Returns a set of the mappings contained in this {@code Hashtable}. Each + * element in the set is a {@link Map.Entry}. The set is backed by this + * {@code Hashtable} so changes to one are reflected by the other. The set + * does not support adding. + * + * @return a set of the mappings. + */ + public synchronized Set> entrySet() { + Set> es = entrySet; + return (es != null) ? es : (entrySet = new EntrySet()); + } + + /** * Returns an enumeration on the keys of this {@code Hashtable} instance. * The results of the enumeration may be affected if the contents of this * {@code Hashtable} are modified. - * + * * @return an enumeration of the keys of this {@code Hashtable}. * @see #elements * @see #size * @see Enumeration - * @since Android 1.0 */ - @Override - @SuppressWarnings("unchecked") public synchronized Enumeration keys() { - if (elementCount == 0) { - return (Enumeration) EMPTY_ENUMERATION; - } - return new HashEnumerator(true); + return new KeyEnumeration(); } /** - * Returns a set of the keys contained in this {@code Hashtable}. The set - * is backed by this {@code Hashtable} so changes to one are reflected by - * the other. The set does not support adding. - * - * @return a set of the keys. - * @since Android 1.0 - */ - public Set keySet() { - return new Collections.SynchronizedSet(new AbstractSet() { - @Override - public boolean contains(Object object) { - synchronized (Hashtable.this) { - return containsKey(object); - } - } + * Returns an enumeration on the values of this {@code Hashtable}. The + * results of the Enumeration may be affected if the contents of this + * {@code Hashtable} are modified. + * + * @return an enumeration of the values of this {@code Hashtable}. + * @see #keys + * @see #size + * @see Enumeration + */ + public synchronized Enumeration elements() { + return new ValueEnumeration(); + } - @Override - public int size() { - synchronized (Hashtable.this) { - return elementCount; - } + /** + * Note: technically the methods of this class should synchronize the + * backing map. However, this would require them to have a reference + * to it, which would cause consiserable bloat. Moreover, the RI + * behaves the same way. + */ + private static class HashtableEntry implements Entry { + final K key; + V value; + final int hash; + HashtableEntry next; + + HashtableEntry(K key, V value, int hash, HashtableEntry next) { + this.key = key; + this.value = value; + this.hash = hash; + this.next = next; + } + + public final K getKey() { + return key; + } + + public final V getValue() { + return value; + } + + public final V setValue(V value) { + if (value == null) { + throw new NullPointerException(); } + V oldValue = this.value; + this.value = value; + return oldValue; + } - @Override - public void clear() { - Hashtable.this.clear(); + @Override public final boolean equals(Object o) { + if (!(o instanceof Entry)) { + return false; } + Entry e = (Entry) o; + return key.equals(e.getKey()) && value.equals(e.getValue()); + } - @Override - public boolean remove(Object key) { - synchronized (Hashtable.this) { - if (containsKey(key)) { - Hashtable.this.remove(key); - return true; - } - return false; - } + @Override public final int hashCode() { + return key.hashCode() ^ value.hashCode(); + } + + @Override public final String toString() { + return key + "=" + value; + } + } + + private abstract class HashIterator { + int nextIndex; + HashtableEntry nextEntry; + HashtableEntry lastEntryReturned; + int expectedModCount = modCount; + + HashIterator() { + HashtableEntry[] tab = table; + HashtableEntry next = null; + while (next == null && nextIndex < tab.length) { + next = tab[nextIndex++]; } + nextEntry = next; + } - @Override - public Iterator iterator() { - return new HashIterator(new MapEntry.Type() { - public K get(MapEntry entry) { - return entry.key; - } - }); + public boolean hasNext() { + return nextEntry != null; + } + + HashtableEntry nextEntry() { + if (modCount != expectedModCount) + throw new ConcurrentModificationException(); + if (nextEntry == null) + throw new NoSuchElementException(); + + HashtableEntry entryToReturn = nextEntry; + HashtableEntry[] tab = table; + HashtableEntry next = entryToReturn.next; + while (next == null && nextIndex < tab.length) { + next = tab[nextIndex++]; } - }, this); - } + nextEntry = next; + return lastEntryReturned = entryToReturn; + } - /** - * Associate the specified value with the specified key in this - * {@code Hashtable}. If the key already exists, the old value is replaced. - * The key and value cannot be null. - * - * @param key - * the key to add. - * @param value - * the value to add. - * @return the old value associated with the specified key, or {@code null} - * if the key did not exist. - * @see #elements - * @see #get - * @see #keys - * @see java.lang.Object#equals - * @since Android 1.0 - */ - @Override - public synchronized V put(K key, V value) { - if (key != null && value != null) { - int hash = key.hashCode(); - int index = (hash & 0x7FFFFFFF) % elementData.length; - Entry entry = elementData[index]; - while (entry != null && !entry.equalsKey(key, hash)) { - entry = entry.next; - } - if (entry == null) { - modCount++; - if (++elementCount > threshold) { - rehash(); - index = (hash & 0x7FFFFFFF) % elementData.length; - } - if (index < firstSlot) { - firstSlot = index; - } - if (index > lastSlot) { - lastSlot = index; - } - entry = newEntry(key, value, hash); - entry.next = elementData[index]; - elementData[index] = entry; - return null; + HashtableEntry nextEntryNotFailFast() { + if (nextEntry == null) + throw new NoSuchElementException(); + + HashtableEntry entryToReturn = nextEntry; + HashtableEntry[] tab = table; + HashtableEntry next = entryToReturn.next; + while (next == null && nextIndex < tab.length) { + next = tab[nextIndex++]; } - V result = entry.value; - entry.value = value; - return result; + nextEntry = next; + return lastEntryReturned = entryToReturn; + } + + public void remove() { + if (lastEntryReturned == null) + throw new IllegalStateException(); + if (modCount != expectedModCount) + throw new ConcurrentModificationException(); + Hashtable.this.remove(lastEntryReturned.key); + lastEntryReturned = null; + expectedModCount = modCount; } - throw new NullPointerException(); + } + + private final class KeyIterator extends HashIterator + implements Iterator { + public K next() { return nextEntry().key; } + } + + private final class ValueIterator extends HashIterator + implements Iterator { + public V next() { return nextEntry().value; } + } + + private final class EntryIterator extends HashIterator + implements Iterator> { + public Entry next() { return nextEntry(); } + } + + private final class KeyEnumeration extends HashIterator + implements Enumeration { + public boolean hasMoreElements() { return hasNext(); } + public K nextElement() { return nextEntryNotFailFast().key; } + } + + private final class ValueEnumeration extends HashIterator + implements Enumeration { + public boolean hasMoreElements() { return hasNext(); } + public V nextElement() { return nextEntryNotFailFast().value; } } /** - * Copies every mapping to this {@code Hashtable} from the specified map. - * - * @param map - * the map to copy mappings from. - * @since Android 1.0 + * Returns true if this map contains the specified mapping. */ - public synchronized void putAll(Map map) { - for (Map.Entry entry : map.entrySet()) { - put(entry.getKey(), entry.getValue()); + private synchronized boolean containsMapping(Object key, Object value) { + int hash = secondaryHash(key.hashCode()); + HashtableEntry[] tab = table; + int index = hash & (tab.length - 1); + for (HashtableEntry e = tab[index]; e != null; e = e.next) { + if (e.hash == hash && e.key.equals(key)) { + return e.value.equals(value); + } } + return false; // No entry for key } /** - * Increases the capacity of this {@code Hashtable}. This method is called - * when the size of this {@code Hashtable} exceeds the load factor. - * - * @since Android 1.0 + * Removes the mapping from key to value and returns true if this mapping + * exists; otherwise, returns does nothing and returns false. */ - protected void rehash() { - int length = (elementData.length << 1) + 1; - if (length == 0) { - length = 1; - } - int newFirst = length; - int newLast = -1; - Entry[] newData = newElementArray(length); - for (int i = lastSlot + 1; --i >= firstSlot;) { - Entry entry = elementData[i]; - while (entry != null) { - int index = (entry.getKeyHash() & 0x7FFFFFFF) % length; - if (index < newFirst) { - newFirst = index; + private synchronized boolean removeMapping(Object key, Object value) { + int hash = secondaryHash(key.hashCode()); + HashtableEntry[] tab = table; + int index = hash & (tab.length - 1); + for (HashtableEntry e = tab[index], prev = null; + e != null; prev = e, e = e.next) { + if (e.hash == hash && e.key.equals(key)) { + if (!e.value.equals(value)) { + return false; // Map has wrong value for key } - if (index > newLast) { - newLast = index; + if (prev == null) { + tab[index] = e.next; + } else { + prev.next = e.next; } - Entry next = entry.next; - entry.next = newData[index]; - newData[index] = entry; - entry = next; + modCount++; + size--; + return true; } } - firstSlot = newFirst; - lastSlot = newLast; - elementData = newData; - computeMaxSize(); + return false; // No entry for key } /** - * Removes the key/value pair with the specified key from this - * {@code Hashtable}. - * - * @param key - * the key to remove. - * @return the value associated with the specified key, or {@code null} if - * the specified key did not exist. - * @see #get - * @see #put - * @since Android 1.0 + * Compares this {@code Hashtable} with the specified object and indicates + * if they are equal. In order to be equal, {@code object} must be an + * instance of Map and contain the same key/value pairs. + * + * @param object + * the object to compare with this object. + * @return {@code true} if the specified object is equal to this Map, + * {@code false} otherwise. + * @see #hashCode */ - @Override - public synchronized V remove(Object key) { - int hash = key.hashCode(); - int index = (hash & 0x7FFFFFFF) % elementData.length; - Entry last = null; - Entry entry = elementData[index]; - while (entry != null && !entry.equalsKey(key, hash)) { - last = entry; - entry = entry.next; - } - if (entry != null) { - modCount++; - if (last == null) { - elementData[index] = entry.next; - } else { - last.next = entry.next; + @Override public synchronized boolean equals(Object object) { + return (object instanceof Map) && + entrySet().equals(((Map)object).entrySet()); + } + + @Override public synchronized int hashCode() { + int result = 0; + for (Entry e : entrySet()) { + K key = e.getKey(); + V value = e.getValue(); + if (key == this || value == this) { + continue; } - elementCount--; - V result = entry.value; - entry.value = null; - return result; + result += (key != null ? key.hashCode() : 0) + ^ (value != null ? value.hashCode() : 0); } - return null; + return result; } /** - * Returns the number of key/value pairs in this {@code Hashtable}. - * - * @return the number of key/value pairs in this {@code Hashtable}. - * @see #elements - * @see #keys - * @since Android 1.0 + * A rough estimate of the number of characters per entry, for use + * when creating a string buffer in the toString method. */ - @Override - public synchronized int size() { - return elementCount; - } + private static final int CHARS_PER_ENTRY = 15; /** * Returns the string representation of this {@code Hashtable}. - * + * * @return the string representation of this {@code Hashtable}. - * @since Android 1.0 */ - @Override - public synchronized String toString() { - if (isEmpty()) { - return "{}"; //$NON-NLS-1$ + @Override public synchronized String toString() { + StringBuilder result = new StringBuilder(CHARS_PER_ENTRY * size); + result.append('{'); + Iterator> i = entrySet().iterator(); + boolean hasMore = i.hasNext(); + while (hasMore) { + Entry entry = i.next(); + + K key = entry.getKey(); + result.append(key == this ? "(this Map)" : key.toString()); + + result.append('='); + + V value = entry.getValue(); + result.append(value == this ? "(this Map)" : value.toString()); + + if (hasMore = i.hasNext()) { + result.append(", "); + } } - StringBuilder buffer = new StringBuilder(size() * 28); - buffer.append('{'); - for (int i = lastSlot; i >= firstSlot; i--) { - Entry entry = elementData[i]; - while (entry != null) { - if (entry.key != this) { - buffer.append(entry.key); - } else { - // luni.04=this Map - buffer.append("(" + Messages.getString("luni.04") + ")"); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ - } - buffer.append('='); - if (entry.value != this) { - buffer.append(entry.value); - } else { - // luni.04=this Map - buffer.append("(" + Messages.getString("luni.04") + ")"); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ - } - buffer.append(", "); //$NON-NLS-1$ - entry = entry.next; + result.append('}'); + return result.toString(); + } + + private final class KeySet extends AbstractSet { + public Iterator iterator() { + return new KeyIterator(); + } + public int size() { + return Hashtable.this.size(); + } + public boolean contains(Object o) { + return containsKey(o); + } + public boolean remove(Object o) { + synchronized (Hashtable.this) { + int oldSize = size; + Hashtable.this.remove(o); + return size != oldSize; + } + } + public void clear() { + Hashtable.this.clear(); + } + public boolean removeAll(Collection collection) { + synchronized (Hashtable.this) { + return super.removeAll(collection); + } + } + public boolean retainAll(Collection collection) { + synchronized (Hashtable.this) { + return super.retainAll(collection); + } + } + public boolean containsAll(Collection collection) { + synchronized (Hashtable.this) { + return super.containsAll(collection); + } + } + public boolean equals(Object object) { + synchronized (Hashtable.this) { + return super.equals(object); + } + } + public int hashCode() { + synchronized (Hashtable.this) { + return super.hashCode(); + } + } + public String toString() { + synchronized (Hashtable.this) { + return super.toString(); + } + } + public Object[] toArray() { + synchronized (Hashtable.this) { + return super.toArray(); + } + } + public T[] toArray(T[] a) { + synchronized (Hashtable.this) { + return super.toArray(a); + } + } + } + + private final class Values extends AbstractCollection { + public Iterator iterator() { + return new ValueIterator(); + } + public int size() { + return Hashtable.this.size(); + } + public boolean contains(Object o) { + return containsValue(o); + } + public void clear() { + Hashtable.this.clear(); + } + public boolean containsAll(Collection collection) { + synchronized (Hashtable.this) { + return super.containsAll(collection); + } + } + public String toString() { + synchronized (Hashtable.this) { + return super.toString(); + } + } + public Object[] toArray() { + synchronized (Hashtable.this) { + return super.toArray(); + } + } + public T[] toArray(T[] a) { + synchronized (Hashtable.this) { + return super.toArray(a); + } + } + } + + private final class EntrySet extends AbstractSet> { + public Iterator> iterator() { + return new EntryIterator(); + } + public boolean contains(Object o) { + if (!(o instanceof Entry)) + return false; + Entry e = (Entry) o; + return containsMapping(e.getKey(), e.getValue()); + } + public boolean remove(Object o) { + if (!(o instanceof Entry)) + return false; + Entry e = (Entry)o; + return removeMapping(e.getKey(), e.getValue()); + } + public int size() { + return Hashtable.this.size(); + } + public void clear() { + Hashtable.this.clear(); + } + public boolean removeAll(Collection collection) { + synchronized (Hashtable.this) { + return super.removeAll(collection); } } - // Remove the last ", " - if (elementCount > 0) { - buffer.setLength(buffer.length() - 2); + public boolean retainAll(Collection collection) { + synchronized (Hashtable.this) { + return super.retainAll(collection); + } + } + public boolean containsAll(Collection collection) { + synchronized (Hashtable.this) { + return super.containsAll(collection); + } + } + public boolean equals(Object object) { + synchronized (Hashtable.this) { + return super.equals(object); + } + } + public int hashCode() { + return Hashtable.this.hashCode(); + } + public String toString() { + synchronized (Hashtable.this) { + return super.toString(); + } + } + public Object[] toArray() { + synchronized (Hashtable.this) { + return super.toArray(); + } + } + public T[] toArray(T[] a) { + synchronized (Hashtable.this) { + return super.toArray(a); + } } - buffer.append('}'); - return buffer.toString(); } /** - * Returns a collection of the values contained in this {@code Hashtable}. - * The collection is backed by this {@code Hashtable} so changes to one are - * reflected by the other. The collection does not support adding. - * - * @return a collection of the values. - * @since Android 1.0 - */ - public Collection values() { - return new Collections.SynchronizedCollection( - new AbstractCollection() { - @Override - public boolean contains(Object object) { - synchronized (Hashtable.this) { - return Hashtable.this.contains(object); - } - } - - @Override - public int size() { - synchronized (Hashtable.this) { - return elementCount; - } - } - - @Override - public void clear() { - Hashtable.this.clear(); - } - - @Override - public Iterator iterator() { - return new HashIterator( - new MapEntry.Type() { - public V get(MapEntry entry) { - return entry.value; - } - }); - } - }, this); + * Applies a supplemental hash function to a given hashCode, which defends + * against poor quality hash functions. This is critical because Hashtable + * uses power-of-two length hash tables, that otherwise encounter collisions + * for hashCodes that do not differ in lower or upper bits. + */ + private static int secondaryHash(int h) { + // Doug Lea's supplemental hash function + h ^= (h >>> 20) ^ (h >>> 12); + return h ^ (h >>> 7) ^ (h >>> 4); + } + + /** + * Returns the smallest power of two >= its argument, with several caveats: + * If the argument is negative but not Integer.MIN_VALUE, the method returns + * zero. If the argument is > 2^30 or equal to Integer.MIN_VALUE, the method + * returns Integer.MIN_VALUE. If the argument is zero, the method returns + * zero. + */ + private static int roundUpToPowerOfTwo(int i) { + i--; // If input is a power of two, shift its high-order bit right + + // "Smear" the high-order bit all the way to the right + i |= i >>> 1; + i |= i >>> 2; + i |= i >>> 4; + i |= i >>> 8; + i |= i >>> 16; + + return i + 1; } + private static final long serialVersionUID = 1421746759512286392L; + + /** + * Serializable fields. + * + * @serialField loadFactor float + * load factor for this Hashtable + */ + private static final ObjectStreamField[] serialPersistentFields = { + new ObjectStreamField("threshold", Integer.TYPE), + new ObjectStreamField("loadFactor", Float.TYPE) + }; + private synchronized void writeObject(ObjectOutputStream stream) throws IOException { - stream.defaultWriteObject(); - stream.writeInt(elementData.length); - stream.writeInt(elementCount); - for (int i = elementData.length; --i >= 0;) { - Entry entry = elementData[i]; - while (entry != null) { - stream.writeObject(entry.key); - stream.writeObject(entry.value); - entry = entry.next; - } + // Emulate loadFactor field for other implementations to read + ObjectOutputStream.PutField fields = stream.putFields(); + fields.put("threshold", (int) (DEFAULT_LOAD_FACTOR * table.length)); + fields.put("loadFactor", DEFAULT_LOAD_FACTOR); + stream.writeFields(); + + stream.writeInt(table.length); // Capacity + stream.writeInt(size); + for (Entry e : entrySet()) { + stream.writeObject(e.getKey()); + stream.writeObject(e.getValue()); } } - @SuppressWarnings("unchecked") private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); - int length = stream.readInt(); - elementData = newElementArray(length); - elementCount = stream.readInt(); - for (int i = elementCount; --i >= 0;) { - Object key = stream.readObject(); - int hash = key.hashCode(); - int index = (hash & 0x7FFFFFFF) % length; - if (index < firstSlot) { - firstSlot = index; - } - if (index > lastSlot) { - lastSlot = index; - } - Entry entry = newEntry((K) key, (V) stream.readObject(), hash); - entry.next = elementData[index]; - elementData[index] = entry; + int capacity = stream.readInt(); + if (capacity < 0) { + throw new InvalidObjectException("Capacity: " + capacity); + } + if (capacity < MINIMUM_CAPACITY) { + capacity = MINIMUM_CAPACITY; + } else if (capacity > MAXIMUM_CAPACITY) { + capacity = MAXIMUM_CAPACITY; + } else { + capacity = roundUpToPowerOfTwo(capacity); + } + makeTable(capacity); + + int size = stream.readInt(); + if (size < 0) { + throw new InvalidObjectException("Size: " + size); + } + + for (int i = 0; i < size; i++) { + @SuppressWarnings("unchecked") K key = (K) stream.readObject(); + @SuppressWarnings("unchecked") V val = (V) stream.readObject(); + constructorPut(key, val); } } } diff --git a/libcore/luni/src/main/java/java/util/HugeEnumSet.java b/libcore/luni/src/main/java/java/util/HugeEnumSet.java index 1d43360df..fd3e1bddb 100644 --- a/libcore/luni/src/main/java/java/util/HugeEnumSet.java +++ b/libcore/luni/src/main/java/java/util/HugeEnumSet.java @@ -92,6 +92,8 @@ final class HugeEnumSet> extends EnumSet { currentElementMask = unProcessedBits[bitsPosition] & (-unProcessedBits[bitsPosition]); unProcessedBits[bitsPosition] -= currentElementMask; + int index = Long.numberOfTrailingZeros(currentElementMask) + + bitsPosition * BIT_IN_LONG; if (0 == unProcessedBits[bitsPosition]) { int oldBitsPosition = bitsPosition; findNextNoneZeroPosition(bitsPosition + 1); @@ -99,8 +101,7 @@ final class HugeEnumSet> extends EnumSet { canProcess = false; } } - return enums[Long.numberOfTrailingZeros(currentElementMask) - + bitsPosition * BIT_IN_LONG]; + return enums[index]; } public void remove() { @@ -185,6 +186,7 @@ final class HugeEnumSet> extends EnumSet { } } + @SuppressWarnings("unchecked") @Override public boolean contains(Object object) { if (null == object) { @@ -329,7 +331,7 @@ final class HugeEnumSet> extends EnumSet { // endElementInBits + 1 is the number of consecutive ones. // 63 - endElementInBits is the following zeros of the right most one. - range = -1l >>> (BIT_IN_LONG - (endElementInBits + 1)) << (63 - endElementInBits); + range = -1l >>> (BIT_IN_LONG - (endElementInBits + 1)); size -= Long.bitCount(bits[endBitsIndex]); bits[endBitsIndex] |= range; size += Long.bitCount(bits[endBitsIndex]); diff --git a/libcore/luni/src/main/java/java/util/IdentityHashMap.java b/libcore/luni/src/main/java/java/util/IdentityHashMap.java index 3301a998f..053de1d88 100644 --- a/libcore/luni/src/main/java/java/util/IdentityHashMap.java +++ b/libcore/luni/src/main/java/java/util/IdentityHashMap.java @@ -30,16 +30,15 @@ import java.io.Serializable; *

          * Note: This class intentionally violates the general contract of {@code * Map}'s on comparing objects by their {@code equals} method. - *

          *

          * IdentityHashMap uses open addressing (linear probing in particular) for * collision resolution. This is different from HashMap which uses Chaining. - *

          + *

          * Like HashMap, IdentityHashMap is not thread safe, so access by multiple * threads must be synchronized by an external mechanism such as * Collections.synchronizedMap. * - * @since Android 1.0 + * @since 1.4 */ public class IdentityHashMap extends AbstractMap implements Map, Serializable, Cloneable { @@ -239,8 +238,6 @@ public class IdentityHashMap extends AbstractMap implements /** * Creates an IdentityHashMap with default expected maximum size. - * - * @since Android 1.0 */ public IdentityHashMap() { this(DEFAULT_MAX_SIZE); @@ -252,7 +249,6 @@ public class IdentityHashMap extends AbstractMap implements * @param maxSize * The estimated maximum number of entries that will be put in * this map. - * @since Android 1.0 */ public IdentityHashMap(int maxSize) { if (maxSize >= 0) { @@ -271,9 +267,19 @@ public class IdentityHashMap extends AbstractMap implements } private int computeElementArraySize() { - return (int) (((long) threshold * 10000) / loadFactor) * 2; + int arraySize = (int) (((long) threshold * 10000) / loadFactor) * 2; + // ensure arraySize is positive, the above cast from long to int type + // leads to overflow and negative arraySize if threshold is too big + return arraySize < 0 ? -arraySize : arraySize; } + /** + * Create a new element array + * + * @param s + * the number of elements + * @return Reference to the element array + */ private Object[] newElementArray(int s) { return new Object[s]; } @@ -283,7 +289,6 @@ public class IdentityHashMap extends AbstractMap implements * * @param map * A map of (key,value) pairs to copy into the IdentityHashMap. - * @since Android 1.0 */ public IdentityHashMap(Map map) { this(map.size() < 6 ? 11 : map.size() * 2); @@ -300,7 +305,6 @@ public class IdentityHashMap extends AbstractMap implements * * @see #isEmpty() * @see #size() - * @since Android 1.0 */ @Override public void clear() { @@ -318,7 +322,6 @@ public class IdentityHashMap extends AbstractMap implements * the key to search for. * @return {@code true} if this map contains the specified key, * {@code false} otherwise. - * @since Android 1.0 */ @Override public boolean containsKey(Object key) { @@ -337,7 +340,6 @@ public class IdentityHashMap extends AbstractMap implements * the value to search for. * @return {@code true} if this map contains the specified value, * {@code false} otherwise. - * @since Android 1.0 */ @Override public boolean containsValue(Object value) { @@ -359,7 +361,6 @@ public class IdentityHashMap extends AbstractMap implements * @param key * the key. * @return the value of the mapping with the specified key. - * @since Android 1.0 */ @Override public V get(Object key) { @@ -390,6 +391,10 @@ public class IdentityHashMap extends AbstractMap implements return null; } + /** + * Convenience method for getting the IdentityHashMapEntry without the + * NULL_OBJECT elements + */ @SuppressWarnings("unchecked") private IdentityHashMapEntry getEntry(int index) { Object key = elementData[index]; @@ -405,6 +410,10 @@ public class IdentityHashMap extends AbstractMap implements return new IdentityHashMapEntry((K) key, (V) value); } + /** + * Returns the index where the key is found at, or the index of the next + * empty spot if the key is not found in this table. + */ private int findIndex(Object key, Object[] array) { int length = array.length; int index = getModuloHash(key, length); @@ -435,7 +444,6 @@ public class IdentityHashMap extends AbstractMap implements * the value. * @return the value of any previous mapping with the specified key or * {@code null} if there was no such mapping. - * @since Android 1.0 */ @Override public V put(K key, V value) { @@ -478,7 +486,8 @@ public class IdentityHashMap extends AbstractMap implements * * @param map * the map to copy mappings from. - * @since Android 1.0 + * @throws NullPointerException + * if {@code map} is {@code null}. */ @Override public void putAll(Map map) { @@ -515,7 +524,6 @@ public class IdentityHashMap extends AbstractMap implements * the key of the mapping to remove. * @return the value of the removed mapping, or {@code null} if no mapping * for the specified key was found. - * @since Android 1.0 */ @Override public V remove(Object key) { @@ -575,7 +583,6 @@ public class IdentityHashMap extends AbstractMap implements * changes in one will be reflected in the other. * * @return a set of the mappings. - * @since Android 1.0 */ @Override public Set> entrySet() { @@ -588,7 +595,6 @@ public class IdentityHashMap extends AbstractMap implements * support adding. * * @return a set of the keys. - * @since Android 1.0 */ @Override public Set keySet() { @@ -643,16 +649,13 @@ public class IdentityHashMap extends AbstractMap implements * "wrapper object" over the iterator of map's entrySet(). The {@code size} * method wraps the map's size method and the {@code contains} method wraps * the map's containsValue method. - *

          *

          * The collection is created when this method is called for the first time * and returned in response to all subsequent calls. This method may return * different collections when multiple concurrent calls occur, since no * synchronization is performed. - *

          * * @return a collection of the values contained in this map. - * @since Android 1.0 */ @Override public Collection values() { @@ -710,7 +713,6 @@ public class IdentityHashMap extends AbstractMap implements * @param object * the object to compare to. * @return whether the argument object is equal to this object. - * @since Android 1.0 */ @Override public boolean equals(Object object) { @@ -745,12 +747,16 @@ public class IdentityHashMap extends AbstractMap implements * * @return a shallow copy of this IdentityHashMap. * @see java.lang.Cloneable - * @since Android 1.0 */ @Override public Object clone() { try { - return super.clone(); + IdentityHashMap cloneHashMap = (IdentityHashMap) super + .clone(); + cloneHashMap.elementData = newElementArray(elementData.length); + System.arraycopy(elementData, 0, cloneHashMap.elementData, 0, + elementData.length); + return cloneHashMap; } catch (CloneNotSupportedException e) { return null; } @@ -762,7 +768,6 @@ public class IdentityHashMap extends AbstractMap implements * @return {@code true} if this IdentityHashMap has no elements, * {@code false} otherwise. * @see #size() - * @since Android 1.0 */ @Override public boolean isEmpty() { @@ -773,7 +778,6 @@ public class IdentityHashMap extends AbstractMap implements * Returns the number of mappings in this IdentityHashMap. * * @return the number of mappings in this IdentityHashMap. - * @since Android 1.0 */ @Override public int size() { diff --git a/libcore/luni/src/main/java/java/util/IllegalFormatCodePointException.java b/libcore/luni/src/main/java/java/util/IllegalFormatCodePointException.java index e04eebaad..c0d6b6f8f 100644 --- a/libcore/luni/src/main/java/java/util/IllegalFormatCodePointException.java +++ b/libcore/luni/src/main/java/java/util/IllegalFormatCodePointException.java @@ -24,7 +24,6 @@ import java.io.Serializable; * passed as a parameter to a Formatter. * * @see java.lang.RuntimeException - * @since Android 1.0 */ public class IllegalFormatCodePointException extends IllegalFormatException implements Serializable { diff --git a/libcore/luni/src/main/java/java/util/IllegalFormatConversionException.java b/libcore/luni/src/main/java/java/util/IllegalFormatConversionException.java index 769381b70..696f4f207 100644 --- a/libcore/luni/src/main/java/java/util/IllegalFormatConversionException.java +++ b/libcore/luni/src/main/java/java/util/IllegalFormatConversionException.java @@ -23,7 +23,8 @@ import java.io.Serializable; * is incompatible with the corresponding format specifier. * * @see java.lang.RuntimeException - * @since Android 1.0 + * + * @since 1.5 */ public class IllegalFormatConversionException extends IllegalFormatException implements Serializable { diff --git a/libcore/luni/src/main/java/java/util/IllegalFormatException.java b/libcore/luni/src/main/java/java/util/IllegalFormatException.java index 2c1f2d777..3d37cd038 100644 --- a/libcore/luni/src/main/java/java/util/IllegalFormatException.java +++ b/libcore/luni/src/main/java/java/util/IllegalFormatException.java @@ -24,7 +24,6 @@ import java.io.Serializable; * allowed to be instantiated. * * @see java.lang.RuntimeException - * @since Android 1.0 */ public class IllegalFormatException extends IllegalArgumentException implements Serializable { diff --git a/libcore/luni/src/main/java/java/util/IllegalFormatFlagsException.java b/libcore/luni/src/main/java/java/util/IllegalFormatFlagsException.java index 6b27c7431..4fa2d9c14 100644 --- a/libcore/luni/src/main/java/java/util/IllegalFormatFlagsException.java +++ b/libcore/luni/src/main/java/java/util/IllegalFormatFlagsException.java @@ -23,7 +23,6 @@ import java.io.Serializable; * the format flags is illegal. * * @see java.lang.RuntimeException - * @since Android 1.0 */ public class IllegalFormatFlagsException extends IllegalFormatException implements Serializable { diff --git a/libcore/luni/src/main/java/java/util/IllegalFormatPrecisionException.java b/libcore/luni/src/main/java/java/util/IllegalFormatPrecisionException.java index 6ccf5d2a1..5d5113f92 100644 --- a/libcore/luni/src/main/java/java/util/IllegalFormatPrecisionException.java +++ b/libcore/luni/src/main/java/java/util/IllegalFormatPrecisionException.java @@ -21,7 +21,6 @@ package java.util; * a negative other than -1 or in other cases where precision is not supported. * * @see java.lang.RuntimeException - * @since Android 1.0 */ public class IllegalFormatPrecisionException extends IllegalFormatException { diff --git a/libcore/luni/src/main/java/java/util/IllegalFormatWidthException.java b/libcore/luni/src/main/java/java/util/IllegalFormatWidthException.java index 8348ec7f3..01904f829 100644 --- a/libcore/luni/src/main/java/java/util/IllegalFormatWidthException.java +++ b/libcore/luni/src/main/java/java/util/IllegalFormatWidthException.java @@ -22,7 +22,6 @@ package java.util; * supported. * * @see java.lang.RuntimeException - * @since Android 1.0 */ public class IllegalFormatWidthException extends IllegalFormatException { diff --git a/libcore/luni/src/main/java/java/util/InputMismatchException.java b/libcore/luni/src/main/java/java/util/InputMismatchException.java index 0fc538525..eed0b8336 100644 --- a/libcore/luni/src/main/java/java/util/InputMismatchException.java +++ b/libcore/luni/src/main/java/java/util/InputMismatchException.java @@ -24,13 +24,12 @@ import java.io.Serializable; * * @see Scanner * @see java.lang.RuntimeException - * @since Android 1.0 */ public class InputMismatchException extends NoSuchElementException implements Serializable { - static final long serialVersionUID = 8811230760997066428L; - + private static final long serialVersionUID = 8811230760997066428L; + /** * Constructs a new {@code InputMismatchException} with the current stack * trace filled in. diff --git a/libcore/luni/src/main/java/java/util/InvalidPropertiesFormatException.java b/libcore/luni/src/main/java/java/util/InvalidPropertiesFormatException.java index 4e8a3e38e..015c3d00e 100644 --- a/libcore/luni/src/main/java/java/util/InvalidPropertiesFormatException.java +++ b/libcore/luni/src/main/java/java/util/InvalidPropertiesFormatException.java @@ -30,8 +30,6 @@ import java.io.IOException; * Even though this Exception inherits the {@code Serializable} interface, it is not * serializable. The methods used for serialization throw * {@code NotSerializableException}s. - * - * @since Android 1.0 */ public class InvalidPropertiesFormatException extends IOException { diff --git a/libcore/luni/src/main/java/java/util/Iterator.java b/libcore/luni/src/main/java/java/util/Iterator.java index 9f20dc5b2..6ebae0436 100644 --- a/libcore/luni/src/main/java/java/util/Iterator.java +++ b/libcore/luni/src/main/java/java/util/Iterator.java @@ -17,17 +17,18 @@ package java.util; - /** - * An Iterator is used to sequence over a collection of objects. Conceptual, an - * iterator is always positioned between two elements of a collection. A fresh - * iterator is always positioned in front of the first element. + * An {@code Iterator} is used to sequence over a collection of objects. + * Conceptually, an iterator is always positioned between two elements of a + * collection. A fresh iterator is always positioned in front of the first + * element. * * If a collection has been changed since its creation, methods {@code next} and * {@code hasNext()} may throw a {@code ConcurrentModificationException}. * Iterators with this behavior are called fail-fast iterators. * - * @since Android 1.0 + * @param + * the type of object returned by the iterator. */ public interface Iterator { /** @@ -36,7 +37,6 @@ public interface Iterator { * * @return {@code true} if there are more elements, {@code false} otherwise. * @see #next - * @since Android 1.0 */ public boolean hasNext(); @@ -48,7 +48,6 @@ public interface Iterator { * @throws NoSuchElementException * if there are no more elements. * @see #hasNext - * @since Android 1.0 */ public E next(); @@ -62,7 +61,6 @@ public interface Iterator { * @throws IllegalStateException * if {@code next} has not been called, or {@code remove} has * already been called after the last call to {@code next}. - * @since Android 1.0 */ public void remove(); } diff --git a/libcore/luni/src/main/java/java/util/LinkedHashMap.java b/libcore/luni/src/main/java/java/util/LinkedHashMap.java index a32a28b12..cd6825bef 100644 --- a/libcore/luni/src/main/java/java/util/LinkedHashMap.java +++ b/libcore/luni/src/main/java/java/util/LinkedHashMap.java @@ -1,10 +1,10 @@ /* * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with + * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at + * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * @@ -15,8 +15,11 @@ * limitations under the License. */ -package java.util; +// BEGIN android-note +// Completely different implementation from harmony. Runs much faster. +// BEGIN android-note +package java.util; /** * LinkedHashMap is a variant of HashMap. Its entries are kept in a @@ -46,73 +49,68 @@ package java.util; * elements during iteration. It is not possible to guarantee that this * mechanism works in all cases of unsynchronized concurrent modification. It * should only be used for debugging purposes. - * - * @since Android 1.0 */ public class LinkedHashMap extends HashMap { - private static final long serialVersionUID = 3801124242820219131L; + /** + * A dummy entry in the circular linked list of entries in the map. + * The first real entry is header.nxt, and the last is header.prv. + * If the map is empty, header.nxt == header && header.prv == header. + */ + transient LinkedEntry header; + /** + * True if access ordered, false if insertion ordered. + */ private final boolean accessOrder; - transient private LinkedHashMapEntry head, tail; - /** * Constructs a new empty {@code LinkedHashMap} instance. - * - * @since Android 1.0 */ public LinkedHashMap() { super(); + init(); accessOrder = false; - head = null; } /** * Constructs a new {@code LinkedHashMap} instance with the specified * capacity. - * - * @param s + * + * @param initialCapacity * the initial capacity of this map. * @exception IllegalArgumentException * when the capacity is less than zero. - * @since Android 1.0 */ - public LinkedHashMap(int s) { - super(s); - accessOrder = false; - head = null; + public LinkedHashMap(int initialCapacity) { + this(initialCapacity, DEFAULT_LOAD_FACTOR); } /** * Constructs a new {@code LinkedHashMap} instance with the specified * capacity and load factor. - * - * @param s + * + * @param initialCapacity * the initial capacity of this map. - * @param lf + * @param loadFactor * the initial load factor. * @throws IllegalArgumentException * when the capacity is less than zero or the load factor is * less or equal to zero. - * @since Android 1.0 */ - public LinkedHashMap(int s, float lf) { - super(s, lf); - accessOrder = false; - head = null; - tail = null; + public LinkedHashMap(int initialCapacity, float loadFactor) { + this(initialCapacity, loadFactor, false); } /** * Constructs a new {@code LinkedHashMap} instance with the specified * capacity, load factor and a flag specifying the ordering behavior. - * - * @param s + * + * @param initialCapacity * the initial capacity of this hash map. - * @param lf + * @param loadFactor * the initial load factor. - * @param order + * @param accessOrder * {@code true} if the ordering should be done based on the last * access (from least-recently accessed to most-recently * accessed), and {@code false} if the ordering should be the @@ -120,536 +118,277 @@ public class LinkedHashMap extends HashMap { * @throws IllegalArgumentException * when the capacity is less than zero or the load factor is * less or equal to zero. - * @since Android 1.0 */ - public LinkedHashMap(int s, float lf, boolean order) { - super(s, lf); - accessOrder = order; - head = null; - tail = null; + public LinkedHashMap( + int initialCapacity, float loadFactor, boolean accessOrder) { + super(initialCapacity, loadFactor); + init(); + this.accessOrder = accessOrder; } /** * Constructs a new {@code LinkedHashMap} instance containing the mappings * from the specified map. The order of the elements is preserved. - * - * @param m + * + * @param map * the mappings to add. - * @since Android 1.0 */ - public LinkedHashMap(Map m) { - accessOrder = false; - head = null; - tail = null; - putAll(m); + public LinkedHashMap(Map map) { + this(capacityForInitSize(map.size())); + constructorPutAll(map); } - static final class LinkedHashIterator extends HashMapIterator { - LinkedHashIterator(MapEntry.Type value, LinkedHashMap hm) { - super(value, hm); - entry = hm.head; - } - - @Override - public boolean hasNext() { - return (entry != null); - } - - @Override - public E next() { - checkConcurrentMod(); - if (!hasNext()) { - throw new NoSuchElementException(); - } - E result = type.get(entry); - lastEntry = entry; - entry = ((LinkedHashMapEntry)entry).chainForward; - canRemove = true; - return result; - } - - @Override - public void remove() { - checkConcurrentMod(); - if (!canRemove) { - throw new IllegalStateException(); - } - - canRemove = false; - associatedMap.modCount++; - - int index = (lastEntry.key == null)? 0 : (lastEntry.key.hashCode() & 0x7FFFFFFF) % associatedMap.elementData.length; - LinkedHashMapEntry m = (LinkedHashMapEntry) associatedMap.elementData[index]; - if (m == lastEntry) { - associatedMap.elementData[index] = lastEntry.next; - } else { - while (m.next != null) { - if (m.next == lastEntry) { - break; - } - m = (LinkedHashMapEntry) m.next; - } - // assert m.next == entry - m.next = lastEntry.next; - } - LinkedHashMapEntry lhme = (LinkedHashMapEntry) lastEntry; - LinkedHashMapEntry p = lhme.chainBackward; - LinkedHashMapEntry n = lhme.chainForward; - LinkedHashMap lhm = (LinkedHashMap) associatedMap; - if (p != null) { - p.chainForward = n; - if (n != null) { - n.chainBackward = p; - } else { - lhm.tail = p; - } - } else { - lhm.head = n; - if (n != null) { - n.chainBackward = null; - } else { - lhm.tail = null; - } - } - associatedMap.elementCount--; - expectedModCount++; - } + @Override void init() { + header = new LinkedEntry(); } - static final class LinkedHashMapEntrySet extends HashMapEntrySet { - public LinkedHashMapEntrySet(LinkedHashMap lhm) { - super(lhm); + /** + * LinkedEntry adds nxt/prv double-links to plain HashMapEntry. + */ + static class LinkedEntry extends HashMapEntry { + LinkedEntry nxt; + LinkedEntry prv; + + /** Create the header entry */ + LinkedEntry() { + super(null, null, 0, null); + nxt = prv = this; } - @Override - public Iterator> iterator() { - return new LinkedHashIterator,KT,VT>(new MapEntry.Type, KT, VT>() { - public Map.Entry get(MapEntry entry) { - return entry; - } - }, (LinkedHashMap) hashMap()); + /** Create a normal entry */ + LinkedEntry(K key, V value, int hash, HashMapEntry next, + LinkedEntry nxt, LinkedEntry prv) { + super(key, value, hash, next); + this.nxt = nxt; + this.prv = prv; } } - static final class LinkedHashMapEntry extends Entry { - LinkedHashMapEntry chainForward, chainBackward; + /** + * Evicts eldest entry if instructed, creates a new entry and links it in + * as head of linked list. This method should call constructorNewEntry + * (instead of duplicating code) if the performance of your VM permits. + * + *

          It may seem strange that this method is tasked with adding the entry + * to the hash table (which is properly the province of our superclass). + * The alternative of passing the "next" link in to this method and + * returning the newly created element does not work! If we remove an + * (eldest) entry that happens to be the first entry in the same bucket + * as the newly created entry, the "next" link would become invalid, and + * the resulting hash table corrupt. + */ + @Override void addNewEntry(K key, V value, int hash, int index) { + LinkedEntry header = this.header; - LinkedHashMapEntry(K theKey, V theValue) { - super(theKey, theValue); - chainForward = null; - chainBackward = null; + // Remove eldest entry if instructed to do so. + LinkedEntry eldest = header.nxt; + if (eldest != header && removeEldestEntry(eldest)) { + remove(eldest.key); } - LinkedHashMapEntry(K theKey, int hash) { - super(theKey, hash); - chainForward = null; - chainBackward = null; - } + // Create new entry, link it on to list, and put it into table + LinkedEntry oldTail = header.prv; + LinkedEntry newTail = new LinkedEntry( + key, value, hash, table[index], header, oldTail); + table[index] = oldTail.nxt = header.prv = newTail; + } + @Override void addNewEntryForNullKey(V value) { + LinkedEntry header = this.header; - @Override - @SuppressWarnings("unchecked") - public Object clone() { - LinkedHashMapEntry entry = (LinkedHashMapEntry) super.clone(); - entry.chainBackward = chainBackward; - entry.chainForward = chainForward; - LinkedHashMapEntry lnext = (LinkedHashMapEntry) entry.next; - if (lnext != null) { - entry.next = (LinkedHashMapEntry) lnext.clone(); - } - return entry; + // Remove eldest entry if instructed to do so. + LinkedEntry eldest = header.nxt; + if (eldest != header && removeEldestEntry(eldest)) { + remove(eldest.key); } + + // Create new entry, link it on to list, and put it into table + LinkedEntry oldTail = header.prv; + LinkedEntry newTail = new LinkedEntry( + null, value, 0, null, header, oldTail); + entryForNullKey = oldTail.nxt = header.prv = newTail; } - @Override - @SuppressWarnings("unchecked") - Entry[] newElementArray(int s) { - return new LinkedHashMapEntry[s]; + /** + * As above, but without eviction. + */ + @Override HashMapEntry constructorNewEntry( + K key, V value, int hash, HashMapEntry next) { + LinkedEntry header = this.header; + LinkedEntry oldTail = header.prv; + LinkedEntry newTail + = new LinkedEntry(key, value, hash, next, header, oldTail); + return oldTail.nxt = header.prv = newTail; } /** * Returns the value of the mapping with the specified key. - * + * * @param key * the key. * @return the value of the mapping with the specified key, or {@code null} * if no mapping for the specified key is found. - * @since Android 1.0 */ - @Override - public V get(Object key) { - LinkedHashMapEntry m; + @Override public V get(Object key) { + /* + * This method is overridden to eliminate the need for a polymorphic + * invocation in superclass at the expense of code duplication. + */ if (key == null) { - m = (LinkedHashMapEntry)findNullKeyEntry(); - } else { - int hash = key.hashCode(); - int index = (hash & 0x7FFFFFFF) % elementData.length; - m = (LinkedHashMapEntry)findNonNullKeyEntry(key, index, hash); + HashMapEntry e = entryForNullKey; + if (e == null) + return null; + if (accessOrder) + makeTail((LinkedEntry) e); + return e.value; } - if (m == null) { - return null; - } - if (accessOrder && tail != m) { - // BEGIN android-added - modCount++; - // END android-added - LinkedHashMapEntry p = m.chainBackward; - LinkedHashMapEntry n = m.chainForward; - n.chainBackward = p; - if (p != null) { - p.chainForward = n; - } else { - head = n; + + // Doug Lea's supplemental secondaryHash function (inlined) + int hash = key.hashCode(); + hash ^= (hash >>> 20) ^ (hash >>> 12); + hash ^= (hash >>> 7) ^ (hash >>> 4); + + HashMapEntry[] tab = table; + for (HashMapEntry e = tab[hash & (tab.length - 1)]; + e != null; e = e.next) { + K eKey = e.key; + if (eKey == key || (e.hash == hash && key.equals(eKey))) { + if (accessOrder) + makeTail((LinkedEntry) e); + return e.value; } - m.chainForward = null; - m.chainBackward = tail; - tail.chainForward = m; - tail = m; } - return m.value; - } - - /* - * @param key @param index @return Entry - */ - @Override - Entry createEntry(K key, int index, V value) { - LinkedHashMapEntry m = new LinkedHashMapEntry(key, value); - m.next = elementData[index]; - elementData[index] = m; - linkEntry(m); - return m; - } - - Entry createHashedEntry(K key, int index, int hash) { - LinkedHashMapEntry m = new LinkedHashMapEntry(key, hash); - m.next = elementData[index]; - elementData[index] = m; - linkEntry(m); - return m; + return null; } - // BEGIN android-changed - // copied from newer version of harmony /** - * Maps the specified key to the specified value. - * - * @param key - * the key. - * @param value - * the value. - * @return the value of any previous mapping with the specified key or - * {@code null} if there was no such mapping. - * @since Android 1.0 + * Relinks the given entry to the tail of the list. Under access ordering, + * this method is invoked whenever the value of a pre-existing entry is + * read by Map.get or modified by Map.put. */ - @Override - public V put(K key, V value) { - V result = putImpl(key,value); + private void makeTail(LinkedEntry e) { + // Unlink e + e.prv.nxt = e.nxt; + e.nxt.prv = e.prv; + + // Relink e as tail + LinkedEntry header = this.header; + LinkedEntry oldTail = header.prv; + e.nxt = header; + e.prv = oldTail; + oldTail.nxt = header.prv = e; + modCount++; + } - if (removeEldestEntry(head)) { - remove(head.key); + @Override void preModify(HashMapEntry e) { + if (accessOrder) { + makeTail((LinkedEntry) e); } - - return result; } - - V putImpl(K key, V value){ - LinkedHashMapEntry m; - if (elementCount == 0){ - head = tail = null; - } - if (key == null) { - m = (LinkedHashMapEntry)findNullKeyEntry(); - if (m == null) { - modCount++; - // Check if we need to remove the oldest entry - // The check includes accessOrder since an accessOrder LinkedHashMap - // does not record - // the oldest member in 'head'. - if (++elementCount > threshold) { - rehash(); - } - m = (LinkedHashMapEntry) createHashedEntry(null, 0, 0); - } else { - linkEntry(m); - } - } else { - int hash = key.hashCode(); - int index = (hash & 0x7FFFFFFF) % elementData.length; - m = (LinkedHashMapEntry)findNonNullKeyEntry(key, index, hash); - if (m == null) { - modCount++; - if (++elementCount > threshold) { - rehash(); - index = (hash & 0x7FFFFFFF) % elementData.length; - } - m = (LinkedHashMapEntry) createHashedEntry(key, index, hash); - } else { - linkEntry(m); - } - } - V result = m.value; - m.value = value; - return result; + @Override void postRemove(HashMapEntry e) { + LinkedEntry le = (LinkedEntry) e; + le.prv.nxt = le.nxt; + le.nxt.prv = le.prv; + le.nxt = le.prv = null; // Help the GC (for performance) } - // END android-changed - /* - * @param m + /** + * This override is done for LinkedHashMap performance: iteration is cheaper + * via LinkedHashMap nxt links. */ - void linkEntry(LinkedHashMapEntry m) { - if (tail == m) { - return; - } - - if (head == null) { - // Check if the map is empty - head = tail = m; - return; - } - - // we need to link the new entry into either the head or tail - // of the chain depending on if the LinkedHashMap is accessOrder or not - LinkedHashMapEntry p = m.chainBackward; - LinkedHashMapEntry n = m.chainForward; - if (p == null) { - if (n != null) { - // The entry must be the head but not the tail - if (accessOrder) { - head = n; - n.chainBackward = null; - m.chainBackward = tail; - m.chainForward = null; - tail.chainForward = m; - tail = m; + @Override public boolean containsValue(Object value) { + if (value == null) { + for (LinkedEntry header = this.header, e = header.nxt; + e != header; e = e.nxt) { + if (e.value == null) { + return true; } - } else { - // This is a new entry - m.chainBackward = tail; - m.chainForward = null; - tail.chainForward = m; - tail = m; } - return; + return false; } - if (n == null) { - // The entry must be the tail so we can't get here - return; + // value is non-null + for (LinkedEntry header = this.header, e = header.nxt; + e != header; e = e.nxt) { + if (value.equals(e.value)) { + return true; + } } + return false; + } - // The entry is neither the head nor tail - if (accessOrder) { - p.chainForward = n; - n.chainBackward = p; - m.chainForward = null; - m.chainBackward = tail; - tail.chainForward = m; - tail = m; + public void clear() { + super.clear(); + + // Clear all links to help GC + LinkedEntry header = this.header; + for (LinkedEntry e = header.nxt; e != header; ) { + LinkedEntry nxt = e.nxt; + e.nxt = e.prv = null; + e = nxt; } + header.nxt = header.prv = header; } - /** - * Returns a set containing all of the mappings in this map. Each mapping is - * an instance of {@link Map.Entry}. As the set is backed by this map, - * changes in one will be reflected in the other. - * - * @return a set of the mappings. - * @since Android 1.0 - */ - @Override - public Set> entrySet() { - return new LinkedHashMapEntrySet(this); - } - - /** - * Returns a set of the keys contained in this map. The set is backed by - * this map so changes to one are reflected by the other. The set does not - * support adding. - * - * @return a set of the keys. - * @since Android 1.0 - */ - @Override - public Set keySet() { - if (keySet == null) { - keySet = new AbstractSet() { - @Override - public boolean contains(Object object) { - return containsKey(object); - } - - @Override - public int size() { - return LinkedHashMap.this.size(); - } + private abstract class LinkedHashIterator implements Iterator { + LinkedEntry next = header.nxt; + LinkedEntry lastReturned = null; + int expectedModCount = modCount; - @Override - public void clear() { - LinkedHashMap.this.clear(); - } + public final boolean hasNext() { + return next != header; + } - @Override - public boolean remove(Object key) { - if (containsKey(key)) { - LinkedHashMap.this.remove(key); - return true; - } - return false; - } + final LinkedEntry nextEntry() { + if (modCount != expectedModCount) + throw new ConcurrentModificationException(); + LinkedEntry e = next; + if (e == header) + throw new NoSuchElementException(); + next = e.nxt; + return lastReturned = e; + } - @Override - public Iterator iterator() { - return new LinkedHashIterator(new MapEntry.Type() { - public K get(MapEntry entry) { - return entry.key; - } - }, LinkedHashMap.this); - } - }; + public final void remove() { + if (modCount != expectedModCount) + throw new ConcurrentModificationException(); + if (lastReturned == null) + throw new IllegalStateException(); + LinkedHashMap.this.remove(lastReturned.key); + lastReturned = null; + expectedModCount = modCount; } - return keySet; } - /** - * Returns a collection of the values contained in this map. The collection - * is backed by this map so changes to one are reflected by the other. The - * collection supports remove, removeAll, retainAll and clear operations, - * and it does not support add or addAll operations. - *

          - * This method returns a collection which is the subclass of - * AbstractCollection. The iterator method of this subclass returns a - * "wrapper object" over the iterator of map's entrySet(). The size method - * wraps the map's size method and the contains method wraps the map's - * containsValue method. - *

          - *

          - * The collection is created when this method is called for the first time - * and returned in response to all subsequent calls. This method may return - * different collections when multiple concurrent calls occur, since no - * synchronization is performed. - *

          - * - * @return a collection of the values contained in this map. - * @since Android 1.0 - */ - @Override - public Collection values() { - if (valuesCollection == null) { - valuesCollection = new AbstractCollection() { - @Override - public boolean contains(Object object) { - return containsValue(object); - } - - @Override - public int size() { - return LinkedHashMap.this.size(); - } - - @Override - public void clear() { - LinkedHashMap.this.clear(); - } - - @Override - public Iterator iterator() { - return new LinkedHashIterator(new MapEntry.Type() { - public V get(MapEntry entry) { - return entry.value; - } - }, LinkedHashMap.this); - } - }; - } - return valuesCollection; + private final class KeyIterator extends LinkedHashIterator { + public final K next() { return nextEntry().key; } } - /** - * Removes the mapping with the specified key from this map. - * - * @param key - * the key of the mapping to remove. - * @return the value of the removed mapping or {@code null} if no mapping - * for the specified key was found. - * @since Android 1.0 - */ - @Override - public V remove(Object key) { - LinkedHashMapEntry m = (LinkedHashMapEntry) removeEntry(key); - if (m == null) { - return null; - } - LinkedHashMapEntry p = m.chainBackward; - LinkedHashMapEntry n = m.chainForward; - if (p != null) { - p.chainForward = n; - } else { - head = n; - } - if (n != null) { - n.chainBackward = p; - } else { - tail = p; - } - return m.value; + private final class ValueIterator extends LinkedHashIterator { + public final V next() { return nextEntry().value; } } - /** - * This method is queried from the put and putAll methods to check if the - * eldest member of the map should be deleted before adding the new member. - * If this map was created with accessOrder = true, then the result of - * removeEldestEntry is assumed to be false. - * - * @param eldest - * the entry to check if it should be removed. - * @return {@code true} if the eldest member should be removed. - * @since Android 1.0 - */ - protected boolean removeEldestEntry(Map.Entry eldest) { - return false; + private final class EntryIterator + extends LinkedHashIterator> { + public final Map.Entry next() { return nextEntry(); } } - // BEGIN android-changed - /** - * Removes all elements from this map, leaving it empty. - * - * @see #isEmpty() - * @see #size() - * @since Android 1.0 - */ - @Override - public void clear() { - internalClear(); + // Override view iterator methods to generate correct iteration order + @Override Iterator newKeyIterator() { + return new KeyIterator(); + } + @Override Iterator newValueIterator() { + return new ValueIterator(); + } + @Override Iterator> newEntryIterator() { + return new EntryIterator(); } - @Override - void internalClear() { - super.internalClear(); - head = tail = null; + protected boolean removeEldestEntry(Map.Entry eldest) { + return false; } - // END android-changed - - // BEGIN android-removed - // copied from newer version of harmony - // /** - // * Answers a new HashMap with the same mappings and size as this HashMap. - // * - // * @return a shallow copy of this HashMap - // * - // * @see java.lang.Cloneable - // */ - // @Override - // @SuppressWarnings("unchecked") - // public Object clone() { - // LinkedHashMap map = (LinkedHashMap) super.clone(); - // map.clear(); - // for (Map.Entry entry : entrySet()) { - // map.put(entry.getKey(), entry.getValue()); - // } - // return map; - // } - // END android-removed + + private static final long serialVersionUID = 3801124242820219131L; } diff --git a/libcore/luni/src/main/java/java/util/LinkedHashSet.java b/libcore/luni/src/main/java/java/util/LinkedHashSet.java index 943e01c2d..3add4603a 100644 --- a/libcore/luni/src/main/java/java/util/LinkedHashSet.java +++ b/libcore/luni/src/main/java/java/util/LinkedHashSet.java @@ -17,7 +17,6 @@ package java.util; - import java.io.Serializable; /** @@ -30,18 +29,16 @@ import java.io.Serializable; * Like HashSet, LinkedHashSet is not thread safe, so access by multiple threads * must be synchronized by an external mechanism such as * {@link Collections#synchronizedSet(Set)}. - * - * @since Android 1.0 + * + * @since 1.4 */ public class LinkedHashSet extends HashSet implements Set, Cloneable, Serializable { - + private static final long serialVersionUID = -2851667679971038690L; /** * Constructs a new empty instance of {@code LinkedHashSet}. - * - * @since Android 1.0 */ public LinkedHashSet() { super(new LinkedHashMap>()); @@ -53,7 +50,6 @@ public class LinkedHashSet extends HashSet implements Set, Cloneable, * * @param capacity * the initial capacity of this {@code LinkedHashSet}. - * @since Android 1.0 */ public LinkedHashSet(int capacity) { super(new LinkedHashMap>(capacity)); @@ -67,7 +63,6 @@ public class LinkedHashSet extends HashSet implements Set, Cloneable, * the initial capacity. * @param loadFactor * the initial load factor. - * @since Android 1.0 */ public LinkedHashSet(int capacity, float loadFactor) { super(new LinkedHashMap>(capacity, loadFactor)); @@ -79,7 +74,6 @@ public class LinkedHashSet extends HashSet implements Set, Cloneable, * * @param collection * the collection of elements to add. - * @since Android 1.0 */ public LinkedHashSet(Collection collection) { super(new LinkedHashMap>(collection.size() < 6 ? 11 diff --git a/libcore/luni/src/main/java/java/util/LinkedList.java b/libcore/luni/src/main/java/java/util/LinkedList.java index 7cfefe604..64326e34b 100644 --- a/libcore/luni/src/main/java/java/util/LinkedList.java +++ b/libcore/luni/src/main/java/java/util/LinkedList.java @@ -17,7 +17,6 @@ package java.util; - import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; @@ -28,12 +27,12 @@ import java.lang.reflect.Array; * LinkedList is an implementation of List, backed by a linked list. All * optional operations (adding, removing and replacing) are supported. The * elements can be any objects. - * - * @since Android 1.0 + * + * @since 1.2 */ public class LinkedList extends AbstractSequentialList implements List, Queue, Cloneable, Serializable { - + private static final long serialVersionUID = 876323262645176354L; transient int size = 0; @@ -178,8 +177,6 @@ public class LinkedList extends AbstractSequentialList implements /** * Constructs a new empty instance of {@code LinkedList}. - * - * @since Android 1.0 */ public LinkedList() { voidLink = new Link(null, null, null); @@ -192,10 +189,9 @@ public class LinkedList extends AbstractSequentialList implements * elements contained in the specified {@code collection}. The order of the * elements in this new {@code LinkedList} will be determined by the * iteration order of {@code collection}. - * + * * @param collection * the collection of elements to add. - * @since Android 1.0 */ public LinkedList(Collection collection) { this(); @@ -207,14 +203,13 @@ public class LinkedList extends AbstractSequentialList implements * specified location. The object is inserted before any previous element at * the specified location. If the location is equal to the size of this * {@code LinkedList}, the object is added at the end. - * + * * @param location * the index at which to insert. * @param object * the object to add. * @throws IndexOutOfBoundsException * if {@code location < 0 || >= size()} - * @since Android 1.0 */ @Override public void add(int location, E object) { @@ -242,11 +237,10 @@ public class LinkedList extends AbstractSequentialList implements /** * Adds the specified object at the end of this {@code LinkedList}. - * + * * @param object * the object to add. * @return always true - * @since Android 1.0 */ @Override public boolean add(E object) { @@ -264,7 +258,7 @@ public class LinkedList extends AbstractSequentialList implements * Inserts the objects in the specified collection at the specified location * in this {@code LinkedList}. The objects are added in the order they are * returned from the collection's iterator. - * + * * @param location * the index at which to insert. * @param collection @@ -277,7 +271,6 @@ public class LinkedList extends AbstractSequentialList implements * if an object cannot be added to this list. * @throws IndexOutOfBoundsException * if {@code location < 0 || > size()} - * @since Android 1.0 */ @Override public boolean addAll(int location, Collection collection) { @@ -288,6 +281,9 @@ public class LinkedList extends AbstractSequentialList implements if (adding == 0) { return false; } + Collection elements = (collection == this) ? + new ArrayList(collection) : collection; + Link previous = voidLink; if (location < (size / 2)) { for (int i = 0; i < location; i++) { @@ -299,7 +295,7 @@ public class LinkedList extends AbstractSequentialList implements } } Link next = previous.next; - for (E e : collection) { + for (E e : elements) { Link newLink = new Link(e, previous, null); previous.next = newLink; previous = newLink; @@ -311,15 +307,13 @@ public class LinkedList extends AbstractSequentialList implements return true; } - /** * Adds the objects in the specified Collection to this {@code LinkedList}. - * + * * @param collection * the collection of objects. * @return {@code true} if this {@code LinkedList} is modified, * {@code false} otherwise. - * @since Android 1.0 */ @Override public boolean addAll(Collection collection) { @@ -327,8 +321,11 @@ public class LinkedList extends AbstractSequentialList implements if (adding == 0) { return false; } + Collection elements = (collection == this) ? + new ArrayList(collection) : collection; + Link previous = voidLink.previous; - for (E e : collection) { + for (E e : elements) { Link newLink = new Link(e, previous, null); previous.next = newLink; previous = newLink; @@ -342,10 +339,9 @@ public class LinkedList extends AbstractSequentialList implements /** * Adds the specified object at the beginning of this {@code LinkedList}. - * + * * @param object * the object to add. - * @since Android 1.0 */ public void addFirst(E object) { Link oldFirst = voidLink.next; @@ -358,10 +354,9 @@ public class LinkedList extends AbstractSequentialList implements /** * Adds the specified object at the end of this {@code LinkedList}. - * + * * @param object * the object to add. - * @since Android 1.0 */ public void addLast(E object) { Link oldLast = voidLink.previous; @@ -374,10 +369,9 @@ public class LinkedList extends AbstractSequentialList implements /** * Removes all elements from this {@code LinkedList}, leaving it empty. - * + * * @see List#isEmpty * @see #size - * @since Android 1.0 */ @Override public void clear() { @@ -392,10 +386,9 @@ public class LinkedList extends AbstractSequentialList implements /** * Returns a new {@code LinkedList} with the same elements and size as this * {@code LinkedList}. - * + * * @return a shallow copy of this {@code LinkedList}. * @see java.lang.Cloneable - * @since Android 1.0 */ @SuppressWarnings("unchecked") @Override @@ -415,12 +408,11 @@ public class LinkedList extends AbstractSequentialList implements /** * Searches this {@code LinkedList} for the specified object. - * + * * @param object * the object to search for. * @return {@code true} if {@code object} is an element of this * {@code LinkedList}, {@code false} otherwise - * @since Android 1.0 */ @Override public boolean contains(Object object) { @@ -463,11 +455,10 @@ public class LinkedList extends AbstractSequentialList implements /** * Returns the first element in this {@code LinkedList}. - * + * * @return the first element. * @throws NoSuchElementException * if this {@code LinkedList} is empty. - * @since Android 1.0 */ public E getFirst() { Link first = voidLink.next; @@ -479,11 +470,10 @@ public class LinkedList extends AbstractSequentialList implements /** * Returns the last element in this {@code LinkedList}. - * + * * @return the last element * @throws NoSuchElementException * if this {@code LinkedList} is empty - * @since Android 1.0 */ public E getLast() { Link last = voidLink.previous; @@ -520,12 +510,11 @@ public class LinkedList extends AbstractSequentialList implements /** * Searches this {@code LinkedList} for the specified object and returns the * index of the last occurrence. - * + * * @param object * the object to search for * @return the index of the last occurrence of the object, or -1 if it was * not found. - * @since Android 1.0 */ @Override public int lastIndexOf(Object object) { @@ -555,14 +544,13 @@ public class LinkedList extends AbstractSequentialList implements * Returns a ListIterator on the elements of this {@code LinkedList}. The * elements are iterated in the same order that they occur in the * {@code LinkedList}. The iteration starts at the specified location. - * + * * @param location * the index at which to start the iteration * @return a ListIterator on the elements of this {@code LinkedList} * @throws IndexOutOfBoundsException * if {@code location < 0 || >= size()} * @see ListIterator - * @since Android 1.0 */ @Override public ListIterator listIterator(int location) { @@ -571,13 +559,12 @@ public class LinkedList extends AbstractSequentialList implements /** * Removes the object at the specified location from this {@code LinkedList}. - * + * * @param location * the index of the object to remove * @return the removed object * @throws IndexOutOfBoundsException * if {@code location < 0 || >= size()} - * @since Android 1.0 */ @Override public E remove(int location) { @@ -629,11 +616,10 @@ public class LinkedList extends AbstractSequentialList implements /** * Removes the first object from this {@code LinkedList}. - * + * * @return the removed object. * @throws NoSuchElementException * if this {@code LinkedList} is empty. - * @since Android 1.0 */ public E removeFirst() { Link first = voidLink.next; @@ -650,11 +636,10 @@ public class LinkedList extends AbstractSequentialList implements /** * Removes the last object from this {@code LinkedList}. - * + * * @return the removed object. * @throws NoSuchElementException * if this {@code LinkedList} is empty. - * @since Android 1.0 */ public E removeLast() { Link last = voidLink.previous; @@ -672,7 +657,7 @@ public class LinkedList extends AbstractSequentialList implements /** * Replaces the element at the specified location in this {@code LinkedList} * with the specified object. - * + * * @param location * the index at which to put the specified object. * @param object @@ -684,7 +669,6 @@ public class LinkedList extends AbstractSequentialList implements * if an object cannot be added to this list. * @throws IndexOutOfBoundsException * if {@code location < 0 || >= size()} - * @since Android 1.0 */ @Override public E set(int location, E object) { @@ -708,15 +692,14 @@ public class LinkedList extends AbstractSequentialList implements /** * Returns the number of elements in this {@code LinkedList}. - * + * * @return the number of elements in this {@code LinkedList}. - * @since Android 1.0 */ @Override public int size() { return size; } - + public boolean offer(E o) { add(o); return true; @@ -742,9 +725,8 @@ public class LinkedList extends AbstractSequentialList implements /** * Returns a new array containing all elements contained in this * {@code LinkedList}. - * + * * @return an array of the elements from this {@code LinkedList}. - * @since Android 1.0 */ @Override public Object[] toArray() { @@ -765,14 +747,13 @@ public class LinkedList extends AbstractSequentialList implements * type is created. If the specified array is used and is larger than this * {@code LinkedList}, the array element following the collection elements * is set to null. - * + * * @param contents * the array. * @return an array of the elements from this {@code LinkedList}. * @throws ArrayStoreException * if the type of an element in this {@code LinkedList} cannot * be stored in the type of the specified array. - * @since Android 1.0 */ @Override @SuppressWarnings("unchecked") @@ -784,7 +765,7 @@ public class LinkedList extends AbstractSequentialList implements } Link link = voidLink.next; while (link != voidLink) { - contents[index++] = (T)link.data; + contents[index++] = (T) link.data; link = link.next; } if (index < contents.length) { @@ -810,7 +791,7 @@ public class LinkedList extends AbstractSequentialList implements voidLink = new Link(null, null, null); Link link = voidLink; for (int i = size; --i >= 0;) { - Link nextLink = new Link((E)stream.readObject(), link, null); + Link nextLink = new Link((E) stream.readObject(), link, null); link.next = nextLink; link = nextLink; } diff --git a/libcore/luni/src/main/java/java/util/List.java b/libcore/luni/src/main/java/java/util/List.java index d95b2d598..68e1ad9b6 100644 --- a/libcore/luni/src/main/java/java/util/List.java +++ b/libcore/luni/src/main/java/java/util/List.java @@ -23,8 +23,6 @@ package java.util; * element in the {@code List} has an index. Each element can thus be accessed by its * index, with the first index being zero. Normally, {@code List}s allow duplicate * elements, as compared to Sets, where elements have to be unique. - * - * @since Android 1.0 */ public interface List extends Collection { /** @@ -39,16 +37,15 @@ public interface List extends Collection { * the index at which to insert. * @param object * the object to add. - * @exception UnsupportedOperationException - * when adding to this {@code List} is not supported. - * @exception ClassCastException - * when the class of the object is inappropriate for this + * @throws UnsupportedOperationException + * if adding to this {@code List} is not supported. + * @throws ClassCastException + * if the class of the object is inappropriate for this * {@code List}. - * @exception IllegalArgumentException - * when the object cannot be added to this {@code List}. - * @exception IndexOutOfBoundsException - * when {@code location < 0 || location > size()} - * @since Android 1.0 + * @throws IllegalArgumentException + * if the object cannot be added to this {@code List}. + * @throws IndexOutOfBoundsException + * if {@code location < 0 || location > size()} */ public void add(int location, E object); @@ -58,14 +55,13 @@ public interface List extends Collection { * @param object * the object to add. * @return always true. - * @exception UnsupportedOperationException - * when adding to this {@code List} is not supported. - * @exception ClassCastException - * when the class of the object is inappropriate for this + * @throws UnsupportedOperationException + * if adding to this {@code List} is not supported. + * @throws ClassCastException + * if the class of the object is inappropriate for this * {@code List}. - * @exception IllegalArgumentException - * when the object cannot be added to this {@code List}. - * @since Android 1.0 + * @throws IllegalArgumentException + * if the object cannot be added to this {@code List}. */ public boolean add(E object); @@ -80,16 +76,15 @@ public interface List extends Collection { * the collection of objects to be inserted. * @return true if this {@code List} has been modified through the insertion, false * otherwise (i.e. if the passed collection was empty). - * @exception UnsupportedOperationException - * when adding to this {@code List} is not supported. - * @exception ClassCastException - * when the class of an object is inappropriate for this + * @throws UnsupportedOperationException + * if adding to this {@code List} is not supported. + * @throws ClassCastException + * if the class of an object is inappropriate for this * {@code List}. - * @exception IllegalArgumentException - * when an object cannot be added to this {@code List}. - * @exception IndexOutOfBoundsException - * when {@code location < 0 || > size()} - * @since Android 1.0 + * @throws IllegalArgumentException + * if an object cannot be added to this {@code List}. + * @throws IndexOutOfBoundsException + * if {@code location < 0 || > size()} */ public boolean addAll(int location, Collection collection); @@ -102,25 +97,23 @@ public interface List extends Collection { * the collection of objects. * @return {@code true} if this {@code List} is modified, {@code false} otherwise * (i.e. if the passed collection was empty). - * @exception UnsupportedOperationException - * when adding to this {@code List} is not supported. - * @exception ClassCastException - * when the class of an object is inappropriate for this + * @throws UnsupportedOperationException + * if adding to this {@code List} is not supported. + * @throws ClassCastException + * if the class of an object is inappropriate for this * {@code List}. - * @exception IllegalArgumentException - * when an object cannot be added to this {@code List}. - * @since Android 1.0 + * @throws IllegalArgumentException + * if an object cannot be added to this {@code List}. */ public boolean addAll(Collection collection); /** * Removes all elements from this {@code List}, leaving it empty. * - * @exception UnsupportedOperationException - * when removing from this {@code List} is not supported. + * @throws UnsupportedOperationException + * if removing from this {@code List} is not supported. * @see #isEmpty * @see #size - * @since Android 1.0 */ public void clear(); @@ -131,7 +124,6 @@ public interface List extends Collection { * the object to search for. * @return {@code true} if object is an element of this {@code List}, {@code false} * otherwise - * @since Android 1.0 */ public boolean contains(Object object); @@ -143,7 +135,6 @@ public interface List extends Collection { * the collection of objects * @return {@code true} if all objects in the specified collection are * elements of this {@code List}, {@code false} otherwise. - * @since Android 1.0 */ public boolean containsAll(Collection collection); @@ -158,7 +149,6 @@ public interface List extends Collection { * @return boolean {@code true} if the object is the same as this object, * and {@code false} if it is different from this object. * @see #hashCode - * @since Android 1.0 */ public boolean equals(Object object); @@ -168,9 +158,8 @@ public interface List extends Collection { * @param location * the index of the element to return. * @return the element at the specified location. - * @exception IndexOutOfBoundsException - * when {@code location < 0 || >= size()} - * @since Android 1.0 + * @throws IndexOutOfBoundsException + * if {@code location < 0 || >= size()} */ public E get(int location); @@ -179,7 +168,6 @@ public interface List extends Collection { * element' hashcode and its position in the {@code List} into account. * * @return the hash code of the {@code List}. - * @since Android 1.0 */ public int hashCode(); @@ -191,7 +179,6 @@ public interface List extends Collection { * the object to search for. * @return the index of the first occurrence of the object or -1 if the * object was not found. - * @since Android 1.0 */ public int indexOf(Object object); @@ -201,7 +188,6 @@ public interface List extends Collection { * @return {@code true} if this {@code List} has no elements, {@code false} * otherwise. * @see #size - * @since Android 1.0 */ public boolean isEmpty(); @@ -211,7 +197,6 @@ public interface List extends Collection { * * @return an iterator on the elements of this {@code List}. * @see Iterator - * @since Android 1.0 */ public Iterator iterator(); @@ -223,7 +208,6 @@ public interface List extends Collection { * the object to search for. * @return the index of the last occurrence of the object, or -1 if the * object was not found. - * @since Android 1.0 */ public int lastIndexOf(Object object); @@ -234,7 +218,6 @@ public interface List extends Collection { * @return a {@code List} iterator on the elements of this {@code List} * * @see ListIterator - * @since Android 1.0 */ public ListIterator listIterator(); @@ -246,10 +229,9 @@ public interface List extends Collection { * @param location * the index at which to start the iteration. * @return a list iterator on the elements of this {@code List}. - * @exception IndexOutOfBoundsException - * when {@code location < 0 || location > size()} + * @throws IndexOutOfBoundsException + * if {@code location < 0 || location > size()} * @see ListIterator - * @since Android 1.0 */ public ListIterator listIterator(int location); @@ -259,11 +241,10 @@ public interface List extends Collection { * @param location * the index of the object to remove. * @return the removed object. - * @exception UnsupportedOperationException - * when removing from this {@code List} is not supported. - * @exception IndexOutOfBoundsException - * when {@code location < 0 || >= size()} - * @since Android 1.0 + * @throws UnsupportedOperationException + * if removing from this {@code List} is not supported. + * @throws IndexOutOfBoundsException + * if {@code location < 0 || >= size()} */ public E remove(int location); @@ -274,9 +255,8 @@ public interface List extends Collection { * the object to remove. * @return true if this {@code List} was modified by this operation, false * otherwise. - * @exception UnsupportedOperationException - * when removing from this {@code List} is not supported. - * @since Android 1.0 + * @throws UnsupportedOperationException + * if removing from this {@code List} is not supported. */ public boolean remove(Object object); @@ -287,9 +267,8 @@ public interface List extends Collection { * @param collection * the collection of objects to remove. * @return {@code true} if this {@code List} is modified, {@code false} otherwise. - * @exception UnsupportedOperationException - * when removing from this {@code List} is not supported. - * @since Android 1.0 + * @throws UnsupportedOperationException + * if removing from this {@code List} is not supported. */ public boolean removeAll(Collection collection); @@ -300,9 +279,8 @@ public interface List extends Collection { * @param collection * the collection of objects to retain. * @return {@code true} if this {@code List} is modified, {@code false} otherwise. - * @exception UnsupportedOperationException - * when removing from this {@code List} is not supported. - * @since Android 1.0 + * @throws UnsupportedOperationException + * if removing from this {@code List} is not supported. */ public boolean retainAll(Collection collection); @@ -315,16 +293,15 @@ public interface List extends Collection { * @param object * the object to insert. * @return the previous element at the index. - * @exception UnsupportedOperationException - * when replacing elements in this {@code List} is not supported. - * @exception ClassCastException - * when the class of an object is inappropriate for this + * @throws UnsupportedOperationException + * if replacing elements in this {@code List} is not supported. + * @throws ClassCastException + * if the class of an object is inappropriate for this * {@code List}. - * @exception IllegalArgumentException - * when an object cannot be added to this {@code List}. - * @exception IndexOutOfBoundsException - * when {@code location < 0 || >= size()} - * @since Android 1.0 + * @throws IllegalArgumentException + * if an object cannot be added to this {@code List}. + * @throws IndexOutOfBoundsException + * if {@code location < 0 || >= size()} */ public E set(int location, E object); @@ -332,7 +309,6 @@ public interface List extends Collection { * Returns the number of elements in this {@code List}. * * @return the number of elements in this {@code List}. - * @since Android 1.0 */ public int size(); @@ -346,10 +322,9 @@ public interface List extends Collection { * @param end * the index one past the end of the sublist. * @return a list of a portion of this {@code List}. - * @exception IndexOutOfBoundsException - * when {@code start < 0, start > end} or {@code end > + * @throws IndexOutOfBoundsException + * if {@code start < 0, start > end} or {@code end > * size()} - * @since Android 1.0 */ public List subList(int start, int end); @@ -357,7 +332,6 @@ public interface List extends Collection { * Returns an array containing all elements contained in this {@code List}. * * @return an array of the elements from this {@code List}. - * @since Android 1.0 */ public Object[] toArray(); @@ -371,10 +345,9 @@ public interface List extends Collection { * @param array * the array. * @return an array of the elements from this {@code List}. - * @exception ArrayStoreException - * when the type of an element in this {@code List} cannot be stored + * @throws ArrayStoreException + * if the type of an element in this {@code List} cannot be stored * in the type of the specified array. - * @since Android 1.0 */ public T[] toArray(T[] array); } diff --git a/libcore/luni/src/main/java/java/util/ListIterator.java b/libcore/luni/src/main/java/java/util/ListIterator.java index 6ce94c33a..1c62a2289 100644 --- a/libcore/luni/src/main/java/java/util/ListIterator.java +++ b/libcore/luni/src/main/java/java/util/ListIterator.java @@ -21,8 +21,6 @@ package java.util; /** * An ListIterator is used to sequence over a List of objects. ListIterator can * move backwards or forwards through the list. - * - * @since Android 1.0 */ public interface ListIterator extends Iterator { @@ -38,7 +36,6 @@ public interface ListIterator extends Iterator { * if the class of the object is inappropriate for the list. * @throws IllegalArgumentException * if the object cannot be added to the list. - * @since Android 1.0 */ void add(E object); @@ -47,7 +44,6 @@ public interface ListIterator extends Iterator { * * @return {@code true} if there are more elements, {@code false} otherwise. * @see #next - * @since Android 1.0 */ public boolean hasNext(); @@ -57,7 +53,6 @@ public interface ListIterator extends Iterator { * @return {@code true} if there are previous elements, {@code false} * otherwise. * @see #previous - * @since Android 1.0 */ public boolean hasPrevious(); @@ -68,7 +63,6 @@ public interface ListIterator extends Iterator { * @throws NoSuchElementException * if there are no more elements. * @see #hasNext - * @since Android 1.0 */ public E next(); @@ -80,7 +74,6 @@ public interface ListIterator extends Iterator { * @throws NoSuchElementException * if there are no more elements. * @see #next - * @since Android 1.0 */ public int nextIndex(); @@ -91,7 +84,6 @@ public interface ListIterator extends Iterator { * @throws NoSuchElementException * if there are no previous elements. * @see #hasPrevious - * @since Android 1.0 */ public E previous(); @@ -103,7 +95,6 @@ public interface ListIterator extends Iterator { * @throws NoSuchElementException * if there are no previous elements. * @see #previous - * @since Android 1.0 */ public int previousIndex(); @@ -117,7 +108,6 @@ public interface ListIterator extends Iterator { * if {@code next} or {@code previous} have not been called, or * {@code remove} or {@code add} have already been called after * the last call to {@code next} or {@code previous}. - * @since Android 1.0 */ public void remove(); @@ -137,7 +127,6 @@ public interface ListIterator extends Iterator { * if {@code next} or {@code previous} have not been called, or * {@code remove} or {@code add} have already been called after * the last call to {@code next} or {@code previous}. - * @since Android 1.0 */ void set(E object); } diff --git a/libcore/luni/src/main/java/java/util/ListResourceBundle.java b/libcore/luni/src/main/java/java/util/ListResourceBundle.java index 20d64ac20..6206ee66d 100644 --- a/libcore/luni/src/main/java/java/util/ListResourceBundle.java +++ b/libcore/luni/src/main/java/java/util/ListResourceBundle.java @@ -17,22 +17,19 @@ package java.util; - /** * {@code ListResourceBundle} is the abstract superclass of classes which provide * resources by implementing the {@code getContents()} method to return * the list of resources. - * + * * @see ResourceBundle - * @since Android 1.0 + * @since 1.1 */ public abstract class ListResourceBundle extends ResourceBundle { - Hashtable table; + HashMap table; /** * Constructs a new instance of this class. - * - * @since Android 1.0 */ public ListResourceBundle() { super(); @@ -43,73 +40,81 @@ public abstract class ListResourceBundle extends ResourceBundle { * {@code ListResourceBundle}. Each element in the array is an array of two * elements, the first is the resource key string and the second is the * resource. - * + * * @return a {@code Object} array containing the resources. - * @since Android 1.0 */ protected abstract Object[][] getContents(); /** * Returns the names of the resources contained in this {@code ListResourceBundle}. - * + * * @return an {@code Enumeration} of the resource names. - * @since Android 1.0 */ @Override public Enumeration getKeys() { - if (table == null) { - initializeTable(); - } - if (parent == null) { - return table.keys(); - } - return new Enumeration() { - Enumeration local = table.keys(); + initializeTable(); + if (parent != null) { + return new Enumeration() { + Iterator local = table.keySet().iterator(); - Enumeration pEnum = parent.getKeys(); + Enumeration pEnum = parent.getKeys(); - String nextElement; + String nextElement; - private boolean findNext() { - if (nextElement != null) { - return true; + private boolean findNext() { + if (nextElement != null) { + return true; + } + while (pEnum.hasMoreElements()) { + String next = pEnum.nextElement(); + if (!table.containsKey(next)) { + nextElement = next; + return true; + } + } + return false; } - while (pEnum.hasMoreElements()) { - String next = pEnum.nextElement(); - if (!table.containsKey(next)) { - nextElement = next; + + public boolean hasMoreElements() { + if (local.hasNext()) { return true; } + return findNext(); } - return false; - } - public boolean hasMoreElements() { - if (local.hasMoreElements()) { - return true; + public String nextElement() { + if (local.hasNext()) { + return local.next(); + } + if (findNext()) { + String result = nextElement; + nextElement = null; + return result; + } + // Cause an exception + return pEnum.nextElement(); } - return findNext(); - } + }; + } else { + return new Enumeration() { + Iterator it = table.keySet().iterator(); - public String nextElement() { - if (local.hasMoreElements()) { - return local.nextElement(); + public boolean hasMoreElements() { + return it.hasNext(); } - if (findNext()) { - String result = nextElement; - nextElement = null; - return result; + + public String nextElement() { + return it.next(); } - // Cause an exception - return pEnum.nextElement(); - } - }; + }; + } } @Override public final Object handleGetObject(String key) { - if (table == null) { - initializeTable(); + initializeTable(); + if (key == null) { + throw new NullPointerException(); } return table.get(key); } @@ -117,9 +122,12 @@ public abstract class ListResourceBundle extends ResourceBundle { private synchronized void initializeTable() { if (table == null) { Object[][] contents = getContents(); - table = new Hashtable(contents.length / 3 * 4 + 3); - for (int i = 0; i < contents.length; i++) { - table.put((String)contents[i][0], contents[i][1]); + table = new HashMap(contents.length / 3 * 4 + 3); + for (Object[] content : contents) { + if (content[0] == null || content[1] == null) { + throw new NullPointerException(); + } + table.put((String) content[0], content[1]); } } } diff --git a/libcore/luni/src/main/java/java/util/Locale.java b/libcore/luni/src/main/java/java/util/Locale.java index bbf723993..b1a182181 100644 --- a/libcore/luni/src/main/java/java/util/Locale.java +++ b/libcore/luni/src/main/java/java/util/Locale.java @@ -17,7 +17,8 @@ package java.util; -import java.io.File; +// BEGIN android-changed +// import java.io.File; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; @@ -25,19 +26,14 @@ import java.io.ObjectStreamField; import java.io.Serializable; import java.security.AccessController; import java.security.PrivilegedAction; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; +// import java.util.zip.ZipEntry; +// import java.util.zip.ZipFile; -// BEGIN android-removed -//import org.apache.harmony.luni.internal.locale.Country; -//import org.apache.harmony.luni.internal.locale.Language; -// END android-removed import org.apache.harmony.luni.util.PriviAction; import org.apache.harmony.luni.util.Util; -//BEGIN android-added import com.ibm.icu4jni.util.Resources; -// END android-added +// END android-changed /** * {@code Locale} represents a language/country/variant combination. It is an identifier @@ -45,15 +41,14 @@ import com.ibm.icu4jni.util.Resources; * The language codes are two letter lowercase codes as defined by ISO-639. The * country codes are three letter uppercase codes as defined by ISO-3166. The * variant codes are unspecified. - * + * * @see ResourceBundle - * @since Android 1.0 */ public final class Locale implements Cloneable, Serializable { - + private static final long serialVersionUID = 9149081749638150636L; - private static Locale[] availableLocales; + private static volatile Locale[] availableLocales; // Initialize a default which is used during static // initialization of the default for the platform. @@ -61,200 +56,159 @@ public final class Locale implements Cloneable, Serializable { /** * Locale constant for en_CA. - * - * @since Android 1.0 */ public static final Locale CANADA = new Locale("en", "CA"); //$NON-NLS-1$ //$NON-NLS-2$ /** * Locale constant for fr_CA. - * - * @since Android 1.0 */ public static final Locale CANADA_FRENCH = new Locale("fr", "CA"); //$NON-NLS-1$ //$NON-NLS-2$ /** * Locale constant for zh_CN. - * - * @since Android 1.0 */ public static final Locale CHINA = new Locale("zh", "CN"); //$NON-NLS-1$ //$NON-NLS-2$ /** * Locale constant for zh. - * - * @since Android 1.0 */ - public static final Locale CHINESE = new Locale("zh", ""); //$NON-NLS-1$//$NON-NLS-2$ + public static final Locale CHINESE = new Locale("zh", ""); //$NON-NLS-1$//$NON-NLS-2$ /** * Locale constant for en. - * - * @since Android 1.0 */ public static final Locale ENGLISH = new Locale("en", ""); //$NON-NLS-1$ //$NON-NLS-2$ /** * Locale constant for fr_FR. - * - * @since Android 1.0 */ - public static final Locale FRANCE = new Locale("fr", "FR"); //$NON-NLS-1$//$NON-NLS-2$ + public static final Locale FRANCE = new Locale("fr", "FR"); //$NON-NLS-1$//$NON-NLS-2$ /** * Locale constant for fr. - * - * @since Android 1.0 */ - public static final Locale FRENCH = new Locale("fr", ""); //$NON-NLS-1$//$NON-NLS-2$ + public static final Locale FRENCH = new Locale("fr", ""); //$NON-NLS-1$//$NON-NLS-2$ /** * Locale constant for de. - * - * @since Android 1.0 */ public static final Locale GERMAN = new Locale("de", ""); //$NON-NLS-1$ //$NON-NLS-2$ /** * Locale constant for de_DE. - * - * @since Android 1.0 */ public static final Locale GERMANY = new Locale("de", "DE"); //$NON-NLS-1$ //$NON-NLS-2$ /** * Locale constant for it. - * - * @since Android 1.0 */ public static final Locale ITALIAN = new Locale("it", ""); //$NON-NLS-1$ //$NON-NLS-2$ /** * Locale constant for it_IT. - * - * @since Android 1.0 */ public static final Locale ITALY = new Locale("it", "IT"); //$NON-NLS-1$ //$NON-NLS-2$ /** * Locale constant for ja_JP. - * - * @since Android 1.0 */ - public static final Locale JAPAN = new Locale("ja", "JP"); //$NON-NLS-1$//$NON-NLS-2$ + public static final Locale JAPAN = new Locale("ja", "JP"); //$NON-NLS-1$//$NON-NLS-2$ /** * Locale constant for ja. - * - * @since Android 1.0 */ - public static final Locale JAPANESE = new Locale("ja", ""); //$NON-NLS-1$//$NON-NLS-2$ + public static final Locale JAPANESE = new Locale("ja", ""); //$NON-NLS-1$//$NON-NLS-2$ /** * Locale constant for ko_KR. - * - * @since Android 1.0 */ - public static final Locale KOREA = new Locale("ko", "KR"); //$NON-NLS-1$//$NON-NLS-2$ + public static final Locale KOREA = new Locale("ko", "KR"); //$NON-NLS-1$//$NON-NLS-2$ /** * Locale constant for ko. - * - * @since Android 1.0 */ - public static final Locale KOREAN = new Locale("ko", ""); //$NON-NLS-1$//$NON-NLS-2$ + public static final Locale KOREAN = new Locale("ko", ""); //$NON-NLS-1$//$NON-NLS-2$ /** * Locale constant for zh_CN. - * - * @since Android 1.0 */ - public static final Locale PRC = new Locale("zh", "CN"); //$NON-NLS-1$//$NON-NLS-2$ + public static final Locale PRC = new Locale("zh", "CN"); //$NON-NLS-1$//$NON-NLS-2$ /** * Locale constant for zh_CN. - * - * @since Android 1.0 */ - public static final Locale SIMPLIFIED_CHINESE = new Locale("zh", "CN"); //$NON-NLS-1$//$NON-NLS-2$ + public static final Locale SIMPLIFIED_CHINESE = new Locale("zh", "CN"); //$NON-NLS-1$//$NON-NLS-2$ /** * Locale constant for zh_TW. - * - * @since Android 1.0 */ public static final Locale TAIWAN = new Locale("zh", "TW"); //$NON-NLS-1$ //$NON-NLS-2$ /** * Locale constant for zh_TW. - * - * @since Android 1.0 */ public static final Locale TRADITIONAL_CHINESE = new Locale("zh", "TW"); //$NON-NLS-1$ //$NON-NLS-2$ /** * Locale constant for en_GB. - * - * @since Android 1.0 */ public static final Locale UK = new Locale("en", "GB"); //$NON-NLS-1$ //$NON-NLS-2$ /** * Locale constant for en_US. - * - * @since Android 1.0 */ - public static final Locale US = new Locale("en", "US"); //$NON-NLS-1$//$NON-NLS-2$ + public static final Locale US = new Locale("en", "US"); //$NON-NLS-1$//$NON-NLS-2$ private static final PropertyPermission setLocalePermission = new PropertyPermission( - "user.language", "write"); //$NON-NLS-1$//$NON-NLS-2$ + "user.language", "write"); //$NON-NLS-1$//$NON-NLS-2$ static { String language = AccessController .doPrivileged(new PriviAction("user.language", "en")); //$NON-NLS-1$ //$NON-NLS-2$ // BEGIN android-changed - String region = AccessController - .doPrivileged(new PriviAction("user.region", "US")); //$NON-NLS-1$ //$NON-NLS-2$ + String region = AccessController.doPrivileged(new PriviAction( + "user.region", "US")); //$NON-NLS-1$ //$NON-NLS-2$ // END android-changed - String variant = AccessController - .doPrivileged(new PriviAction("user.variant", "")); //$NON-NLS-1$ //$NON-NLS-2$ + String variant = AccessController.doPrivileged(new PriviAction( + "user.variant", "")); //$NON-NLS-1$ //$NON-NLS-2$ defaultLocale = new Locale(language, region, variant); } - + private transient String countryCode; private transient String languageCode; private transient String variantCode; - /** - * Constructs a default which is used during static initialization of the - * default for the platform. - */ - private Locale() { - languageCode = "en"; //$NON-NLS-1$ - countryCode = "US"; //$NON-NLS-1$ - variantCode = ""; //$NON-NLS-1$ - } + // BEGIN android-removed + // private transient ULocale uLocale; + // END android-removed + + /** + * Constructs a default which is used during static initialization of the + * default for the platform. + */ + private Locale() { + languageCode = "en"; //$NON-NLS-1$ + countryCode = "US"; //$NON-NLS-1$ + variantCode = ""; //$NON-NLS-1$ + } /** * Constructs a new {@code Locale} using the specified language. - * + * * @param language * the language this {@code Locale} represents. - * - * @since Android 1.0 */ public Locale(String language) { - this(language, "", ""); //$NON-NLS-1$//$NON-NLS-2$ + this(language, "", ""); //$NON-NLS-1$//$NON-NLS-2$ } /** * Constructs a new {@code Locale} using the specified language and country codes. - * + * * @param language * the language this {@code Locale} represents. * @param country * the country this {@code Locale} represents. - * @since Android 1.0 */ public Locale(String language, String country) { this(language, country, ""); //$NON-NLS-1$ @@ -263,7 +217,7 @@ public final class Locale implements Cloneable, Serializable { /** * Constructs a new {@code Locale} using the specified language, country, and * variant codes. - * + * * @param language * the language this {@code Locale} represents. * @param country @@ -273,13 +227,22 @@ public final class Locale implements Cloneable, Serializable { * @throws NullPointerException * if {@code language}, {@code country}, or * {@code variant} is {@code null}. - * @since Android 1.0 */ public Locale(String language, String country, String variant) { if (language == null || country == null || variant == null) { throw new NullPointerException(); } + if(language.length() == 0 && country.length() == 0){ + languageCode = ""; + countryCode = ""; + variantCode = variant; + return; + } + // BEGIN android-changed + // this.uLocale = new ULocale(language, country, variant); + // languageCode = uLocale.getLanguage(); languageCode = Util.toASCIILowerCase(language); + // END android-changed // Map new language codes to the obsolete language // codes so the correct resource bundles will be used. if (languageCode.equals("he")) {//$NON-NLS-1$ @@ -291,18 +254,21 @@ public final class Locale implements Cloneable, Serializable { } // countryCode is defined in ASCII character set + // BEGIN android-changed + // countryCode = country.length()!=0?uLocale.getCountry():""; countryCode = Util.toASCIIUpperCase(country); + // END android-changed + // Work around for be compatible with RI variantCode = variant; } /** * Returns a new {@code Locale} with the same language, country and variant codes as * this {@code Locale}. - * + * * @return a shallow copy of this {@code Locale}. * @see java.lang.Cloneable - * @since Android 1.0 */ @Override public Object clone() { @@ -317,13 +283,12 @@ public final class Locale implements Cloneable, Serializable { * Compares the specified object to this {@code Locale} and returns whether they are * equal. The object must be an instance of {@code Locale} and have the same * language, country and variant. - * + * * @param object * the object to compare with this object. * @return {@code true} if the specified object is equal to this {@code Locale}, * {@code false} otherwise. * @see #hashCode - * @since Android 1.0 */ @Override public boolean equals(Object object) { @@ -347,13 +312,8 @@ public final class Locale implements Cloneable, Serializable { // final String classPrefix = prefix.substring(last + 1, length); // Set result = new HashSet(); // StringTokenizer paths = new StringTokenizer(System.getProperty( - // // BEGIN android-removed - // // "org.apache.harmony.boot.class.path", ""), System.getProperty( //$NON-NLS-1$ //$NON-NLS-2$ - // // END android-removed - // // BEGIN android-added - // "java.boot.class.path", ""), System.getProperty( //$NON-NLS-1$ //$NON-NLS-2$ - // // END android-added - // "path.separator", ";")); //$NON-NLS-1$//$NON-NLS-2$ + // "org.apache.harmony.boot.class.path", ""), System.getProperty( //$NON-NLS-1$ //$NON-NLS-2$ + // "path.separator", ";")); //$NON-NLS-1$//$NON-NLS-2$ // while (paths.hasMoreTokens()) { // String nextToken = paths.nextToken(); // File directory = new File(nextToken); @@ -378,12 +338,13 @@ public final class Locale implements Cloneable, Serializable { // String name = list[i]; // if (name.startsWith(classPrefix) // && name.endsWith(".class")) { //$NON-NLS-1$ - // result.add(name.substring(0, + // result + // .add(name.substring(0, // name.length() - 6)); // } // } // } - // + // // } else { // // Handle ZIP/JAR files. // try { @@ -394,25 +355,26 @@ public final class Locale implements Cloneable, Serializable { // String name = e.getName(); // if (name.startsWith(prefix) // && name.endsWith(".class")) {//$NON-NLS-1$ - // result.add(name.substring(last+1, name.length() - 6)); + // result.add(name.substring(last + 1, name + // .length() - 6)); // } // } // zip.close(); // } catch (IOException e) { - // // Empty + // // Empty // } // } // } // } // Locale[] locales = new Locale[result.size()]; // int i = 0; - // for (String name: result) { + // for (String name : result) { // int index = name.indexOf('_'); // int nextIndex = name.indexOf('_', index + 1); // if (nextIndex == -1) { - // locales[i++] = new Locale(name - // .substring(index + 1, name.length()), ""); //$NON-NLS-1$ - // }else{ + // locales[i++] = new Locale(name.substring(index + 1, name + // .length()), ""); //$NON-NLS-1$ + // } else { // String language = name.substring(index + 1, nextIndex); // String variant; // if ((index = name.indexOf('_', nextIndex + 1)) == -1) { @@ -437,7 +399,7 @@ public final class Locale implements Cloneable, Serializable { String s = locales[i]; int first = s.indexOf('_'); int second = s.indexOf('_', first + 1); - + if (first == -1) { // Language only temp.add(new Locale(s)); @@ -454,37 +416,32 @@ public final class Locale implements Cloneable, Serializable { } // END android-added - /** + /** * Gets the list of installed {@code Locale}. At least a {@code Locale} that is equal to * {@code Locale.US} must be contained in this array. - * + * * @return an array of {@code Locale}s. - * @since Android 1.0 - */ - public static Locale[] getAvailableLocales() { + */ + public static Locale[] getAvailableLocales() { + // BEGIN android-changed + // ULocale[] ulocales = ULocale.getAvailableLocales(); + // Locale[] locales = new Locale[ulocales.length]; + // for (int i = 0; i < locales.length; i++) { + // locales[i] = ulocales[i].toLocale(); + // } + // return locales; if (availableLocales == null) { - // BEGIN android-removed - // availableLocales = AccessController - // .doPrivileged(new PrivilegedAction() { - // public Locale[] run() { - // return find("org/apache/harmony/luni/internal/locale/Locale_"); //$NON-NLS-1$ - // } - // }); - // END android-removed - - // BEGIN android-added availableLocales = find(); - // END android-added } return availableLocales.clone(); - } + // END android-changed + } /** * Gets the country code for this {@code Locale} or an empty string of no country * was set. - * + * * @return a country code. - * @since Android 1.0 */ public String getCountry() { return countryCode; @@ -492,9 +449,8 @@ public final class Locale implements Cloneable, Serializable { /** * Gets the default {@code Locale}. - * + * * @return the default {@code Locale}. - * @since Android 1.0 */ public static Locale getDefault() { return defaultLocale; @@ -504,9 +460,8 @@ public final class Locale implements Cloneable, Serializable { * Gets the full country name in the default {@code Locale} for the country code of * this {@code Locale}. If there is no matching country name, the country code is * returned. - * + * * @return a country name. - * @since Android 1.0 */ public final String getDisplayCountry() { return getDisplayCountry(getDefault()); @@ -516,22 +471,21 @@ public final class Locale implements Cloneable, Serializable { * Gets the full country name in the specified {@code Locale} for the country code * of this {@code Locale}. If there is no matching country name, the country code is * returned. - * + * * @param locale * the {@code Locale} for which the display name is retrieved. * @return a country name. - * @since Android 1.0 */ - public String getDisplayCountry(Locale locale) { + public String getDisplayCountry(Locale locale) { + // BEGIN android-changed + // return ULocale.forLocale(this).getDisplayCountry(ULocale.forLocale(locale)); if (countryCode.length() == 0) { return countryCode; } try { // First try the specified locale ResourceBundle bundle = getBundle("Country", locale); //$NON-NLS-1$ - // BEGIN android-changed String result = bundle.getString(this.toString()); - // END android-changed if (result != null) { return result; } @@ -543,15 +497,15 @@ public final class Locale implements Cloneable, Serializable { } catch (MissingResourceException e) { return countryCode; } - } + // END android-changed + } /** * Gets the full language name in the default {@code Locale} for the language code * of this {@code Locale}. If there is no matching language name, the language code * is returned. - * + * * @return a language name. - * @since Android 1.0 */ public final String getDisplayLanguage() { return getDisplayLanguage(getDefault()); @@ -561,22 +515,21 @@ public final class Locale implements Cloneable, Serializable { * Gets the full language name in the specified {@code Locale} for the language code * of this {@code Locale}. If there is no matching language name, the language code * is returned. - * + * * @param locale * the {@code Locale} for which the display name is retrieved. * @return a language name. - * @since Android 1.0 */ - public String getDisplayLanguage(Locale locale) { + public String getDisplayLanguage(Locale locale) { + // BEGIN android-changed + // return ULocale.forLocale(this).getDisplayLanguage(ULocale.forLocale(locale)); if (languageCode.length() == 0) { return languageCode; } try { // First try the specified locale ResourceBundle bundle = getBundle("Language", locale); //$NON-NLS-1$ - // BEGIN android-changed String result = bundle.getString(this.toString()); - // END android-changed if (result != null) { return result; } @@ -588,14 +541,14 @@ public final class Locale implements Cloneable, Serializable { } catch (MissingResourceException e) { return languageCode; } - } + // END android-changed + } /** * Gets the full language, country, and variant names in the default {@code Locale} * for the codes of this {@code Locale}. - * + * * @return a {@code Locale} name. - * @since Android 1.0 */ public final String getDisplayName() { return getDisplayName(getDefault()); @@ -604,15 +557,14 @@ public final class Locale implements Cloneable, Serializable { /** * Gets the full language, country, and variant names in the specified * Locale for the codes of this {@code Locale}. - * + * * @param locale * the {@code Locale} for which the display name is retrieved. * @return a {@code Locale} name. - * @since Android 1.0 */ public String getDisplayName(Locale locale) { int count = 0; - StringBuffer buffer = new StringBuffer(); + StringBuilder buffer = new StringBuilder(); if (languageCode.length() > 0) { buffer.append(getDisplayLanguage(locale)); count++; @@ -643,9 +595,8 @@ public final class Locale implements Cloneable, Serializable { * Gets the full variant name in the default {@code Locale} for the variant code of * this {@code Locale}. If there is no matching variant name, the variant code is * returned. - * + * * @return a variant name. - * @since Android 1.0 */ public final String getDisplayVariant() { return getDisplayVariant(getDefault()); @@ -655,41 +606,17 @@ public final class Locale implements Cloneable, Serializable { * Gets the full variant name in the specified {@code Locale} for the variant code * of this {@code Locale}. If there is no matching variant name, the variant code is * returned. - * + * * @param locale * the {@code Locale} for which the display name is retrieved. * @return a variant name. - * @since Android 1.0 */ - public String getDisplayVariant(Locale locale) { + public String getDisplayVariant(Locale locale) { + // BEGIN android-changed + // return ULocale.forLocale(this).getDisplayVariant(ULocale.forLocale(locale)); if (variantCode.length() == 0) { return variantCode; } -// BEGIN android-removed -// ResourceBundle bundle; -// try { -// bundle = getBundle("Variant", locale); //$NON-NLS-1$ -// } catch (MissingResourceException e) { -// return variantCode.replace('_', ','); -// } -// -// StringBuffer result = new StringBuffer(); -// StringTokenizer tokens = new StringTokenizer(variantCode, "_"); //$NON-NLS-1$ -// while (tokens.hasMoreTokens()) { -// String code, variant = tokens.nextToken(); -// try { -// code = bundle.getString(variant); -// } catch (MissingResourceException e) { -// code = variant; -// } -// result.append(code); -// if (tokens.hasMoreTokens()) { -// result.append(','); -// } -// } -// return result.toString(); -// END android-removed -// BEGIN android-added try { // First try the specified locale ResourceBundle bundle = getBundle("Variant", locale); //$NON-NLS-1$ @@ -705,105 +632,78 @@ public final class Locale implements Cloneable, Serializable { } catch (MissingResourceException e) { return variantCode; } -// END android-added - } + // END android-changed + } /** * Gets the three letter ISO country code which corresponds to the country * code for this {@code Locale}. - * + * * @return a three letter ISO language code. - * @exception MissingResourceException - * when there is no matching three letter ISO country code. - * @since Android 1.0 + * @throws MissingResourceException + * if there is no matching three letter ISO country code. */ - public String getISO3Country() throws MissingResourceException { + public String getISO3Country() throws MissingResourceException { + // BEGIN android-changed + // return ULocale.forLocale(this).getISO3Country(); if (countryCode.length() == 0) { return ""; //$NON-NLS-1$ } ResourceBundle bundle = getBundle("ISO3Countries", this); //$NON-NLS-1$ - // BEGIN android-changed return bundle.getString(this.toString()); // END android-changed - } + } /** * Gets the three letter ISO language code which corresponds to the language * code for this {@code Locale}. - * + * * @return a three letter ISO language code. - * @exception MissingResourceException - * when there is no matching three letter ISO language code. - * @since Android 1.0 + * @throws MissingResourceException + * if there is no matching three letter ISO language code. */ - public String getISO3Language() throws MissingResourceException { + public String getISO3Language() throws MissingResourceException { + // BEGIN android-changed + // return ULocale.forLocale(this).getISO3Language(); if (languageCode.length() == 0) { return ""; //$NON-NLS-1$ } ResourceBundle bundle = getBundle("ISO3Languages", this); //$NON-NLS-1$ - // BEGIN android-changed return bundle.getString(this.toString()); // END android-changed - } + } /** * Gets the list of two letter ISO country codes which can be used as the * country code for a {@code Locale}. - * + * * @return an array of strings. - * @since Android 1.0 */ public static String[] getISOCountries() { - // BEGIN android-removed - // ListResourceBundle bundle = new Country(); - // - // // To initialize the table - // Enumeration keys = bundle.getKeys(); - // int size = bundle.table.size(); - // String[] result = new String[size]; - // int index = 0; - // while (keys.hasMoreElements()) { - // String element = keys.nextElement(); - // result[index++] = element; - // } - // return result; - // END android-removed - - // BEGIN android-added + // BEGIN android-changed + // return ULocale.getISOCountries(); return Resources.getISOCountries(); - // END android-added + // END android-changed } /** * Gets the list of two letter ISO language codes which can be used as the * language code for a {@code Locale}. - * + * * @return an array of strings. - * @since Android 1.0 - */ - public static String[] getISOLanguages() { - // BEGIN android-removed - // ListResourceBundle bundle = new Language(); - // Enumeration keys = bundle.getKeys(); // to initialize the table - // String[] result = new String[bundle.table.size()]; - // int index = 0; - // while (keys.hasMoreElements()) { - // result[index++] = keys.nextElement(); - // } - // return result; - // END android-removed - - // BEGIN android-added + */ + public static String[] getISOLanguages() { + // BEGIN android-changed + // return ULocale.getISOLanguages(); return Resources.getISOLanguages(); - // END android-added - } + // END android-changed + } /** * Gets the language code for this {@code Locale} or the empty string of no language * was set. - * + * * @return a language code. - * @since Android 1.0 */ public String getLanguage() { return languageCode; @@ -812,9 +712,8 @@ public final class Locale implements Cloneable, Serializable { /** * Gets the variant code for this {@code Locale} or an empty {@code String} of no variant * was set. - * + * * @return a variant code. - * @since Android 1.0 */ public String getVariant() { return variantCode; @@ -823,10 +722,9 @@ public final class Locale implements Cloneable, Serializable { /** * Returns an integer hash code for the receiver. Objects which are equal * return the same value for this method. - * + * * @return the receiver's hash. * @see #equals - * @since Android 1.0 */ @Override public synchronized int hashCode() { @@ -836,13 +734,12 @@ public final class Locale implements Cloneable, Serializable { /** * Sets the default {@code Locale} to the specified {@code Locale}. - * + * * @param locale * the new default {@code Locale}. - * @exception SecurityException + * @throws SecurityException * if there is a {@code SecurityManager} in place which does not allow this * operation. - * @since Android 1.0 */ public synchronized static void setDefault(Locale locale) { if (locale != null) { @@ -864,11 +761,10 @@ public final class Locale implements Cloneable, Serializable { * between the language and the variant. the variant alone canot be defined * without a language and/or a country (in this case this method would * return the empty string). - * + * * Examples: "en", "en_US", "_US", "en__POSIX", "en_US_POSIX" - * + * * @return the string representation of this {@code Locale}. - * @since Android 1.0 */ @Override public final String toString() { @@ -878,7 +774,7 @@ public final class Locale implements Cloneable, Serializable { result.append('_'); result.append(countryCode); } - if (variantCode.length() > 0 && result.length() > 0 ) { + if (variantCode.length() > 0 && result.length() > 0) { if (0 == countryCode.length()) { result.append("__"); //$NON-NLS-1$ } else { @@ -889,14 +785,16 @@ public final class Locale implements Cloneable, Serializable { return result.toString(); } + // BEGIN android-added static ResourceBundle getBundle(final String clName, final Locale locale) { return AccessController.doPrivileged(new PrivilegedAction() { - public ResourceBundle run() { - return ResourceBundle.getBundle("org.apache.harmony.luni.internal.locale." //$NON-NLS-1$ - + clName, locale); - } - }); + public ResourceBundle run() { + return ResourceBundle.getBundle("org.apache.harmony.luni.internal.locale." //$NON-NLS-1$ + + clName, locale); + } + }); } + // END android-added private static final ObjectStreamField[] serialPersistentFields = { new ObjectStreamField("country", String.class), //$NON-NLS-1$ @@ -916,9 +814,8 @@ public final class Locale implements Cloneable, Serializable { private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { ObjectInputStream.GetField fields = stream.readFields(); - countryCode = (String) fields.get("country", ""); //$NON-NLS-1$//$NON-NLS-2$ - languageCode = (String) fields.get("language", ""); //$NON-NLS-1$//$NON-NLS-2$ - variantCode = (String) fields.get("variant", ""); //$NON-NLS-1$//$NON-NLS-2$ + countryCode = (String) fields.get("country", ""); //$NON-NLS-1$//$NON-NLS-2$ + languageCode = (String) fields.get("language", ""); //$NON-NLS-1$//$NON-NLS-2$ + variantCode = (String) fields.get("variant", ""); //$NON-NLS-1$//$NON-NLS-2$ } } - diff --git a/libcore/luni/src/main/java/java/util/Map.java b/libcore/luni/src/main/java/java/util/Map.java index d8b1e62fb..7509d1acf 100644 --- a/libcore/luni/src/main/java/java/util/Map.java +++ b/libcore/luni/src/main/java/java/util/Map.java @@ -23,19 +23,15 @@ package java.util; * in which each key is mapped to a single value. The class of the objects * used as keys is declared when the {@code Map} is declared, as is the * class of the corresponding values. - *

          A {@code Map} provides helper methods to iterate through all of the + *

          + * A {@code Map} provides helper methods to iterate through all of the * keys contained in it, as well as various methods to access and update * the key/value pairs. - *

          - * - * @since Android 1.0 */ public interface Map { /** * {@code Map.Entry} is a key/value mapping contained in a {@code Map}. - * - * @since Android 1.0 */ public static interface Entry { /** @@ -48,7 +44,6 @@ public interface Map { * @return {@code true} if the specified {@code Object} is equal to this * {@code Map.Entry}, {@code false} otherwise. * @see #hashCode() - * @since Android 1.0 */ public boolean equals(Object object); @@ -56,7 +51,6 @@ public interface Map { * Returns the key. * * @return the key - * @since Android 1.0 */ public K getKey(); @@ -64,7 +58,6 @@ public interface Map { * Returns the value. * * @return the value - * @since Android 1.0 */ public V getValue(); @@ -74,7 +67,6 @@ public interface Map { * * @return the receiver's hash code. * @see #equals(Object) - * @since Android 1.0 */ public int hashCode(); @@ -85,7 +77,6 @@ public interface Map { * @param object * the new value to set. * @return object the replaced value of this entry. - * @since Android 1.0 */ public V setValue(V object); }; @@ -97,7 +88,6 @@ public interface Map { * if removing elements from this {@code Map} is not supported. * @see #isEmpty() * @see #size() - * @since Android 1.0 */ public void clear(); @@ -108,7 +98,6 @@ public interface Map { * the key to search for. * @return {@code true} if this map contains the specified key, * {@code false} otherwise. - * @since Android 1.0 */ public boolean containsKey(Object key); @@ -119,7 +108,6 @@ public interface Map { * the value to search for. * @return {@code true} if this map contains the specified value, * {@code false} otherwise. - * @since Android 1.0 */ public boolean containsValue(Object value); @@ -129,7 +117,6 @@ public interface Map { * changes in one will be reflected in the other. * * @return a set of the mappings - * @since Android 1.0 */ public Set> entrySet(); @@ -143,7 +130,6 @@ public interface Map { * {@code false} if it is different from this {@code Object}. * @see #hashCode() * @see #entrySet() - * @since Android 1.0 */ public boolean equals(Object object); @@ -154,7 +140,6 @@ public interface Map { * the key. * @return the value of the mapping with the specified key, or {@code null} * if no mapping for the specified key is found. - * @since Android 1.0 */ public V get(Object key); @@ -164,7 +149,6 @@ public interface Map { * * @return the receiver's hash. * @see #equals(Object) - * @since Android 1.0 */ public int hashCode(); @@ -174,7 +158,6 @@ public interface Map { * @return {@code true} if this map has no elements, {@code false} * otherwise. * @see #size() - * @since Android 1.0 */ public boolean isEmpty(); @@ -184,7 +167,6 @@ public interface Map { * support adding. * * @return a set of the keys. - * @since Android 1.0 */ public Set keySet(); @@ -207,7 +189,6 @@ public interface Map { * @throws NullPointerException * if the key or value is {@code null} and this {@code Map} does * not support {@code null} keys or values. - * @since Android 1.0 */ public V put(K key, V value); @@ -226,7 +207,6 @@ public interface Map { * @throws NullPointerException * if a key or value is {@code null} and this {@code Map} does not * support {@code null} keys or values. - * @since Android 1.0 */ public void putAll(Map map); @@ -239,7 +219,6 @@ public interface Map { * for the specified key was found. * @throws UnsupportedOperationException * if removing from this {@code Map} is not supported. - * @since Android 1.0 */ public V remove(Object key); @@ -247,7 +226,6 @@ public interface Map { * Returns the number of mappings in this {@code Map}. * * @return the number of mappings in this {@code Map}. - * @since Android 1.0 */ public int size(); @@ -263,16 +241,13 @@ public interface Map { * "wrapper object" over the iterator of this {@code Map}'s {@link #entrySet()}. The {@link AbstractCollection#size} method * wraps this {@code Map}'s {@link #size} method and the {@link AbstractCollection#contains} method wraps this {@code Map}'s * {@link #containsValue} method. - *

          *

          * The collection is created when this method is called at first time and * returned in response to all subsequent calls. This method may return * different Collection when multiple calls to this method, since it has no * synchronization performed. - *

          * * @return a collection of the values contained in this map. - * @since Android 1.0 */ public Collection values(); } diff --git a/libcore/luni/src/main/java/java/util/MapEntry.java b/libcore/luni/src/main/java/java/util/MapEntry.java index a63f2fd05..6a5bf0f1a 100644 --- a/libcore/luni/src/main/java/java/util/MapEntry.java +++ b/libcore/luni/src/main/java/java/util/MapEntry.java @@ -17,17 +17,16 @@ package java.util; - /** * MapEntry is an internal class which provides an implementation of Map.Entry. */ -class MapEntry implements Map.Entry, Cloneable { - +class MapEntry implements Map.Entry, Cloneable { + K key; V value; - interface Type { - RT get(MapEntry entry); + interface Type { + RT get(MapEntry entry); } MapEntry(K theKey) { @@ -82,7 +81,7 @@ class MapEntry implements Map.Entry, Cloneable { value = object; return result; } - + @Override public String toString() { return key + "=" + value; diff --git a/libcore/luni/src/main/java/java/util/MissingFormatArgumentException.java b/libcore/luni/src/main/java/java/util/MissingFormatArgumentException.java index bcd240c23..dfa0c73cb 100644 --- a/libcore/luni/src/main/java/java/util/MissingFormatArgumentException.java +++ b/libcore/luni/src/main/java/java/util/MissingFormatArgumentException.java @@ -24,7 +24,6 @@ import org.apache.harmony.luni.util.Msg; * that refers to a missing argument. * * @see java.lang.RuntimeException - * @since Android 1.0 */ public class MissingFormatArgumentException extends IllegalFormatException { private static final long serialVersionUID = 19190115L; diff --git a/libcore/luni/src/main/java/java/util/MissingFormatWidthException.java b/libcore/luni/src/main/java/java/util/MissingFormatWidthException.java index 19af7d755..8b4e1f8af 100644 --- a/libcore/luni/src/main/java/java/util/MissingFormatWidthException.java +++ b/libcore/luni/src/main/java/java/util/MissingFormatWidthException.java @@ -21,7 +21,6 @@ package java.util; * missing but is required. * * @see java.lang.RuntimeException - * @since Android 1.0 */ public class MissingFormatWidthException extends IllegalFormatException { private static final long serialVersionUID = 15560123L; diff --git a/libcore/luni/src/main/java/java/util/MissingResourceException.java b/libcore/luni/src/main/java/java/util/MissingResourceException.java index c3a7efead..9a0cbbb8f 100644 --- a/libcore/luni/src/main/java/java/util/MissingResourceException.java +++ b/libcore/luni/src/main/java/java/util/MissingResourceException.java @@ -25,7 +25,6 @@ package java.util; * * @see ResourceBundle * @see java.lang.RuntimeException - * @since Android 1.0 */ public class MissingResourceException extends RuntimeException { diff --git a/libcore/luni/src/main/java/java/util/Observable.java b/libcore/luni/src/main/java/java/util/Observable.java index a4c0333ba..5ed4563a5 100644 --- a/libcore/luni/src/main/java/java/util/Observable.java +++ b/libcore/luni/src/main/java/java/util/Observable.java @@ -17,7 +17,6 @@ package java.util; - /** * Observable is used to notify a group of Observer objects when a change * occurs. On creation, the set of observers is empty. After a change occurred, @@ -26,21 +25,17 @@ package java.util; * Observers. The order of invocation is not specified. This implementation will * call the Observers in the order they registered. Subclasses are completely * free in what order they call the update methods. - * + * * @see Observer - * - * @since Android 1.0 */ public class Observable { - - Vector observers = new Vector(); + + List observers = new ArrayList(); boolean changed = false; /** * Constructs a new {@code Observable} object. - * - * @since Android 1.0 */ public Observable() { super(); @@ -49,68 +44,62 @@ public class Observable { /** * Adds the specified observer to the list of observers. If it is already * registered, it is not added a second time. - * + * * @param observer * the Observer to add. - * @since Android 1.0 */ - public synchronized void addObserver(Observer observer) { + public void addObserver(Observer observer) { if (observer == null) { throw new NullPointerException(); } - if (!observers.contains(observer)) - observers.addElement(observer); + synchronized (this) { + if (!observers.contains(observer)) + observers.add(observer); + } } /** * Clears the changed flag for this {@code Observable}. After calling * {@code clearChanged()}, {@code hasChanged()} will return {@code false}. - * - * @since Android 1.0 */ - protected synchronized void clearChanged() { + protected void clearChanged() { changed = false; } /** * Returns the number of observers registered to this {@code Observable}. - * + * * @return the number of observers. - * @since Android 1.0 */ - public synchronized int countObservers() { + public int countObservers() { return observers.size(); } /** * Removes the specified observer from the list of observers. Passing null * won't do anything. - * + * * @param observer * the observer to remove. - * @since Android 1.0 */ public synchronized void deleteObserver(Observer observer) { - observers.removeElement(observer); + observers.remove(observer); } /** * Removes all observers from the list of observers. - * - * @since Android 1.0 */ public synchronized void deleteObservers() { - observers.setSize(0); + observers.clear(); } /** * Returns the changed flag for this {@code Observable}. - * + * * @return {@code true} when the changed flag for this {@code Observable} is * set, {@code false} otherwise. - * @since Android 1.0 */ - public synchronized boolean hasChanged() { + public boolean hasChanged() { return changed; } @@ -120,9 +109,6 @@ public class Observable { * argument. Afterwards, calls {@code clearChanged()}. *

          * Equivalent to calling {@code notifyObservers(null)}. - *

          - * - * @since Android 1.0 */ public void notifyObservers() { notifyObservers(null); @@ -132,31 +118,34 @@ public class Observable { * If {@code hasChanged()} returns {@code true}, calls the {@code update()} * method for every Observer in the list of observers using the specified * argument. Afterwards calls {@code clearChanged()}. - * + * * @param data * the argument passed to {@code update()}. - * @since Android 1.0 */ @SuppressWarnings("unchecked") public void notifyObservers(Object data) { - if (hasChanged()) { - // Must clone the vector in case deleteObserver is called - Vector clone = (Vector)observers.clone(); - int size = clone.size(); - for (int i = 0; i < size; i++) { - clone.elementAt(i).update(this, data); + int size = 0; + Observer[] arrays = null; + synchronized (this) { + if (hasChanged()) { + clearChanged(); + size = observers.size(); + arrays = new Observer[size]; + observers.toArray(arrays); + } + } + if (arrays != null) { + for (Observer observer : arrays) { + observer.update(this, data); } - clearChanged(); } } /** * Sets the changed flag for this {@code Observable}. After calling * {@code setChanged()}, {@code hasChanged()} will return {@code true}. - * - * @since Android 1.0 */ - protected synchronized void setChanged() { + protected void setChanged() { changed = true; } } diff --git a/libcore/luni/src/main/java/java/util/Observer.java b/libcore/luni/src/main/java/java/util/Observer.java index 2c8417b2b..c398e1bbe 100644 --- a/libcore/luni/src/main/java/java/util/Observer.java +++ b/libcore/luni/src/main/java/java/util/Observer.java @@ -23,7 +23,6 @@ package java.util; * receive notification of updates on an {@code Observable} object. * * @see Observable - * @since Android 1.0 */ public interface Observer { @@ -36,7 +35,6 @@ public interface Observer { * the {@link Observable} object. * @param data * the data passed to {@link Observable#notifyObservers(Object)}. - * @since Android 1.0 */ void update(Observable observable, Object data); } diff --git a/libcore/luni/src/main/java/java/util/PriorityQueue.java b/libcore/luni/src/main/java/java/util/PriorityQueue.java index 544c5387a..c17e7f9f7 100644 --- a/libcore/luni/src/main/java/java/util/PriorityQueue.java +++ b/libcore/luni/src/main/java/java/util/PriorityQueue.java @@ -31,8 +31,6 @@ import java.io.Serializable; *

          * A PriorityQueue is not synchronized. If multiple threads will have to access * it concurrently, use the {@link java.util.concurrent.PriorityBlockingQueue}. - * - * @since Android 1.0 */ public class PriorityQueue extends AbstractQueue implements Serializable { @@ -53,8 +51,6 @@ public class PriorityQueue extends AbstractQueue implements Serializable { /** * Constructs a priority queue with an initial capacity of 11 and natural * ordering. - * - * @since Android 1.0 */ public PriorityQueue() { this(DEFAULT_CAPACITY); @@ -68,7 +64,6 @@ public class PriorityQueue extends AbstractQueue implements Serializable { * the specified capacity. * @throws IllegalArgumentException * if the initialCapacity is less than 1. - * @since Android 1.0 */ public PriorityQueue(int initialCapacity) { this(initialCapacity, null); @@ -84,7 +79,6 @@ public class PriorityQueue extends AbstractQueue implements Serializable { * will be used. * @throws IllegalArgumentException * if the initialCapacity is less than 1. - * @since Android 1.0 */ public PriorityQueue(int initialCapacity, Comparator comparator) { if (initialCapacity < 1) { @@ -107,7 +101,6 @@ public class PriorityQueue extends AbstractQueue implements Serializable { * if any of the elements in the collection are not comparable. * @throws NullPointerException * if any of the elements in the collection are null. - * @since Android 1.0 */ public PriorityQueue(Collection c) { if (c instanceof PriorityQueue) { @@ -129,7 +122,6 @@ public class PriorityQueue extends AbstractQueue implements Serializable { * @param c * the priority queue whose elements will be added to the * priority queue to be constructed. - * @since Android 1.0 */ public PriorityQueue(PriorityQueue c) { getFromPriorityQueue(c); @@ -144,7 +136,6 @@ public class PriorityQueue extends AbstractQueue implements Serializable { * @param c * the sorted set whose elements will be added to the priority * queue to be constructed. - * @since Android 1.0 */ public PriorityQueue(SortedSet c) { getFromSortedSet(c); @@ -155,7 +146,6 @@ public class PriorityQueue extends AbstractQueue implements Serializable { * in any specified ordering. * * @return the iterator of the priority queue. - * @since Android 1.0 */ @Override public Iterator iterator() { @@ -167,7 +157,6 @@ public class PriorityQueue extends AbstractQueue implements Serializable { * than the Integer.MAX, then it returns Integer.MAX. * * @return the size of the priority queue. - * @since Android 1.0 */ @Override public int size() { @@ -176,8 +165,6 @@ public class PriorityQueue extends AbstractQueue implements Serializable { /** * Removes all the elements of the priority queue. - * - * @since Android 1.0 */ @Override public void clear() { @@ -194,7 +181,8 @@ public class PriorityQueue extends AbstractQueue implements Serializable { * @throws ClassCastException * if the element cannot be compared with the elements in the * priority queue using the ordering of the priority queue. - * @since Android 1.0 + * @throws NullPointerException + * if {@code o} is {@code null}. */ public boolean offer(E o) { if (null == o) { @@ -210,7 +198,6 @@ public class PriorityQueue extends AbstractQueue implements Serializable { * Gets and removes the head of the queue. * * @return the head of the queue or null if the queue is empty. - * @since Android 1.0 */ public E poll() { if (isEmpty()) { @@ -225,7 +212,6 @@ public class PriorityQueue extends AbstractQueue implements Serializable { * Gets but does not remove the head of the queue. * * @return the head of the queue or null if the queue is empty. - * @since Android 1.0 */ public E peek() { if (isEmpty()) { @@ -239,7 +225,6 @@ public class PriorityQueue extends AbstractQueue implements Serializable { * * @return the comparator of the priority queue or null if the natural * ordering is used. - * @since Android 1.0 */ public Comparator comparator() { return comparator; @@ -252,7 +237,6 @@ public class PriorityQueue extends AbstractQueue implements Serializable { * the object to be removed. * @return true if the object was in the priority queue, false if the object * was not in the priority queue. - * @since Android 1.0 */ @Override @SuppressWarnings("unchecked") @@ -282,7 +266,8 @@ public class PriorityQueue extends AbstractQueue implements Serializable { * @throws ClassCastException * if the element cannot be compared with the elements in the * priority queue using the ordering of the priority queue. - * @since Android 1.0 + * @throws NullPointerException + * if {@code o} is {@code null}. */ @Override public boolean add(E o) { diff --git a/libcore/luni/src/main/java/java/util/Properties.java b/libcore/luni/src/main/java/java/util/Properties.java index 15b74eef3..b0f3b9dac 100644 --- a/libcore/luni/src/main/java/java/util/Properties.java +++ b/libcore/luni/src/main/java/java/util/Properties.java @@ -17,9 +17,9 @@ package java.util; - import java.io.IOException; import java.io.InputStream; +import java.io.BufferedInputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintStream; @@ -48,45 +48,37 @@ import org.w3c.dom.Node; import org.w3c.dom.Text; // END android-added -// BEGIN android-added -// copied from newer version of Haromny import org.apache.harmony.luni.internal.nls.Messages; -// END android-added import org.apache.harmony.luni.util.PriviAction; /** - * A {@code Properties} object is a {@code Hashtable} where the keys and values - * must be {@code String}s. Each property can have a default + * A {@code Properties} object is a {@code Hashtable} where the keys and values + * must be {@code String}s. Each property can have a default * {@code Properties} list which specifies the default * values to be used when a given key is not found in this {@code Properties} * instance. - * + * * @see Hashtable * @see java.lang.System#getProperties - * @since Android 1.0 */ -public class Properties extends Hashtable { - +public class Properties extends Hashtable { + private static final long serialVersionUID = 4112578634029874840L; private transient DocumentBuilder builder = null; - private static final String PROP_DTD_NAME - = "http://java.sun.com/dtd/properties.dtd"; + private static final String PROP_DTD_NAME = "http://java.sun.com/dtd/properties.dtd"; - private static final String PROP_DTD - = "" + private static final String PROP_DTD = "" + " " + " " + " " + " " + " "; - + /** * The default values for keys not found in this {@code Properties} * instance. - * - * @since Android 1.0 */ protected Properties defaults; @@ -95,8 +87,6 @@ public class Properties extends Hashtable { /** * Constructs a new {@code Properties} object. - * - * @since Android 1.0 */ public Properties() { super(); @@ -105,10 +95,9 @@ public class Properties extends Hashtable { /** * Constructs a new {@code Properties} object using the specified default * {@code Properties}. - * + * * @param properties * the default {@code Properties}. - * @since Android 1.0 */ public Properties(Properties properties) { defaults = properties; @@ -158,11 +147,10 @@ public class Properties extends Hashtable { * Searches for the property with the specified name. If the property is not * found, the default {@code Properties} are checked. If the property is not * found in the default {@code Properties}, {@code null} is returned. - * + * * @param name * the name of the property to find. * @return the named property value, or {@code null} if it can't be found. - * @since Android 1.0 */ public String getProperty(String name) { Object result = super.get(name); @@ -178,13 +166,12 @@ public class Properties extends Hashtable { * found, it looks in the default {@code Properties}. If the property is not * found in the default {@code Properties}, it returns the specified * default. - * + * * @param name * the name of the property to find. * @param defaultValue * the default value. * @return the named property value. - * @since Android 1.0 */ public String getProperty(String name, String defaultValue) { Object result = super.get(name); @@ -202,17 +189,16 @@ public class Properties extends Hashtable { * Lists the mappings in this {@code Properties} to the specified * {@code PrintStream} in a * human readable form. - * + * * @param out * the {@code PrintStream} to write the content to in human readable * form. - * @since Android 1.0 */ public void list(PrintStream out) { if (out == null) { throw new NullPointerException(); } - StringBuffer buffer = new StringBuffer(80); + StringBuilder buffer = new StringBuilder(80); Enumeration keys = propertyNames(); while (keys.hasMoreElements()) { String key = (String) keys.nextElement(); @@ -239,17 +225,16 @@ public class Properties extends Hashtable { * Lists the mappings in this {@code Properties} to the specified * {@code PrintWriter} in a * human readable form. - * + * * @param writer * the {@code PrintWriter} to write the content to in human * readable form. - * @since Android 1.0 */ public void list(PrintWriter writer) { if (writer == null) { throw new NullPointerException(); } - StringBuffer buffer = new StringBuffer(80); + StringBuilder buffer = new StringBuilder(80); Enumeration keys = propertyNames(); while (keys.hasMoreElements()) { String key = (String) keys.nextElement(); @@ -293,29 +278,43 @@ public class Properties extends Hashtable { *

        • Following escape sequences are recognized: "\ ", "\\", "\r", "\n", * "\!", "\#", "\t", "\b", "\f", and "\uXXXX" (unicode character).
        • * - * + * * @param in * the {@code InputStream}. * @throws IOException * if error occurs during reading from the {@code InputStream}. - * @since Android 1.0 */ + @SuppressWarnings("fallthrough") public synchronized void load(InputStream in) throws IOException { + if (in == null) { + throw new NullPointerException(); + } int mode = NONE, unicode = 0, count = 0; char nextChar, buf[] = new char[40]; - int offset = 0, keyLength = -1; + int offset = 0, keyLength = -1, intVal; boolean firstChar = true; - byte[] inbuf = new byte[256]; - int inbufCount = 0, inbufPos = 0; + + // BEGIN android-changed + BufferedInputStream bis = new BufferedInputStream(in, 8192); + // END android-changed while (true) { - if (inbufPos == inbufCount) { - if ((inbufCount = in.read(inbuf)) == -1) { - break; + intVal = bis.read(); + if (intVal == -1) { + // if mode is UNICODE but has less than 4 hex digits, should + // throw an IllegalArgumentException + // luni.08=Invalid Unicode sequence: expected format \\uxxxx + if (mode == UNICODE && count < 4) { + throw new IllegalArgumentException(Messages.getString("luni.08")); //$NON-NLS-1$ + } + // if mode is SLASH and no data is read, should append '\u0000' + // to buf + if (mode == SLASH) { + buf[offset++] = '\u0000'; } - inbufPos = 0; + break; } - nextChar = (char) (inbuf[inbufPos++] & 0xff); + nextChar = (char) (intVal & 0xff); if (offset == buf.length) { char[] newBuf = new char[buf.length * 2]; @@ -324,8 +323,6 @@ public class Properties extends Hashtable { } if (mode == UNICODE) { int digit = Character.digit(nextChar, 16); - // BEGIN android-changed - // copied from newer version of Harmony if (digit >= 0) { unicode = (unicode << 4) + digit; if (++count < 4) { @@ -333,9 +330,8 @@ public class Properties extends Hashtable { } } else if (count <= 4) { // luni.09=Invalid Unicode sequence: illegal character - throw new IllegalArgumentException(Messages.getString("luni.09")); + throw new IllegalArgumentException(Messages.getString("luni.09")); //$NON-NLS-1$ } - // END android-changed mode = NONE; buf[offset++] = (char) unicode; if (nextChar != '\n') { @@ -377,16 +373,12 @@ public class Properties extends Hashtable { case '!': if (firstChar) { while (true) { - if (inbufPos == inbufCount) { - if ((inbufCount = in.read(inbuf)) == -1) { - inbufPos = -1; - break; - } - inbufPos = 0; + intVal = bis.read(); + if (intVal == -1) { + break; } - nextChar = (char) inbuf[inbufPos++]; // & 0xff - // not - // required + // & 0xff not required + nextChar = (char) intVal; if (nextChar == '\r' || nextChar == '\n') { break; } @@ -399,11 +391,11 @@ public class Properties extends Hashtable { mode = IGNORE; // Ignore whitespace on the next line continue; } - // fall into the next case + // fall into the next case case '\r': mode = NONE; firstChar = true; - if (offset > 0) { + if (offset > 0 || (offset == 0 && keyLength == 0)) { if (keyLength == -1) { keyLength = offset; } @@ -453,13 +445,9 @@ public class Properties extends Hashtable { } buf[offset++] = nextChar; } - // BEGIN android-added - // copied from a newer version of Harmony - if (mode == UNICODE && count <= 4) { - // luni.08=Invalid Unicode sequence: expected format \\uxxxx - throw new IllegalArgumentException(Messages.getString("luni.08")); + if (keyLength == -1 && offset > 0) { + keyLength = offset; } - // END android-added if (keyLength >= 0) { String temp = new String(buf, 0, offset); put(temp.substring(0, keyLength), temp.substring(keyLength)); @@ -469,17 +457,18 @@ public class Properties extends Hashtable { /** * Returns all of the property names that this {@code Properties} object * contains. - * + * * @return an {@code Enumeration} containing the names of all properties * that this {@code Properties} object contains. - * @since Android 1.0 */ public Enumeration propertyNames() { if (defaults == null) { return keys(); } - Hashtable set = new Hashtable(defaults.size() + size()); + Hashtable set = new Hashtable(defaults + .size() + + size()); Enumeration keys = defaults.propertyNames(); while (keys.hasMoreElements()) { set.put(keys.nextElement(), set); @@ -496,15 +485,14 @@ public class Properties extends Hashtable { * OutputStream}, putting the specified comment at the beginning. The output * from this method is suitable for being read by the * {@link #load(InputStream)} method. - * + * * @param out the {@code OutputStream} to write to. * @param comment the comment to add at the beginning. - * @exception ClassCastException when the key or value of a mapping is not a + * @throws ClassCastException if the key or value of a mapping is not a * String. * @deprecated This method ignores any {@code IOException} thrown while * writing -- use {@link #store} instead for better exception * handling. - * @since Android 1.0 */ @Deprecated public void save(OutputStream out, String comment) { @@ -517,13 +505,12 @@ public class Properties extends Hashtable { /** * Maps the specified key to the specified value. If the key already exists, * the old value is replaced. The key and value cannot be {@code null}. - * + * * @param name * the key. * @param value * the value. * @return the old value mapped to the key, or {@code null}. - * @since Android 1.0 */ public Object setProperty(String name, String value) { return put(name, value); @@ -536,14 +523,13 @@ public class Properties extends Hashtable { * OutputStream}, putting the specified comment at the beginning. The output * from this method is suitable for being read by the * {@link #load(InputStream)} method. - * + * * @param out the {@code OutputStream} to write to. * @param comment the comment to put at the beginning. * @throws IOException if an error occurs during the write to the {@code * OutputStream}. - * @exception ClassCastException when the key or value of a mapping is not a + * @throws ClassCastException if the key or value of a mapping is not a * {@code String}. - * @since Android 1.0 */ public synchronized void store(OutputStream out, String comment) throws IOException { @@ -557,11 +543,11 @@ public class Properties extends Hashtable { if (comment != null) { writer.write("#"); //$NON-NLS-1$ writer.write(comment); - writer.write(lineSeparator); + writer.write(lineSeparator); } writer.write("#"); //$NON-NLS-1$ writer.write(new Date().toString()); - writer.write(lineSeparator); + writer.write(lineSeparator); for (Map.Entry entry : entrySet()) { String key = (String) entry.getKey(); @@ -579,39 +565,38 @@ public class Properties extends Hashtable { * Loads the properties from an {@code InputStream} containing the * properties in XML form. The XML document must begin with (and conform to) * following DOCTYPE: - * + * *
                * <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
                * 
          - * + * * Also the content of the XML data must satisfy the DTD but the xml is not * validated against it. The DTD is not loaded from the SYSTEM ID. After * this method returns the InputStream is not closed. - * + * * @param in the InputStream containing the XML document. * @throws IOException in case an error occurs during a read operation. * @throws InvalidPropertiesFormatException if the XML data is not a valid * properties file. - * @since Android 1.0 */ - public synchronized void loadFromXML(InputStream in) - throws IOException, InvalidPropertiesFormatException { + public synchronized void loadFromXML(InputStream in) throws IOException, + InvalidPropertiesFormatException { if (in == null) { throw new NullPointerException(); } - + if (builder == null) { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); // BEGIN android-removed // factory.setValidating(true); // END android-removed - + try { builder = factory.newDocumentBuilder(); } catch (ParserConfigurationException e) { throw new Error(e); } - + builder.setErrorHandler(new ErrorHandler() { public void warning(SAXParseException e) throws SAXException { throw e; @@ -625,30 +610,30 @@ public class Properties extends Hashtable { throw e; } }); - + builder.setEntityResolver(new EntityResolver() { - public InputSource resolveEntity(String publicId, String systemId) - throws SAXException, IOException { + public InputSource resolveEntity(String publicId, + String systemId) throws SAXException, IOException { if (systemId.equals(PROP_DTD_NAME)) { InputSource result = new InputSource(new StringReader( PROP_DTD)); result.setSystemId(PROP_DTD_NAME); return result; } - throw new SAXException( - "Invalid DOCTYPE declaration: " + systemId); + throw new SAXException("Invalid DOCTYPE declaration: " + + systemId); } }); } - + try { Document doc = builder.parse(in); - NodeList entries = doc.getElementsByTagName("entry"); + NodeList entries = doc.getElementsByTagName("entry"); if (entries == null) { return; } int entriesListLength = entries.getLength(); - + for (int i = 0; i < entriesListLength; i++) { Element entry = (Element) entries.item(i); String key = entry.getAttribute("key"); @@ -658,10 +643,10 @@ public class Properties extends Hashtable { // BEGIN android-added String value = getTextContent(entry); // END android-added - + /* - * key != null & value != null - * but key or(and) value can be empty String + * key != null & value != null but key or(and) value can be + * empty String */ put(key, value); } @@ -675,44 +660,41 @@ public class Properties extends Hashtable { /** * Writes all properties stored in this instance into the {@code * OutputStream} in XML representation. The DOCTYPE is - * + * *
                * <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
                * 
          - * + * * If the comment is null, no comment is added to the output. UTF-8 is used * as the encoding. The {@code OutputStream} is not closed at the end. A * call to this method is the same as a call to {@code storeToXML(os, * comment, "UTF-8")}. - * + * * @param os the {@code OutputStream} to write to. * @param comment the comment to add. If null, no comment is added. * @throws IOException if an error occurs during writing to the output. - * @since Android 1.0 */ - public void storeToXML(OutputStream os, String comment) - throws IOException { - storeToXML(os, comment, "UTF-8"); + public void storeToXML(OutputStream os, String comment) throws IOException { + storeToXML(os, comment, "UTF-8"); //$NON-NLS-1$ } /** * Writes all properties stored in this instance into the {@code * OutputStream} in XML representation. The DOCTYPE is - * + * *
                * <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
                * 
          - * + * * If the comment is null, no comment is added to the output. The parameter * {@code encoding} defines which encoding should be used. The {@code * OutputStream} is not closed at the end. - * + * * @param os the {@code OutputStream} to write to. * @param comment the comment to add. If null, no comment is added. * @param encoding the code identifying the encoding that should be used to * write into the {@code OutputStream}. * @throws IOException if an error occurs during writing to the output. - * @since Android 1.0 */ public synchronized void storeToXML(OutputStream os, String comment, String encoding) throws IOException { @@ -720,14 +702,14 @@ public class Properties extends Hashtable { if (os == null || encoding == null) { throw new NullPointerException(); } - + /* * We can write to XML file using encoding parameter but note that some * aliases for encodings are not supported by the XML parser. Thus we * have to know canonical name for encoding used to store data in XML * since the XML parser must recognize encoding name used to store data. */ - + String encodingCanonicalName; try { encodingCanonicalName = Charset.forName(encoding).name(); @@ -741,18 +723,19 @@ public class Properties extends Hashtable { encodingCanonicalName = "UTF-8"; } - PrintStream printStream = new PrintStream(os, false, encodingCanonicalName); - + PrintStream printStream = new PrintStream(os, false, + encodingCanonicalName); + printStream.print(""); - + printStream.print(""); - + printStream.println(""); - + if (comment != null) { printStream.print(""); printStream.print(substitutePredefinedEntries(comment)); @@ -771,18 +754,16 @@ public class Properties extends Hashtable { printStream.println(""); printStream.flush(); } - + private String substitutePredefinedEntries(String s) { - + /* - * substitution for predefined character entities - * to use them safely in XML + * substitution for predefined character entities to use them safely in + * XML */ - return s.replaceAll("&", "&") - .replaceAll("<", "<") - .replaceAll(">", ">") - .replaceAll("\u0027", "'") - .replaceAll("\"", """); + return s.replaceAll("&", "&").replaceAll("<", "<").replaceAll( + ">", ">").replaceAll("\u0027", "'").replaceAll("\"", + """); } // BEGIN android-added diff --git a/libcore/luni/src/main/java/java/util/PropertyPermission.java b/libcore/luni/src/main/java/java/util/PropertyPermission.java index cdbfe341a..1cd7143d7 100644 --- a/libcore/luni/src/main/java/java/util/PropertyPermission.java +++ b/libcore/luni/src/main/java/java/util/PropertyPermission.java @@ -39,8 +39,6 @@ import org.apache.harmony.luni.util.Util; *

          * There are two possible permission action types: read and write. Possible * actions are "read", "write", or "read,write"/"write,read". - * - * @since Android 1.0 */ public final class PropertyPermission extends BasicPermission { private static final long serialVersionUID = 885438825399942851L; @@ -56,7 +54,6 @@ public final class PropertyPermission extends BasicPermission { * the actions which are applicable to it. Possible actions are * "read", "write", or "read,write"/"write,read". Anything else * will result in an {@code IllegalArgumentException}. - * @since Android 1.0 */ public PropertyPermission(String name, String actions) { super(name); @@ -84,17 +81,16 @@ public final class PropertyPermission extends BasicPermission { /** * Compares the argument to the receiver, and returns true if they represent * the same object using a class specific comparison. In this - * case, the receiver must be a {@code PropertyPermission} for the same + * case, the receiver must be a {@code PropertyPermission} for the same * property as the argument, and must have the same actions. * If {@code o} is a permission that is not a {@code PropertyPermission}, - * this method may throw a {@code ClassCastException}. - * + * this method may throw a {@code ClassCastException}. + * * @param o * the {@code Object} to compare with this {@code Object}. * @return {@code true} if the {@code Object} is the same as this {@code Object}, * {@code false} if it is different from this {@code Object}. * @see #hashCode - * @since Android 1.0 */ @Override public boolean equals(Object o) { @@ -110,7 +106,6 @@ public final class PropertyPermission extends BasicPermission { * either "read", "write", or "read,write". * * @return the actions associated with the receiver. - * @since Android 1.0 */ @Override public String getActions() { @@ -124,7 +119,6 @@ public final class PropertyPermission extends BasicPermission { * * @return the receiver's hash. * @see #equals - * @since Android 1.0 */ @Override public int hashCode() { @@ -138,7 +132,6 @@ public final class PropertyPermission extends BasicPermission { * receiver, and {@code false} if it is not. * @param permission * the permission to check. - * @since Android 1.0 */ @Override public boolean implies(Permission permission) { @@ -155,7 +148,6 @@ public final class PropertyPermission extends BasicPermission { * * @return a new {@code PermissionCollection} or {@code null}. * @see java.security.PermissionCollection - * @since Android 1.0 */ @Override public PermissionCollection newPermissionCollection() { diff --git a/libcore/luni/src/main/java/java/util/PropertyPermissionCollection.java b/libcore/luni/src/main/java/java/util/PropertyPermissionCollection.java index 45e89d949..bf8853450 100644 --- a/libcore/luni/src/main/java/java/util/PropertyPermissionCollection.java +++ b/libcore/luni/src/main/java/java/util/PropertyPermissionCollection.java @@ -26,8 +26,6 @@ import java.security.PermissionCollection; /** * A {@code PermissionCollection} for holding {@code PropertyPermission}s. - * - * @since Android 1.0 */ class PropertyPermissionCollection extends PermissionCollection { diff --git a/libcore/luni/src/main/java/java/util/PropertyResourceBundle.java b/libcore/luni/src/main/java/java/util/PropertyResourceBundle.java index acc32630b..835e892d3 100644 --- a/libcore/luni/src/main/java/java/util/PropertyResourceBundle.java +++ b/libcore/luni/src/main/java/java/util/PropertyResourceBundle.java @@ -17,7 +17,6 @@ package java.util; - import java.io.IOException; import java.io.InputStream; @@ -28,9 +27,10 @@ import java.io.InputStream; * * @see ResourceBundle * @see Properties - * @since Android 1.0 + * @since 1.1 */ public class PropertyResourceBundle extends ResourceBundle { + Properties resources; /** @@ -42,18 +42,23 @@ public class PropertyResourceBundle extends ResourceBundle { * @throws IOException * if an error occurs during a read operation on the * {@code InputStream}. - * @since Android 1.0 */ public PropertyResourceBundle(InputStream stream) throws IOException { resources = new Properties(); resources.load(stream); } - + @SuppressWarnings("unchecked") private Enumeration getLocalKeys() { - return (Enumeration)resources.propertyNames(); + return (Enumeration) resources.propertyNames(); } + /** + * Returns the names of the resources contained in this + * PropertyResourceBundle. + * + * @return an Enumeration of the resource names + */ @Override public Enumeration getKeys() { if (parent == null) { @@ -102,6 +107,14 @@ public class PropertyResourceBundle extends ResourceBundle { }; } + /** + * Returns the named resource from this PropertyResourceBundle, or null if + * the resource is not found. + * + * @param key + * the name of the resource + * @return the resource object + */ @Override public Object handleGetObject(String key) { return resources.get(key); diff --git a/libcore/luni/src/main/java/java/util/Queue.java b/libcore/luni/src/main/java/java/util/Queue.java index 5035ce7ff..6ef776a63 100644 --- a/libcore/luni/src/main/java/java/util/Queue.java +++ b/libcore/luni/src/main/java/java/util/Queue.java @@ -24,22 +24,17 @@ package java.util; * However, a priority queue orders its elements according to a comparator * specified or the elements' natural order. Furthermore, a stack orders its * elements last-in-first out. - *

          *

          * A typical queue does not allow {@code null} to be inserted as its element, * while some implementations such as {@code LinkedList} allow it. But {@code * null} should not be inserted even in these implementations, since the method * {@code poll} returns {@code null} to indicate that there is no element left * in the queue. - *

          *

          * {@code Queue} does not provide blocking queue methods, which would block * until the operation of the method is allowed. See the * {@link java.util.concurrent.BlockingQueue} interface for information about * blocking queue methods. - *

          - * - * @since Android 1.0 */ public interface Queue extends Collection { @@ -53,7 +48,6 @@ public interface Queue extends Collection { * the specified element to insert into the queue. * @return {@code true} if the operation succeeds and {@code false} if it * fails. - * @since Android 1.0 */ public boolean offer(E o); @@ -63,7 +57,6 @@ public interface Queue extends Collection { * * @return the element at the head of the queue or {@code null} if there is * no element in the queue. - * @since Android 1.0 */ public E poll(); @@ -74,7 +67,6 @@ public interface Queue extends Collection { * @return the element at the head of the queue. * @throws NoSuchElementException * if there is no element in the queue. - * @since Android 1.0 */ public E remove(); @@ -83,7 +75,6 @@ public interface Queue extends Collection { * * @return the element at the head of the queue or {@code null} if there is * no element in the queue. - * @since Android 1.0 */ public E peek(); @@ -94,7 +85,6 @@ public interface Queue extends Collection { * @return the element at the head of the queue. * @throws NoSuchElementException * if there is no element in the queue. - * @since Android 1.0 */ public E element(); diff --git a/libcore/luni/src/main/java/java/util/Random.java b/libcore/luni/src/main/java/java/util/Random.java index f92c3ee51..be27258d6 100644 --- a/libcore/luni/src/main/java/java/util/Random.java +++ b/libcore/luni/src/main/java/java/util/Random.java @@ -26,44 +26,40 @@ import java.io.Serializable; * * @see Properties * @see PropertyResourceBundle - * @since Android 1.0 */ public class Random implements Serializable { private static final long serialVersionUID = 3905348978240129619L; - static final long multiplier = 0x5deece66dL; + private static final long multiplier = 0x5deece66dL; /** * The boolean value indicating if the second Gaussian number is available. * * @serial */ - boolean haveNextNextGaussian = false; + private boolean haveNextNextGaussian; /** - * It is associated with the internal state of this generator. - * - * @serial + * @serial It is associated with the internal state of this generator. */ - long seed; + private long seed; /** * The second Gaussian generated number. * * @serial */ - double nextNextGaussian = 0; + private double nextNextGaussian; /** * Construct a random generator with the current time of day in milliseconds * as the initial state. * * @see #setSeed - * @since Android 1.0 */ public Random() { - setSeed(System.currentTimeMillis()); + setSeed(System.currentTimeMillis() + hashCode()); } /** @@ -74,7 +70,6 @@ public class Random implements Serializable { * the seed that will determine the initial state of this random * number generator. * @see #setSeed - * @since Android 1.0 */ public Random(long seed) { setSeed(seed); @@ -89,7 +84,6 @@ public class Random implements Serializable { * @param bits * number of bits of the returned value. * @return a pseudo-random generated int number. - * @since Android 1.0 * @see #nextBytes * @see #nextDouble * @see #nextFloat @@ -97,7 +91,6 @@ public class Random implements Serializable { * @see #nextInt(int) * @see #nextGaussian * @see #nextLong - * @since Android 1.0 */ protected synchronized int next(int bits) { seed = (seed * multiplier + 0xbL) & ((1L << 48) - 1); @@ -109,7 +102,6 @@ public class Random implements Serializable { * generated by this generator. * * @return a pseudo-random, uniformly distributed boolean value. - * @since Android 1.0 */ public boolean nextBoolean() { return next(1) != 0; @@ -122,7 +114,6 @@ public class Random implements Serializable { * @param buf * non-null array to contain the new random {@code byte}s. * @see #next - * @since Android 1.0 */ public void nextBytes(byte[] buf) { int rand = 0, count = 0, loop = 0; @@ -144,7 +135,6 @@ public class Random implements Serializable { * * @return a random {@code double} in the range [0.0 - 1.0) * @see #nextFloat - * @since Android 1.0 */ public double nextDouble() { return ((((long) next(26) << 27) + next(27)) / (double) (1L << 53)); @@ -156,7 +146,6 @@ public class Random implements Serializable { * * @return float a random {@code float} number between [0.0 and 1.0) * @see #nextDouble - * @since Android 1.0 */ public float nextFloat() { return (next(24) / 16777216f); @@ -172,7 +161,6 @@ public class Random implements Serializable { * * @return a random {@code double} * @see #nextDouble - * @since Android 1.0 */ public synchronized double nextGaussian() { if (haveNextNextGaussian) { // if X1 has been returned, return the @@ -205,7 +193,6 @@ public class Random implements Serializable { * @see java.lang.Integer#MIN_VALUE * @see #next * @see #nextLong - * @since Android 1.0 */ public int nextInt() { return next(32); @@ -218,7 +205,6 @@ public class Random implements Serializable { * @param n * the exclusive upper border of the range [0 - n). * @return a random {@code int}. - * @since Android 1.0 */ public int nextInt(int n) { if (n > 0) { @@ -245,7 +231,6 @@ public class Random implements Serializable { * @see #next * @see #nextInt() * @see #nextInt(int) - * @since Android 1.0 */ public long nextLong() { return ((long) next(32) << 32) + next(32); @@ -260,7 +245,6 @@ public class Random implements Serializable { * @see #next * @see #Random() * @see #Random(long) - * @since Android 1.0 */ public synchronized void setSeed(long seed) { this.seed = (seed ^ multiplier) & ((1L << 48) - 1); diff --git a/libcore/luni/src/main/java/java/util/RandomAccess.java b/libcore/luni/src/main/java/java/util/RandomAccess.java index a8a90de0c..7121bff5c 100644 --- a/libcore/luni/src/main/java/java/util/RandomAccess.java +++ b/libcore/luni/src/main/java/java/util/RandomAccess.java @@ -17,13 +17,10 @@ package java.util; - /** * RandomAccess is implemented by {@code List} implementations that support fast * (usually constant time) random access. - * - * @since Android 1.0 */ public interface RandomAccess { - /*empty*/ + /* empty */ } diff --git a/libcore/luni/src/main/java/java/util/ResourceBundle.java b/libcore/luni/src/main/java/java/util/ResourceBundle.java index 1187358fe..218d3f30c 100644 --- a/libcore/luni/src/main/java/java/util/ResourceBundle.java +++ b/libcore/luni/src/main/java/java/util/ResourceBundle.java @@ -27,6 +27,7 @@ import java.security.PrivilegedAction; import com.ibm.icu4jni.util.Resources; import dalvik.system.VMStack; // END android-changed +import org.apache.harmony.luni.util.Msg; /** * {@code ResourceBundle} is an abstract class which is the superclass of classes which @@ -35,7 +36,7 @@ import dalvik.system.VMStack; * and when a resource is not found in a bundle, the parent bundle is searched for * the resource. If the fallback mechanism reaches the base bundle and still * can't find the resource it throws a {@code MissingResourceException}. - * + * *
            *
          • All bundles for the same group of resources share a common base bundle. * This base bundle acts as the root and is the last fallback in case none of @@ -53,7 +54,7 @@ import dalvik.system.VMStack; * current currency (Euro) and the {@code PREEURO} variant bundle would return the old * currency (e.g. DM for Germany).
          • *
          - * + * * Examples *
            *
          • BaseName (base bundle) @@ -67,24 +68,22 @@ import dalvik.system.VMStack; *
          • BaseName_fr_FR_PREEURO (bundle with France specific resources in french of * the time before the Euro) *
          - * + * * It's also possible to create variants for languages or countries. This can be * done by just skipping the country or language abbreviation: * BaseName_us__POSIX or BaseName__DE_PREEURO. But it's not allowed to * circumvent both language and country: BaseName___VARIANT is illegal. - * + * * @see Properties * @see PropertyResourceBundle * @see ListResourceBundle - * @since Android 1.0 + * @since 1.1 */ public abstract class ResourceBundle { /** * The parent of this {@code ResourceBundle} that is used if this bundle doesn't * include the requested resource. - * - * @since Android 1.0 */ protected ResourceBundle parent; @@ -114,8 +113,6 @@ public abstract class ResourceBundle { /** * Constructs a new instance of this class. - * - * @since Android 1.0 */ public ResourceBundle() { /* empty */ @@ -124,13 +121,12 @@ public abstract class ResourceBundle { /** * Finds the named resource bundle for the default {@code Locale} and the caller's * {@code ClassLoader}. - * + * * @param bundleName * the name of the {@code ResourceBundle}. * @return the requested {@code ResourceBundle}. - * @exception MissingResourceException + * @throws MissingResourceException * if the {@code ResourceBundle} cannot be found. - * @since Android 1.0 */ public static final ResourceBundle getBundle(String bundleName) throws MissingResourceException { @@ -143,15 +139,14 @@ public abstract class ResourceBundle { /** * Finds the named {@code ResourceBundle} for the specified {@code Locale} and the caller * {@code ClassLoader}. - * + * * @param bundleName * the name of the {@code ResourceBundle}. * @param locale * the {@code Locale}. * @return the requested resource bundle. - * @exception MissingResourceException + * @throws MissingResourceException * if the resource bundle cannot be found. - * @since Android 1.0 */ public static final ResourceBundle getBundle(String bundleName, Locale locale) { @@ -163,17 +158,17 @@ public abstract class ResourceBundle { /** * Finds the named resource bundle for the specified {@code Locale} and {@code ClassLoader}. - * + * * The passed base name and {@code Locale} are used to create resource bundle names. * The first name is created by concatenating the base name with the result * of {@link Locale#toString()}. From this name all parent bundle names are * derived. Then the same thing is done for the default {@code Locale}. This results * in a list of possible bundle names. - * + * * Example For the basename "BaseName", the {@code Locale} of the * German part of Switzerland (de_CH) and the default {@code Locale} en_US the list * would look something like this: - * + * *
            *
          1. BaseName_de_CH
          2. *
          3. BaseName_de
          4. @@ -181,11 +176,11 @@ public abstract class ResourceBundle { *
          5. Basename_en
          6. *
          7. BaseName
          8. *
          - * + * * This list also shows the order in which the bundles will be searched for a requested * resource in the German part of Switzerland (de_CH). - * - * As a first step, this method tries to instantiate + * + * As a first step, this method tries to instantiate * a {@code ResourceBundle} with the names provided. * If such a class can be instantiated and initialized, it is returned and * all the parent bundles are instantiated too. If no such class can be @@ -195,11 +190,11 @@ public abstract class ResourceBundle { * by calling {@link ClassLoader#getResource(String)} it is used to * initialize a {@link PropertyResourceBundle}. If this succeeds, it will * also load the parents of this {@code ResourceBundle}. - * + * * For compatibility with older code, the bundle name isn't required to be * a fully qualified class name. It's also possible to directly pass * the path to a properties file (without a file extension). - * + * * @param bundleName * the name of the {@code ResourceBundle}. * @param locale @@ -207,9 +202,8 @@ public abstract class ResourceBundle { * @param loader * the {@code ClassLoader} to use. * @return the requested {@code ResourceBundle}. - * @exception MissingResourceException + * @throws MissingResourceException * if the {@code ResourceBundle} cannot be found. - * @since Android 1.0 */ public static ResourceBundle getBundle(String bundleName, Locale locale, ClassLoader loader) throws MissingResourceException { @@ -248,7 +242,7 @@ public abstract class ResourceBundle { if ((bundle = handleGetBundle(bundleName, localeName, true, loader)) != null) { return bundle; } - throw new MissingResourceException(null, bundleName + '_' + locale, + throw new MissingResourceException(Msg.getString("KA029", bundleName, locale), bundleName + '_' + locale, //$NON-NLS-1$ ""); //$NON-NLS-1$ } throw new NullPointerException(); @@ -256,9 +250,8 @@ public abstract class ResourceBundle { /** * Returns the names of the resources contained in this {@code ResourceBundle}. - * + * * @return an {@code Enumeration} of the resource names. - * @since Android 1.0 */ public abstract Enumeration getKeys(); @@ -266,9 +259,8 @@ public abstract class ResourceBundle { * Gets the {@code Locale} of this {@code ResourceBundle}. In case a bundle was not * found for the requested {@code Locale}, this will return the actual {@code Locale} of * this resource bundle that was found after doing a fallback. - * + * * @return the {@code Locale} of this {@code ResourceBundle}. - * @since Android 1.0 */ public Locale getLocale() { return locale; @@ -279,13 +271,12 @@ public abstract class ResourceBundle { * cannot be found in this bundle, it falls back to the parent bundle (if * it's not null) by calling the {@link #handleGetObject} method. If the resource still * can't be found it throws a {@code MissingResourceException}. - * + * * @param key * the name of the resource. * @return the resource object. - * @exception MissingResourceException + * @throws MissingResourceException * if the resource is not found. - * @since Android 1.0 */ public final Object getObject(String key) { ResourceBundle last, theParent = this; @@ -297,21 +288,20 @@ public abstract class ResourceBundle { last = theParent; theParent = theParent.parent; } while (theParent != null); - throw new MissingResourceException(null, last.getClass().getName(), key); + throw new MissingResourceException(Msg.getString("KA029", last.getClass().getName(), key), last.getClass().getName(), key); //$NON-NLS-1$ } /** * Returns the named string resource from this {@code ResourceBundle}. - * + * * @param key * the name of the resource. * @return the resource string. - * @exception MissingResourceException + * @throws MissingResourceException * if the resource is not found. - * @exception ClassCastException + * @throws ClassCastException * if the resource found is not a string. * @see #getObject(String) - * @since Android 1.0 */ public final String getString(String key) { return (String) getObject(key); @@ -319,16 +309,15 @@ public abstract class ResourceBundle { /** * Returns the named resource from this {@code ResourceBundle}. - * + * * @param key * the name of the resource. * @return the resource string array. - * @exception MissingResourceException + * @throws MissingResourceException * if the resource is not found. - * @exception ClassCastException + * @throws ClassCastException * if the resource found is not an array of strings. * @see #getObject(String) - * @since Android 1.0 */ public final String[] getStringArray(String key) { return (String[]) getObject(key); @@ -343,7 +332,7 @@ public abstract class ResourceBundle { synchronized (cache) { loaderCache = cache.get(cacheKey); if (loaderCache == null) { - loaderCache = new Hashtable(13); + loaderCache = new Hashtable(); cache.put(cacheKey, loaderCache); } } @@ -389,13 +378,9 @@ public abstract class ResourceBundle { } catch (Exception e) { } - // BEGIN android-added - // copied from newer version of Harmony if (bundle != null) { bundle.setLocale(locale); - } - // END android-added - if (bundle == null) { + } else { final String fileName = bundleName.replace('.', '/'); InputStream stream = AccessController .doPrivileged(new PrivilegedAction() { @@ -447,21 +432,19 @@ public abstract class ResourceBundle { /** * Returns the named resource from this {@code ResourceBundle}, or null if the * resource is not found. - * + * * @param key * the name of the resource. * @return the resource object. - * @since Android 1.0 */ protected abstract Object handleGetObject(String key); /** * Sets the parent resource bundle of this {@code ResourceBundle}. The parent is * searched for resources which are not found in this {@code ResourceBundle}. - * + * * @param bundle * the parent {@code ResourceBundle}. - * @since Android 1.0 */ protected void setParent(ResourceBundle bundle) { parent = bundle; diff --git a/libcore/luni/src/main/java/java/util/Scanner.java b/libcore/luni/src/main/java/java/util/Scanner.java index acacebb7a..dd5c024ea 100644 --- a/libcore/luni/src/main/java/java/util/Scanner.java +++ b/libcore/luni/src/main/java/java/util/Scanner.java @@ -44,7 +44,7 @@ import java.util.regex.Pattern; * next* methods. If the token is not in a valid format, an * {@code InputMismatchException} is thrown. *

          - * For example:

          + * For example: *
            * Scanner s = new Scanner("1A true");
            * System.out.println(s.nextInt(16));
          @@ -52,28 +52,25 @@ import java.util.regex.Pattern;
            * 
          *

          * Yields the result: {@code 26 true} - *

          *

          A {@code Scanner} can also find or skip specific patterns without regard for the * delimiter. All these methods and the various next* and hasNext* methods may * block. - *

          + *

          * The {@code Scanner} class is not thread-safe. - * @since Android 1.0 */ public final class Scanner implements Iterator { - // Default delimiting pattern. + // Default delimiting pattern. private static final Pattern DEFAULT_DELIMITER = Pattern .compile("\\p{javaWhitespace}+"); //$NON-NLS-1$ - + // The boolean's pattern. private static final Pattern BOOLEAN_PATTERN = Pattern.compile( "true|false", Pattern.CASE_INSENSITIVE); //$NON-NLS-1$ - - + // Pattern used to recognize line terminator. private static final Pattern LINE_TERMINATOR; - + // Pattern used to recognize multiple line terminators. private static final Pattern MULTI_LINE_TERMINATOR; @@ -81,24 +78,22 @@ public final class Scanner implements Iterator { private static final Pattern LINE_PATTERN; static { - String terminator = "\n|\r\n|\r|\u0085|\u2028|\u2029"; //$NON-NLS-1$ - + String terminator = "\n|\r\n|\r|\u0085|\u2028|\u2029"; //$NON-NLS-1$ + LINE_TERMINATOR = Pattern.compile(terminator); - + + // BEGIN android-note + // consider plain old string concatenation for better performance + // END android-note StringBuilder multiTerminator = new StringBuilder(); - MULTI_LINE_TERMINATOR = Pattern - .compile(multiTerminator.append("(") //$NON-NLS-1$ - .append(terminator) - .append(")+").toString()); //$NON-NLS-1$ + MULTI_LINE_TERMINATOR = Pattern.compile(multiTerminator.append("(") //$NON-NLS-1$ + .append(terminator).append(")+").toString()); //$NON-NLS-1$ StringBuilder line = new StringBuilder(); - LINE_PATTERN = Pattern - .compile(line.append(".*(") //$NON-NLS-1$ - .append(terminator) - .append(")|.+(") //$NON-NLS-1$ - .append(terminator) - .append(")?").toString()); //$NON-NLS-1$ - } - + LINE_PATTERN = Pattern.compile(line.append(".*(") //$NON-NLS-1$ + .append(terminator).append(")|.+(") //$NON-NLS-1$ + .append(terminator).append(")?").toString()); //$NON-NLS-1$ + } + // The pattern matches anything. private static final Pattern ANY_PATTERN = Pattern.compile("(?s).*"); //$NON-NLS-1$ @@ -131,24 +126,24 @@ public final class Scanner implements Iterator { // The length of the buffer. private int bufferLength = 0; - // Record the status of this scanner. True if the scanner + // Record the status of this scanner. True if the scanner // is closed. private boolean closed = false; private IOException lastIOException; - + private boolean matchSuccessful = false; - + private DecimalFormat decimalFormat; - + // Records whether the underlying readable has more input. private boolean inputExhausted = false; - + private Object cacheHasNextValue = null; - + private int cachehasNextIndex = -1; - - private enum DataType{ + + private enum DataType { /* * Stands for Integer */ @@ -162,12 +157,11 @@ public final class Scanner implements Iterator { /** * Creates a {@code Scanner} with the specified {@code File} as input. The default charset * is applied when reading the file. - * + * * @param src * the file to be scanned. * @throws FileNotFoundException * if the specified file does not exist. - * @since Android 1.0 */ public Scanner(File src) throws FileNotFoundException { this(src, Charset.defaultCharset().name()); @@ -176,7 +170,7 @@ public final class Scanner implements Iterator { /** * Creates a {@code Scanner} with the specified {@code File} as input. The specified charset * is applied when reading the file. - * + * * @param src * the file to be scanned. * @param charsetName @@ -185,7 +179,6 @@ public final class Scanner implements Iterator { * if the specified file does not exist. * @throws IllegalArgumentException * if the specified coding does not exist. - * @since Android 1.0 */ public Scanner(File src, String charsetName) throws FileNotFoundException { if (null == src) { @@ -212,10 +205,9 @@ public final class Scanner implements Iterator { /** * Creates a {@code Scanner} on the specified string. - * + * * @param src * the string to be scanned. - * @since Android 1.0 */ public Scanner(String src) { input = new StringReader(src); @@ -225,10 +217,9 @@ public final class Scanner implements Iterator { /** * Creates a {@code Scanner} on the specified {@code InputStream}. The default charset is * applied when decoding the input. - * + * * @param src * the {@code InputStream} to be scanned. - * @since Android 1.0 */ public Scanner(InputStream src) { this(src, Charset.defaultCharset().name()); @@ -237,14 +228,13 @@ public final class Scanner implements Iterator { /** * Creates a {@code Scanner} on the specified {@code InputStream}. The specified charset is * applied when decoding the input. - * + * * @param src * the {@code InputStream} to be scanned. * @param charsetName * the encoding type of the {@code InputStream}. * @throws IllegalArgumentException * if the specified character set is not found. - * @since Android 1.0 */ public Scanner(InputStream src, String charsetName) { if (null == src) { @@ -261,10 +251,9 @@ public final class Scanner implements Iterator { /** * Creates a {@code Scanner} with the specified {@code Readable} as input. - * + * * @param src * the {@code Readable} to be scanned. - * @since Android 1.0 */ public Scanner(Readable src) { if (null == src) { @@ -277,10 +266,9 @@ public final class Scanner implements Iterator { /** * Creates a {@code Scanner} with the specified {@code ReadableByteChannel} as * input. The default charset is applied when decoding the input. - * + * * @param src * the {@code ReadableByteChannel} to be scanned. - * @since Android 1.0 */ public Scanner(ReadableByteChannel src) { this(src, Charset.defaultCharset().name()); @@ -289,14 +277,13 @@ public final class Scanner implements Iterator { /** * Creates a {@code Scanner} with the specified {@code ReadableByteChannel} as * input. The specified charset is applied when decoding the input. - * + * * @param src * the {@code ReadableByteChannel} to be scanned. * @param charsetName * the encoding type of the content. * @throws IllegalArgumentException * if the specified character set is not found. - * @since Android 1.0 */ public Scanner(ReadableByteChannel src, String charsetName) { if (null == src) { @@ -316,9 +303,8 @@ public final class Scanner implements Iterator { * {@code Closeable}. If the {@code Scanner} has been closed, this method will have * no effect. Any scanning operation called after calling this method will throw * an {@code IllegalStateException}. - * + * * @see Closeable - * @since Android 1.0 */ public void close() { if (closed) { @@ -336,9 +322,8 @@ public final class Scanner implements Iterator { /** * Returns the delimiter {@code Pattern} in use by this {@code Scanner}. - * + * * @return the delimiter {@code Pattern} in use by this {@code Scanner}. - * @since Android 1.0 */ public Pattern delimiter() { return delimiter; @@ -351,14 +336,13 @@ public final class Scanner implements Iterator { * Otherwise, {@code null} will be returned and the {@code Scanner} will not advance. * When waiting for input, the {@code Scanner} may be blocked. All the * input may be cached if no line terminator exists in the buffer. - * + * * @param pattern * the pattern to find in the input. * @return the matched string or {@code null} if the pattern is not found * before the next line terminator. * @throws IllegalStateException * if the {@code Scanner} is closed. - * @since Android 1.0 */ public String findInLine(Pattern pattern) { checkClosed(); @@ -395,17 +379,32 @@ public final class Scanner implements Iterator { * bug is fixed. */ int oldLimit = buffer.limit(); - buffer.limit(horizonLineSeparator); + // Considering the look ahead feature, the line terminator should be involved as RI + buffer.limit(horizonLineSeparator + terminatorLength); // ========== To deal with regex bug ==================== - matcher.region(findStartIndex, horizonLineSeparator); + // Considering the look ahead feature, the line terminator should be involved as RI + matcher.region(findStartIndex, horizonLineSeparator + terminatorLength); if (matcher.find()) { // The scanner advances past the input that matched findStartIndex = matcher.end(); - // If the matched pattern is immediately followed by line terminator. - if(horizonLineSeparator == matcher.end()) { + // If the matched pattern is immediately followed by line + // terminator. + if (horizonLineSeparator == matcher.end()) { findStartIndex += terminatorLength; } + // the line terminator itself should not be a part of + // the match result according to the Spec + if (horizonLineSeparator != bufferLength + && (horizonLineSeparator + terminatorLength == matcher + .end())) { + // ========== To deal with regex bug ==================== + buffer.limit(oldLimit); + // ========== To deal with regex bug ==================== + + matchSuccessful = false; + return null; + } matchSuccessful = true; // ========== To deal with regex bug ==================== @@ -427,7 +426,7 @@ public final class Scanner implements Iterator { * Compiles the pattern string and tries to find a substing matching it in the input data. The * delimiter will be ignored. This is the same as invoking * {@code findInLine(Pattern.compile(pattern))}. - * + * * @param pattern * a string used to construct a pattern which is in turn used to * match a substring of the input data. @@ -436,7 +435,6 @@ public final class Scanner implements Iterator { * @throws IllegalStateException * if the {@code Scanner} is closed. * @see #findInLine(Pattern) - * @since Android 1.0 */ public String findInLine(String pattern) { return findInLine(Pattern.compile(pattern)); @@ -450,7 +448,7 @@ public final class Scanner implements Iterator { * advance. When waiting for input, the {@code Scanner} may be blocked. *

          * The {@code Scanner}'s search will never go more than {@code horizon} code points from current - * position. The position of {@code horizon} does have an effect on the result of the + * position. The position of {@code horizon} does have an effect on the result of the * match. For example, when the input is "123" and current position is at zero, * {@code findWithinHorizon(Pattern.compile("\\p{Digit}{3}"), 2)} * will return {@code null}. While @@ -459,10 +457,10 @@ public final class Scanner implements Iterator { * non-anchoring bound. (refer to * {@link Matcher#useTransparentBounds(boolean)} and * {@link Matcher#useAnchoringBounds(boolean)}) - *

          + *

          * A {@code horizon} whose value is zero will be ignored and the whole input will be * used for search. In this situation, all the input may be cached. - * + * * @param pattern * the pattern used to scan. * @param horizon @@ -473,7 +471,6 @@ public final class Scanner implements Iterator { * if the {@code Scanner} is closed. * @throws IllegalArgumentException * if {@code horizon} is less than zero. - * @since Android 1.0 */ public String findWithinHorizon(Pattern pattern, int horizon) { checkClosed(); @@ -538,7 +535,7 @@ public final class Scanner implements Iterator { * Tries to find the pattern in the input between the current position and the specified * {@code horizon}. Delimiters are ignored. This call is the same as invoking * {@code findWithinHorizon(Pattern.compile(pattern))}. - * + * * @param pattern * the pattern used to scan. * @param horizon @@ -550,7 +547,6 @@ public final class Scanner implements Iterator { * @throws IllegalArgumentException * if {@code horizon} is less than zero. * @see #findWithinHorizon(Pattern, int) - * @since Android 1.0 */ public String findWithinHorizon(String pattern, int horizon) { return findWithinHorizon(Pattern.compile(pattern), horizon); @@ -559,12 +555,11 @@ public final class Scanner implements Iterator { /** * Returns whether this {@code Scanner} has one or more tokens remaining to parse. * This method will block if the data is still being read. - * + * * @return {@code true} if this {@code Scanner} has one or more tokens remaining, * otherwise {@code false}. * @throws IllegalStateException * if the {@code Scanner} has been closed. - * @since Android 1.0 */ public boolean hasNext() { return hasNext(ANY_PATTERN); @@ -574,28 +569,28 @@ public final class Scanner implements Iterator { * Returns whether this {@code Scanner} has one or more tokens remaining to parse * and the next token matches the given pattern. This method will block if the data is * still being read. - * + * * @param pattern * the pattern to check for. * @return {@code true} if this {@code Scanner} has more tokens and the next token * matches the pattern, {@code false} otherwise. * @throws IllegalStateException * if the {@code Scanner} has been closed. - * @since Android 1.0 */ public boolean hasNext(Pattern pattern) { checkClosed(); checkNull(pattern); matchSuccessful = false; saveCurrentStatus(); - //if the next token exists, set the match region, otherwise return false + // if the next token exists, set the match region, otherwise return + // false if (!setTokenRegion()) { recoverPreviousStatus(); return false; } matcher.usePattern(pattern); boolean hasNext = false; - //check whether next token matches the specified pattern + // check whether next token matches the specified pattern if (matcher.matches()) { cachehasNextIndex = findStartIndex; matchSuccessful = true; @@ -605,20 +600,18 @@ public final class Scanner implements Iterator { return hasNext; } - /** * Returns {@code true} if this {@code Scanner} has one or more tokens remaining to parse * and the next token matches a pattern compiled from the given string. This method will * block if the data is still being read. This call is equivalent to * {@code hasNext(Pattern.compile(pattern))}. - * + * * @param pattern * the string specifying the pattern to scan for * @return {@code true} if the specified pattern matches this {@code Scanner}'s * next token, {@code false} otherwise. * @throws IllegalStateException * if the {@code Scanner} has been closed. - * @since Android 1.0 */ public boolean hasNext(String pattern) { return hasNext(Pattern.compile(pattern)); @@ -627,12 +620,11 @@ public final class Scanner implements Iterator { /** * Returns whether the next token can be translated into a valid * {@code BigDecimal}. - * + * * @return {@code true} if the next token can be translated into a valid * {@code BigDecimal}, otherwise {@code false.} * @throws IllegalStateException * if the {@code Scanner} has been closed. - * @since Android 1.0 */ public boolean hasNextBigDecimal() { Pattern floatPattern = getFloatPattern(); @@ -653,12 +645,11 @@ public final class Scanner implements Iterator { /** * Returns whether the next token can be translated into a valid * {@code BigInteger} in the default radix. - * + * * @return {@code true} if the next token can be translated into a valid * {@code BigInteger}, otherwise {@code false}. * @throws IllegalStateException * if the {@code Scanner} has been closed. - * @since Android 1.0 */ public boolean hasNextBigInteger() { return hasNextBigInteger(integerRadix); @@ -667,7 +658,7 @@ public final class Scanner implements Iterator { /** * Returns whether the next token can be translated into a valid * {@code BigInteger} in the specified radix. - * + * * @param radix * the radix used to translate the token into a * {@code BigInteger}. @@ -675,7 +666,6 @@ public final class Scanner implements Iterator { * {@code BigInteger}, otherwise {@code false}. * @throws IllegalStateException * if the {@code Scanner} has been closed. - * @since Android 1.0 */ public boolean hasNextBigInteger(int radix) { Pattern integerPattern = getIntegerPattern(radix); @@ -696,12 +686,11 @@ public final class Scanner implements Iterator { /** * Returns whether the next token can be translated into a valid * {@code boolean} value. - * + * * @return {@code true} if the next token can be translated into a valid * {@code boolean} value, otherwise {@code false}. * @throws IllegalStateException * if the {@code Scanner} has been closed. - * @since Android 1.0 */ public boolean hasNextBoolean() { return hasNext(BOOLEAN_PATTERN); @@ -710,12 +699,11 @@ public final class Scanner implements Iterator { /** * Returns whether the next token can be translated into a valid * {@code byte} value in the default radix. - * + * * @return {@code true} if the next token can be translated into a valid * {@code byte} value, otherwise {@code false}. * @throws IllegalStateException * if the {@code Scanner} has been closed. - * @since Android 1.0 */ public boolean hasNextByte() { return hasNextByte(integerRadix); @@ -724,7 +712,7 @@ public final class Scanner implements Iterator { /** * Returns whether the next token can be translated into a valid * {@code byte} value in the specified radix. - * + * * @param radix * the radix used to translate the token into a {@code byte} * value @@ -732,7 +720,6 @@ public final class Scanner implements Iterator { * {@code byte} value, otherwise {@code false}. * @throws IllegalStateException * if the {@code Scanner} has been closed. - * @since Android 1.0 */ public boolean hasNextByte(int radix) { Pattern integerPattern = getIntegerPattern(radix); @@ -753,12 +740,11 @@ public final class Scanner implements Iterator { /** * Returns whether the next token translated into a valid {@code double} * value. - * + * * @return {@code true} if the next token can be translated into a valid * {@code double} value, otherwise {@code false}. * @throws IllegalStateException * if the {@code Scanner} has been closed. - * @since Android 1.0 */ public boolean hasNextDouble() { Pattern floatPattern = getFloatPattern(); @@ -779,12 +765,11 @@ public final class Scanner implements Iterator { /** * Returns whether the next token can be translated into a valid * {@code float} value. - * + * * @return {@code true} if the next token can be translated into a valid * {@code float} value, otherwise {@code false}. * @throws IllegalStateException * if the {@code Scanner} has been closed. - * @since Android 1.0 */ public boolean hasNextFloat() { Pattern floatPattern = getFloatPattern(); @@ -805,12 +790,11 @@ public final class Scanner implements Iterator { /** * Returns whether the next token can be translated into a valid {@code int} * value in the default radix. - * + * * @return {@code true} if the next token can be translated into a valid * {@code int} value, otherwise {@code false}. * @throws IllegalStateException * if the {@code Scanner} has been closed, - * @since Android 1.0 */ public boolean hasNextInt() { return hasNextInt(integerRadix); @@ -819,7 +803,7 @@ public final class Scanner implements Iterator { /** * Returns whether the next token can be translated into a valid {@code int} * value in the specified radix. - * + * * @param radix * the radix used to translate the token into an {@code int} * value. @@ -828,7 +812,6 @@ public final class Scanner implements Iterator { * {@code false}. * @throws IllegalStateException * if the {@code Scanner} has been closed. - * @since Android 1.0 */ public boolean hasNextInt(int radix) { Pattern integerPattern = getIntegerPattern(radix); @@ -849,12 +832,11 @@ public final class Scanner implements Iterator { /** * Returns whether there is a line terminator in the input. * This method may block. - * + * * @return {@code true} if there is a line terminator in the input, * otherwise, {@code false}. * @throws IllegalStateException * if the {@code Scanner} is closed. - * @since Android 1.0 */ public boolean hasNextLine() { checkClosed(); @@ -886,12 +868,11 @@ public final class Scanner implements Iterator { /** * Returns whether the next token can be translated into a valid * {@code long} value in the default radix. - * + * * @return {@code true} if the next token can be translated into a valid * {@code long} value, otherwise {@code false}. * @throws IllegalStateException * if the {@code Scanner} has been closed. - * @since Android 1.0 */ public boolean hasNextLong() { return hasNextLong(integerRadix); @@ -900,7 +881,7 @@ public final class Scanner implements Iterator { /** * Returns whether the next token can be translated into a valid * {@code long} value in the specified radix. - * + * * @param radix * the radix used to translate the token into a {@code long} * value. @@ -908,7 +889,6 @@ public final class Scanner implements Iterator { * {@code long} value, otherwise {@code false}. * @throws IllegalStateException * if the {@code Scanner} has been closed. - * @since Android 1.0 */ public boolean hasNextLong(int radix) { Pattern integerPattern = getIntegerPattern(radix); @@ -926,16 +906,14 @@ public final class Scanner implements Iterator { return isLongValue; } - /** * Returns whether the next token can be translated into a valid * {@code short} value in the default radix. - * + * * @return {@code true} if the next token can be translated into a valid * {@code short} value, otherwise {@code false}. * @throws IllegalStateException * if the {@code Scanner} has been closed. - * @since Android 1.0 */ public boolean hasNextShort() { return hasNextShort(integerRadix); @@ -944,7 +922,7 @@ public final class Scanner implements Iterator { /** * Returns whether the next token can be translated into a valid * {@code short} value in the specified radix. - * + * * @param radix * the radix used to translate the token into a {@code short} * value. @@ -952,7 +930,6 @@ public final class Scanner implements Iterator { * {@code short} value, otherwise {@code false}. * @throws IllegalStateException * if the {@code Scanner} has been closed. - * @since Android 1.0 */ public boolean hasNextShort(int radix) { Pattern integerPattern = getIntegerPattern(radix); @@ -973,9 +950,8 @@ public final class Scanner implements Iterator { /** * Returns the last {@code IOException} that was raised while reading from the underlying * input. - * + * * @return the last thrown {@code IOException}, or {@code null} if none was thrown. - * @since Android 1.0 */ public IOException ioException() { return lastIOException; @@ -983,9 +959,8 @@ public final class Scanner implements Iterator { /** * Return the {@code Locale} of this {@code Scanner}. - * + * * @return the {@code Locale} of this {@code Scanner}. - * @since Android 1.0 */ public Locale locale() { return locale; @@ -996,13 +971,11 @@ public final class Scanner implements Iterator { *

          * The next* and find* methods return the match result in the case of a * successful match. - *

          - * + * * @return the match result of the last successful match operation * @throws IllegalStateException * if the match result is not available, of if the last match * was not successful. - * @since Android 1.0 */ public MatchResult match() { if (!matchSuccessful) { @@ -1015,13 +988,12 @@ public final class Scanner implements Iterator { * Returns the next token. The token will be both prefixed and postfixed by * the delimiter that is currently being used (or a string that matches the * delimiter pattern). This method will block if input is being read. - * + * * @return the next complete token. * @throws IllegalStateException * if this {@code Scanner} has been closed. * @throws NoSuchElementException * if input has been exhausted. - * @since Android 1.0 */ public String next() { return next(ANY_PATTERN); @@ -1032,7 +1004,7 @@ public final class Scanner implements Iterator { * will be both prefixed and postfixed by the delimiter that is currently * being used (or a string that matches the delimiter pattern). This method will block * if input is being read. - * + * * @param pattern * the specified pattern to scan. * @return the next token. @@ -1042,7 +1014,6 @@ public final class Scanner implements Iterator { * if input has been exhausted. * @throws InputMismatchException * if the next token does not match the pattern given. - * @since Android 1.0 */ public String next(Pattern pattern) { checkClosed(); @@ -1070,7 +1041,7 @@ public final class Scanner implements Iterator { * being used (or a string that matches the delimiter pattern). This method will block * if input is being read. Calling this methos is equivalent to * {@code next(Pattern.compile(pattern))}. - * + * * @param pattern * the string specifying the pattern to scan for. * @return the next token. @@ -1080,7 +1051,6 @@ public final class Scanner implements Iterator { * if input has been exhausted. * @throws InputMismatchException * if the next token does not match the pattern given. - * @since Android 1.0 */ public String next(String pattern) { return next(Pattern.compile(pattern)); @@ -1095,7 +1065,7 @@ public final class Scanner implements Iterator { * negative sign (-) is added if the {@code Locale}-specific negative prefix or * suffix was present. Finally the resulting string is passed to * {@code BigDecimal(String) }. - * + * * @return the next token as a {@code BigDecimal}. * @throws IllegalStateException * if this {@code Scanner} has been closed. @@ -1104,7 +1074,6 @@ public final class Scanner implements Iterator { * @throws InputMismatchException * if the next token can not be translated into a valid * {@code BigDecimal}. - * @since Android 1.0 */ public BigDecimal nextBigDecimal() { checkClosed(); @@ -1131,7 +1100,7 @@ public final class Scanner implements Iterator { /** * Returns the next token as a {@code BigInteger}. This method will block if input is * being read. Equivalent to {@code nextBigInteger(DEFAULT_RADIX)}. - * + * * @return the next token as {@code BigInteger}. * @throws IllegalStateException * if this {@code Scanner} has been closed. @@ -1140,7 +1109,6 @@ public final class Scanner implements Iterator { * @throws InputMismatchException * if the next token can not be translated into a valid * {@code BigInteger}. - * @since Android 1.0 */ public BigInteger nextBigInteger() { return nextBigInteger(integerRadix); @@ -1156,7 +1124,7 @@ public final class Scanner implements Iterator { * {@code Locale}-specific negative prefix or suffix was present. Finally the * resulting String is passed to {@link BigInteger#BigInteger(String, int)}} * with the specified radix. - * + * * @param radix * the radix used to translate the token into a * {@code BigInteger}. @@ -1168,7 +1136,6 @@ public final class Scanner implements Iterator { * @throws InputMismatchException * if the next token can not be translated into a valid * {@code BigInteger}. - * @since Android 1.0 */ public BigInteger nextBigInteger(int radix) { checkClosed(); @@ -1195,7 +1162,7 @@ public final class Scanner implements Iterator { /** * Returns the next token as a {@code boolean}. This method will block if input is * being read. - * + * * @return the next token as a {@code boolean}. * @throws IllegalStateException * if this {@code Scanner} has been closed. @@ -1204,7 +1171,6 @@ public final class Scanner implements Iterator { * @throws InputMismatchException * if the next token can not be translated into a valid * {@code boolean} value. - * @since Android 1.0 */ public boolean nextBoolean() { return Boolean.parseBoolean(next(BOOLEAN_PATTERN)); @@ -1213,7 +1179,7 @@ public final class Scanner implements Iterator { /** * Returns the next token as a {@code byte}. This method will block if input is being * read. Equivalent to {@code nextByte(DEFAULT_RADIX)}. - * + * * @return the next token as a {@code byte}. * @throws IllegalStateException * if this {@code Scanner} has been closed. @@ -1222,7 +1188,6 @@ public final class Scanner implements Iterator { * @throws InputMismatchException * if the next token can not be translated into a valid * {@code byte} value. - * @since Android 1.0 */ public byte nextByte() { return nextByte(integerRadix); @@ -1238,7 +1203,7 @@ public final class Scanner implements Iterator { * {@code Locale}-specific negative prefix or suffix was present. Finally the * resulting String is passed to {@link Byte#parseByte(String, int)}} with * the specified radix. - * + * * @param radix * the radix used to translate the token into {@code byte} value. * @return the next token as a {@code byte}. @@ -1249,7 +1214,6 @@ public final class Scanner implements Iterator { * @throws InputMismatchException * if the next token can not be translated into a valid * {@code byte} value. - * @since Android 1.0 */ @SuppressWarnings("boxing") public byte nextByte(int radix) { @@ -1285,7 +1249,7 @@ public final class Scanner implements Iterator { * {@link Double#parseDouble(String)}}. If the token matches the localized * NaN or infinity strings, it is also passed to * {@link Double#parseDouble(String)}}. - * + * * @return the next token as a {@code double}. * @throws IllegalStateException * if this {@code Scanner} has been closed. @@ -1294,7 +1258,6 @@ public final class Scanner implements Iterator { * @throws InputMismatchException * if the next token can not be translated into a valid * {@code double} value. - * @since Android 1.0 */ @SuppressWarnings("boxing") public double nextDouble() { @@ -1330,7 +1293,7 @@ public final class Scanner implements Iterator { * {@link Float#parseFloat(String)}}.If the token matches the localized NaN * or infinity strings, it is also passed to * {@link Float#parseFloat(String)}}. - * + * * @return the next token as a {@code float}. * @throws IllegalStateException * if this {@code Scanner} has been closed. @@ -1339,7 +1302,6 @@ public final class Scanner implements Iterator { * @throws InputMismatchException * if the next token can not be translated into a valid * {@code float} value. - * @since Android 1.0 */ @SuppressWarnings("boxing") public float nextFloat() { @@ -1367,7 +1329,7 @@ public final class Scanner implements Iterator { /** * Returns the next token as an {@code int}. This method will block if input is being * read. Equivalent to {@code nextInt(DEFAULT_RADIX)}. - * + * * @return the next token as an {@code int} * @throws IllegalStateException * if this {@code Scanner} has been closed. @@ -1376,7 +1338,6 @@ public final class Scanner implements Iterator { * @throws InputMismatchException * if the next token can not be translated into a valid * {@code int} value. - * @since Android 1.0 */ public int nextInt() { return nextInt(integerRadix); @@ -1392,7 +1353,7 @@ public final class Scanner implements Iterator { * {@code Locale}-specific negative prefix or suffix was present. Finally the * resulting String is passed to {@link Integer#parseInt(String, int)} with * the specified radix. - * + * * @param radix * the radix used to translate the token into an {@code int} * value. @@ -1404,7 +1365,6 @@ public final class Scanner implements Iterator { * @throws InputMismatchException * if the next token can not be translated into a valid * {@code int} value. - * @since Android 1.0 */ @SuppressWarnings("boxing") public int nextInt(int radix) { @@ -1416,7 +1376,7 @@ public final class Scanner implements Iterator { return (Integer) obj; } Pattern integerPattern = getIntegerPattern(radix); - String intString=next(integerPattern); + String intString = next(integerPattern); intString = removeLocaleInfo(intString, DataType.INT); int intValue = 0; try { @@ -1435,13 +1395,12 @@ public final class Scanner implements Iterator { * searching, if no line terminator is found, then a large amount of input * will be cached. If no line at all can be found, a {@code NoSuchElementException} * will be thrown. - * + * * @return the skipped line. * @throws IllegalStateException * if the {@code Scanner} is closed. * @throws NoSuchElementException * if no line can be found, e.g. when input is an empty string. - * @since Android 1.0 */ public String nextLine() { checkClosed(); @@ -1451,7 +1410,8 @@ public final class Scanner implements Iterator { String result = null; while (true) { if (matcher.find()) { - if (inputExhausted || matcher.end() != bufferLength) { + if (inputExhausted || matcher.end() != bufferLength + || bufferLength < buffer.capacity()) { matchSuccessful = true; findStartIndex = matcher.end(); result = matcher.group(); @@ -1466,7 +1426,7 @@ public final class Scanner implements Iterator { if (!inputExhausted) { readMore(); resetMatcher(); - } + } } // Find text without line terminator here. if (null != result) { @@ -1481,7 +1441,7 @@ public final class Scanner implements Iterator { /** * Returns the next token as a {@code long}. This method will block if input is being * read. Equivalent to {@code nextLong(DEFAULT_RADIX)}. - * + * * @return the next token as a {@code long}. * @throws IllegalStateException * if this {@code Scanner} has been closed. @@ -1490,7 +1450,6 @@ public final class Scanner implements Iterator { * @throws InputMismatchException * if the next token can not be translated into a valid * {@code long} value. - * @since Android 1.0 */ public long nextLong() { return nextLong(integerRadix); @@ -1506,7 +1465,7 @@ public final class Scanner implements Iterator { * {@code Locale}-specific negative prefix or suffix was present. Finally the * resulting String is passed to {@link Long#parseLong(String, int)}} with * the specified radix. - * + * * @param radix * the radix used to translate the token into a {@code long} * value. @@ -1518,7 +1477,6 @@ public final class Scanner implements Iterator { * @throws InputMismatchException * if the next token can not be translated into a valid * {@code long} value. - * @since Android 1.0 */ @SuppressWarnings("boxing") public long nextLong(int radix) { @@ -1546,7 +1504,7 @@ public final class Scanner implements Iterator { /** * Returns the next token as a {@code short}. This method will block if input is being * read. Equivalent to {@code nextShort(DEFAULT_RADIX)}. - * + * * @return the next token as a {@code short}. * @throws IllegalStateException * if this {@code Scanner} has been closed. @@ -1555,7 +1513,6 @@ public final class Scanner implements Iterator { * @throws InputMismatchException * if the next token can not be translated into a valid * {@code short} value. - * @since Android 1.0 */ public short nextShort() { return nextShort(integerRadix); @@ -1571,7 +1528,7 @@ public final class Scanner implements Iterator { * {@code Locale}-specific negative prefix or suffix was present. Finally the * resulting String is passed to {@link Short#parseShort(String, int)}} * with the specified radix. - * + * * @param radix * the radix used to translate the token into {@code short} * value. @@ -1583,7 +1540,6 @@ public final class Scanner implements Iterator { * @throws InputMismatchException * if the next token can not be translated into a valid * {@code short} value. - * @since Android 1.0 */ @SuppressWarnings("boxing") public short nextShort(int radix) { @@ -1610,9 +1566,8 @@ public final class Scanner implements Iterator { /** * Return the radix of this {@code Scanner}. - * + * * @return the radix of this {@code Scanner} - * @since Android 1.0 */ public int radix() { return integerRadix; @@ -1624,8 +1579,8 @@ public final class Scanner implements Iterator { * skipped. If an anchored match of the specified pattern succeeds, the corresponding input * will also be skipped. Otherwise, a {@code NoSuchElementException} will be thrown. * Patterns that can match a lot of input may cause the {@code Scanner} to read - * in a large amount of input. - * + * in a large amount of input. + * * @param pattern * used to skip over input. * @return the {@code Scanner} itself. @@ -1633,7 +1588,6 @@ public final class Scanner implements Iterator { * if the {@code Scanner} is closed. * @throws NoSuchElementException * if the specified pattern match fails. - * @since Android 1.0 */ public Scanner skip(Pattern pattern) { checkClosed(); @@ -1668,14 +1622,13 @@ public final class Scanner implements Iterator { * the constructed pattern to match input starting from the current position. The * delimiter will be ignored. This call is the same as invoke * {@code skip(Pattern.compile(pattern))}. - * + * * @param pattern * the string used to construct a pattern which in turn is used to * match input. * @return the {@code Scanner} itself. * @throws IllegalStateException * if the {@code Scanner} is closed. - * @since Android 1.0 */ public Scanner skip(String pattern) { return skip(Pattern.compile(pattern)); @@ -1684,9 +1637,8 @@ public final class Scanner implements Iterator { /** * Returns a string representation of this {@code Scanner}. The information * returned may be helpful for debugging. The format of the string is unspecified. - * + * * @return a string represendation of this {@code Scanner}. - * @since Android 1.0 */ @Override public String toString() { @@ -1702,11 +1654,10 @@ public final class Scanner implements Iterator { /** * Sets the delimiting pattern of this {@code Scanner}. - * + * * @param pattern * the delimiting pattern to use. * @return this {@code Scanner}. - * @since Android 1.0 */ public Scanner useDelimiter(Pattern pattern) { delimiter = pattern; @@ -1716,11 +1667,10 @@ public final class Scanner implements Iterator { /** * Sets the delimiting pattern of this {@code Scanner} with a pattern compiled from * the supplied string value. - * + * * @param pattern * a string from which a {@code Pattern} can be compiled. * @return this {@code Scanner}. - * @since Android 1.0 */ public Scanner useDelimiter(String pattern) { return useDelimiter(Pattern.compile(pattern)); @@ -1728,11 +1678,10 @@ public final class Scanner implements Iterator { /** * Sets the {@code Locale} of this {@code Scanner} to a specified {@code Locale}. - * + * * @param l * the specified {@code Locale} to use. * @return this {@code Scanner}. - * @since Android 1.0 */ public Scanner useLocale(Locale l) { if (null == l) { @@ -1744,11 +1693,10 @@ public final class Scanner implements Iterator { /** * Sets the radix of this {@code Scanner} to the specified radix. - * + * * @param radix * the specified radix to use. * @return this {@code Scanner}. - * @since Android 1.0 */ public Scanner useRadix(int radix) { if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) { @@ -1761,10 +1709,9 @@ public final class Scanner implements Iterator { /** * Remove is not a supported operation on {@code Scanner}. - * + * * @throws UnsupportedOperationException * if this method is invoked. - * @since Android 1.0 */ public void remove() { throw new UnsupportedOperationException(); @@ -1778,7 +1725,7 @@ public final class Scanner implements Iterator { buffer.limit(0); matcher = delimiter.matcher(buffer); } - + /* * Check the {@code Scanner}'s state, if it is closed, IllegalStateException will be * thrown. @@ -1788,7 +1735,7 @@ public final class Scanner implements Iterator { throw new IllegalStateException(); } } - + /* * Check the inputed pattern. If it is null, then a NullPointerException * will be thrown out. @@ -1824,7 +1771,7 @@ public final class Scanner implements Iterator { private void recoverPreviousStatus() { findStartIndex = preStartIndex; } - + /* * Get integer's pattern */ @@ -1834,12 +1781,12 @@ public final class Scanner implements Iterator { .getString("KA00e", radix)); //$NON-NLS-1$ } decimalFormat = (DecimalFormat) NumberFormat.getInstance(locale); - - String allAvailableDigits="0123456789abcdefghijklmnopqrstuvwxyz"; //$NON-NLS-1$ - String ASCIIDigit=allAvailableDigits.substring(0, radix); - String nonZeroASCIIDigit=allAvailableDigits.substring(1, radix); - StringBuilder digit = new StringBuilder("((?i)[").append(ASCIIDigit) //$NON-NLS-1$ + String allAvailableDigits = "0123456789abcdefghijklmnopqrstuvwxyz"; //$NON-NLS-1$ + String ASCIIDigit = allAvailableDigits.substring(0, radix); + String nonZeroASCIIDigit = allAvailableDigits.substring(1, radix); + + StringBuilder digit = new StringBuilder("((?i)[").append(ASCIIDigit) //$NON-NLS-1$ .append("]|\\p{javaDigit})"); //$NON-NLS-1$ StringBuilder nonZeroDigit = new StringBuilder("((?i)[").append( //$NON-NLS-1$ nonZeroASCIIDigit).append("]|([\\p{javaDigit}&&[^0]]))"); //$NON-NLS-1$ @@ -1912,16 +1859,16 @@ public final class Scanner implements Iterator { } /* - * Add the {@code Locale}-specific positive prefixes and suffixes to the pattern + * Add the locale specific positive prefixes and suffixes to the pattern */ private StringBuilder addPositiveSign(StringBuilder unSignNumeral) { String positivePrefix = ""; //$NON-NLS-1$ String positiveSuffix = ""; //$NON-NLS-1$ if (!decimalFormat.getPositivePrefix().equals("")) { //$NON-NLS-1$ - positivePrefix = "\\Q" + decimalFormat.getPositivePrefix()+"\\E"; //$NON-NLS-1$ //$NON-NLS-2$ + positivePrefix = "\\Q" + decimalFormat.getPositivePrefix() + "\\E"; //$NON-NLS-1$ //$NON-NLS-2$ } if (!decimalFormat.getPositiveSuffix().equals("")) { //$NON-NLS-1$ - positiveSuffix = "\\Q" + decimalFormat.getPositiveSuffix()+"\\E"; //$NON-NLS-1$ //$NON-NLS-2$ + positiveSuffix = "\\Q" + decimalFormat.getPositiveSuffix() + "\\E"; //$NON-NLS-1$ //$NON-NLS-2$ } StringBuilder signedNumeral = new StringBuilder() .append(positivePrefix).append(unSignNumeral).append( @@ -1930,16 +1877,16 @@ public final class Scanner implements Iterator { } /* - * Add the {@code Locale} specific negative prefixes and suffixes to the pattern + * Add the locale specific negative prefixes and suffixes to the pattern */ private StringBuilder addNegativeSign(StringBuilder unSignNumeral) { String negativePrefix = ""; //$NON-NLS-1$ String negativeSuffix = ""; //$NON-NLS-1$ if (!decimalFormat.getNegativePrefix().equals("")) { //$NON-NLS-1$ - negativePrefix = "\\Q" + decimalFormat.getNegativePrefix()+"\\E"; //$NON-NLS-1$//$NON-NLS-2$ + negativePrefix = "\\Q" + decimalFormat.getNegativePrefix() + "\\E"; //$NON-NLS-1$//$NON-NLS-2$ } if (!decimalFormat.getNegativeSuffix().equals("")) { //$NON-NLS-1$ - negativeSuffix = "\\Q" + decimalFormat.getNegativeSuffix()+"\\E"; //$NON-NLS-1$//$NON-NLS-2$ + negativeSuffix = "\\Q" + decimalFormat.getNegativeSuffix() + "\\E"; //$NON-NLS-1$//$NON-NLS-2$ } StringBuilder signedNumeral = new StringBuilder() .append(negativePrefix).append(unSignNumeral).append( @@ -1948,15 +1895,14 @@ public final class Scanner implements Iterator { } /* - * Remove {@code Locale} related information from float String + * Remove locale related information from float String */ private String removeLocaleInfoFromFloat(String floatString) { // If the token is HexFloat - if (-1 != floatString.indexOf('x') - || -1 != floatString.indexOf('X')) { + if (-1 != floatString.indexOf('x') || -1 != floatString.indexOf('X')) { return floatString; } - + int exponentIndex; String decimalNumeralString; String exponentString; @@ -1968,11 +1914,11 @@ public final class Scanner implements Iterator { floatString.length()); decimalNumeralString = removeLocaleInfo(decimalNumeralString, DataType.FLOAT); - return decimalNumeralString + "e" + exponentString; //$NON-NLS-1$ + return decimalNumeralString + "e" + exponentString; //$NON-NLS-1$ } return removeLocaleInfo(floatString, DataType.FLOAT); } - + /* * Remove the locale specific prefixes, group separators, and locale * specific suffixes from input string @@ -2001,12 +1947,12 @@ public final class Scanner implements Iterator { } } if (DataType.FLOAT == type) { - if (tokenBuilder.toString().equals(decimalFormat.getDecimalFormatSymbols() - .getNaN())) { - result.append("NaN");//$NON-NLS-1$ - } else if (tokenBuilder.toString().equals(decimalFormat - .getDecimalFormatSymbols().getInfinity())) { - result.append("Infinity");//$NON-NLS-1$ + if (tokenBuilder.toString().equals( + decimalFormat.getDecimalFormatSymbols().getNaN())) { + result.append("NaN");//$NON-NLS-1$ + } else if (tokenBuilder.toString().equals( + decimalFormat.getDecimalFormatSymbols().getInfinity())) { + result.append("Infinity");//$NON-NLS-1$ } else { for (int i = 0; i < tokenBuilder.length(); i++) { if (-1 != Character.digit(tokenBuilder.charAt(i), 10)) { @@ -2029,8 +1975,9 @@ public final class Scanner implements Iterator { } return result.toString(); } + /* - * remove positive and negative sign from the parameter stringBuilder, and + * Remove positive and negative sign from the parameter stringBuilder, and * return whether the input string is negative */ private boolean removeLocaleSign(StringBuilder tokenBuilder) { @@ -2106,7 +2053,7 @@ public final class Scanner implements Iterator { } /* - * Find prefixed delimiter + * Find prefix delimiter */ private int findPreDelimiter() { int tokenStartIndex; @@ -2166,7 +2113,7 @@ public final class Scanner implements Iterator { } /* - * Find postfixed delimiter + * Find postfix delimiter */ private int findPostDelimiter() { int tokenEndIndex = 0; @@ -2215,7 +2162,8 @@ public final class Scanner implements Iterator { } } catch (IOException e) { // Consider the scenario: readable puts 4 chars into - // buffer and then an IOException is thrown out. In this case, buffer is + // buffer and then an IOException is thrown out. In this case, + // buffer is // actually grown, but readable.read() will never return. bufferLength = buffer.position(); /* @@ -2226,7 +2174,6 @@ public final class Scanner implements Iterator { lastIOException = e; } - buffer.flip(); buffer.position(oldPosition); if (-1 == readCount) { diff --git a/libcore/luni/src/main/java/java/util/Set.java b/libcore/luni/src/main/java/java/util/Set.java index f52666ddf..109c8df1a 100644 --- a/libcore/luni/src/main/java/java/util/Set.java +++ b/libcore/luni/src/main/java/java/util/Set.java @@ -20,8 +20,8 @@ package java.util; /** * A {@code Set} is a data structure which does not allow duplicate elements. - * - * @since Android 1.0 + * + * @since 1.2 */ public interface Set extends Collection { @@ -38,7 +38,6 @@ public interface Set extends Collection { * when the class of the object is inappropriate for this set. * @throws IllegalArgumentException * when the object cannot be added to this set. - * @since Android 1.0 */ public boolean add(E object); @@ -55,7 +54,6 @@ public interface Set extends Collection { * when the class of an object is inappropriate for this set. * @throws IllegalArgumentException * when an object cannot be added to this set. - * @since Android 1.0 */ public boolean addAll(Collection collection); @@ -66,7 +64,6 @@ public interface Set extends Collection { * when removing from this set is not supported. * @see #isEmpty * @see #size - * @since Android 1.0 */ public void clear(); @@ -77,7 +74,6 @@ public interface Set extends Collection { * the object to search for. * @return {@code true} if object is an element of this set, {@code false} * otherwise. - * @since Android 1.0 */ public boolean contains(Object object); @@ -88,7 +84,6 @@ public interface Set extends Collection { * the collection of objects. * @return {@code true} if all objects in the specified collection are * elements of this set, {@code false} otherwise. - * @since Android 1.0 */ public boolean containsAll(Collection collection); @@ -103,7 +98,6 @@ public interface Set extends Collection { * @return boolean {@code true} if the object is the same as this object, * and {@code false} if it is different from this object. * @see #hashCode - * @since Android 1.0 */ public boolean equals(Object object); @@ -114,7 +108,6 @@ public interface Set extends Collection { * @return the hash code of this set. * * @see #equals - * @since Android 1.0 */ public int hashCode(); @@ -124,7 +117,6 @@ public interface Set extends Collection { * @return {@code true} if this set has no elements, {@code false} * otherwise. * @see #size - * @since Android 1.0 */ public boolean isEmpty(); @@ -134,7 +126,6 @@ public interface Set extends Collection { * * @return an iterator on the elements of this set. * @see Iterator - * @since Android 1.0 */ public Iterator iterator(); @@ -146,7 +137,6 @@ public interface Set extends Collection { * @return {@code true} if this set was modified, {@code false} otherwise. * @throws UnsupportedOperationException * when removing from this set is not supported. - * @since Android 1.0 */ public boolean remove(Object object); @@ -158,7 +148,6 @@ public interface Set extends Collection { * @return {@code true} if this set was modified, {@code false} otherwise. * @throws UnsupportedOperationException * when removing from this set is not supported. - * @since Android 1.0 */ public boolean removeAll(Collection collection); @@ -171,7 +160,6 @@ public interface Set extends Collection { * @return {@code true} if this set was modified, {@code false} otherwise. * @throws UnsupportedOperationException * when removing from this set is not supported. - * @since Android 1.0 */ public boolean retainAll(Collection collection); @@ -179,7 +167,6 @@ public interface Set extends Collection { * Returns the number of elements in this set. * * @return the number of elements in this set. - * @since Android 1.0 */ public int size(); @@ -187,7 +174,6 @@ public interface Set extends Collection { * Returns an array containing all elements contained in this set. * * @return an array of the elements from this set. - * @since Android 1.0 */ public Object[] toArray(); @@ -205,7 +191,6 @@ public interface Set extends Collection { * when the type of an element in this set cannot be stored in * the type of the specified array. * @see Collection#toArray(Object[]) - * @since Android 1.0 */ public T[] toArray(T[] array); } diff --git a/libcore/luni/src/main/java/java/util/SimpleTimeZone.java b/libcore/luni/src/main/java/java/util/SimpleTimeZone.java index 18d076f02..702b6efbc 100644 --- a/libcore/luni/src/main/java/java/util/SimpleTimeZone.java +++ b/libcore/luni/src/main/java/java/util/SimpleTimeZone.java @@ -14,12 +14,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/** -******************************************************************************* -* Copyright (C) 1996-2008, International Business Machines Corporation and * -* others. All Rights Reserved. * -******************************************************************************* -*/ + +// BEGIN android-note +// This implementation is based on an old version of Apache Harmony. The current +// Harmony uses ICU4J, which makes it much simpler. We should consider updating +// this implementation to leverage ICU4JNI. +// END android-note package java.util; @@ -27,33 +27,41 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectStreamField; +// BEGIN android-removed +// import java.security.AccessController; +// import java.security.PrivilegedAction; +// END android-removed import org.apache.harmony.luni.util.Msg; -// BEGIN android-note -// The class javadoc and some of the method descriptions are copied from ICU4J -// source files. Changes have been made to the copied descriptions. -// The icu license header was added to this file. -// END android-note /** * {@code SimpleTimeZone} is a concrete subclass of {@code TimeZone} * that represents a time zone for use with a Gregorian calendar. This class * does not handle historical changes. - *

          + *

          * Use a negative value for {@code dayOfWeekInMonth} to indicate that * {@code SimpleTimeZone} should count from the end of the month * backwards. For example, Daylight Savings Time ends at the last * (dayOfWeekInMonth = -1) Sunday in October, at 2 AM in standard time. - * + * * @see Calendar * @see GregorianCalendar * @see TimeZone - * @since Android 1.0 */ public class SimpleTimeZone extends TimeZone { - + private static final long serialVersionUID = -403250971215465050L; + // BEGIN android-removed + // private static com.ibm.icu.util.TimeZone getICUTimeZone(final String name){ + // return AccessController.doPrivileged(new PrivilegedAction(){ + // public com.ibm.icu.util.TimeZone run() { + // return com.ibm.icu.util.TimeZone.getTimeZone(name); + // } + // }); + // } + // END android-removed + private int rawOffset; private int startYear, startMonth, startDay, startDayOfWeek, startTime; @@ -67,8 +75,6 @@ public class SimpleTimeZone extends TimeZone { /** * The constant for representing a start or end time in GMT time mode. - * - * @since Android 1.0 */ public static final int UTC_TIME = 2; @@ -76,8 +82,6 @@ public class SimpleTimeZone extends TimeZone { * The constant for representing a start or end time in standard local time mode, * based on timezone's raw offset from GMT; does not include Daylight * savings. - * - * @since Android 1.0 */ public static final int STANDARD_TIME = 1; @@ -85,8 +89,6 @@ public class SimpleTimeZone extends TimeZone { * The constant for representing a start or end time in local wall clock time * mode, based on timezone's adjusted offset from GMT; includes * Daylight savings. - * - * @since Android 1.0 */ public static final int WALL_TIME = 0; @@ -96,22 +98,37 @@ public class SimpleTimeZone extends TimeZone { private int dstSavings = 3600000; + // BEGIN android-removed + // private final transient com.ibm.icu.util.TimeZone icuTZ; + // + // private final transient boolean isSimple; + // END android-removed + /** * Constructs a {@code SimpleTimeZone} with the given base time zone offset from GMT * and time zone ID. Timezone IDs can be obtained from * {@code TimeZone.getAvailableIDs}. Normally you should use {@code TimeZone.getDefault} to * construct a {@code TimeZone}. - * + * * @param offset * the given base time zone offset to GMT. * @param name * the time zone ID which is obtained from * {@code TimeZone.getAvailableIDs}. - * @since Android 1.0 */ - public SimpleTimeZone(int offset, String name) { + public SimpleTimeZone(int offset, final String name) { setID(name); rawOffset = offset; + // BEGIN android-removed + // icuTZ = getICUTimeZone(name); + // if (icuTZ instanceof com.ibm.icu.util.SimpleTimeZone) { + // isSimple = true; + // icuTZ.setRawOffset(offset); + // } else { + // isSimple = false; + // } + // useDaylight = icuTZ.useDaylightTime(); + // END android-removed } /** @@ -146,8 +163,8 @@ public class SimpleTimeZone extends TimeZone { * The above examples refer to the {@code startMonth}, {@code startDay}, and {@code startDayOfWeek}; * the same applies for the {@code endMonth}, {@code endDay}, and {@code endDayOfWeek}. *

          - * The daylight savings time difference is set to the default value: one hour. - * + * The daylight savings time difference is set to the default value: one hour. + * * @param offset * the given base time zone offset to GMT. * @param name @@ -182,7 +199,6 @@ public class SimpleTimeZone extends TimeZone { * @throws IllegalArgumentException * if the month, day, dayOfWeek, or time parameters are out of * range for the start or end rule. - * @since Android 1.0 */ public SimpleTimeZone(int offset, String name, int startMonth, int startDay, int startDayOfWeek, int startTime, int endMonth, @@ -193,9 +209,9 @@ public class SimpleTimeZone extends TimeZone { /** * Constructs a {@code SimpleTimeZone} with the given base time zone offset from GMT, - * time zone ID, times to start and end the daylight savings time, and - * the daylight savings time difference in milliseconds - * + * time zone ID, times to start and end the daylight savings time, and + * the daylight savings time difference in milliseconds. + * * @param offset * the given base time zone offset to GMT. * @param name @@ -212,7 +228,7 @@ public class SimpleTimeZone extends TimeZone { * description of {@link #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)} for an example. * @param startTime * The daylight savings starting time in local wall time, which - * is standard time in this case. Please see the description of + * is standard time in this case. Please see the description of * {@link #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)} for an example. * @param endMonth * the daylight savings ending month. Month is 0-based. eg, 0 for @@ -221,7 +237,7 @@ public class SimpleTimeZone extends TimeZone { * the daylight savings ending day-of-week-in-month. Please see * the description of {@link #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)} for an example. * @param endDayOfWeek - * the daylight savings ending day-of-week. Please see the description of + * the daylight savings ending day-of-week. Please see the description of * {@link #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)} for an example. * @param endTime * the daylight savings ending time in local wall time, which is @@ -229,31 +245,50 @@ public class SimpleTimeZone extends TimeZone { * for an example. * @param daylightSavings * the daylight savings time difference in milliseconds. - * @exception IllegalArgumentException - * the month, day, dayOfWeek, or time parameters are out of + * @throws IllegalArgumentException + * if the month, day, dayOfWeek, or time parameters are out of * range for the start or end rule. - * @since Android 1.0 */ public SimpleTimeZone(int offset, String name, int startMonth, int startDay, int startDayOfWeek, int startTime, int endMonth, int endDay, int endDayOfWeek, int endTime, int daylightSavings) { + // BEGIN android-changed + // icuTZ = getICUTimeZone(name); + // if (icuTZ instanceof com.ibm.icu.util.SimpleTimeZone) { + // isSimple = true; + // com.ibm.icu.util.SimpleTimeZone tz = (com.ibm.icu.util.SimpleTimeZone)icuTZ; + // tz.setRawOffset(offset); + // tz.setStartRule(startMonth, startDay, startDayOfWeek, startTime); + // tz.setEndRule(endMonth, endDay, endDayOfWeek, endTime); + // tz.setDSTSavings(daylightSavings); + // } else { + // isSimple = false; + // } + // setID(name); + // rawOffset = offset; this(offset, name); + // END android-changed if (daylightSavings <= 0) { - throw new IllegalArgumentException(Msg.getString("K00e9", daylightSavings)); //$NON-NLS-1$ + throw new IllegalArgumentException(Msg.getString( + "K00e9", daylightSavings)); //$NON-NLS-1$ } dstSavings = daylightSavings; setStartRule(startMonth, startDay, startDayOfWeek, startTime); setEndRule(endMonth, endDay, endDayOfWeek, endTime); + + // BEGIN android-removed + // useDaylight = daylightSavings > 0 || icuTZ.useDaylightTime(); + // END android-removed } /** * Construct a {@code SimpleTimeZone} with the given base time zone offset from GMT, - * time zone ID, times to start and end the daylight savings time including a + * time zone ID, times to start and end the daylight savings time including a * mode specifier, the daylight savings time difference in milliseconds. * The mode specifies either {@link #WALL_TIME}, {@link #STANDARD_TIME}, or * {@link #UTC_TIME}. - * + * * @param offset * the given base time zone offset to GMT. * @param name @@ -269,21 +304,21 @@ public class SimpleTimeZone extends TimeZone { * the daylight savings starting day-of-week. Please see the * description of {@link #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)} for an example. * @param startTime - * the time of day in milliseconds on which daylight savings + * the time of day in milliseconds on which daylight savings * time starts, based on the {@code startTimeMode}. * @param startTimeMode * the mode (UTC, standard, or wall time) of the start time * value. - * @param endDay - * the day of the week on which daylight savings time ends. + * @param endDay + * the day of the week on which daylight savings time ends. * @param endMonth * the daylight savings ending month. The month indexing is 0-based. eg, 0 for * January. * @param endDayOfWeek - * the daylight savings ending day-of-week. Please see the description of + * the daylight savings ending day-of-week. Please see the description of * {@link #SimpleTimeZone(int, String, int, int, int, int, int, int, int, int)} for an example. * @param endTime - * the time of day in milliseconds on which daylight savings + * the time of day in milliseconds on which daylight savings * time ends, based on the {@code endTimeMode}. * @param endTimeMode * the mode (UTC, standard, or wall time) of the end time value. @@ -292,7 +327,6 @@ public class SimpleTimeZone extends TimeZone { * @throws IllegalArgumentException * if the month, day, dayOfWeek, or time parameters are out of * range for the start or end rule. - * @since Android 1.0 */ public SimpleTimeZone(int offset, String name, int startMonth, int startDay, int startDayOfWeek, int startTime, int startTimeMode, @@ -308,10 +342,9 @@ public class SimpleTimeZone extends TimeZone { /** * Returns a new {@code SimpleTimeZone} with the same ID, {@code rawOffset} and daylight * savings time rules as this SimpleTimeZone. - * + * * @return a shallow copy of this {@code SimpleTimeZone}. * @see java.lang.Cloneable - * @since Android 1.0 */ @Override public Object clone() { @@ -326,13 +359,12 @@ public class SimpleTimeZone extends TimeZone { * Compares the specified object to this {@code SimpleTimeZone} and returns whether they * are equal. The object must be an instance of {@code SimpleTimeZone} and have the * same internal data. - * + * * @param object * the object to compare with this object. * @return {@code true} if the specified object is equal to this * {@code SimpleTimeZone}, {@code false} otherwise. * @see #hashCode - * @since Android 1.0 */ @Override public boolean equals(Object object) { @@ -372,6 +404,8 @@ public class SimpleTimeZone extends TimeZone { checkDay(month, day); } + // BEGIN android-changed + // return icuTZ.getOffset(era, year, month, day, dayOfWeek, time); if (!useDaylightTime() || era != GregorianCalendar.AD || year < startYear) { return rawOffset; @@ -389,38 +423,39 @@ public class SimpleTimeZone extends TimeZone { int ruleDay = 0, daysInMonth, firstDayOfMonth = mod7(dayOfWeek - day); if (month == startMonth) { switch (startMode) { - case DOM_MODE: - ruleDay = startDay; - break; - case DOW_IN_MONTH_MODE: - if (startDay >= 0) { - ruleDay = mod7(startDayOfWeek - firstDayOfMonth) + 1 - + (startDay - 1) * 7; - } else { - daysInMonth = GregorianCalendar.DaysInMonth[startMonth]; - if (startMonth == Calendar.FEBRUARY && isLeapYear(year)) { - daysInMonth += 1; + case DOM_MODE: + ruleDay = startDay; + break; + case DOW_IN_MONTH_MODE: + if (startDay >= 0) { + ruleDay = mod7(startDayOfWeek - firstDayOfMonth) + 1 + + (startDay - 1) * 7; + } else { + daysInMonth = GregorianCalendar.DaysInMonth[startMonth]; + if (startMonth == Calendar.FEBRUARY && isLeapYear( + year)) { + daysInMonth += 1; + } + ruleDay = daysInMonth + + 1 + + mod7(startDayOfWeek + - (firstDayOfMonth + daysInMonth)) + + startDay * 7; } - ruleDay = daysInMonth - + 1 + break; + case DOW_GE_DOM_MODE: + ruleDay = startDay + mod7(startDayOfWeek - - (firstDayOfMonth + daysInMonth)) - + startDay * 7; - } - break; - case DOW_GE_DOM_MODE: - ruleDay = startDay - + mod7(startDayOfWeek - - (firstDayOfMonth + startDay - 1)); - break; - case DOW_LE_DOM_MODE: - ruleDay = startDay - + mod7(startDayOfWeek - - (firstDayOfMonth + startDay - 1)); - if (ruleDay != startDay) { - ruleDay -= 7; - } - break; + - (firstDayOfMonth + startDay - 1)); + break; + case DOW_LE_DOM_MODE: + ruleDay = startDay + + mod7(startDayOfWeek + - (firstDayOfMonth + startDay - 1)); + if (ruleDay != startDay) { + ruleDay -= 7; + } + break; } if (ruleDay > day || ruleDay == day && time < startTime) { return rawOffset; @@ -431,36 +466,38 @@ public class SimpleTimeZone extends TimeZone { int nextMonth = (month + 1) % 12; if (month == endMonth || (ruleTime < 0 && nextMonth == endMonth)) { switch (endMode) { - case DOM_MODE: - ruleDay = endDay; - break; - case DOW_IN_MONTH_MODE: - if (endDay >= 0) { - ruleDay = mod7(endDayOfWeek - firstDayOfMonth) + 1 - + (endDay - 1) * 7; - } else { - daysInMonth = GregorianCalendar.DaysInMonth[endMonth]; - if (endMonth == Calendar.FEBRUARY && isLeapYear(year)) { - daysInMonth++; + case DOM_MODE: + ruleDay = endDay; + break; + case DOW_IN_MONTH_MODE: + if (endDay >= 0) { + ruleDay = mod7(endDayOfWeek - firstDayOfMonth) + 1 + + (endDay - 1) * 7; + } else { + daysInMonth = GregorianCalendar.DaysInMonth[endMonth]; + if (endMonth == Calendar.FEBRUARY && isLeapYear(year)) { + daysInMonth++; + } + ruleDay = daysInMonth + + 1 + + mod7(endDayOfWeek + - (firstDayOfMonth + daysInMonth)) + endDay + * 7; } - ruleDay = daysInMonth - + 1 - + mod7(endDayOfWeek - - (firstDayOfMonth + daysInMonth)) + endDay - * 7; - } - break; - case DOW_GE_DOM_MODE: - ruleDay = endDay - + mod7(endDayOfWeek - (firstDayOfMonth + endDay - 1)); - break; - case DOW_LE_DOM_MODE: - ruleDay = endDay - + mod7(endDayOfWeek - (firstDayOfMonth + endDay - 1)); - if (ruleDay != endDay) { - ruleDay -= 7; - } - break; + break; + case DOW_GE_DOM_MODE: + ruleDay = endDay + + mod7( + endDayOfWeek - (firstDayOfMonth + endDay - 1)); + break; + case DOW_LE_DOM_MODE: + ruleDay = endDay + + mod7( + endDayOfWeek - (firstDayOfMonth + endDay - 1)); + if (ruleDay != endDay) { + ruleDay -= 7; + } + break; } int ruleMonth = endMonth; @@ -488,10 +525,13 @@ public class SimpleTimeZone extends TimeZone { } } return rawOffset + dstSavings; + // END android-changed } @Override public int getOffset(long time) { + // BEGIN android-changed + // return icuTZ.getOffset(time); if (!useDaylightTime()) { return rawOffset; } @@ -499,6 +539,7 @@ public class SimpleTimeZone extends TimeZone { daylightSavings = new GregorianCalendar(this); } return daylightSavings.getOffset(time + rawOffset); + // END android-changed } @Override @@ -509,10 +550,9 @@ public class SimpleTimeZone extends TimeZone { /** * Returns an integer hash code for the receiver. Objects which are equal * return the same value for this method. - * + * * @return the receiver's hash. * @see #equals - * @since Android 1.0 */ @Override public synchronized int hashCode() { @@ -548,6 +588,8 @@ public class SimpleTimeZone extends TimeZone { @Override public boolean inDaylightTime(Date time) { + // BEGIN android-changed + // return icuTZ.inDaylightTime(time); // check for null pointer long millis = time.getTime(); if (!useDaylightTime()) { @@ -557,6 +599,7 @@ public class SimpleTimeZone extends TimeZone { daylightSavings = new GregorianCalendar(this); } return daylightSavings.getOffset(millis + rawOffset) != rawOffset; + // END android-changed } private boolean isLeapYear(int year) { @@ -566,17 +609,18 @@ public class SimpleTimeZone extends TimeZone { return year % 4 == 0; } + // BEGIN android-added private int mod7(int num1) { int rem = num1 % 7; return (num1 < 0 && rem < 0) ? 7 + rem : rem; } + // END android-added /** * Sets the daylight savings offset in milliseconds for this {@code SimpleTimeZone}. - * + * * @param milliseconds * the daylight savings offset in milliseconds. - * @since Android 1.0 */ public void setDSTSavings(int milliseconds) { if (milliseconds > 0) { @@ -591,7 +635,8 @@ public class SimpleTimeZone extends TimeZone { throw new IllegalArgumentException(Msg.getString("K00e5", month)); //$NON-NLS-1$ } if (dayOfWeek < Calendar.SUNDAY || dayOfWeek > Calendar.SATURDAY) { - throw new IllegalArgumentException(Msg.getString("K00e7", dayOfWeek)); //$NON-NLS-1$ + throw new IllegalArgumentException(Msg + .getString("K00e7", dayOfWeek)); //$NON-NLS-1$ } if (time < 0 || time >= 24 * 3600000) { throw new IllegalArgumentException(Msg.getString("K00e8", time)); //$NON-NLS-1$ @@ -626,7 +671,8 @@ public class SimpleTimeZone extends TimeZone { checkDay(endMonth, endDay); } else { if (endDay < -5 || endDay > 5) { - throw new IllegalArgumentException(Msg.getString("K00f8", endDay)); //$NON-NLS-1$ + throw new IllegalArgumentException(Msg.getString( + "K00f8", endDay)); //$NON-NLS-1$ } } } @@ -637,7 +683,7 @@ public class SimpleTimeZone extends TimeZone { /** * Sets the rule which specifies the end of daylight savings time. - * + * * @param month * the {@code Calendar} month in which daylight savings time ends. * @param dayOfMonth @@ -646,7 +692,6 @@ public class SimpleTimeZone extends TimeZone { * @param time * the time of day in milliseconds standard time on which * daylight savings time ends. - * @since Android 1.0 */ public void setEndRule(int month, int dayOfMonth, int time) { endMonth = month; @@ -654,11 +699,17 @@ public class SimpleTimeZone extends TimeZone { endDayOfWeek = 0; // Initialize this value for hasSameRules() endTime = time; setEndMode(); + // BEGIN android-removed + // if (isSimple) { + // ((com.ibm.icu.util.SimpleTimeZone) icuTZ).setEndRule(month, + // dayOfMonth, time); + // } + // END android-removed } /** * Sets the rule which specifies the end of daylight savings time. - * + * * @param month * the {@code Calendar} month in which daylight savings time ends. * @param day @@ -670,7 +721,6 @@ public class SimpleTimeZone extends TimeZone { * @param time * the time of day in milliseconds standard time on which * daylight savings time ends. - * @since Android 1.0 */ public void setEndRule(int month, int day, int dayOfWeek, int time) { endMonth = month; @@ -678,11 +728,17 @@ public class SimpleTimeZone extends TimeZone { endDayOfWeek = dayOfWeek; endTime = time; setEndMode(); + // BEGIN android-removed + // if (isSimple) { + // ((com.ibm.icu.util.SimpleTimeZone) icuTZ).setEndRule(month, day, + // dayOfWeek, time); + // } + // END android-removed } /** * Sets the rule which specifies the end of daylight savings time. - * + * * @param month * the {@code Calendar} month in which daylight savings time ends. * @param day @@ -695,7 +751,6 @@ public class SimpleTimeZone extends TimeZone { * ends. * @param after * selects the day after or before the day of month. - * @since Android 1.0 */ public void setEndRule(int month, int day, int dayOfWeek, int time, boolean after) { @@ -704,18 +759,26 @@ public class SimpleTimeZone extends TimeZone { endDayOfWeek = -dayOfWeek; endTime = time; setEndMode(); + // BEGIN android-removed + // if (isSimple) { + // ((com.ibm.icu.util.SimpleTimeZone) icuTZ).setEndRule(month, day, + // dayOfWeek, time, after); + // } + // END android-removed } /** * Sets the offset for standard time from GMT for this {@code SimpleTimeZone}. - * + * * @param offset * the offset from GMT of standard time in milliseconds. - * @since Android 1.0 */ @Override public void setRawOffset(int offset) { rawOffset = offset; + // BEGIN android-removed + // icuTZ.setRawOffset(offset); + // END android-removed } private void setStartMode() { @@ -740,7 +803,8 @@ public class SimpleTimeZone extends TimeZone { checkDay(startMonth, startDay); } else { if (startDay < -5 || startDay > 5) { - throw new IllegalArgumentException(Msg.getString("K00f8", startDay)); //$NON-NLS-1$ + throw new IllegalArgumentException(Msg.getString( + "K00f8", startDay)); //$NON-NLS-1$ } } } @@ -751,7 +815,7 @@ public class SimpleTimeZone extends TimeZone { /** * Sets the rule which specifies the start of daylight savings time. - * + * * @param month * the {@code Calendar} month in which daylight savings time starts. * @param dayOfMonth @@ -760,7 +824,6 @@ public class SimpleTimeZone extends TimeZone { * @param time * the time of day in milliseconds on which daylight savings time * starts. - * @since Android 1.0 */ public void setStartRule(int month, int dayOfMonth, int time) { startMonth = month; @@ -768,11 +831,17 @@ public class SimpleTimeZone extends TimeZone { startDayOfWeek = 0; // Initialize this value for hasSameRules() startTime = time; setStartMode(); + // BEGIN android-removed + // if (isSimple) { + // ((com.ibm.icu.util.SimpleTimeZone) icuTZ).setStartRule(month, + // dayOfMonth, time); + // } + // END android-removed } /** * Sets the rule which specifies the start of daylight savings time. - * + * * @param month * the {@code Calendar} month in which daylight savings time starts. * @param day @@ -784,7 +853,6 @@ public class SimpleTimeZone extends TimeZone { * @param time * the time of day in milliseconds on which daylight savings time * starts. - * @since Android 1.0 */ public void setStartRule(int month, int day, int dayOfWeek, int time) { startMonth = month; @@ -792,11 +860,17 @@ public class SimpleTimeZone extends TimeZone { startDayOfWeek = dayOfWeek; startTime = time; setStartMode(); + // BEGIN android-removed + // if (isSimple) { + // ((com.ibm.icu.util.SimpleTimeZone) icuTZ).setStartRule(month, day, + // dayOfWeek, time); + // } + // END android-removed } /** * Sets the rule which specifies the start of daylight savings time. - * + * * @param month * the {@code Calendar} month in which daylight savings time starts. * @param day @@ -809,7 +883,6 @@ public class SimpleTimeZone extends TimeZone { * starts. * @param after * selects the day after or before the day of month. - * @since Android 1.0 */ public void setStartRule(int month, int day, int dayOfWeek, int time, boolean after) { @@ -818,15 +891,20 @@ public class SimpleTimeZone extends TimeZone { startDayOfWeek = -dayOfWeek; startTime = time; setStartMode(); + // BEGIN android-removed + // if (isSimple) { + // ((com.ibm.icu.util.SimpleTimeZone) icuTZ).setStartRule(month, day, + // dayOfWeek, time, after); + // } + // END android-removed } /** * Sets the starting year for daylight savings time in this {@code SimpleTimeZone}. * Years before this start year will always be in standard time. - * + * * @param year * the starting year. - * @since Android 1.0 */ public void setStartYear(int year) { startYear = year; @@ -835,9 +913,8 @@ public class SimpleTimeZone extends TimeZone { /** * Returns the string representation of this {@code SimpleTimeZone}. - * + * * @return the string representation of this {@code SimpleTimeZone}. - * @since Android 1.0 */ @Override public String toString() { @@ -864,7 +941,7 @@ public class SimpleTimeZone extends TimeZone { + endMode + ",endMonth=" + endMonth + ",endDay=" + endDay //$NON-NLS-1$ //$NON-NLS-2$ + ",endDayOfWeek=" //$NON-NLS-1$ + (useDaylight && (endMode != DOM_MODE) ? endDayOfWeek + 1 : 0) - + ",endTime=" + endTime + "]"; //$NON-NLS-1$//$NON-NLS-2$ + + ",endTime=" + endTime + "]"; //$NON-NLS-1$//$NON-NLS-2$ } @Override diff --git a/libcore/luni/src/main/java/java/util/SortedMap.java b/libcore/luni/src/main/java/java/util/SortedMap.java index 10b2647bb..2fb25fd07 100644 --- a/libcore/luni/src/main/java/java/util/SortedMap.java +++ b/libcore/luni/src/main/java/java/util/SortedMap.java @@ -21,8 +21,6 @@ package java.util; /** * A map that has its keys ordered. The sorting is according to either the * natural ordering of its keys or the ordering given by a specified comparator. - * - * @since Android 1.0 */ public interface SortedMap extends Map { @@ -30,7 +28,6 @@ public interface SortedMap extends Map { * Returns the comparator used to compare keys in this sorted map. * * @return the comparator or {@code null} if the natural order is used. - * @since Android 1.0 */ public Comparator comparator(); @@ -38,9 +35,8 @@ public interface SortedMap extends Map { * Returns the first key in this sorted map. * * @return the first key in this sorted map. - * @exception NoSuchElementException + * @throws NoSuchElementException * if this sorted map is empty. - * @since Android 1.0 */ public K firstKey(); @@ -51,8 +47,7 @@ public interface SortedMap extends Map { *

          * Note: The returned map will not allow an insertion of a key outside the * specified range. - *

          - * + * * @param endKey * the high boundary of the range specified. * @return a sorted map where the keys are less than {@code endKey}. @@ -65,7 +60,6 @@ public interface SortedMap extends Map { * @throws IllegalArgumentException * if this map is itself a sorted map over a range of another * map and the specified key is outside of its range. - * @since Android 1.0 */ public SortedMap headMap(K endKey); @@ -73,9 +67,8 @@ public interface SortedMap extends Map { * Returns the last key in this sorted map. * * @return the last key in this sorted map. - * @exception NoSuchElementException + * @throws NoSuchElementException * if this sorted map is empty. - * @since Android 1.0 */ public K lastKey(); @@ -87,8 +80,7 @@ public interface SortedMap extends Map { *

          * Note: The returned map will not allow an insertion of a key outside the * specified range. - *

          - * + * * @param startKey * the low boundary of the range (inclusive). * @param endKey @@ -104,7 +96,6 @@ public interface SortedMap extends Map { * if the start key is greater than the end key, or if this map * is itself a sorted map over a range of another sorted map and * the specified range is outside of its range. - * @since Android 1.0 */ public SortedMap subMap(K startKey, K endKey); @@ -115,7 +106,6 @@ public interface SortedMap extends Map { *

          * Note: The returned map will not allow an insertion of a key outside the * specified range. - *

          * * @param startKey * the low boundary of the range specified. @@ -130,7 +120,6 @@ public interface SortedMap extends Map { * @throws IllegalArgumentException * if this map itself a sorted map over a range of another map * and the specified key is outside of its range. - * @since Android 1.0 */ public SortedMap tailMap(K startKey); } diff --git a/libcore/luni/src/main/java/java/util/SortedSet.java b/libcore/luni/src/main/java/java/util/SortedSet.java index 24fa017a8..d387540b5 100644 --- a/libcore/luni/src/main/java/java/util/SortedSet.java +++ b/libcore/luni/src/main/java/java/util/SortedSet.java @@ -27,7 +27,6 @@ package java.util; * * @see Comparator * @see Comparable - * @since Android 1.0 */ public interface SortedSet extends Set { @@ -35,7 +34,6 @@ public interface SortedSet extends Set { * Returns the comparator used to compare elements in this {@code SortedSet}. * * @return a comparator or null if the natural ordering is used. - * @since Android 1.0 */ public Comparator comparator(); @@ -46,7 +44,6 @@ public interface SortedSet extends Set { * @return the first element. * @throws NoSuchElementException * when this {@code SortedSet} is empty. - * @since Android 1.0 */ public E first(); @@ -65,7 +62,6 @@ public interface SortedSet extends Set { * @throws NullPointerException * when the end element is null and this {@code SortedSet} does * not support null elements. - * @since Android 1.0 */ public SortedSet headSet(E end); @@ -76,7 +72,6 @@ public interface SortedSet extends Set { * @return the last element. * @throws NoSuchElementException * when this {@code SortedSet} is empty. - * @since Android 1.0 */ public E last(); @@ -101,7 +96,6 @@ public interface SortedSet extends Set { * {@code SortedSet} does not support null elements. * @throws IllegalArgumentException * when the start element is greater than the end element. - * @since Android 1.0 */ public SortedSet subSet(E start, E end); @@ -120,7 +114,6 @@ public interface SortedSet extends Set { * @throws NullPointerException * when the start element is null and this {@code SortedSet} * does not support null elements. - * @since Android 1.0 */ public SortedSet tailSet(E start); } diff --git a/libcore/luni/src/main/java/java/util/Stack.java b/libcore/luni/src/main/java/java/util/Stack.java index 8ecb765cf..1572a0896 100644 --- a/libcore/luni/src/main/java/java/util/Stack.java +++ b/libcore/luni/src/main/java/java/util/Stack.java @@ -17,22 +17,17 @@ package java.util; - /** * {@code Stack} is a Last-In/First-Out(LIFO) data structure which represents a * stack of objects. It enables users to pop to and push from the stack, * including null objects. There is no limit to the size of the stack. - * - * @since Android 1.0 */ public class Stack extends Vector { - + private static final long serialVersionUID = 1224463164541339165L; /** * Constructs a stack with the default size of {@code Vector}. - * - * @since Android 1.0 */ public Stack() { super(); @@ -40,9 +35,8 @@ public class Stack extends Vector { /** * Returns whether the stack is empty or not. - * + * * @return {@code true} if the stack is empty, {@code false} otherwise. - * @since Android 1.0 */ public boolean empty() { return elementCount == 0; @@ -50,16 +44,16 @@ public class Stack extends Vector { /** * Returns the element at the top of the stack without removing it. - * + * * @return the element at the top of the stack. * @throws EmptyStackException * if the stack is empty. * @see #pop - * @since Android 1.0 */ + @SuppressWarnings("unchecked") public synchronized E peek() { try { - return (E)elementData[elementCount - 1]; + return (E) elementData[elementCount - 1]; } catch (IndexOutOfBoundsException e) { throw new EmptyStackException(); } @@ -67,36 +61,35 @@ public class Stack extends Vector { /** * Returns the element at the top of the stack and removes it. - * + * * @return the element at the top of the stack. * @throws EmptyStackException * if the stack is empty. * @see #peek * @see #push - * @since Android 1.0 */ + @SuppressWarnings("unchecked") public synchronized E pop() { - try { - int index = elementCount - 1; - E obj = (E)elementData[index]; - removeElementAt(index); - return obj; - } catch (IndexOutOfBoundsException e) { + if (elementCount == 0) { throw new EmptyStackException(); } + final int index = --elementCount; + final E obj = (E) elementData[index]; + elementData[index] = null; + modCount++; + return obj; } /** * Pushes the specified object onto the top of the stack. - * + * * @param object * The object to be added on top of the stack. * @return the object argument. * @see #peek * @see #pop - * @since Android 1.0 */ - public synchronized E push(E object) { + public E push(E object) { addElement(object); return object; } @@ -104,17 +97,28 @@ public class Stack extends Vector { /** * Returns the index of the first occurrence of the object, starting from * the top of the stack. - * + * * @return the index of the first occurrence of the object, assuming that * the topmost object on the stack has a distance of one. * @param o * the object to be searched. - * @since Android 1.0 */ public synchronized int search(Object o) { - int index = lastIndexOf(o); - if (index >= 0) - return (elementCount - index); + final Object[] dumpArray = elementData; + final int size = elementCount; + if (o != null) { + for (int i = size - 1; i >= 0; i--) { + if (o.equals(dumpArray[i])) { + return size - i; + } + } + } else { + for (int i = size - 1; i >= 0; i--) { + if (dumpArray[i] == null) { + return size - i; + } + } + } return -1; } } diff --git a/libcore/luni/src/main/java/java/util/StringTokenizer.java b/libcore/luni/src/main/java/java/util/StringTokenizer.java index 3bbeefaa6..2800283c5 100644 --- a/libcore/luni/src/main/java/java/util/StringTokenizer.java +++ b/libcore/luni/src/main/java/java/util/StringTokenizer.java @@ -14,32 +14,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/** -******************************************************************************* -* Copyright (C) 1996-2006, International Business Machines Corporation and * -* others. All Rights Reserved. * -******************************************************************************* -*/ package java.util; - -// BEGIN android-note -// The class javadoc and some of the method descriptions are copied from ICU4J -// source files. Changes have been made to the copied descriptions. -// The icu license header was added to this file. -// END android-note /** - *

          * The {@code StringTokenizer} class allows an application to break a string * into tokens by performing code point comparison. The {@code StringTokenizer} * methods do not distinguish among identifiers, numbers, and quoted strings, * nor do they recognize and skip comments. - *

          *

          * The set of delimiters (the codepoints that separate tokens) may be specified * either at creation time or on a per-token basis. - *

          *

          * An instance of {@code StringTokenizer} behaves in one of three ways, * depending on whether it was created with the {@code returnDelimiters} flag @@ -55,45 +40,40 @@ package java.util; *

          * A token is thus either one delimiter code point, or a maximal sequence of * consecutive code points that are not delimiters. - *

          *

          * A {@code StringTokenizer} object internally maintains a current position * within the string to be tokenized. Some operations advance this current * position past the code point processed. - *

          *

          * A token is returned by taking a substring of the string that was used to * create the {@code StringTokenizer} object. - *

          *

          * Here's an example of the use of the default delimiter {@code StringTokenizer} * :

          - * + * *
            * StringTokenizer st = new StringTokenizer("this is a test");
            * while (st.hasMoreTokens()) {
            *     println(st.nextToken());
            * }
            * 
          - * + * *
          - *

          *

          * This prints the following output:

          - * + * *
            *     this
            *     is
            *     a
            *     test
            * 
          - * + * *
          - *

          *

          * Here's an example of how to use a {@code StringTokenizer} with a user * specified delimiter:

          - * + * *
            * StringTokenizer st = new StringTokenizer(
            *         "this is a test with supplementary characters \ud800\ud800\udc00\udc00",
          @@ -102,12 +82,11 @@ package java.util;
            *     println(st.nextToken());
            * }
            * 
          - * + * *
          - *

          *

          * This prints the following output:

          - * + * *
            *     this
            *     is
          @@ -119,14 +98,11 @@ package java.util;
            *     \ud800
            *     \udc00
            * 
          - * + * *
          - *

          - * - * @since Android 1.0 */ public class StringTokenizer implements Enumeration { - + private String string; private String delimiters; @@ -139,10 +115,9 @@ public class StringTokenizer implements Enumeration { * Constructs a new {@code StringTokenizer} for the parameter string using * whitespace as the delimiter. The {@code returnDelimiters} flag is set to * {@code false}. - * + * * @param string * the string to be tokenized. - * @since Android 1.0 */ public StringTokenizer(String string) { this(string, " \t\n\r\f", false); //$NON-NLS-1$ @@ -154,12 +129,11 @@ public class StringTokenizer implements Enumeration { * {@code false}. If {@code delimiters} is {@code null}, this constructor * doesn't throw an {@code Exception}, but later calls to some methods might * throw a {@code NullPointerException}. - * + * * @param string * the string to be tokenized. * @param delimiters * the delimiters to use. - * @since Android 1.0 */ public StringTokenizer(String string, String delimiters) { this(string, delimiters, false); @@ -171,14 +145,13 @@ public class StringTokenizer implements Enumeration { * parameter {@code returnDelimiters} is {@code true}. If {@code delimiters} * is null this constructor doesn't throw an {@code Exception}, but later * calls to some methods might throw a {@code NullPointerException}. - * + * * @param string * the string to be tokenized. * @param delimiters * the delimiters to use. * @param returnDelimiters * {@code true} to return each delimiter as a token. - * @since Android 1.0 */ public StringTokenizer(String string, String delimiters, boolean returnDelimiters) { @@ -193,10 +166,9 @@ public class StringTokenizer implements Enumeration { /** * Returns the number of unprocessed tokens remaining in the string. - * + * * @return number of tokens that can be retreived before an {@code * Exception} will result from a call to {@code nextToken()}. - * @since Android 1.0 */ public int countTokens() { int count = 0; @@ -221,9 +193,8 @@ public class StringTokenizer implements Enumeration { /** * Returns {@code true} if unprocessed tokens remain. This method is * implemented in order to satisfy the {@code Enumeration} interface. - * + * * @return {@code true} if unprocessed tokens remain. - * @since Android 1.0 */ public boolean hasMoreElements() { return hasMoreTokens(); @@ -231,11 +202,13 @@ public class StringTokenizer implements Enumeration { /** * Returns {@code true} if unprocessed tokens remain. - * + * * @return {@code true} if unprocessed tokens remain. - * @since Android 1.0 */ public boolean hasMoreTokens() { + if (delimiters == null) { + throw new NullPointerException(); + } int length = string.length(); if (position < length) { if (returnDelimiters) @@ -253,11 +226,10 @@ public class StringTokenizer implements Enumeration { /** * Returns the next token in the string as an {@code Object}. This method is * implemented in order to satisfy the {@code Enumeration} interface. - * + * * @return next token in the string as an {@code Object} - * @exception NoSuchElementException + * @throws NoSuchElementException * if no tokens remain. - * @since Android 1.0 */ public Object nextElement() { return nextToken(); @@ -265,13 +237,15 @@ public class StringTokenizer implements Enumeration { /** * Returns the next token in the string as a {@code String}. - * + * * @return next token in the string as a {@code String}. - * @exception NoSuchElementException + * @throws NoSuchElementException * if no tokens remain. - * @since Android 1.0 */ public String nextToken() { + if (delimiters == null) { + throw new NullPointerException(); + } int i = position; int length = string.length(); @@ -301,20 +275,14 @@ public class StringTokenizer implements Enumeration { /** * Returns the next token in the string as a {@code String}. The delimiters * used are changed to the specified delimiters. - * + * * @param delims * the new delimiters to use. * @return next token in the string as a {@code String}. - * @exception NoSuchElementException + * @throws NoSuchElementException * if no tokens remain. - * @since Android 1.0 */ public String nextToken(String delims) { - // BEGIN android-added - if (delims == null) { - throw new NullPointerException(); - } - // END android-added this.delimiters = delims; return nextToken(); } diff --git a/libcore/luni/src/main/java/java/util/TimSort.java b/libcore/luni/src/main/java/java/util/TimSort.java index 9c27ddc52..4a9c05bfe 100644 --- a/libcore/luni/src/main/java/java/util/TimSort.java +++ b/libcore/luni/src/main/java/java/util/TimSort.java @@ -182,7 +182,7 @@ class TimSort { // If array is small, do a "mini-TimSort" with no merges if (nRemaining < MIN_MERGE) { - int initRunLen = countRunAndMakeAscending(a, lo, nRemaining, c); + int initRunLen = countRunAndMakeAscending(a, lo, hi, c); binarySort(a, lo, hi, lo + initRunLen, c); return; } diff --git a/libcore/luni/src/main/java/java/util/TimeZone.java b/libcore/luni/src/main/java/java/util/TimeZone.java index c9709cecc..b4878cae9 100644 --- a/libcore/luni/src/main/java/java/util/TimeZone.java +++ b/libcore/luni/src/main/java/java/util/TimeZone.java @@ -14,35 +14,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/** -******************************************************************************* -* Copyright (C) 1996-2008, International Business Machines Corporation and * -* others. All Rights Reserved. * -******************************************************************************* -*/ - -// BEGIN android-note -// The class javadoc and some of the method descriptions are copied from ICU4J -// source files. Changes have been made to the copied descriptions. -// The icu license header was added to this file. -// END android-note package java.util; - import java.io.Serializable; -// BEGIN android-removed -// import java.security.AccessController; -// import java.text.DateFormatSymbols; -// -// import org.apache.harmony.luni.util.PriviAction; -// END android-removed // BEGIN android-added import org.apache.harmony.luni.internal.util.ZoneInfo; import org.apache.harmony.luni.internal.util.ZoneInfoDB; import com.ibm.icu4jni.util.Resources; // END android-added + /** * {@code TimeZone} represents a time zone offset, taking into account * daylight savings. @@ -57,21 +39,21 @@ import com.ibm.icu4jni.util.Resources; * along with a time zone ID. For instance, the time zone ID for the U.S. * Pacific Time zone is "America/Los_Angeles". So, you can get a U.S. Pacific * Time {@code TimeZone} object with the following:
          - * + * *
            * TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
            * 
          - * + * *
          You can use the {@code getAvailableIDs} method to iterate * through all the supported time zone IDs. You can then choose a supported ID * to get a {@code TimeZone}. If the time zone you want is not * represented by one of the supported IDs, then you can create a custom time * zone ID with the following syntax:
          - * + * *
            * GMT[+|-]hh[[:]mm]
            * 
          - * + * *
          For example, you might specify GMT+14:00 as a custom time zone * ID. The {@code TimeZone} that is returned when you specify a custom * time zone ID does not include daylight savings time. @@ -83,33 +65,26 @@ import com.ibm.icu4jni.util.Resources; * "China Standard Time"), and the Java platform can then only recognize one of * them. *

          - * Please note the type returned by factory methods, i.e. - * {@code getDefault()} and {@code getTimeZone(String)}, is - * implementation dependent, so it may introduce serialization - * incompatibility issues between different implementations. Android returns - * instances of {@link SimpleTimeZone} so that the bytes serialized by Android - * can be deserialized successfully on other implementations, but the reverse - * compatibility cannot be guaranteed. - * + * Please note the type returned by factory methods, i.e. {@code getDefault()} + * and {@code getTimeZone(String)}, is implementation dependent, so it may + * introduce serialization incompatibility issues between different + * implementations. Android returns instances of {@link SimpleTimeZone} so that + * the bytes serialized by Android can be deserialized successfully on other + * implementations, but the reverse compatibility cannot be guaranteed. + * * @see GregorianCalendar * @see SimpleTimeZone - * @since Android 1.0 */ - public abstract class TimeZone implements Serializable, Cloneable { private static final long serialVersionUID = 3581463369166924961L; /** * The SHORT display name style. - * - * @since Android 1.0 */ public static final int SHORT = 0; /** * The LONG display name style. - * - * @since Android 1.0 */ public static final int LONG = 1; @@ -124,41 +99,49 @@ public abstract class TimeZone implements Serializable, Cloneable { private String ID; // BEGIN android-removed + // private com.ibm.icu.util.TimeZone icuTimeZone = null; + // // private static void initializeAvailable() { // TimeZone[] zones = TimeZones.getTimeZones(); - // AvailableZones = new HashMap((zones.length + 1) * 4 / 3); + // AvailableZones = new HashMap( + // (zones.length + 1) * 4 / 3); // AvailableZones.put(GMT.getID(), GMT); // for (int i = 0; i < zones.length; i++) { // AvailableZones.put(zones[i].getID(), zones[i]); // } + //} + // + // private static boolean isAvailableIDInICU(String name) { + // String[] availableIDs = com.ibm.icu.util.TimeZone.getAvailableIDs(); + // for (int i = 0; i < availableIDs.length; i++) { + // if (availableIDs[i].equals(name)) { + // return true; + // } + // } + // return false; + // } + // + // private static void appendAvailableZones(String name) { + // com.ibm.icu.util.TimeZone icuTZ = com.ibm.icu.util.TimeZone + // .getTimeZone(name); + // int raw = icuTZ.getRawOffset(); + // TimeZone zone = new SimpleTimeZone(raw, name); + // AvailableZones.put(name, zone); // } // END android-removed /** * Constructs a new instance of this class. - * - * @since Android 1.0 */ public TimeZone() { } - private void appendNumber(StringBuffer buffer, int count, int value) { - String string = Integer.toString(value); - if (count > string.length()) { - for (int i = 0; i < count - string.length(); i++) { - buffer.append('0'); - } - } - buffer.append(string); - } - /** * Returns a new {@code TimeZone} with the same ID, {@code rawOffset} and daylight savings * time rules as this {@code TimeZone}. - * + * * @return a shallow copy of this {@code TimeZone}. * @see java.lang.Cloneable - * @since Android 1.0 */ @Override public Object clone() { @@ -173,49 +156,34 @@ public abstract class TimeZone implements Serializable, Cloneable { /** * Gets the available time zone IDs. Any one of these IDs can be passed to * {@code get()} to create the corresponding {@code TimeZone} instance. - * + * * @return an array of time zone ID strings. - * @since Android 1.0 */ public static synchronized String[] getAvailableIDs() { - // BEGIN android-removed - // if (AvailableZones == null) { - // initializeAvailable(); - // } - // int length = AvailableZones.size(); - // String[] answer = new String[length]; - // Iterator keys = AvailableZones.keySet().iterator(); - // for (int i = 0; i < length; i++) { - // answer[i] = keys.next(); - // } - // return answer; - // END android-removed - - // BEGIN android-added + // BEGIN android-changed + // return com.ibm.icu.util.TimeZone.getAvailableIDs(); return ZoneInfoDB.getAvailableIDs(); - // END android-added + // END android-changed } /** * Gets the available time zone IDs which match the specified offset from * GMT. Any one of these IDs can be passed to {@code get()} to create the corresponding * {@code TimeZone} instance. - * + * * @param offset * the offset from GMT in milliseconds. * @return an array of time zone ID strings. - * @since Android 1.0 */ public static synchronized String[] getAvailableIDs(int offset) { - // BEGIN android-removed - // if (AvailableZones == null) { - // initializeAvailable(); - // } - // int count = 0, length = AvailableZones.size(); + // BEGIN android-changed + // String[] availableIDs = com.ibm.icu.util.TimeZone.getAvailableIDs(); + // int count = 0; + // int length = availableIDs.length; // String[] all = new String[length]; - // Iterator zones = AvailableZones.values().iterator(); // for (int i = 0; i < length; i++) { - // TimeZone tz = zones.next(); + // com.ibm.icu.util.TimeZone tz = com.ibm.icu.util.TimeZone + // .getTimeZone(availableIDs[i]); // if (tz.getRawOffset() == offset) { // all[count++] = tz.getID(); // } @@ -223,23 +191,21 @@ public abstract class TimeZone implements Serializable, Cloneable { // String[] answer = new String[count]; // System.arraycopy(all, 0, answer, 0, count); // return answer; - // END android-removed - - // BEGIN android-added return ZoneInfoDB.getAvailableIDs(offset); - // END android-added - + // END android-changed } /** * Gets the default time zone. - * + * * @return the default time zone. - * @since Android 1.0 */ public static synchronized TimeZone getDefault() { if (Default == null) { + // BEGIN android-changed + // setDefault(null); Default = ZoneInfoDB.getDefault(); + // END android-changed } return (TimeZone) Default.clone(); } @@ -248,9 +214,8 @@ public abstract class TimeZone implements Serializable, Cloneable { * Gets the LONG name for this {@code TimeZone} for the default {@code Locale} in standard * time. If the name is not available, the result is in the format * {@code GMT[+-]hh:mm}. - * + * * @return the {@code TimeZone} name. - * @since Android 1.0 */ public final String getDisplayName() { return getDisplayName(false, LONG, Locale.getDefault()); @@ -260,11 +225,10 @@ public abstract class TimeZone implements Serializable, Cloneable { * Gets the LONG name for this {@code TimeZone} for the specified {@code Locale} in standard * time. If the name is not available, the result is in the format * {@code GMT[+-]hh:mm}. - * + * * @param locale * the {@code Locale}. * @return the {@code TimeZone} name. - * @since Android 1.0 */ public final String getDisplayName(Locale locale) { return getDisplayName(false, LONG, locale); @@ -274,14 +238,13 @@ public abstract class TimeZone implements Serializable, Cloneable { * Gets the specified style of name ({@code LONG} or {@code SHORT}) for this {@code TimeZone} for * the default {@code Locale} in either standard or daylight time as specified. If * the name is not available, the result is in the format {@code GMT[+-]hh:mm}. - * + * * @param daylightTime * {@code true} for daylight time, {@code false} for standard * time. * @param style - * Either {@code LONG} or {@code SHORT}. + * either {@code LONG} or {@code SHORT}. * @return the {@code TimeZone} name. - * @since Android 1.0 */ public final String getDisplayName(boolean daylightTime, int style) { return getDisplayName(daylightTime, style, Locale.getDefault()); @@ -291,39 +254,31 @@ public abstract class TimeZone implements Serializable, Cloneable { * Gets the specified style of name ({@code LONG} or {@code SHORT}) for this {@code TimeZone} for * the specified {@code Locale} in either standard or daylight time as specified. If * the name is not available, the result is in the format {@code GMT[+-]hh:mm}. - * + * * @param daylightTime * {@code true} for daylight time, {@code false} for standard * time. * @param style - * Either LONG or SHORT. + * either LONG or SHORT. * @param locale - * Either {@code LONG} or {@code SHORT}. + * either {@code LONG} or {@code SHORT}. * @return the {@code TimeZone} name. - * @since Android 1.0 */ public String getDisplayName(boolean daylightTime, int style, Locale locale) { + // BEGIN android-changed + // if(icuTimeZone == null || !ID.equals(icuTimeZone.getID())){ + // icuTimeZone = com.ibm.icu.util.TimeZone.getTimeZone(ID); + // } + // return icuTimeZone.getDisplayName( + // daylightTime, style, locale); if (style == SHORT || style == LONG) { boolean useDaylight = daylightTime && useDaylightTime(); - // BEGIN android-removed - // DateFormatSymbols data = new DateFormatSymbols(locale); - // String id = getID(); - // String[][] zones = data.getZoneStrings(); - // for (int i = 0; i < zones.length; i++) { - // if (id.equals(zones[i][0])) { - // return style == SHORT ? zones[i][useDaylight ? 4 : 2] - // : zones[i][useDaylight ? 3 : 1]; - // } - // } - // BEGIN android-removed - - // BEGIN android-added + String result = Resources.getDisplayTimeZone(getID(), daylightTime, style, locale.toString()); if (result != null) { return result; } - // END android-added - + int offset = getRawOffset(); if (useDaylight && this instanceof SimpleTimeZone) { offset += ((SimpleTimeZone) this).getDSTSavings(); @@ -343,13 +298,25 @@ public abstract class TimeZone implements Serializable, Cloneable { return buffer.toString(); } throw new IllegalArgumentException(); + // END android-changed } + // BEGIN android-added + private void appendNumber(StringBuffer buffer, int count, int value) { + String string = Integer.toString(value); + if (count > string.length()) { + for (int i = 0; i < count - string.length(); i++) { + buffer.append('0'); + } + } + buffer.append(string); + } + // END android-added + /** * Gets the ID of this {@code TimeZone}. - * + * * @return the time zone ID string. - * @since Android 1.0 */ public String getID() { return ID; @@ -364,10 +331,9 @@ public abstract class TimeZone implements Serializable, Cloneable { * Subclasses may override to return daylight savings values other than 1 * hour. *

          - * + * * @return the daylight savings offset in milliseconds if this {@code TimeZone} * observes daylight savings, zero otherwise. - * @since Android 1.0 */ public int getDSTSavings() { if (useDaylightTime()) { @@ -380,11 +346,10 @@ public abstract class TimeZone implements Serializable, Cloneable { * Gets the offset from GMT of this {@code TimeZone} for the specified date. The * offset includes daylight savings time if the specified date is within the * daylight savings time period. - * + * * @param time * the date in milliseconds since January 1, 1970 00:00:00 GMT * @return the offset from GMT in milliseconds. - * @since Android 1.0 */ public int getOffset(long time) { if (inDaylightTime(new Date(time))) { @@ -397,7 +362,7 @@ public abstract class TimeZone implements Serializable, Cloneable { * Gets the offset from GMT of this {@code TimeZone} for the specified date and * time. The offset includes daylight savings time if the specified date and * time are within the daylight savings time period. - * + * * @param era * the {@code GregorianCalendar} era, either {@code GregorianCalendar.BC} or * {@code GregorianCalendar.AD}. @@ -412,39 +377,38 @@ public abstract class TimeZone implements Serializable, Cloneable { * @param time * the time of day in milliseconds. * @return the offset from GMT in milliseconds. - * @since Android 1.0 */ abstract public int getOffset(int era, int year, int month, int day, int dayOfWeek, int time); /** * Gets the offset for standard time from GMT for this {@code TimeZone}. - * + * * @return the offset from GMT in milliseconds. - * @since Android 1.0 */ abstract public int getRawOffset(); /** * Gets the {@code TimeZone} with the specified ID. - * + * * @param name * a time zone string ID. * @return the {@code TimeZone} with the specified ID or null if no {@code TimeZone} with * the specified ID exists. - * @since Android 1.0 */ public static synchronized TimeZone getTimeZone(String name) { - // BEGIN android-removed + // BEGIN android-changed // if (AvailableZones == null) { // initializeAvailable(); // } + // // TimeZone zone = AvailableZones.get(name); - // END android-removed - - // BEGIN android-added + // if(zone == null && isAvailableIDInICU(name)){ + // appendAvailableZones(name); + // zone = AvailableZones.get(name); + // } TimeZone zone = ZoneInfo.getTimeZone(name); - // END android-added + // END android-changed if (zone == null) { if (name.startsWith("GMT") && name.length() > 3) { char sign = name.charAt(3); @@ -482,7 +446,7 @@ public abstract class TimeZone implements Serializable, Cloneable { } private static String formatTimeZoneName(String name, int offset) { - StringBuffer buf = new StringBuffer(); + StringBuilder buf = new StringBuilder(); int index = offset, length = name.length(); buf.append(name.substring(0, offset)); @@ -513,12 +477,11 @@ public abstract class TimeZone implements Serializable, Cloneable { /** * Returns whether the specified {@code TimeZone} has the same raw offset as this * {@code TimeZone}. - * + * * @param zone * a {@code TimeZone}. * @return {@code true} when the {@code TimeZone} have the same raw offset, {@code false} * otherwise. - * @since Android 1.0 */ public boolean hasSameRules(TimeZone zone) { if (zone == null) { @@ -530,12 +493,11 @@ public abstract class TimeZone implements Serializable, Cloneable { /** * Returns whether the specified {@code Date} is in the daylight savings time period for * this {@code TimeZone}. - * + * * @param time * a {@code Date}. * @return {@code true} when the {@code Date} is in the daylight savings time period, {@code false} * otherwise. - * @since Android 1.0 */ abstract public boolean inDaylightTime(Date time); @@ -555,34 +517,30 @@ public abstract class TimeZone implements Serializable, Cloneable { * time {@link #getDefault} is called, the default time zone will be * determined. This behavior is slightly different than the canonical * description of this method, but it follows the spirit of it. - * + * * @param timezone * a {@code TimeZone} object. - * @since Android 1.0 */ public static synchronized void setDefault(TimeZone timezone) { // BEGIN android-removed // if (timezone != null) { + // setICUDefaultTimeZone(timezone); // Default = timezone; // return; // } - // END android-removed - - // BEGIN android-added - Default = timezone; - - // TODO Not sure if this is spec-compliant. Shouldn't be persistent. - ZoneInfoDB.setDefault(timezone); - // END android-added - - // BEGIN android-removed + // // String zone = AccessController.doPrivileged(new PriviAction( - // "user.timezone")); + // "user.timezone")); + // + // // sometimes DRLVM incorrectly adds "\n" to the end of timezone ID + // if (zone != null && zone.contains("\n")) { + // zone = zone.substring(0, zone.indexOf("\n")); + // } // // // if property user.timezone is not set, we call the native method // // getCustomTimeZone - // if (zone == null) { - // int[] tzinfo = new int[10]; + // if (zone == null || zone.length() == 0) { + // int[] tzinfo = new int[10]; // boolean[] isCustomTimeZone = new boolean[1]; // // String zoneId = getCustomTimeZone(tzinfo, isCustomTimeZone); @@ -609,15 +567,45 @@ public abstract class TimeZone implements Serializable, Cloneable { // // if property user.timezone is set in command line (with -D option) // Default = getTimeZone(zone); // } + // setICUDefaultTimeZone(Default); // END android-removed + + // BEGIN android-added + Default = timezone; + + // TODO Not sure if this is spec-compliant. Shouldn't be persistent. + ZoneInfoDB.setDefault(timezone); + // END android-added } + // BEGIN android-removed + // private static void setICUDefaultTimeZone(TimeZone timezone) { + // final com.ibm.icu.util.TimeZone icuTZ = com.ibm.icu.util.TimeZone + // .getTimeZone(timezone.getID()); + // + // AccessController + // .doPrivileged(new PrivilegedAction() { + // public java.lang.reflect.Field run() { + // java.lang.reflect.Field field = null; + // try { + // field = com.ibm.icu.util.TimeZone.class + // .getDeclaredField("defaultZone"); + // field.setAccessible(true); + // field.set("defaultZone", icuTZ); + // } catch (Exception e) { + // return null; + // } + // return field; + // } + // }); + // } + // END android-removed + /** * Sets the ID of this {@code TimeZone}. - * + * * @param name * a string which is the time zone ID. - * @since Android 1.0 */ public void setID(String name) { if (name == null) { @@ -628,44 +616,40 @@ public abstract class TimeZone implements Serializable, Cloneable { /** * Sets the offset for standard time from GMT for this {@code TimeZone}. - * + * * @param offset * the offset from GMT in milliseconds. - * @since Android 1.0 */ abstract public void setRawOffset(int offset); /** * Returns whether this {@code TimeZone} has a daylight savings time period. - * + * * @return {@code true} if this {@code TimeZone} has a daylight savings time period, {@code false} * otherwise. - * @since Android 1.0 */ abstract public boolean useDaylightTime(); - // BEGIN android-removed - // /** - // * Gets the name and the details of the user-selected TimeZone on the - // * device. - // * - // * @param tzinfo - // * int array of 10 elements to be filled with the TimeZone - // * information. Once filled, the contents of the array are - // * formatted as follows: tzinfo[0] -> the timezone offset; - // * tzinfo[1] -> the dst adjustment; tzinfo[2] -> the dst start - // * hour; tzinfo[3] -> the dst start day of week; tzinfo[4] -> the - // * dst start week of month; tzinfo[5] -> the dst start month; - // * tzinfo[6] -> the dst end hour; tzinfo[7] -> the dst end day of - // * week; tzinfo[8] -> the dst end week of month; tzinfo[9] -> the - // * dst end month; - // * @param isCustomTimeZone - // * boolean array of size 1 that indicates if a timezone - // * match is found - // * @return the name of the TimeZone or null if error occurs in native - // * method. - // */ - // private static native String getCustomTimeZone(int[] tzinfo, - // boolean[] isCustomTimeZone); - // END android-removed + /** + * Gets the name and the details of the user-selected TimeZone on the + * device. + * + * @param tzinfo + * int array of 10 elements to be filled with the TimeZone + * information. Once filled, the contents of the array are + * formatted as follows: tzinfo[0] -> the timezone offset; + * tzinfo[1] -> the dst adjustment; tzinfo[2] -> the dst start + * hour; tzinfo[3] -> the dst start day of week; tzinfo[4] -> the + * dst start week of month; tzinfo[5] -> the dst start month; + * tzinfo[6] -> the dst end hour; tzinfo[7] -> the dst end day of + * week; tzinfo[8] -> the dst end week of month; tzinfo[9] -> the + * dst end month; + * @param isCustomTimeZone + * boolean array of size 1 that indicates if a timezone match is + * found + * @return the name of the TimeZone or null if error occurs in native + * method. + */ + private static native String getCustomTimeZone(int[] tzinfo, + boolean[] isCustomTimeZone); } diff --git a/libcore/luni/src/main/java/java/util/Timer.java b/libcore/luni/src/main/java/java/util/Timer.java index ccff92995..609003199 100644 --- a/libcore/luni/src/main/java/java/util/Timer.java +++ b/libcore/luni/src/main/java/java/util/Timer.java @@ -29,145 +29,143 @@ import org.apache.harmony.luni.util.Msg; * excessive amount of time to run it may impact the time at which subsequent * tasks may run. *

          - * + * * The {@code TimerTask} does not offer any guarantees about the real-time nature of * scheduling tasks as its underlying implementation relies on the * {@code Object.wait(long)} method. - *

          *

          * Multiple threads can share a single {@code Timer} without the need for their own - * synchronization.

          + * synchronization. *

          - * A {@code Timer} can be set to schedule tasks either at a fixed rate or - * with a fixed period. Fixed-period execution is the default.

          + * A {@code Timer} can be set to schedule tasks either at a fixed rate or + * with a fixed period. Fixed-period execution is the default. *

          - * The difference between fixed-rate and fixed-period execution - * is the following: With fixed-rate execution, the start time of each + * The difference between fixed-rate and fixed-period execution + * is the following: With fixed-rate execution, the start time of each * successive run of the task is scheduled in absolute terms without regard for when the previous - * task run actually took place. This can result in a series of bunched-up runs - * (one launched immediately after another) if busy resources or other + * task run actually took place. This can result in a series of bunched-up runs + * (one launched immediately after another) if busy resources or other * system delays prevent the {@code Timer} from firing for an extended time. - * With fixed-period execution, each successive run of the - * task is scheduled relative to the start time of the previous run of the - * task, so two runs of the task are never fired closer together in time than - * the specified {@code period}.

          - *

          - * + * With fixed-period execution, each successive run of the + * task is scheduled relative to the start time of the previous run of the + * task, so two runs of the task are never fired closer together in time than + * the specified {@code period}. + * * @see TimerTask * @see java.lang.Object#wait(long) - * @since Android 1.0 */ public class Timer { private static final class TimerImpl extends Thread { - private static final class TimerNode { - TimerNode parent, left, right; + private static final class TimerHeap { + private int DEFAULT_HEAP_SIZE = 256; + + private TimerTask[] timers = new TimerTask[DEFAULT_HEAP_SIZE]; + + private int size = 0; - TimerTask task; + private int deletedCancelledNumber = 0; - public TimerNode(TimerTask value) { - this.task = value; + public TimerTask minimum() { + return timers[0]; } - public void deleteIfCancelled(TimerTree tasks) { - /* - * All changes in the tree structure during deleting this node - * affect only the structure of the subtree having this node as - * its root - */ - if (left != null) { - left.deleteIfCancelled(tasks); - } - if (right != null) { - right.deleteIfCancelled(tasks); - } - if (task.cancelled) { - tasks.delete(this); - tasks.deletedCancelledNumber++; + public boolean isEmpty() { + return size == 0; + } + + public void insert(TimerTask task) { + if (timers.length == size) { + TimerTask[] appendedTimers = new TimerTask[size * 2]; + System.arraycopy(timers, 0, appendedTimers, 0, size); + timers = appendedTimers; } + timers[size++] = task; + upHeap(); } - } - private static final class TimerTree { + public void delete(int pos) { + // posible to delete any position of the heap + if (pos >= 0 && pos < size) { + timers[pos] = timers[--size]; + timers[size] = null; + downHeap(pos); + } + } - int deletedCancelledNumber; + private void upHeap() { + int current = size - 1; + int parent = (current - 1) / 2; - TimerNode root; + while (timers[current].when < timers[parent].when) { + // swap the two + TimerTask tmp = timers[current]; + timers[current] = timers[parent]; + timers[parent] = tmp; - boolean isEmpty() { - return root == null; + // update pos and current + current = parent; + parent = (current - 1) / 2; + } } - void insert(TimerNode z) { - TimerNode y = null, x = root; - while (x != null) { - y = x; - if (z.task.getWhen() < x.task.getWhen()) { - x = x.left; - } else { - x = x.right; + private void downHeap(int pos) { + int current = pos; + int child = 2 * current + 1; + + while (child < size && size > 0) { + // compare the children if they exist + if (child + 1 < size + && timers[child + 1].when < timers[child].when) { + child++; } - } - z.parent = y; - if (y == null) { - root = z; - } else if (z.task.getWhen() < y.task.getWhen()) { - y.left = z; - } else { - y.right = z; + + // compare selected child with parent + if (timers[current].when < timers[child].when) { + break; + } + + // swap the two + TimerTask tmp = timers[current]; + timers[current] = timers[child]; + timers[child] = tmp; + + // update pos and current + current = child; + child = 2 * current + 1; } } - void delete(TimerNode z) { - TimerNode y = null, x = null; - if (z.left == null || z.right == null) { - y = z; - } else { - y = successor(z); - } - if (y.left != null) { - x = y.left; - } else { - x = y.right; - } - if (x != null) { - x.parent = y.parent; - } - if (y.parent == null) { - root = x; - } else if (y == y.parent.left) { - y.parent.left = x; - } else { - y.parent.right = x; - } - if (y != z) { - z.task = y.task; - } + public void reset() { + timers = new TimerTask[DEFAULT_HEAP_SIZE]; + size = 0; } - private TimerNode successor(TimerNode x) { - if (x.right != null) { - return minimum(x.right); - } - TimerNode y = x.parent; - while (y != null && x == y.right) { - x = y; - y = y.parent; - } - return y; + public void adjustMinimum() { + downHeap(0); } - private TimerNode minimum(TimerNode x) { - while (x.left != null) { - x = x.left; + public void deleteIfCancelled() { + for (int i = 0; i < size; i++) { + if (timers[i].cancelled) { + deletedCancelledNumber++; + delete(i); + // re-try this point + i--; + } } - return x; } - TimerNode minimum() { - return minimum(root); + private int getTask(TimerTask task) { + for (int i = 0; i < timers.length; i++) { + if (timers[i] == task) { + return i; + } + } + return -1; } + } /** @@ -185,18 +183,14 @@ public class Timer { * Vector consists of scheduled events, sorted according to * {@code when} field of TaskScheduled object. */ - private TimerTree tasks = new TimerTree(); + private TimerHeap tasks = new TimerHeap(); /** * Starts a new timer. * - * @param isDaemon + * @param name thread's name + * @param isDaemon daemon thread or not */ - TimerImpl(boolean isDaemon) { - this.setDaemon(isDaemon); - this.start(); - } - TimerImpl(String name, boolean isDaemon) { this.setName(name); this.setDaemon(isDaemon); @@ -230,13 +224,12 @@ public class Timer { long currentTime = System.currentTimeMillis(); - TimerNode taskNode = tasks.minimum(); - task = taskNode.task; + task = tasks.minimum(); long timeToSleep; synchronized (task.lock) { if (task.cancelled) { - tasks.delete(taskNode); + tasks.delete(0); continue; } @@ -257,8 +250,12 @@ public class Timer { // no sleep is necessary before launching the task synchronized (task.lock) { + int pos = 0; + if (tasks.minimum().when != task.when) { + pos = tasks.getTask(task); + } if (task.cancelled) { - tasks.delete(taskNode); + tasks.delete(tasks.getTask(task)); continue; } @@ -266,7 +263,7 @@ public class Timer { task.setScheduledTime(task.when); // remove task from queue - tasks.delete(taskNode); + tasks.delete(pos); // set when the next task should be launched if (task.period >= 0) { @@ -299,7 +296,7 @@ public class Timer { private void insertTask(TimerTask newTask) { // callers are synchronized - tasks.insert(new TimerNode(newTask)); + tasks.insert(newTask); this.notify(); } @@ -308,7 +305,7 @@ public class Timer { */ public synchronized void cancel() { cancelled = true; - tasks = new TimerTree(); + tasks.reset(); this.notify(); } @@ -318,82 +315,89 @@ public class Timer { } // callers are synchronized tasks.deletedCancelledNumber = 0; - tasks.root.deleteIfCancelled(tasks); + tasks.deleteIfCancelled(); return tasks.deletedCancelledNumber; } } + + private static final class FinalizerHelper { + private final TimerImpl impl; + + FinalizerHelper(TimerImpl impl) { + super(); + this.impl = impl; + } + + @Override + protected void finalize() { + synchronized (impl) { + impl.finished = true; + impl.notify(); + } + } + } + + private static long timerId; + + private synchronized static long nextId() { + return timerId++; + } /* This object will be used in synchronization purposes */ - private TimerImpl impl; + private final TimerImpl impl; // Used to finalize thread @SuppressWarnings("unused") - private Object finalizer = new Object() { // $NON-LOCK-1$ - @Override - protected void finalize() { - synchronized (impl) { - impl.finished = true; - impl.notify(); - } - } - }; + private final FinalizerHelper finalizer; /** - * Creates a new {@code Timer} which may be specified to be run as a daemon thread. - * - * @param isDaemon - * {@code true} if the {@code Timer}'s thread should be a daemon thread. - * @since Android 1.0 + * Creates a new named {@code Timer} which may be specified to be run as a + * daemon thread. + * + * @param name the name of the {@code Timer}. + * @param isDaemon true if {@code Timer}'s thread should be a daemon thread. + * @throws NullPointerException is {@code name} is {@code null} */ - public Timer(boolean isDaemon) { - // BEGIN android-changed - impl = new TimerImpl("java.util.Timer", isDaemon); - // END android-changed + public Timer(String name, boolean isDaemon) { + super(); + if (name == null){ + throw new NullPointerException("name is null"); + } + this.impl = new TimerImpl(name, isDaemon); + this.finalizer = new FinalizerHelper(impl); } - + /** - * Creates a new non-daemon {@code Timer}. - * - * @since Android 1.0 + * Creates a new named {@code Timer} which does not run as a daemon thread. + * + * @param name the name of the Timer. + * @throws NullPointerException is {@code name} is {@code null} */ - public Timer() { - // BEGIN android-changed - impl = new TimerImpl("java.util.Timer", false); - // END android-changed + public Timer(String name) { + this(name, false); } - + /** - * Creates a new named {@code Timer} which may be specified to be run as a - * daemon thread. - * - * @param name - * the name of the {@code Timer}. - * @param isDaemon - * true if {@code Timer}'s thread should be a daemon thread. - * @since Android 1.0 + * Creates a new {@code Timer} which may be specified to be run as a daemon thread. + * + * @param isDaemon {@code true} if the {@code Timer}'s thread should be a daemon thread. */ - public Timer(String name, boolean isDaemon) { - impl = new TimerImpl(name, isDaemon); + public Timer(boolean isDaemon) { + this("Timer-" + Timer.nextId(), isDaemon); } /** - * Creates a new named {@code Timer} which does not run as a daemon thread. - * - * @param name - * the name of the Timer. - * @since Android 1.0 + * Creates a new non-daemon {@code Timer}. */ - public Timer(String name) { - impl = new TimerImpl(name, false); + public Timer() { + this(false); } /** * Cancels the {@code Timer} and removes any scheduled tasks. If there is a * currently running task it is not affected. No more tasks may be scheduled * on this {@code Timer}. Subsequent calls do nothing. - * - * @since Android 1.0 */ public void cancel() { impl.cancel(); @@ -401,12 +405,11 @@ public class Timer { /** * Removes all canceled tasks from the task queue. If there are no - * other references on the tasks, then after this call they are free + * other references on the tasks, then after this call they are free * to be garbage collected. - * + * * @return the number of canceled tasks that were removed from the task * queue. - * @since Android 1.0 */ public int purge() { synchronized (impl) { @@ -417,17 +420,16 @@ public class Timer { /** * Schedule a task for single execution. If {@code when} is less than the * current time, it will be scheduled to be executed as soon as possible. - * + * * @param task * the task to schedule. * @param when * time of execution. - * @exception IllegalArgumentException + * @throws IllegalArgumentException * if {@code when.getTime() < 0}. - * @exception IllegalStateException + * @throws IllegalStateException * if the {@code Timer} has been canceled, or if the task has been * scheduled or canceled. - * @since Android 1.0 */ public void schedule(TimerTask task, Date when) { if (when.getTime() < 0) { @@ -439,17 +441,16 @@ public class Timer { /** * Schedule a task for single execution after a specified delay. - * + * * @param task * the task to schedule. * @param delay - * amount of time before execution. - * @exception IllegalArgumentException + * amount of time in milliseconds before execution. + * @throws IllegalArgumentException * if {@code delay < 0}. - * @exception IllegalStateException + * @throws IllegalStateException * if the {@code Timer} has been canceled, or if the task has been * scheduled or canceled. - * @since Android 1.0 */ public void schedule(TimerTask task, long delay) { if (delay < 0) { @@ -460,19 +461,18 @@ public class Timer { /** * Schedule a task for repeated fixed-delay execution after a specific delay. - * + * * @param task * the task to schedule. * @param delay - * amount of time before first execution. + * amount of time in milliseconds before first execution. * @param period - * amount of time between subsequent executions. - * @exception IllegalArgumentException + * amount of time in milliseconds between subsequent executions. + * @throws IllegalArgumentException * if {@code delay < 0} or {@code period < 0}. - * @exception IllegalStateException + * @throws IllegalStateException * if the {@code Timer} has been canceled, or if the task has been * scheduled or canceled. - * @since Android 1.0 */ public void schedule(TimerTask task, long delay, long period) { if (delay < 0 || period <= 0) { @@ -484,19 +484,18 @@ public class Timer { /** * Schedule a task for repeated fixed-delay execution after a specific time * has been reached. - * + * * @param task * the task to schedule. * @param when * time of first execution. * @param period - * amount of time between subsequent executions. - * @exception IllegalArgumentException + * amount of time in milliseconds between subsequent executions. + * @throws IllegalArgumentException * if {@code when.getTime() < 0} or {@code period < 0}. - * @exception IllegalStateException + * @throws IllegalStateException * if the {@code Timer} has been canceled, or if the task has been * scheduled or canceled. - * @since Android 1.0 */ public void schedule(TimerTask task, Date when, long period) { if (period <= 0 || when.getTime() < 0) { @@ -509,19 +508,18 @@ public class Timer { /** * Schedule a task for repeated fixed-rate execution after a specific delay * has passed. - * + * * @param task * the task to schedule. * @param delay - * amount of time before first execution. + * amount of time in milliseconds before first execution. * @param period - * amount of time between subsequent executions. - * @exception IllegalArgumentException + * amount of time in milliseconds between subsequent executions. + * @throws IllegalArgumentException * if {@code delay < 0} or {@code period < 0}. - * @exception IllegalStateException + * @throws IllegalStateException * if the {@code Timer} has been canceled, or if the task has been * scheduled or canceled. - * @since Android 1.0 */ public void scheduleAtFixedRate(TimerTask task, long delay, long period) { if (delay < 0 || period <= 0) { @@ -533,19 +531,18 @@ public class Timer { /** * Schedule a task for repeated fixed-rate execution after a specific time * has been reached. - * + * * @param task * the task to schedule. * @param when * time of first execution. * @param period - * amount of time between subsequent executions. - * @exception IllegalArgumentException + * amount of time in milliseconds between subsequent executions. + * @throws IllegalArgumentException * if {@code when.getTime() < 0} or {@code period < 0}. - * @exception IllegalStateException + * @throws IllegalStateException * if the {@code Timer} has been canceled, or if the task has been * scheduled or canceled. - * @since Android 1.0 */ public void scheduleAtFixedRate(TimerTask task, Date when, long period) { if (period <= 0 || when.getTime() < 0) { @@ -555,13 +552,8 @@ public class Timer { scheduleImpl(task, delay < 0 ? 0 : delay, period, true); } - /** + /* * Schedule a task. - * - * @param task - * @param delay - * @param period - * @param fixed */ private void scheduleImpl(TimerTask task, long delay, long period, boolean fixed) { diff --git a/libcore/luni/src/main/java/java/util/TimerTask.java b/libcore/luni/src/main/java/java/util/TimerTask.java index 384746f1e..319ed5818 100644 --- a/libcore/luni/src/main/java/java/util/TimerTask.java +++ b/libcore/luni/src/main/java/java/util/TimerTask.java @@ -23,7 +23,6 @@ package java.util; * * @see Timer * @see java.lang.Object#wait(long) - * @since Android 1.0 */ public abstract class TimerTask implements Runnable { /* Lock object for synchronization. It's also used by Timer class. */ @@ -77,8 +76,6 @@ public abstract class TimerTask implements Runnable { /** * Creates a new {@code TimerTask}. - * - * @since Android 1.0 */ protected TimerTask() { super(); @@ -91,7 +88,6 @@ public abstract class TimerTask implements Runnable { * * @return {@code true} if the call prevented a scheduled execution * from taking place, {@code false} otherwise. - * @since Android 1.0 */ public boolean cancel() { synchronized (lock) { @@ -107,7 +103,6 @@ public abstract class TimerTask implements Runnable { * have not yet run return an undefined value. * * @return the most recent execution time. - * @since Android 1.0 */ public long scheduledExecutionTime() { synchronized (lock) { @@ -118,8 +113,6 @@ public abstract class TimerTask implements Runnable { /** * The task to run should be specified in the implementation of the {@code run()} * method. - * - * @since Android 1.0 */ public abstract void run(); diff --git a/libcore/luni/src/main/java/java/util/TooManyListenersException.java b/libcore/luni/src/main/java/java/util/TooManyListenersException.java index 32b213345..8c044c0cb 100644 --- a/libcore/luni/src/main/java/java/util/TooManyListenersException.java +++ b/libcore/luni/src/main/java/java/util/TooManyListenersException.java @@ -22,8 +22,6 @@ package java.util; * A {@code TooManyListenersException} is thrown when an attempt is made to add * more than one listener to an event source which only supports a single * listener. It is also thrown when the same listener is added more than once. - * - * @since Android 1.0 */ public class TooManyListenersException extends Exception { diff --git a/libcore/luni/src/main/java/java/util/TreeMap.java b/libcore/luni/src/main/java/java/util/TreeMap.java index e3dc5a705..b97821fba 100644 --- a/libcore/luni/src/main/java/java/util/TreeMap.java +++ b/libcore/luni/src/main/java/java/util/TreeMap.java @@ -27,43 +27,124 @@ import java.io.Serializable; * and removing) are supported. The values can be any objects. The keys can be * any objects which are comparable to each other either using their natural * order or a specified Comparator. - * - * @since Android 1.0 + * + * @since 1.2 */ -public class TreeMap extends AbstractMap implements SortedMap, Cloneable, - Serializable { +public class TreeMap extends AbstractMap implements SortedMap, + Cloneable, Serializable { private static final long serialVersionUID = 919286545866124006L; transient int size; - transient Entry root; - private Comparator comparator; transient int modCount; transient Set> entrySet; - /** - * Entry is an internal class which is used to hold the entries of a - * TreeMap. - */ - static class Entry extends MapEntry { - Entry parent, left, right; - + transient Node root; + +class MapEntry implements Map.Entry, Cloneable { + + final int offset; + final Node node; + final K key; + + MapEntry(Node node, int offset) { + this.node = node; + this.offset = offset; + key = node.keys[offset]; + } + + @Override + public Object clone() { + try { + return super.clone(); + } catch (CloneNotSupportedException e) { + return null; + } + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (object instanceof Map.Entry) { + Map.Entry entry = (Map.Entry) object; + V value = getValue(); + return (key == null ? entry.getKey() == null : key.equals(entry + .getKey())) + && (value == null ? entry.getValue() == null : value + .equals(entry.getValue())); + } + return false; + } + + public K getKey() { + return key; + } + + public V getValue() { + if (node.keys[offset] == key) { + return node.values[offset]; + } + if (containsKey(key)) { + return get(key); + } + throw new IllegalStateException(); + } + + @Override + public int hashCode() { + V value = getValue(); + return (key == null ? 0 : key.hashCode()) + ^ (value == null ? 0 : value.hashCode()); + } + + public V setValue(V object) { + if (node.keys[offset] == key) { + V res = node.values[offset]; + node.values[offset] = object; + return res; + } + if (containsKey(key)) { + return put(key, object); + } + throw new IllegalStateException(); + } + + @Override + public String toString() { + return key + "=" + getValue(); + } + } + + static class Node implements Cloneable { + static final int NODE_SIZE = 64; + Node prev, next; + Node parent, left, right; + V[] values; + K[] keys; + int left_idx = 0; + int right_idx = -1; + int size = 0; boolean color; - Entry(K key) { - super(key); - } - - Entry(K key, V value) { - super(key, value); + public Node() { + keys = (K[]) new Object[NODE_SIZE]; + values = (V[]) new Object[NODE_SIZE]; } @SuppressWarnings("unchecked") - Entry clone(Entry parent) { - Entry clone = (Entry) super.clone(); + Node clone(Node parent) throws CloneNotSupportedException { + Node clone = (Node) super.clone(); + clone.keys = (K[]) new Object[NODE_SIZE]; + clone.values = (V[]) new Object[NODE_SIZE]; + System.arraycopy(keys, 0, clone.keys, 0, keys.length); + System.arraycopy(values, 0, clone.values, 0, values.length); + clone.left_idx = left_idx; + clone.right_idx = right_idx; clone.parent = parent; if (left != null) { clone.left = left.clone(clone); @@ -71,35 +152,68 @@ public class TreeMap extends AbstractMap implements SortedMap, if (right != null) { clone.right = right.clone(clone); } + clone.prev = null; + clone.next = null; return clone; } } - + @SuppressWarnings("unchecked") - private static Comparable toComparable(T obj) { - return (Comparable)obj; + private static Comparable toComparable(T obj) { + return (Comparable) obj; } - private static class AbstractMapIterator { + static class AbstractMapIterator { TreeMap backingMap; int expectedModCount; - TreeMap.Entry node; - TreeMap.Entry lastNode; + Node node; + Node lastNode; + int offset; + int lastOffset; - AbstractMapIterator(TreeMap map, Entry startNode) { + AbstractMapIterator(TreeMap map, Node startNode, int startOffset) { backingMap = map; expectedModCount = map.modCount; node = startNode; + offset = startOffset; + } + + AbstractMapIterator(TreeMap map, Node startNode) { + this(map, startNode, startNode != null ? + startNode.right_idx - startNode.left_idx : 0); + } + + AbstractMapIterator(TreeMap map) { + this(map, minimum(map.root)); } public boolean hasNext() { return node != null; } + final void makeNext() { + if (expectedModCount != backingMap.modCount) { + throw new ConcurrentModificationException(); + } else if (node == null) { + throw new NoSuchElementException(); + } + lastNode = node; + lastOffset = offset; + if (offset != 0) { + offset--; + } else { + node = node.next; + if (node != null) { + offset = node.right_idx - node.left_idx; + } + } + } + final public void remove() { if (expectedModCount == backingMap.modCount) { if (lastNode != null) { - backingMap.rbDelete(lastNode); + int idx = lastNode.right_idx - lastOffset; + backingMap.removeFromIterator(lastNode, idx); lastNode = null; expectedModCount++; } else { @@ -109,572 +223,752 @@ public class TreeMap extends AbstractMap implements SortedMap, throw new ConcurrentModificationException(); } } + } - final void makeNext() { - if (expectedModCount != backingMap.modCount) { - throw new ConcurrentModificationException(); - } else if (node == null) { - throw new NoSuchElementException(); - } - lastNode = node; - node = TreeMap.successor(node); - } - } - - private static class UnboundedEntryIterator extends AbstractMapIterator implements Iterator> { - - UnboundedEntryIterator(TreeMap map, Entry startNode) { - super(map, startNode); - } - - UnboundedEntryIterator(TreeMap map) { - super(map, map.root == null ? null : TreeMap.minimum(map.root)); - } + static class UnboundedEntryIterator extends AbstractMapIterator + implements Iterator> { - public Map.Entry next() { - makeNext(); - return lastNode; - } + UnboundedEntryIterator(TreeMap map, Node startNode, int startOffset) { + super(map, startNode, startOffset); } - static class UnboundedKeyIterator extends AbstractMapIterator implements Iterator { - public UnboundedKeyIterator(TreeMap treeMap, Entry entry) { - super(treeMap, entry); - } - - public UnboundedKeyIterator(TreeMap map) { - super(map, map.root == null ? null : TreeMap.minimum(map.root)); - } - - public K next() { - makeNext(); - return lastNode.key; - } + UnboundedEntryIterator(TreeMap map) { + super(map); } - static class UnboundedValueIterator extends AbstractMapIterator implements Iterator { - - public UnboundedValueIterator(TreeMap treeMap, Entry startNode) { - super(treeMap, startNode); - } - - public UnboundedValueIterator(TreeMap map) { - super(map, map.root == null ? null : TreeMap.minimum(map.root)); - } - - public V next() { - makeNext(); - return lastNode.value; - } + public Map.Entry next() { + makeNext(); + int idx = lastNode.right_idx - lastOffset; + return backingMap.new MapEntry(lastNode, idx); } + } - private static class ComparatorBoundedIterator extends AbstractMapIterator { - private final K endKey; - - private final Comparator cmp; + static class UnboundedKeyIterator extends AbstractMapIterator + implements Iterator { - ComparatorBoundedIterator(TreeMap map, Entry startNode, K end) { - super(map, startNode); - endKey = end; - cmp = map.comparator(); + UnboundedKeyIterator(TreeMap map, Node startNode, int startOffset) { + super(map, startNode, startOffset); } - final void cleanNext() { - if (node != null && cmp.compare(endKey, node.key) <= 0) { - node = null; - } + UnboundedKeyIterator(TreeMap map) { + super(map); } - @Override - public boolean hasNext() { - return (node != null && endKey != null) && (cmp.compare(node.key, endKey) < 0); + public K next() { + makeNext(); + return lastNode.keys[lastNode.right_idx - lastOffset]; } } - private static class ComparatorBoundedEntryIterator extends - ComparatorBoundedIterator implements Iterator> { - - ComparatorBoundedEntryIterator(TreeMap map, Entry startNode, K end) { - super(map, startNode, end); - } + static class UnboundedValueIterator extends AbstractMapIterator + implements Iterator { - public Map.Entry next() { - makeNext(); - cleanNext(); - return lastNode; + UnboundedValueIterator(TreeMap map, Node startNode, int startOffset) { + super(map, startNode, startOffset); } - } - private static class ComparatorBoundedKeyIterator extends - ComparatorBoundedIterator implements Iterator { - - ComparatorBoundedKeyIterator(TreeMap map, Entry startNode, K end) { - super(map, startNode, end); + UnboundedValueIterator(TreeMap map) { + super(map); } - public K next() { + public V next() { makeNext(); - cleanNext(); - return lastNode.key; + return lastNode.values[lastNode.right_idx - lastOffset]; } } - private static class ComparatorBoundedValueIterator extends - ComparatorBoundedIterator implements Iterator { + static class BoundedMapIterator extends AbstractMapIterator { - ComparatorBoundedValueIterator(TreeMap map, Entry startNode, K end) { - super(map, startNode, end); - } + Node finalNode; + int finalOffset; - public V next() { - makeNext(); - cleanNext(); - return lastNode.value; + BoundedMapIterator(Node startNode, int startOffset, TreeMap map, + Node finalNode, int finalOffset) { + super(map, finalNode==null? null : startNode, startOffset); + this.finalNode = finalNode; + this.finalOffset = finalOffset; } - } - private static class ComparableBoundedIterator extends AbstractMapIterator { - private final Comparable endKey; + BoundedMapIterator(Node startNode, TreeMap map, + Node finalNode, int finalOffset) { + this(startNode, startNode != null ? + startNode.right_idx - startNode.left_idx : 0, + map, finalNode, finalOffset); + } - public ComparableBoundedIterator(TreeMap treeMap, Entry entry, - Comparable endKey) { - super(treeMap, entry); - this.endKey = endKey; + BoundedMapIterator(Node startNode, int startOffset, + TreeMap map, Node finalNode) { + this(startNode, startOffset, map, finalNode, + finalNode.right_idx - finalNode.left_idx); } - final void cleanNext() { - if ((node != null) && (endKey.compareTo(node.key) <= 0)) { + void makeBoundedNext() { + makeNext(); + if (lastNode == finalNode && lastOffset == finalOffset) { node = null; } } - - @Override - public boolean hasNext() { - return (node != null) && (endKey.compareTo(node.key) > 0); - } } - private static class ComparableBoundedEntryIterator extends - ComparableBoundedIterator implements Iterator> { + static class BoundedEntryIterator extends BoundedMapIterator + implements Iterator> { - ComparableBoundedEntryIterator(TreeMap map, Entry startNode, - Comparable end) { - super(map, startNode, end); + public BoundedEntryIterator(Node startNode, int startOffset, TreeMap map, + Node finalNode, int finalOffset) { + super(startNode, startOffset, map, finalNode, finalOffset); } public Map.Entry next() { - makeNext(); - cleanNext(); - return lastNode; + makeBoundedNext(); + int idx = lastNode.right_idx - lastOffset; + return backingMap.new MapEntry(lastNode, idx); } - } - private static class ComparableBoundedKeyIterator extends - ComparableBoundedIterator implements Iterator { + static class BoundedKeyIterator extends BoundedMapIterator + implements Iterator { - ComparableBoundedKeyIterator(TreeMap map, Entry startNode, Comparable end) { - super(map, startNode, end); + public BoundedKeyIterator(Node startNode, int startOffset, TreeMap map, + Node finalNode, int finalOffset) { + super(startNode, startOffset, map, finalNode, finalOffset); } public K next() { - makeNext(); - cleanNext(); - return lastNode.key; + makeBoundedNext(); + return lastNode.keys[lastNode.right_idx - lastOffset]; } } - private static class ComparableBoundedValueIterator extends - ComparableBoundedIterator implements Iterator { + static class BoundedValueIterator extends BoundedMapIterator + implements Iterator { - ComparableBoundedValueIterator(TreeMap map, Entry startNode, - Comparable end) { - super(map, startNode, end); + public BoundedValueIterator(Node startNode, int startOffset, TreeMap map, + Node finalNode, int finalOffset) { + super(startNode, startOffset, map, finalNode, finalOffset); } public V next() { - makeNext(); - cleanNext(); - return lastNode.value; + makeBoundedNext(); + return lastNode.values[lastNode.right_idx - lastOffset]; } } - static final class SubMap extends AbstractMap implements SortedMap, Serializable { - private static final long serialVersionUID = -6520786458950516097L; + static final class SubMap extends AbstractMap + implements SortedMap, Serializable { + private static final long serialVersionUID = -6520786458950516097L; - private TreeMap backingMap; + private TreeMap backingMap; - boolean hasStart, hasEnd; + boolean hasStart, hasEnd; + K startKey, endKey; + transient Set> entrySet = null; + transient int firstKeyModCount = -1; + transient int lastKeyModCount = -1; + transient Node firstKeyNode; + transient int firstKeyIndex; + transient Node lastKeyNode; + transient int lastKeyIndex; - K startKey, endKey; + SubMap(K start, TreeMap map) { + backingMap = map; + hasStart = true; + startKey = start; + } - transient Set> entrySet = null; + SubMap(K start, TreeMap map, K end) { + backingMap = map; + hasStart = hasEnd = true; + startKey = start; + endKey = end; + } - SubMap(K start, TreeMap map) { - backingMap = map; - hasStart = true; - startKey = start; - } + SubMap(TreeMap map, K end) { + backingMap = map; + hasEnd = true; + endKey = end; + } - SubMap(K start, TreeMap map, K end) { - backingMap = map; - hasStart = hasEnd = true; - startKey = start; - endKey = end; + private void checkRange(K key) { + Comparator cmp = backingMap.comparator; + if (cmp == null) { + Comparable object = toComparable(key); + if (hasStart && object.compareTo(startKey) < 0) { + throw new IllegalArgumentException(); + } + if (hasEnd && object.compareTo(endKey) > 0) { + throw new IllegalArgumentException(); + } + } else { + if (hasStart + && backingMap.comparator().compare(key, startKey) < 0) { + throw new IllegalArgumentException(); + } + if (hasEnd && backingMap.comparator().compare(key, endKey) > 0) { + throw new IllegalArgumentException(); + } } - - SubMap(TreeMap map, K end) { - backingMap = map; - hasEnd = true; - endKey = end; + } + + private boolean isInRange(K key) { + Comparator cmp = backingMap.comparator; + if (cmp == null) { + Comparable object = toComparable(key); + if (hasStart && object.compareTo(startKey) < 0) { + return false; + } + if (hasEnd && object.compareTo(endKey) >= 0) { + return false; + } + } else { + if (hasStart && cmp.compare(key, startKey) < 0) { + return false; + } + if (hasEnd && cmp.compare(key, endKey) >= 0) { + return false; + } } + return true; + } - private void checkRange(K key) { + private boolean checkUpperBound(K key) { + if (hasEnd) { Comparator cmp = backingMap.comparator; if (cmp == null) { - Comparable object = toComparable(key); - if (hasStart && object.compareTo(startKey) < 0) { - throw new IllegalArgumentException(); - } - if (hasEnd && object.compareTo(endKey) > 0) { - throw new IllegalArgumentException(); - } - } else { - if (hasStart - && backingMap.comparator().compare(key, startKey) < 0) { - throw new IllegalArgumentException(); - } - if (hasEnd && backingMap.comparator().compare(key, endKey) > 0) { - throw new IllegalArgumentException(); - } + return (toComparable(key).compareTo(endKey) < 0); } + return (cmp.compare(key, endKey) < 0); } + return true; + } - private boolean isInRange(K key) { + private boolean checkLowerBound(K key) { + if (hasStart) { Comparator cmp = backingMap.comparator; if (cmp == null) { - Comparable object = toComparable(key); - if (hasStart && object.compareTo(startKey) < 0) { - return false; - } - if (hasEnd && object.compareTo(endKey) >= 0) { - return false; - } - } else { - if (hasStart && cmp.compare(key, startKey) < 0) { - return false; - } - if (hasEnd && cmp.compare(key, endKey) >= 0) { - return false; - } + return (toComparable(key).compareTo(startKey) >= 0); } - return true; + return (cmp.compare(key, startKey) >= 0); } + return true; + } - private boolean checkUpperBound(K key) { - if (hasEnd) { - Comparator cmp = backingMap.comparator; - if (cmp == null) { - return (toComparable(key).compareTo(endKey) < 0); - } - return (cmp.compare(key, endKey) < 0); - } - return true; + public Comparator comparator() { + return backingMap.comparator(); + } + + @SuppressWarnings("unchecked") + @Override + public boolean containsKey(Object key) { + if (isInRange((K) key)) { + return backingMap.containsKey(key); } + return false; + } + + @Override + public void clear() { + keySet().clear(); + } - private boolean checkLowerBound(K key) { - if (hasStart) { - Comparator cmp = backingMap.comparator; - if (cmp == null) { - return (toComparable(key).compareTo(startKey) >= 0); + @Override + public boolean containsValue(Object value) { + Iterator it = values().iterator(); + if (value != null) { + while (it.hasNext()) { + if (value.equals(it.next())) { + return true; + } + } + } else { + while (it.hasNext()) { + if (it.next() == null) { + return true; } - return (cmp.compare(key, startKey) >= 0); } - return true; } + return false; + } - public Comparator comparator() { - return backingMap.comparator(); + @Override + public Set> entrySet() { + if (entrySet == null) { + entrySet = new SubMapEntrySet(this); } + return entrySet; + } - @SuppressWarnings("unchecked") - @Override - public boolean containsKey(Object key) { - if (isInRange((K)key)) { - return backingMap.containsKey(key); - } - return false; + private void setFirstKey() { + if (firstKeyModCount == backingMap.modCount) { + return; } - - @Override - public Set> entrySet() { - if(entrySet==null) { - entrySet = new SubMapEntrySet(this); + Comparable object = backingMap.comparator == null ? + toComparable((K) startKey) : null; + K key = (K) startKey; + Node node = backingMap.root; + Node foundNode = null; + int foundIndex = -1; + TOP_LOOP: + while (node != null) { + K[] keys = node.keys; + int left_idx = node.left_idx; + int result = backingMap.cmp(object, key, keys[left_idx]); + if (result < 0) { + foundNode = node; + foundIndex = left_idx; + node = node.left; + } else if (result == 0) { + foundNode = node; + foundIndex = left_idx; + break; + } else { + int right_idx = node.right_idx; + if (left_idx != right_idx) { + result = backingMap.cmp(object, key, keys[right_idx]); + } + if (result > 0) { + node = node.right; + } else if (result == 0) { + foundNode = node; + foundIndex = right_idx; + break; + } else { /*search in node*/ + foundNode = node; + foundIndex = right_idx; + int low = left_idx + 1, mid = 0, high = right_idx - 1; + while (low <= high) { + mid = (low + high) >> 1; + result = backingMap.cmp(object, key, keys[mid]); + if (result > 0) { + low = mid + 1; + } else if (result == 0) { + foundNode = node; + foundIndex = mid; + break TOP_LOOP; + } else { + foundNode = node; + foundIndex = mid; + high = mid - 1; + } + } + break TOP_LOOP; + } } - return entrySet; } - - public K firstKey() { - TreeMap.Entry node = firstEntry(); - if (node != null ) { - return node.key; - } - throw new NoSuchElementException(); + if (foundNode != null && !checkUpperBound(foundNode.keys[foundIndex])) { + foundNode = null; } + firstKeyNode = foundNode; + firstKeyIndex = foundIndex; + firstKeyModCount = backingMap.modCount; + } - TreeMap.Entry firstEntry() { + public K firstKey() { + if (backingMap.size > 0) { if (!hasStart) { - TreeMap.Entry root = backingMap.root; - return (root == null) ? null : minimum(backingMap.root); - } - TreeMap.Entry node = backingMap.findAfter(startKey); - if (node != null && checkUpperBound(node.key)) { - return node; + Node node = minimum(backingMap.root); + if (node != null && checkUpperBound(node.keys[node.left_idx])) { + return node.keys[node.left_idx]; + } + } else { + setFirstKey(); + if (firstKeyNode != null) { + return firstKeyNode.keys[firstKeyIndex]; + } } - return null; } + throw new NoSuchElementException(); + } - @SuppressWarnings("unchecked") - @Override - public V get(Object key) { - if (isInRange((K)key)) { - return backingMap.get(key); - } - return null; - } - public SortedMap headMap(K endKey) { - checkRange(endKey); - if (hasStart) { - return new SubMap(startKey, backingMap, endKey); - } - return new SubMap(backingMap, endKey); + @SuppressWarnings("unchecked") + @Override + public V get(Object key) { + if (isInRange((K) key)) { + return backingMap.get(key); } + return null; + } - @Override - public boolean isEmpty() { - if (hasStart) { - TreeMap.Entry node = backingMap.findAfter(startKey); - return node == null || !checkUpperBound(node.key); - } - return backingMap.findBefore(endKey) == null; + public SortedMap headMap(K endKey) { + checkRange(endKey); + if (hasStart) { + return new SubMap(startKey, backingMap, endKey); } + return new SubMap(backingMap, endKey); + } - @Override - public Set keySet() { - if (keySet == null) { - keySet = new SubMapKeySet(this); - } - return keySet; + @Override + public boolean isEmpty() { + if (hasStart) { + setFirstKey(); + return firstKeyNode == null; + } else { + setLastKey(); + return lastKeyNode == null; } + } - public K lastKey() { - if (!hasEnd) { - return backingMap.lastKey(); - } - TreeMap.Entry node = backingMap.findBefore(endKey); - if (node != null && checkLowerBound(node.key)) { - return node.key; - } - throw new NoSuchElementException(); + @Override + public Set keySet() { + if (keySet == null) { + keySet = new SubMapKeySet(this); } + return keySet; + } - @Override - public V put(K key, V value) { - if (isInRange(key)) { - return backingMap.put(key, value); - } - throw new IllegalArgumentException(); + private void setLastKey() { + if (lastKeyModCount == backingMap.modCount) { + return; } - - @SuppressWarnings("unchecked") - @Override - public V remove(Object key) { - if (isInRange((K)key)) { - return backingMap.remove(key); + Comparable object = backingMap.comparator == null ? + toComparable((K) endKey) : null; + K key = (K) endKey; + Node node = backingMap.root; + Node foundNode = null; + int foundIndex = -1; + TOP_LOOP: + while (node != null) { + K[] keys = node.keys; + int left_idx = node.left_idx; + int result = backingMap.cmp(object, key, keys[left_idx]); + if (result <= 0) { + node = node.left; + } else { + int right_idx = node.right_idx; + if (left_idx != right_idx) { + result = backingMap.cmp(object, key, keys[right_idx]); + } + if (result > 0) { + foundNode = node; + foundIndex = right_idx; + node = node.right; + } else if (result == 0) { + if (node.left_idx == node.right_idx) { + foundNode = node.prev; + if (foundNode != null) { + foundIndex = foundNode.right_idx - 1; + } + } else { + foundNode = node; + foundIndex = right_idx - 1; + } + break; + } else { /*search in node*/ + foundNode = node; + foundIndex = left_idx; + int low = left_idx + 1, mid = 0, high = right_idx - 1; + while (low <= high) { + mid = (low + high) >> 1; + result = backingMap.cmp(object, key, keys[mid]); + if (result > 0) { + foundNode = node; + foundIndex = mid; + low = mid + 1; + } else if (result == 0) { + foundNode = node; + foundIndex = mid - 1; + break TOP_LOOP; + } else { + high = mid - 1; + } + } + break TOP_LOOP; + } } - return null; } + if (foundNode != null && !checkLowerBound(foundNode.keys[foundIndex])) { + foundNode = null; + } + lastKeyNode = foundNode; + lastKeyIndex = foundIndex; + lastKeyModCount = backingMap.modCount; + } - public SortedMap subMap(K startKey, K endKey) { - checkRange(startKey); - checkRange(endKey); - Comparator c = backingMap.comparator(); - if (c == null) { - if (toComparable(startKey).compareTo(endKey) <= 0) { - return new SubMap(startKey, backingMap, endKey); + public K lastKey() { + if (backingMap.size > 0) { + if (!hasEnd) { + Node node = maximum(backingMap.root); + if (node != null && checkLowerBound(node.keys[node.right_idx])) { + return node.keys[node.right_idx]; } } else { - if (c.compare(startKey, endKey) <= 0) { - return new SubMap(startKey, backingMap, endKey); + setLastKey(); + if (lastKeyNode != null) { + return lastKeyNode.keys[lastKeyIndex]; } } - throw new IllegalArgumentException(); } + throw new NoSuchElementException(); + } - public SortedMap tailMap(K startKey) { - checkRange(startKey); - if (hasEnd) { - return new SubMap(startKey, backingMap, endKey); - } - return new SubMap(startKey, backingMap); - } - @Override - public Collection values() { - if(valuesCollection==null) { - valuesCollection = new SubMapValuesCollection(this); - } - return valuesCollection; + @Override + public V put(K key, V value) { + if (isInRange(key)) { + return backingMap.put(key, value); } + throw new IllegalArgumentException(); } - static class SubMapEntrySet extends AbstractSet> implements Set> { - SubMap subMap; - - SubMapEntrySet(SubMap map) { - subMap = map; + @SuppressWarnings("unchecked") + @Override + public V remove(Object key) { + if (isInRange((K) key)) { + return backingMap.remove(key); } + return null; + } - @Override - public boolean isEmpty() { - return subMap.isEmpty(); + public SortedMap subMap(K startKey, K endKey) { + checkRange(startKey); + checkRange(endKey); + Comparator c = backingMap.comparator(); + if (c == null) { + if (toComparable(startKey).compareTo(endKey) <= 0) { + return new SubMap(startKey, backingMap, endKey); + } + } else { + if (c.compare(startKey, endKey) <= 0) { + return new SubMap(startKey, backingMap, endKey); + } } + throw new IllegalArgumentException(); + } - @Override - public Iterator> iterator() { - TreeMap.Entry startNode = subMap.firstEntry(); - if (subMap.hasEnd) { - Comparator cmp = subMap.comparator(); - if (cmp == null) { - return new ComparableBoundedEntryIterator(subMap.backingMap, startNode, toComparable(subMap.endKey)); - } - return new ComparatorBoundedEntryIterator(subMap.backingMap, startNode, subMap.endKey); - } - return new UnboundedEntryIterator(subMap.backingMap, startNode); + public SortedMap tailMap(K startKey) { + checkRange(startKey); + if (hasEnd) { + return new SubMap(startKey, backingMap, endKey); } + return new SubMap(startKey, backingMap); + } - @Override - public int size() { - int size = 0; - Iterator> it = iterator(); - while (it.hasNext()) { - size++; - it.next(); - } - return size; + @Override + public Collection values() { + if (valuesCollection == null) { + valuesCollection = new SubMapValuesCollection(this); } + return valuesCollection; + } - @SuppressWarnings("unchecked") - @Override - public boolean contains(Object object) { - if (object instanceof Map.Entry) { - Map.Entry entry = (Map.Entry) object; - K key = entry.getKey(); - if (subMap.isInRange(key)) { - V v1 = subMap.get(key), v2 = entry.getValue(); - return v1 == null ? v2 == null : v1.equals(v2); - } - } - return false; + public int size() { + Node from, to; + int fromIndex, toIndex; + if (hasStart) { + setFirstKey(); + from = firstKeyNode; + fromIndex = firstKeyIndex; + } else { + from = minimum(backingMap.root); + fromIndex = from == null ? 0 : from.left_idx; + } + if (from == null) { + return 0; + } + if (hasEnd) { + setLastKey(); + to = lastKeyNode; + toIndex = lastKeyIndex; + } else { + to = maximum(backingMap.root); + toIndex = to == null ? 0 : to.right_idx; } + if (to == null) { + return 0; + } + if (from == to) { + return toIndex - fromIndex + 1; + } + int sum = 0; + while (from != to) { + sum += (from.right_idx - fromIndex + 1); + from = from.next; + fromIndex = from.left_idx; + } + return sum + toIndex - fromIndex + 1; + } + private void readObject(ObjectInputStream stream) throws IOException, + ClassNotFoundException { + stream.defaultReadObject(); + firstKeyModCount = -1; + lastKeyModCount = -1; } + } - static class SubMapKeySet extends AbstractSet implements Set { - SubMap subMap; + static class SubMapEntrySet extends AbstractSet> + implements Set> { + SubMap subMap; - SubMapKeySet(SubMap map) { - subMap = map; - } + SubMapEntrySet(SubMap map) { + subMap = map; + } - @Override - public boolean contains(Object object) { - return subMap.containsKey(object); - } + @Override + public boolean isEmpty() { + return subMap.isEmpty(); + } - @Override - public boolean isEmpty() { - return subMap.isEmpty(); + public Iterator> iterator() { + Node from; + int fromIndex; + if (subMap.hasStart) { + subMap.setFirstKey(); + from = subMap.firstKeyNode; + fromIndex = subMap.firstKeyIndex; + } else { + from = minimum(subMap.backingMap.root); + fromIndex = from != null ? from.left_idx : 0; + } + if (!subMap.hasEnd) { + return new UnboundedEntryIterator(subMap.backingMap, from, from == null ? 0 : from.right_idx - fromIndex); } + subMap.setLastKey(); + Node to = subMap.lastKeyNode; + int toIndex = subMap.lastKeyIndex; + return new BoundedEntryIterator(from, from == null ? 0 : from.right_idx - fromIndex, subMap.backingMap, to, to == null ? 0 : to.right_idx - toIndex); + } - @Override - public int size() { - int size = 0; - Iterator it = iterator(); - while (it.hasNext()) { - size++; - it.next(); + @Override + public int size() { + return subMap.size(); + } + + @SuppressWarnings("unchecked") + @Override + public boolean contains(Object object) { + if (object instanceof Map.Entry) { + Map.Entry entry = (Map.Entry) object; + K key = entry.getKey(); + if (subMap.isInRange(key)) { + V v1 = subMap.get(key), v2 = entry.getValue(); + return v1 == null ? ( v2 == null && subMap.containsKey(key) ) : v1.equals(v2); } - return size; } + return false; + } - @Override - public Iterator iterator() { - TreeMap.Entry startNode = subMap.firstEntry(); - if (subMap.hasEnd) { - Comparator cmp = subMap.comparator(); - if (cmp == null) { - return new ComparableBoundedKeyIterator(subMap.backingMap, startNode, toComparable(subMap.endKey)); - } - return new ComparatorBoundedKeyIterator(subMap.backingMap, startNode, subMap.endKey); - } - return new UnboundedKeyIterator(subMap.backingMap, startNode); + @Override + public boolean remove(Object object) { + if (contains(object)) { + Map.Entry entry = (Map.Entry) object; + K key = entry.getKey(); + subMap.remove(key); + return true; } + return false; } + } - static class SubMapValuesCollection extends AbstractCollection { - SubMap subMap; + static class SubMapKeySet extends AbstractSet implements Set { + SubMap subMap; - public SubMapValuesCollection(SubMap subMap) { - this.subMap = subMap; - } + SubMapKeySet(SubMap map) { + subMap = map; + } - @Override - public boolean isEmpty() { - return subMap.isEmpty(); - } + @Override + public boolean contains(Object object) { + return subMap.containsKey(object); + } - @Override - public Iterator iterator() { - TreeMap.Entry startNode = subMap.firstEntry(); - if (subMap.hasEnd) { - Comparator cmp = subMap.comparator(); - if (cmp == null) { - return new ComparableBoundedValueIterator(subMap.backingMap, startNode, toComparable(subMap.endKey)); - } - return new ComparatorBoundedValueIterator(subMap.backingMap, startNode, subMap.endKey); - } - return new UnboundedValueIterator(subMap.backingMap, startNode); - } + @Override + public boolean isEmpty() { + return subMap.isEmpty(); + } - @Override - public int size() { - int cnt = 0; - for (Iterator it = iterator(); it.hasNext();) { - it.next(); - cnt++; - } - return cnt; - } + @Override + public int size() { + return subMap.size(); } - /** - * Constructs a new empty {@code TreeMap} instance. - * - * @since Android 1.0 + @Override + public boolean remove(Object object) { + if (subMap.containsKey(object)) { + subMap.remove(object); + return true; + } + return false; + } + + public Iterator iterator() { + Node from; + int fromIndex; + if (subMap.hasStart) { + subMap.setFirstKey(); + from = subMap.firstKeyNode; + fromIndex = subMap.firstKeyIndex; + } else { + from = minimum(subMap.backingMap.root); + fromIndex = from != null ? from.left_idx : 0; + } + if (!subMap.hasEnd) { + return new UnboundedKeyIterator(subMap.backingMap, from, + from == null ? 0 : from.right_idx - fromIndex); + } + subMap.setLastKey(); + Node to = subMap.lastKeyNode; + int toIndex = subMap.lastKeyIndex; + return new BoundedKeyIterator(from, + from == null ? 0 : from.right_idx - fromIndex, subMap.backingMap, to, + to == null ? 0 : to.right_idx - toIndex); + } + } + + static class SubMapValuesCollection extends AbstractCollection { + SubMap subMap; + + public SubMapValuesCollection(SubMap subMap) { + this.subMap = subMap; + } + + @Override + public boolean isEmpty() { + return subMap.isEmpty(); + } + + @Override + public Iterator iterator() { + Node from; + int fromIndex; + if (subMap.hasStart) { + subMap.setFirstKey(); + from = subMap.firstKeyNode; + fromIndex = subMap.firstKeyIndex; + } else { + from = minimum(subMap.backingMap.root); + fromIndex = from != null ? from.left_idx : 0; + } + if (!subMap.hasEnd) { + return new UnboundedValueIterator(subMap.backingMap, from, + from == null ? 0 : from.right_idx - fromIndex); + } + subMap.setLastKey(); + Node to = subMap.lastKeyNode; + int toIndex = subMap.lastKeyIndex; + return new BoundedValueIterator(from, + from == null ? 0 : from.right_idx - fromIndex, subMap.backingMap, to, + to == null ? 0 : to.right_idx - toIndex); + } + + @Override + public int size() { + return subMap.size(); + } + } + + /** + * Constructs a new empty {@code TreeMap} instance. */ public TreeMap() { - super(); } /** * Constructs a new empty {@code TreeMap} instance with the specified * comparator. - * + * * @param comparator * the comparator to compare keys with. - * @since Android 1.0 */ public TreeMap(Comparator comparator) { this.comparator = comparator; @@ -683,95 +977,57 @@ public class TreeMap extends AbstractMap implements SortedMap, /** * Constructs a new {@code TreeMap} instance containing the mappings from * the specified map and using natural ordering. - * + * * @param map * the mappings to add. * @throws ClassCastException * if a key in the specified map does not implement the * Comparable interface, or if the keys in the map cannot be * compared. - * @since Android 1.0 */ - public TreeMap(Map map) { - this(); + public TreeMap(Map map) { putAll(map); } /** * Constructs a new {@code TreeMap} instance containing the mappings from * the specified SortedMap and using the same comparator. - * + * * @param map * the mappings to add. - * @since Android 1.0 */ - public TreeMap(SortedMap map) { + public TreeMap(SortedMap map) { this(map.comparator()); + Node lastNode = null; Iterator> it = map.entrySet().iterator(); - if (it.hasNext()) { + while (it.hasNext()) { Map.Entry entry = it.next(); - Entry last = new Entry(entry.getKey(), entry.getValue()); - root = last; - size = 1; - while (it.hasNext()) { - entry = it.next(); - Entry x = new Entry(entry.getKey(), entry.getValue()); - x.parent = last; - last.right = x; - size++; - balance(x); - last = x; - } + lastNode = addToLast(lastNode, entry.getKey(), entry.getValue()); } } - void balance(Entry x) { - Entry y; - x.color = true; - while (x != root && x.parent.color) { - if (x.parent == x.parent.parent.left) { - y = x.parent.parent.right; - if (y != null && y.color) { - x.parent.color = false; - y.color = false; - x.parent.parent.color = true; - x = x.parent.parent; - } else { - if (x == x.parent.right) { - x = x.parent; - leftRotate(x); - } - x.parent.color = false; - x.parent.parent.color = true; - rightRotate(x.parent.parent); - } - } else { - y = x.parent.parent.left; - if (y != null && y.color) { - x.parent.color = false; - y.color = false; - x.parent.parent.color = true; - x = x.parent.parent; - } else { - if (x == x.parent.left) { - x = x.parent; - rightRotate(x); - } - x.parent.color = false; - x.parent.parent.color = true; - leftRotate(x.parent.parent); - } - } + Node addToLast(Node last, K key, V value) { + if (last == null) { + root = last = createNode(key, value); + size = 1; + } else if (last.size == Node.NODE_SIZE) { + Node newNode = createNode(key, value); + attachToRight(last, newNode); + balance(newNode); + size++; + last = newNode; + } else { + appendFromRight(last, key, value); + size++; } - root.color = false; + return last; } /** * Removes all mappings from this TreeMap, leaving it empty. - * + * * @see Map#isEmpty() * @see #size() - * @since Android 1.0 */ @Override public void clear() { @@ -783,10 +1039,9 @@ public class TreeMap extends AbstractMap implements SortedMap, /** * Returns a new {@code TreeMap} with the same mappings, size and comparator * as this instance. - * + * * @return a shallow copy of this instance. * @see java.lang.Cloneable - * @since Android 1.0 */ @SuppressWarnings("unchecked") @Override @@ -796,6 +1051,17 @@ public class TreeMap extends AbstractMap implements SortedMap, clone.entrySet = null; if (root != null) { clone.root = root.clone(null); + // restore prev/next chain + Node node = minimum(clone.root); + while (true) { + Node nxt = successor(node); + if (nxt == null) { + break; + } + nxt.prev = node; + node.next = nxt; + node = nxt; + } } return clone; } catch (CloneNotSupportedException e) { @@ -803,11 +1069,22 @@ public class TreeMap extends AbstractMap implements SortedMap, } } + static private Node successor(Node x) { + if (x.right != null) { + return minimum(x.right); + } + Node y = x.parent; + while (y != null && x == y.right) { + x = y; + y = y.parent; + } + return y; + } + /** * Returns the comparator used to compare elements in this map. - * + * * @return the comparator or {@code null} if the natural ordering is used. - * @since Android 1.0 */ public Comparator comparator() { return comparator; @@ -815,47 +1092,95 @@ public class TreeMap extends AbstractMap implements SortedMap, /** * Returns whether this map contains the specified key. - * + * * @param key * the key to search for. * @return {@code true} if this map contains the specified key, * {@code false} otherwise. - * @since Android 1.0 + * @throws ClassCastException + * if the specified key cannot be compared with the keys in this + * map. + * @throws NullPointerException + * if the specified key is {@code null} and the comparator + * cannot handle {@code null} keys. */ @Override public boolean containsKey(Object key) { - return find(key) != null; + Comparable object = comparator == null ? toComparable((K) key) : null; + K keyK = (K) key; + Node node = root; + while (node != null) { + K[] keys = node.keys; + int left_idx = node.left_idx; + int result = cmp(object, keyK, keys[left_idx]); + if (result < 0) { + node = node.left; + } else if (result == 0) { + return true; + } else { + int right_idx = node.right_idx; + if (left_idx != right_idx) { + result = cmp(object, keyK, keys[right_idx]); + } + if (result > 0) { + node = node.right; + } else if (result == 0) { + return true; + } else { /*search in node*/ + int low = left_idx + 1, mid = 0, high = right_idx - 1; + while (low <= high) { + mid = (low + high) >> 1; + result = cmp(object, keyK, keys[mid]); + if (result > 0) { + low = mid + 1; + } else if (result == 0) { + return true; + } else { + high = mid - 1; + } + } + return false; + } + } + } + return false; } /** * Returns whether this map contains the specified value. - * + * * @param value * the value to search for. * @return {@code true} if this map contains the specified value, * {@code false} otherwise. - * @since Android 1.0 */ @Override public boolean containsValue(Object value) { - if (root != null) { - return containsValue(root, value); + if (root == null) { + return false; } - return false; - } - - private boolean containsValue(Entry node, Object value) { - if (value == null ? node.value == null : value.equals(node.value)) { - return true; - } - if (node.left != null) { - if (containsValue(node.left, value)) { - return true; + Node node = minimum(root); + if (value != null) { + while (node != null) { + int to = node.right_idx; + V[] values = node.values; + for (int i = node.left_idx; i <= to; i++) { + if (value.equals(values[i])) { + return true; + } + } + node = node.next; } - } - if (node.right != null) { - if (containsValue(node.right, value)) { - return true; + } else { + while (node != null) { + int to = node.right_idx; + V[] values = node.values; + for (int i = node.left_idx; i <= to; i++) { + if (values[i] == null) { + return true; + } + } + node = node.next; } } return false; @@ -866,15 +1191,14 @@ public class TreeMap extends AbstractMap implements SortedMap, * an instance of {@link Map.Entry}. As the set is backed by this map, * changes in one will be reflected in the other. It does not support adding * operations. - * + * * @return a set of the mappings. - * @since Android 1.0 */ @Override public Set> entrySet() { if (entrySet == null) { entrySet = new AbstractSet>() { - @Override + @Override public int size() { return size; } @@ -889,8 +1213,20 @@ public class TreeMap extends AbstractMap implements SortedMap, public boolean contains(Object object) { if (object instanceof Map.Entry) { Map.Entry entry = (Map.Entry) object; - Object v1 = get(entry.getKey()), v2 = entry.getValue(); - return v1 == null ? v2 == null : v1.equals(v2); + K key = entry.getKey(); + Object v1 = TreeMap.this.get(key), v2 = entry.getValue(); + return v1 == null ? ( v2 == null && TreeMap.this.containsKey(key) ) : v1.equals(v2); + } + return false; + } + + @Override + public boolean remove(Object object) { + if (contains(object)) { + Map.Entry entry = (Map.Entry) object; + K key = entry.getKey(); + TreeMap.this.remove(key); + return true; } return false; } @@ -904,88 +1240,944 @@ public class TreeMap extends AbstractMap implements SortedMap, return entrySet; } - @SuppressWarnings("unchecked") - private Entry find(Object keyObj) { - int result; - K key = (K)keyObj; - Comparable object = null; - if (comparator == null) { - object = toComparable(key); + /** + * Returns the first key in this map. + * + * @return the first key in this map. + * @throws NoSuchElementException + * if this map is empty. + */ + public K firstKey() { + if (root != null) { + Node node = minimum(root); + return node.keys[node.left_idx]; } - Entry x = root; - while (x != null) { - result = object != null ? object.compareTo(x.key) : comparator - .compare(key, x.key); - if (result == 0) { - return x; + throw new NoSuchElementException(); + } + + + /** + * Returns the value of the mapping with the specified key. + * + * @param key + * the key. + * @return the value of the mapping with the specified key. + * @throws ClassCastException + * if the key cannot be compared with the keys in this map. + * @throws NullPointerException + * if the key is {@code null} and the comparator cannot handle + * {@code null}. + */ + @Override + public V get(Object key) { + Comparable object = comparator == null ? toComparable((K) key) : null; + K keyK = (K) key; + Node node = root; + while (node != null) { + K[] keys = node.keys; + int left_idx = node.left_idx; + int result = cmp(object, keyK, keys[left_idx]); + if (result < 0) { + node = node.left; + } else if (result == 0) { + return node.values[left_idx]; + } else { + int right_idx = node.right_idx; + if (left_idx != right_idx) { + result = cmp(object, keyK, keys[right_idx]); + } + if (result > 0) { + node = node.right; + } else if (result == 0) { + return node.values[right_idx]; + } else { /*search in node*/ + int low = left_idx + 1, mid = 0, high = right_idx - 1; + while (low <= high) { + mid = (low + high) >> 1; + result = cmp(object, keyK, keys[mid]); + if (result > 0) { + low = mid + 1; + } else if (result == 0) { + return node.values[mid]; + } else { + high = mid - 1; + } + } + return null; + } } - x = result < 0 ? x.left : x.right; } return null; } - @SuppressWarnings("unchecked") - Entry findAfter(Object keyObj) { - K key = (K)keyObj; - int result; - Comparable object = null; + private int cmp(Comparable object, K key1, K key2) { + return object != null ? + object.compareTo(key2) : comparator.compare(key1, key2); + } + + /** + * Returns a sorted map over a range of this sorted map with all keys that + * are less than the specified {@code endKey}. Changes to the returned + * sorted map are reflected in this sorted map and vice versa. + *

          + * Note: The returned map will not allow an insertion of a key outside the + * specified range. + * + * @param endKey + * the high boundary of the range specified. + * @return a sorted map where the keys are less than {@code endKey}. + * @throws ClassCastException + * if the specified key cannot be compared with the keys in this + * map. + * @throws NullPointerException + * if the specified key is {@code null} and the comparator + * cannot handle {@code null} keys. + * @throws IllegalArgumentException + * if this map is itself a sorted map over a range of another + * map and the specified key is outside of its range. + */ + public SortedMap headMap(K endKey) { + // Check for errors if (comparator == null) { - object = toComparable(key); + toComparable(endKey).compareTo(endKey); + } else { + comparator.compare(endKey, endKey); + } + return new SubMap(this, endKey); + } + + /** + * Returns a set of the keys contained in this map. The set is backed by + * this map so changes to one are reflected by the other. The set does not + * support adding. + * + * @return a set of the keys. + */ + @Override + public Set keySet() { + if (keySet == null) { + keySet = new AbstractSet() { + @Override + public boolean contains(Object object) { + return TreeMap.this.containsKey(object); + } + + @Override + public int size() { + return TreeMap.this.size; + } + + @Override + public void clear() { + TreeMap.this.clear(); + } + + @Override + public boolean remove(Object object) { + if (contains(object)) { + TreeMap.this.remove(object); + return true; + } + return false; + } + + @Override + public Iterator iterator() { + return new UnboundedKeyIterator(TreeMap.this); + } + }; + } + return keySet; + } + + /** + * Returns the last key in this map. + * + * @return the last key in this map. + * @throws NoSuchElementException + * if this map is empty. + */ + public K lastKey() { + if (root != null) { + Node node = maximum(root); + return node.keys[node.right_idx]; + } + throw new NoSuchElementException(); + } + + static Node minimum(Node x) { + if (x == null) { + return null; + } + while (x.left != null) { + x = x.left; + } + return x; + } + + static Node maximum(Node x) { + if (x == null) { + return null; + } + while (x.right != null) { + x = x.right; + } + return x; + } + + /** + * Maps the specified key to the specified value. + * + * @param key + * the key. + * @param value + * the value. + * @return the value of any previous mapping with the specified key or + * {@code null} if there was no mapping. + * @throws ClassCastException + * if the specified key cannot be compared with the keys in this + * map. + * @throws NullPointerException + * if the specified key is {@code null} and the comparator + * cannot handle {@code null} keys. + */ + @Override + public V put(K key, V value) { + if (root == null) { + root = createNode(key, value); + size = 1; + modCount++; + return null; + } + Comparable object = comparator == null ? toComparable((K) key) : null; + K keyK = (K) key; + Node node = root; + Node prevNode = null; + int result = 0; + while (node != null) { + prevNode = node; + K[] keys = node.keys; + int left_idx = node.left_idx; + result = cmp(object, keyK, keys[left_idx]); + if (result < 0) { + node = node.left; + } else if (result == 0) { + V res = node.values[left_idx]; + node.values[left_idx] = value; + return res; + } else { + int right_idx = node.right_idx; + if (left_idx != right_idx) { + result = cmp(object, keyK, keys[right_idx]); + } + if (result > 0) { + node = node.right; + } else if (result == 0) { + V res = node.values[right_idx]; + node.values[right_idx] = value; + return res; + } else { /*search in node*/ + int low = left_idx + 1, mid = 0, high = right_idx - 1; + while (low <= high) { + mid = (low + high) >> 1; + result = cmp(object, keyK, keys[mid]); + if (result > 0) { + low = mid + 1; + } else if (result == 0) { + V res = node.values[mid]; + node.values[mid] = value; + return res; + } else { + high = mid - 1; + } + } + result = low; + break; + } + } + } /* while */ +/* + if(node == null) { + if(prevNode==null) { + - case of empty Tree + } else { + result < 0 - prevNode.left==null - attach here + result > 0 - prevNode.right==null - attach here + } + } else { + insert into node. + result - index where it should be inserted. + } + */ + size++; + modCount++; + if (node == null) { + if (prevNode == null) { + // case of empty Tree + root = createNode(key, value); + } else if (prevNode.size < Node.NODE_SIZE) { + // there is a place for insert + if (result < 0) { + appendFromLeft(prevNode, key, value); + } else { + appendFromRight(prevNode, key, value); + } + } else { + // create and link + Node newNode = createNode(key, value); + if (result < 0) { + attachToLeft(prevNode, newNode); + } else { + attachToRight(prevNode, newNode); + } + balance(newNode); + } + } else { + // insert into node. + // result - index where it should be inserted. + if (node.size < Node.NODE_SIZE) { // insert and ok + int left_idx = node.left_idx; + int right_idx = node.right_idx; + if (left_idx == 0 || ((right_idx != Node.NODE_SIZE - 1) && (right_idx - result <= result - left_idx))) { + int right_idxPlus1 = right_idx + 1; + System.arraycopy(node.keys, result, node.keys, result + 1, right_idxPlus1 - result); + System.arraycopy(node.values, result, node.values, result + 1, right_idxPlus1 - result); + node.right_idx = right_idxPlus1; + node.keys[result] = key; + node.values[result] = value; + } else { + int left_idxMinus1 = left_idx - 1; + System.arraycopy(node.keys, left_idx, node.keys, left_idxMinus1, result - left_idx); + System.arraycopy(node.values, left_idx, node.values, left_idxMinus1, result - left_idx); + node.left_idx = left_idxMinus1; + node.keys[result - 1] = key; + node.values[result - 1] = value; + } + node.size++; + } else { + // there are no place here + // insert and push old pair + Node previous = node.prev; + Node nextNode = node.next; + boolean removeFromStart; + boolean attachFromLeft = false; + Node attachHere = null; + if (previous == null) { + if (nextNode != null && nextNode.size < Node.NODE_SIZE) { + // move last pair to next + removeFromStart = false; + } else { + // next node doesn't exist or full + // left==null + // drop first pair to new node from left + removeFromStart = true; + attachFromLeft = true; + attachHere = node; + } + } else if (nextNode == null) { + if (previous.size < Node.NODE_SIZE) { + // move first pair to prev + removeFromStart = true; + } else { + // right == null; + // drop last pair to new node from right + removeFromStart = false; + attachFromLeft = false; + attachHere = node; + } + } else { + if (previous.size < Node.NODE_SIZE) { + if (nextNode.size < Node.NODE_SIZE) { + // choose prev or next for moving + removeFromStart = previous.size < nextNode.size; + } else { + // move first pair to prev + removeFromStart = true; + } + } else { + if (nextNode.size < Node.NODE_SIZE) { + // move last pair to next + removeFromStart = false; + } else { + // prev & next are full + // if node.right!=null then node.next.left==null + // if node.left!=null then node.prev.right==null + if (node.right == null) { + attachHere = node; + attachFromLeft = false; + removeFromStart = false; + } else { + attachHere = nextNode; + attachFromLeft = true; + removeFromStart = false; + } + } + } + } + K movedKey; + V movedValue; + if (removeFromStart) { + // node.left_idx == 0 + movedKey = node.keys[0]; + movedValue = node.values[0]; + int resMunus1 = result - 1; + System.arraycopy(node.keys, 1, node.keys, 0, resMunus1); + System.arraycopy(node.values, 1, node.values, 0, resMunus1); + node.keys [resMunus1] = key; + node.values[resMunus1] = value; + } else { + // node.right_idx == Node.NODE_SIZE - 1 + movedKey = node.keys[Node.NODE_SIZE - 1]; + movedValue = node.values[Node.NODE_SIZE - 1]; + System.arraycopy(node.keys, result, node.keys, result + 1, Node.NODE_SIZE - 1 - result); + System.arraycopy(node.values, result, node.values, result + 1, Node.NODE_SIZE - 1 - result); + node.keys[result] = key; + node.values[result] = value; + } + if (attachHere == null) { + if (removeFromStart) { + appendFromRight(previous, movedKey, movedValue); + } else { + appendFromLeft(nextNode, movedKey, movedValue); + } + } else { + Node newNode = createNode(movedKey, movedValue); + if (attachFromLeft) { + attachToLeft(attachHere, newNode); + } else { + attachToRight(attachHere, newNode); + } + balance(newNode); + } + } + } + return null; + } + + private void appendFromLeft(Node node, K keyObj, V value) { + if (node.left_idx == 0) { + int new_right = node.right_idx + 1; + System.arraycopy(node.keys, 0, node.keys, 1, new_right); + System.arraycopy(node.values, 0, node.values, 1, new_right); + node.right_idx = new_right; + } else { + node.left_idx--; + } + node.size++; + node.keys[node.left_idx] = keyObj; + node.values[node.left_idx] = value; + } + + private void attachToLeft(Node node, Node newNode) { + newNode.parent = node; + // node.left==null - attach here + node.left = newNode; + Node predecessor = node.prev; + newNode.prev = predecessor; + newNode.next = node; + if (predecessor != null) { + predecessor.next = newNode; + } + node.prev = newNode; + } + + /* add pair into node; existence free room in the node should be checked + * before call + */ + private void appendFromRight(Node node, K keyObj, V value) { + if (node.right_idx == Node.NODE_SIZE - 1) { + int left_idx = node.left_idx; + int left_idxMinus1 = left_idx - 1; + System.arraycopy(node.keys, left_idx, node.keys, left_idxMinus1, Node.NODE_SIZE - left_idx); + System.arraycopy(node.values, left_idx, node.values, left_idxMinus1, Node.NODE_SIZE - left_idx); + node.left_idx = left_idxMinus1; + } else { + node.right_idx++; } - Entry x = root, last = null; - while (x != null) { - result = object != null ? object.compareTo(x.key) : comparator - .compare(key, x.key); - if (result == 0) { - return x; + node.size++; + node.keys[node.right_idx] = keyObj; + node.values[node.right_idx] = value; + } + + private void attachToRight(Node node, Node newNode) { + newNode.parent = node; + // - node.right==null - attach here + node.right = newNode; + newNode.prev = node; + Node successor = node.next; + newNode.next = successor; + if (successor != null) { + successor.prev = newNode; + } + node.next = newNode; + } + + private Node createNode(K keyObj, V value) { + Node node = new Node(); + node.keys[0] = keyObj; + node.values[0] = value; + node.left_idx = 0; + node.right_idx = 0; + node.size = 1; + return node; + } + + void balance(Node x) { + Node y; + x.color = true; + while (x != root && x.parent.color) { + if (x.parent == x.parent.parent.left) { + y = x.parent.parent.right; + if (y != null && y.color) { + x.parent.color = false; + y.color = false; + x.parent.parent.color = true; + x = x.parent.parent; + } else { + if (x == x.parent.right) { + x = x.parent; + leftRotate(x); + } + x.parent.color = false; + x.parent.parent.color = true; + rightRotate(x.parent.parent); + } + } else { + y = x.parent.parent.left; + if (y != null && y.color) { + x.parent.color = false; + y.color = false; + x.parent.parent.color = true; + x = x.parent.parent; + } else { + if (x == x.parent.left) { + x = x.parent; + rightRotate(x); + } + x.parent.color = false; + x.parent.parent.color = true; + leftRotate(x.parent.parent); + } } + } + root.color = false; + } + + private void rightRotate(Node x) { + Node y = x.left; + x.left = y.right; + if (y.right != null) { + y.right.parent = x; + } + y.parent = x.parent; + if (x.parent == null) { + root = y; + } else { + if (x == x.parent.right) { + x.parent.right = y; + } else { + x.parent.left = y; + } + } + y.right = x; + x.parent = y; + } + + + private void leftRotate(Node x) { + Node y = x.right; + x.right = y.left; + if (y.left != null) { + y.left.parent = x; + } + y.parent = x.parent; + if (x.parent == null) { + root = y; + } else { + if (x == x.parent.left) { + x.parent.left = y; + } else { + x.parent.right = y; + } + } + y.left = x; + x.parent = y; + } + + + /** + * Copies all the mappings in the given map to this map. These mappings will + * replace all mappings that this map had for any of the keys currently in + * the given map. + * + * @param map + * the map to copy mappings from. + * @throws ClassCastException + * if a key in the specified map cannot be compared with the + * keys in this map. + * @throws NullPointerException + * if a key in the specified map is {@code null} and the + * comparator cannot handle {@code null} keys. + */ + @Override + public void putAll(Map map) { + super.putAll(map); + } + + /** + * Removes the mapping with the specified key from this map. + * + * @param key + * the key of the mapping to remove. + * @return the value of the removed mapping or {@code null} if no mapping + * for the specified key was found. + * @throws ClassCastException + * if the specified key cannot be compared with the keys in this + * map. + * @throws NullPointerException + * if the specified key is {@code null} and the comparator + * cannot handle {@code null} keys. + */ + @Override + public V remove(Object key) { + if (size == 0) { + return null; + } + Comparable object = comparator == null ? toComparable((K) key) : null; + K keyK = (K) key; + Node node = root; + while (node != null) { + K[] keys = node.keys; + int left_idx = node.left_idx; + int result = cmp(object, keyK, keys[left_idx]); if (result < 0) { - last = x; - x = x.left; + node = node.left; + } else if (result == 0) { + V value = node.values[left_idx]; + removeLeftmost(node); + return value; + } else { + int right_idx = node.right_idx; + if (left_idx != right_idx) { + result = cmp(object, keyK, keys[right_idx]); + } + if (result > 0) { + node = node.right; + } else if (result == 0) { + V value = node.values[right_idx]; + removeRightmost(node); + return value; + } else { /*search in node*/ + int low = left_idx + 1, mid = 0, high = right_idx - 1; + while (low <= high) { + mid = (low + high) >> 1; + result = cmp(object, keyK, keys[mid]); + if (result > 0) { + low = mid + 1; + } else if (result == 0) { + V value = node.values[mid]; + removeMiddleElement(node, mid); + return value; + } else { + high = mid - 1; + } + } + return null; + } + } + } + return null; + } + + void removeLeftmost(Node node) { + int index = node.left_idx; + if (node.size == 1) { + deleteNode(node); + } else if (node.prev != null && (Node.NODE_SIZE - 1 - node.prev.right_idx) > node.size) { + // move all to prev node and kill it + Node prev = node.prev; + int size = node.right_idx - index; + System.arraycopy(node.keys, index + 1, prev.keys, prev.right_idx + 1, size); + System.arraycopy(node.values, index + 1, prev.values, prev.right_idx + 1, size); + prev.right_idx += size; + prev.size += size; + deleteNode(node); + } else if (node.next != null && (node.next.left_idx) > node.size) { + // move all to next node and kill it + Node next = node.next; + int size = node.right_idx - index; + int next_new_left = next.left_idx - size; + next.left_idx = next_new_left; + System.arraycopy(node.keys, index + 1, next.keys, next_new_left, size); + System.arraycopy(node.values, index + 1, next.values, next_new_left, size); + next.size += size; + deleteNode(node); + } else { + node.keys[index] = null; + node.values[index] = null; + node.left_idx++; + node.size--; + Node prev = node.prev; + if (prev != null && prev.size == 1) { + node.size++; + node.left_idx--; + node.keys [node.left_idx] = prev.keys [prev.left_idx]; + node.values[node.left_idx] = prev.values[prev.left_idx]; + deleteNode(prev); + } + } + modCount++; + size--; + } + + void removeRightmost(Node node) { + int index = node.right_idx; + if (node.size == 1) { + deleteNode(node); + } else if (node.prev != null && (Node.NODE_SIZE - 1 - node.prev.right_idx) > node.size) { + // move all to prev node and kill it + Node prev = node.prev; + int left_idx = node.left_idx; + int size = index - left_idx; + System.arraycopy(node.keys, left_idx, prev.keys, prev.right_idx + 1, size); + System.arraycopy(node.values, left_idx, prev.values, prev.right_idx + 1, size); + prev.right_idx += size; + prev.size += size; + deleteNode(node); + } else if (node.next != null && (node.next.left_idx) > node.size) { + // move all to next node and kill it + Node next = node.next; + int left_idx = node.left_idx; + int size = index - left_idx; + int next_new_left = next.left_idx - size; + next.left_idx = next_new_left; + System.arraycopy(node.keys, left_idx, next.keys, next_new_left, size); + System.arraycopy(node.values, left_idx, next.values, next_new_left, size); + next.size += size; + deleteNode(node); + } else { + node.keys[index] = null; + node.values[index] = null; + node.right_idx--; + node.size--; + Node next = node.next; + if (next != null && next.size == 1) { + node.size++; + node.right_idx++; + node.keys[node.right_idx] = next.keys[next.left_idx]; + node.values[node.right_idx] = next.values[next.left_idx]; + deleteNode(next); + } + } + modCount++; + size--; + } + + void removeMiddleElement(Node node, int index) { + // this function is called iff index if some middle element; + // so node.left_idx < index < node.right_idx + // condition above assume that node.size > 1 + if (node.prev != null && (Node.NODE_SIZE - 1 - node.prev.right_idx) > node.size) { + // move all to prev node and kill it + Node prev = node.prev; + int left_idx = node.left_idx; + int size = index - left_idx; + System.arraycopy(node.keys, left_idx, prev.keys, prev.right_idx + 1, size); + System.arraycopy(node.values, left_idx, prev.values, prev.right_idx + 1, size); + prev.right_idx += size; + size = node.right_idx - index; + System.arraycopy(node.keys, index + 1, prev.keys, prev.right_idx + 1, size); + System.arraycopy(node.values, index + 1, prev.values, prev.right_idx + 1, size); + prev.right_idx += size; + prev.size += (node.size - 1); + deleteNode(node); + } else if (node.next != null && (node.next.left_idx) > node.size) { + // move all to next node and kill it + Node next = node.next; + int left_idx = node.left_idx; + int next_new_left = next.left_idx - node.size + 1; + next.left_idx = next_new_left; + int size = index - left_idx; + System.arraycopy(node.keys, left_idx, next.keys, next_new_left, size); + System.arraycopy(node.values, left_idx, next.values, next_new_left, size); + next_new_left += size; + size = node.right_idx - index; + System.arraycopy(node.keys, index + 1, next.keys, next_new_left, size); + System.arraycopy(node.values, index + 1, next.values, next_new_left, size); + next.size += (node.size - 1); + deleteNode(node); + } else { + int moveFromRight = node.right_idx - index; + int left_idx = node.left_idx; + int moveFromLeft = index - left_idx ; + if (moveFromRight <= moveFromLeft) { + System.arraycopy(node.keys, index + 1, node.keys, index, moveFromRight); + System.arraycopy(node.values, index + 1, node.values, index, moveFromRight); + Node next = node.next; + if (next != null && next.size == 1) { + node.keys [node.right_idx] = next.keys [next.left_idx]; + node.values[node.right_idx] = next.values[next.left_idx]; + deleteNode(next); + } else { + node.keys [node.right_idx] = null; + node.values[node.right_idx] = null; + node.right_idx--; + node.size--; + } + } else { + System.arraycopy(node.keys, left_idx , node.keys, left_idx + 1, moveFromLeft); + System.arraycopy(node.values, left_idx , node.values, left_idx + 1, moveFromLeft); + Node prev = node.prev; + if (prev != null && prev.size == 1) { + node.keys [left_idx ] = prev.keys [prev.left_idx]; + node.values[left_idx ] = prev.values[prev.left_idx]; + deleteNode(prev); + } else { + node.keys [left_idx ] = null; + node.values[left_idx ] = null; + node.left_idx++; + node.size--; + } + } + } + modCount++; + size--; + } + + void removeFromIterator(Node node, int index) { + if (node.size == 1) { + // it is safe to delete the whole node here. + // iterator already moved to the next node; + deleteNode(node); + } else { + int left_idx = node.left_idx; + if (index == left_idx) { + Node prev = node.prev; + if (prev != null && prev.size == 1) { + node.keys [left_idx] = prev.keys [prev.left_idx]; + node.values[left_idx] = prev.values[prev.left_idx]; + deleteNode(prev); + } else { + node.keys [left_idx] = null; + node.values[left_idx] = null; + node.left_idx++; + node.size--; + } + } else if (index == node.right_idx) { + node.keys [index] = null; + node.values[index] = null; + node.right_idx--; + node.size--; + } else { + int moveFromRight = node.right_idx - index; + int moveFromLeft = index - left_idx; + if (moveFromRight <= moveFromLeft) { + System.arraycopy(node.keys, index + 1, node.keys, index, moveFromRight ); + System.arraycopy(node.values, index + 1, node.values, index, moveFromRight ); + node.keys [node.right_idx] = null; + node.values[node.right_idx] = null; + node.right_idx--; + node.size--; + } else { + System.arraycopy(node.keys, left_idx, node.keys, left_idx+ 1, moveFromLeft); + System.arraycopy(node.values, left_idx, node.values, left_idx+ 1, moveFromLeft); + node.keys [left_idx] = null; + node.values[left_idx] = null; + node.left_idx++; + node.size--; + } + } + } + modCount++; + size--; + } + + private void deleteNode(Node node) { + if (node.right == null) { + if (node.left != null) { + attachToParent(node, node.left); + } else { + attachNullToParent(node); + } + fixNextChain(node); + } else if(node.left == null) { // node.right != null + attachToParent(node, node.right); + fixNextChain(node); + } else { + // Here node.left!=nul && node.right!=null + // node.next should replace node in tree + // node.next!=null by tree logic. + // node.next.left==null by tree logic. + // node.next.right may be null or non-null + Node toMoveUp = node.next; + fixNextChain(node); + if(toMoveUp.right==null){ + attachNullToParent(toMoveUp); } else { - x = x.right; + attachToParent(toMoveUp, toMoveUp.right); + } + // Here toMoveUp is ready to replace node + toMoveUp.left = node.left; + if (node.left != null) { + node.left.parent = toMoveUp; } + toMoveUp.right = node.right; + if (node.right != null) { + node.right.parent = toMoveUp; + } + attachToParentNoFixup(node,toMoveUp); + toMoveUp.color = node.color; + } + } + + private void attachToParentNoFixup(Node toDelete, Node toConnect) { + // assert toConnect!=null + Node parent = toDelete.parent; + toConnect.parent = parent; + if (parent == null) { + root = toConnect; + } else if (toDelete == parent.left) { + parent.left = toConnect; + } else { + parent.right = toConnect; + } + } + + private void attachToParent(Node toDelete, Node toConnect) { + // assert toConnect!=null + attachToParentNoFixup(toDelete,toConnect); + if (!toDelete.color) { + fixup(toConnect); } - return last; } - Entry findBefore(K key) { - int result; - Comparable object = null; - if (comparator == null) { - object = toComparable(key); - } - Entry x = root, last = null; - while (x != null) { - result = object != null ? object.compareTo(x.key) : comparator - .compare(key, x.key); - if (result <= 0) { - x = x.left; + private void attachNullToParent(Node toDelete) { + Node parent = toDelete.parent; + if (parent == null) { + root = null; + } else { + if (toDelete == parent.left) { + parent.left = null; } else { - last = x; - x = x.right; + parent.right = null; + } + if (!toDelete.color) { + fixup(parent); } } - return last; } - /** - * Returns the first key in this map. - * - * @return the first key in this map. - * @exception NoSuchElementException - * if this sorted map is empty. - * @since Android 1.0 - */ - public K firstKey() { - if (root != null) { - return minimum(root).key; + private void fixNextChain(Node node) { + if (node.prev != null) { + node.prev.next = node.next; + } + if (node.next != null) { + node.next.prev = node.prev; } - throw new NoSuchElementException(); } - private void fixup(Entry x) { - Entry w; + private void fixup(Node x) { + Node w; while (x != root && !x.color) { if (x == x.parent.left) { w = x.parent.right; @@ -1004,7 +2196,7 @@ public class TreeMap extends AbstractMap implements SortedMap, } } if ((w.left == null || !w.left.color) - && (w.right == null || !w.right.color)) { + && (w.right == null || !w.right.color)) { w.color = true; x = x.parent; } else { @@ -1037,7 +2229,7 @@ public class TreeMap extends AbstractMap implements SortedMap, } } if ((w.left == null || !w.left.color) - && (w.right == null || !w.right.color)) { + && (w.right == null || !w.right.color)) { w.color = true; x = x.parent; } else { @@ -1058,321 +2250,11 @@ public class TreeMap extends AbstractMap implements SortedMap, x.color = false; } - /** - * Returns the value of the mapping with the specified key. - * - * @param key - * the key. - * @return the value of the mapping with the specified key. - * @throws ClassCastException - * if the key cannot be compared with the keys in this map. - * @throws NullPointerException - * if the key is {@code null} and the comparator cannot handle - * {@code null}. - * @since Android 1.0 - */ - @Override - public V get(Object key) { - Entry node = find(key); - if (node != null) { - return node.value; - } - return null; - } - - /** - * Returns a sorted map over a range of this sorted map with all keys that - * are less than the specified {@code endKey}. Changes to the returned - * sorted map are reflected in this sorted map and vice versa. - *

          - * Note: The returned map will not allow an insertion of a key outside the - * specified range. - *

          - * - * @param endKey - * the high boundary of the range specified. - * @return a sorted map where the keys are less than {@code endKey}. - * @throws ClassCastException - * if the specified key cannot be compared with the keys in this - * map. - * @throws NullPointerException - * if the specified key is {@code null} and the comparator - * cannot handle {@code null} keys. - * @throws IllegalArgumentException - * if this map is itself a sorted map over a range of another - * map and the specified key is outside of its range. - * @since Android 1.0 - */ - public SortedMap headMap(K endKey) { - // Check for errors - if (comparator == null) { - toComparable(endKey).compareTo(endKey); - } else { - comparator.compare(endKey, endKey); - } - return new SubMap(this, endKey); - } - - /** - * Returns a set of the keys contained in this map. The set is backed by - * this map so changes to one are reflected by the other. The set does not - * support adding. - * - * @return a set of the keys. - * @since Android 1.0 - */ - @Override - public Set keySet() { - if (keySet == null) { - keySet = new AbstractSet() { - @Override - public boolean contains(Object object) { - return containsKey(object); - } - - @Override - public int size() { - return size; - } - - @Override - public void clear() { - TreeMap.this.clear(); - } - - @Override - public Iterator iterator() { - return new UnboundedKeyIterator (TreeMap.this); - } - }; - } - return keySet; - } - - /** - * Returns the last key in this map. - * - * @return the last key in this map. - * @throws NoSuchElementException - * if this map is empty. - * @since Android 1.0 - */ - public K lastKey() { - if (root != null) { - return maximum(root).key; - } - throw new NoSuchElementException(); - } - - private void leftRotate(Entry x) { - Entry y = x.right; - x.right = y.left; - if (y.left != null) { - y.left.parent = x; - } - y.parent = x.parent; - if (x.parent == null) { - root = y; - } else { - if (x == x.parent.left) { - x.parent.left = y; - } else { - x.parent.right = y; - } - } - y.left = x; - x.parent = y; - } - - static Entry maximum(Entry x) { - while (x.right != null) { - x = x.right; - } - return x; - } - - static Entry minimum(Entry x) { - while (x.left != null) { - x = x.left; - } - return x; - } - - static Entry predecessor(Entry x) { - if (x.left != null) { - return maximum(x.left); - } - Entry y = x.parent; - while (y != null && x == y.left) { - x = y; - y = y.parent; - } - return y; - } - - /** - * Maps the specified key to the specified value. - * - * @param key - * the key. - * @param value - * the value. - * @return the value of any previous mapping with the specified key or - * {@code null} if there was no mapping. - * @throws ClassCastException - * if the specified key cannot be compared with the keys in this - * map. - * @throws NullPointerException - * if the specified key is {@code null} and the comparator - * cannot handle {@code null} keys. - * @since Android 1.0 - */ - @Override - public V put(K key, V value) { - MapEntry entry = rbInsert(key); - V result = entry.value; - entry.value = value; - return result; - } - - /** - * Copies all the mappings in the given map to this map. These mappings will - * replace all mappings that this map had for any of the keys currently in - * the given map. - * - * @param map - * the map to copy mappings from. - * @throws ClassCastException - * if a key in the specified map cannot be compared with the - * keys in this map. - * @throws NullPointerException - * if a key in the specified map is {@code null} and the - * comparator cannot handle {@code null} keys. - * @since Android 1.0 - */ - @Override - public void putAll(Map map) { - super.putAll(map); - } - - void rbDelete(Entry z) { - Entry y = z.left == null || z.right == null ? z : successor(z); - Entry x = y.left != null ? y.left : y.right; - if (x != null) { - x.parent = y.parent; - } - if (y.parent == null) { - root = x; - } else if (y == y.parent.left) { - y.parent.left = x; - } else { - y.parent.right = x; - } - modCount++; - if (y != z) { - z.key = y.key; - z.value = y.value; - } - if (!y.color && root != null) { - if (x == null) { - fixup(y.parent); - } else { - fixup(x); - } - } - size--; - } - - private Entry rbInsert(K object) { - int result = 0; - Entry y = null; - if (size != 0) { - Comparable key = null; - if (comparator == null) { - key = toComparable(object); - } - Entry x = root; - while (x != null) { - y = x; - result = key != null ? key.compareTo(x.key) : comparator - .compare(object, x.key); - if (result == 0) { - return x; - } - x = result < 0 ? x.left : x.right; - } - } - - size++; - modCount++; - Entry z = new Entry(object); - if (y == null) { - return root = z; - } - z.parent = y; - if (result < 0) { - y.left = z; - } else { - y.right = z; - } - balance(z); - return z; - } - - /** - * Removes the mapping with the specified key from this map. - * - * @param key - * the key of the mapping to remove. - * @return the value of the removed mapping or {@code null} if no mapping - * for the specified key was found. - * @throws ClassCastException - * if the specified key cannot be compared with the keys in this - * map. - * @throws NullPointerException - * if the specified key is {@code null} and the comparator - * cannot handle {@code null} keys. - * @since Android 1.0 - */ - @Override - public V remove(Object key) { - if (size == 0) { - return null; - } - Entry node = find(key); - if (node == null) { - return null; - } - V result = node.value; - rbDelete(node); - return result; - } - - private void rightRotate(Entry x) { - Entry y = x.left; - x.left = y.right; - if (y.right != null) { - y.right.parent = x; - } - y.parent = x.parent; - if (x.parent == null) { - root = y; - } else { - if (x == x.parent.right) { - x.parent.right = y; - } else { - x.parent.left = y; - } - } - y.right = x; - x.parent = y; - } /** * Returns the number of mappings in this map. - * + * * @return the number of mappings in this map. - * @since Android 1.0 */ @Override public int size() { @@ -1387,8 +2269,7 @@ public class TreeMap extends AbstractMap implements SortedMap, *

          * Note: The returned map will not allow an insertion of a key outside the * specified range. - *

          - * + * * @param startKey * the low boundary of the range (inclusive). * @param endKey @@ -1404,7 +2285,6 @@ public class TreeMap extends AbstractMap implements SortedMap, * if the start key is greater than the end key, or if this map * is itself a sorted map over a range of another sorted map and * the specified range is outside of its range. - * @since Android 1.0 */ public SortedMap subMap(K startKey, K endKey) { if (comparator == null) { @@ -1419,18 +2299,6 @@ public class TreeMap extends AbstractMap implements SortedMap, throw new IllegalArgumentException(); } - static Entry successor(Entry x) { - if (x.right != null) { - return minimum(x.right); - } - Entry y = x.parent; - while (y != null && x == y.right) { - x = y; - y = y.parent; - } - return y; - } - /** * Returns a sorted map over a range of this sorted map with all keys that * are greater than or equal to the specified {@code startKey}. Changes to @@ -1438,8 +2306,7 @@ public class TreeMap extends AbstractMap implements SortedMap, *

          * Note: The returned map will not allow an insertion of a key outside the * specified range. - *

          - * + * * @param startKey * the low boundary of the range specified. * @return a sorted map where the keys are greater or equal to @@ -1453,7 +2320,6 @@ public class TreeMap extends AbstractMap implements SortedMap, * @throws IllegalArgumentException * if this map itself a sorted map over a range of another map * and the specified key is outside of its range. - * @since Android 1.0 */ public SortedMap tailMap(K startKey) { // Check for errors @@ -1476,16 +2342,13 @@ public class TreeMap extends AbstractMap implements SortedMap, * "wrapper object" over the iterator of map's entrySet(). The {@code size} * method wraps the map's size method and the {@code contains} method wraps * the map's containsValue method. - *

          *

          * The collection is created when this method is called for the first time * and returned in response to all subsequent calls. This method may return * different collections when multiple concurrent calls occur, since no * synchronization is performed. - *

          - * + * * @return a collection of the values contained in this map. - * @since Android 1.0 */ @Override public Collection values() { @@ -1508,7 +2371,7 @@ public class TreeMap extends AbstractMap implements SortedMap, @Override public Iterator iterator() { - return new UnboundedValueIterator (TreeMap.this); + return new UnboundedValueIterator(TreeMap.this); } }; } @@ -1519,32 +2382,26 @@ public class TreeMap extends AbstractMap implements SortedMap, stream.defaultWriteObject(); stream.writeInt(size); if (size > 0) { - Entry node = minimum(root); + Node node = minimum(root); while (node != null) { - stream.writeObject(node.key); - stream.writeObject(node.value); - node = successor(node); + int to = node.right_idx; + for (int i = node.left_idx; i <= to; i++) { + stream.writeObject(node.keys[i]); + stream.writeObject(node.values[i]); + } + node = node.next; } } } @SuppressWarnings("unchecked") private void readObject(ObjectInputStream stream) throws IOException, - ClassNotFoundException { + ClassNotFoundException { stream.defaultReadObject(); - size = stream.readInt(); - Entry last = null; - for (int i = size; --i >= 0;) { - Entry node = new Entry((K)stream.readObject()); - node.value = (V)stream.readObject(); - if (last == null) { - root = node; - } else { - node.parent = last; - last.right = node; - balance(node); - } - last = node; + int size = stream.readInt(); + Node lastNode = null; + for (int i = 0; i < size; i++) { + lastNode = addToLast(lastNode, (K) stream.readObject(), (V) stream.readObject()); } } } diff --git a/libcore/luni/src/main/java/java/util/TreeSet.java b/libcore/luni/src/main/java/java/util/TreeSet.java index bfd572ff7..375b9c83e 100644 --- a/libcore/luni/src/main/java/java/util/TreeSet.java +++ b/libcore/luni/src/main/java/java/util/TreeSet.java @@ -17,7 +17,6 @@ package java.util; - import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; @@ -28,25 +27,23 @@ import java.io.Serializable; * and removing) are supported. The elements can be any objects which are * comparable to each other either using their natural order or a specified * Comparator. - * - * @since Android 1.0 + * + * @since 1.2 */ -public class TreeSet extends AbstractSet implements SortedSet, Cloneable, - Serializable { - +public class TreeSet extends AbstractSet implements SortedSet, + Cloneable, Serializable { + private static final long serialVersionUID = -2479143000061671589L; private transient SortedMap backingMap; - private TreeSet(SortedMap map) { + private TreeSet(SortedMap map) { this.backingMap = map; } /** * Constructs a new empty instance of {@code TreeSet} which uses natural * ordering. - * - * @since Android 1.0 */ public TreeSet() { backingMap = new TreeMap(); @@ -55,14 +52,13 @@ public class TreeSet extends AbstractSet implements SortedSet, Cloneabl /** * Constructs a new instance of {@code TreeSet} which uses natural ordering * and containing the unique elements in the specified collection. - * + * * @param collection * the collection of elements to add. * @throws ClassCastException * when an element in the collection does not implement the * Comparable interface, or the elements in the collection * cannot be compared. - * @since Android 1.0 */ public TreeSet(Collection collection) { this(); @@ -72,10 +68,9 @@ public class TreeSet extends AbstractSet implements SortedSet, Cloneabl /** * Constructs a new empty instance of {@code TreeSet} which uses the * specified comparator. - * + * * @param comparator * the comparator to use. - * @since Android 1.0 */ public TreeSet(Comparator comparator) { backingMap = new TreeMap(comparator); @@ -84,10 +79,9 @@ public class TreeSet extends AbstractSet implements SortedSet, Cloneabl /** * Constructs a new instance of {@code TreeSet} containing the elements of * the specified SortedSet and using the same Comparator. - * + * * @param set * the SortedSet of elements to add. - * @since Android 1.0 */ public TreeSet(SortedSet set) { this(set.comparator()); @@ -99,7 +93,7 @@ public class TreeSet extends AbstractSet implements SortedSet, Cloneabl /** * Adds the specified object to this {@code TreeSet}. - * + * * @param object * the object to add. * @return {@code true} when this {@code TreeSet} did not already contain @@ -110,7 +104,6 @@ public class TreeSet extends AbstractSet implements SortedSet, Cloneabl * @throws NullPointerException * when the object is null and the comparator cannot handle * null. - * @since Android 1.0 */ @Override public boolean add(E object) { @@ -119,7 +112,7 @@ public class TreeSet extends AbstractSet implements SortedSet, Cloneabl /** * Adds the objects in the specified collection to this {@code TreeSet}. - * + * * @param collection * the collection of objects to add. * @return {@code true} if this {@code TreeSet} was modified, {@code false} @@ -130,7 +123,6 @@ public class TreeSet extends AbstractSet implements SortedSet, Cloneabl * @throws NullPointerException * when an object in the collection is null and the comparator * cannot handle null. - * @since Android 1.0 */ @Override public boolean addAll(Collection collection) { @@ -139,10 +131,9 @@ public class TreeSet extends AbstractSet implements SortedSet, Cloneabl /** * Removes all elements from this {@code TreeSet}, leaving it empty. - * + * * @see #isEmpty * @see #size - * @since Android 1.0 */ @Override public void clear() { @@ -152,10 +143,9 @@ public class TreeSet extends AbstractSet implements SortedSet, Cloneabl /** * Returns a new {@code TreeSet} with the same elements, size and comparator * as this {@code TreeSet}. - * + * * @return a shallow copy of this {@code TreeSet}. * @see java.lang.Cloneable - * @since Android 1.0 */ @SuppressWarnings("unchecked") @Override @@ -163,7 +153,8 @@ public class TreeSet extends AbstractSet implements SortedSet, Cloneabl try { TreeSet clone = (TreeSet) super.clone(); if (backingMap instanceof TreeMap) { - clone.backingMap = (SortedMap) ((TreeMap) backingMap).clone(); + clone.backingMap = (SortedMap) ((TreeMap) backingMap) + .clone(); } else { clone.backingMap = new TreeMap(backingMap); } @@ -175,9 +166,8 @@ public class TreeSet extends AbstractSet implements SortedSet, Cloneabl /** * Returns the comparator used to compare elements in this {@code TreeSet}. - * + * * @return a Comparator or null if the natural ordering is used - * @since Android 1.0 */ public Comparator comparator() { return backingMap.comparator(); @@ -185,7 +175,7 @@ public class TreeSet extends AbstractSet implements SortedSet, Cloneabl /** * Searches this {@code TreeSet} for the specified object. - * + * * @param object * the object to search for. * @return {@code true} if {@code object} is an element of this @@ -196,7 +186,6 @@ public class TreeSet extends AbstractSet implements SortedSet, Cloneabl * @throws NullPointerException * when the object is null and the comparator cannot handle * null. - * @since Android 1.0 */ @Override public boolean contains(Object object) { @@ -205,11 +194,10 @@ public class TreeSet extends AbstractSet implements SortedSet, Cloneabl /** * Returns the first element in this {@code TreeSet}. - * + * * @return the first element. * @throws NoSuchElementException * when this {@code TreeSet} is empty. - * @since Android 1.0 */ public E first() { return backingMap.firstKey(); @@ -220,7 +208,7 @@ public class TreeSet extends AbstractSet implements SortedSet, Cloneabl * which contains elements which are all less than the end element. The * returned SortedSet is backed by this {@code TreeSet} so changes to one * are reflected by the other. - * + * * @param end * the end element. * @return a subset where the elements are less than {@code end} @@ -230,7 +218,6 @@ public class TreeSet extends AbstractSet implements SortedSet, Cloneabl * @throws NullPointerException * when the end object is null and the comparator cannot handle * null. - * @since Android 1.0 */ @SuppressWarnings("unchecked") public SortedSet headSet(E end) { @@ -246,10 +233,9 @@ public class TreeSet extends AbstractSet implements SortedSet, Cloneabl /** * Returns true if this {@code TreeSet} has no element, otherwise false. - * + * * @return true if this {@code TreeSet} has no element. * @see #size - * @since Android 1.0 */ @Override public boolean isEmpty() { @@ -258,10 +244,9 @@ public class TreeSet extends AbstractSet implements SortedSet, Cloneabl /** * Returns an Iterator on the elements of this {@code TreeSet}. - * + * * @return an Iterator on the elements of this {@code TreeSet}. * @see Iterator - * @since Android 1.0 */ @Override public Iterator iterator() { @@ -271,11 +256,10 @@ public class TreeSet extends AbstractSet implements SortedSet, Cloneabl /** * Returns the last element in this {@code TreeSet}. The last element is * the highest element. - * + * * @return the last element. * @throws NoSuchElementException * when this {@code TreeSet} is empty. - * @since Android 1.0 */ public E last() { return backingMap.lastKey(); @@ -283,7 +267,7 @@ public class TreeSet extends AbstractSet implements SortedSet, Cloneabl /** * Removes an occurrence of the specified object from this {@code TreeSet}. - * + * * @param object * the object to remove. * @return {@code true} if this {@code TreeSet} was modified, {@code false} @@ -294,7 +278,6 @@ public class TreeSet extends AbstractSet implements SortedSet, Cloneabl * @throws NullPointerException * when the object is null and the comparator cannot handle * null. - * @since Android 1.0 */ @Override public boolean remove(Object object) { @@ -303,9 +286,8 @@ public class TreeSet extends AbstractSet implements SortedSet, Cloneabl /** * Returns the number of elements in this {@code TreeSet}. - * + * * @return the number of elements in this {@code TreeSet}. - * @since Android 1.0 */ @Override public int size() { @@ -317,7 +299,7 @@ public class TreeSet extends AbstractSet implements SortedSet, Cloneabl * which contains elements greater or equal to the start element but less * than the end element. The returned SortedSet is backed by this * {@code TreeSet} so changes to one are reflected by the other. - * + * * @param start * the start element. * @param end @@ -330,7 +312,6 @@ public class TreeSet extends AbstractSet implements SortedSet, Cloneabl * @throws NullPointerException * when the start or end object is null and the comparator * cannot handle null. - * @since Android 1.0 */ @SuppressWarnings("unchecked") public SortedSet subSet(E start, E end) { @@ -352,7 +333,7 @@ public class TreeSet extends AbstractSet implements SortedSet, Cloneabl * which contains elements greater or equal to the start element. The * returned SortedSet is backed by this {@code TreeSet} so changes to one * are reflected by the other. - * + * * @param start * the start element. * @return a subset where the elements are greater or equal to {@code start} @@ -362,7 +343,6 @@ public class TreeSet extends AbstractSet implements SortedSet, Cloneabl * @throws NullPointerException * when the start object is null and the comparator cannot * handle null. - * @since Android 1.0 */ @SuppressWarnings("unchecked") public SortedSet tailSet(E start) { @@ -391,23 +371,16 @@ public class TreeSet extends AbstractSet implements SortedSet, Cloneabl @SuppressWarnings("unchecked") private void readObject(ObjectInputStream stream) throws IOException, - ClassNotFoundException { + ClassNotFoundException { stream.defaultReadObject(); - TreeMap map = new TreeMap((Comparator) stream.readObject()); + TreeMap map = new TreeMap((Comparator) stream + .readObject()); int size = stream.readInt(); if (size > 0) { - E key = (E)stream.readObject(); - TreeMap.Entry last = new TreeMap.Entry(key,key); - map.root = last; - map.size = 1; - for (int i=1; i x = new TreeMap.Entry(key,key); - x.parent = last; - last.right = x; - map.size++; - map.balance(x); - last = x; + TreeMap.Node lastNode = null; + for(int i=0; i * UUID is an immutable representation of a 128-bit universally unique * identifier (UUID). - *

          *

          * There are multiple, variant layouts of UUIDs, but this class is based upon * variant 2 of RFC 4122, the * Leach-Salz variant. This class can be used to model alternate variants, but * most of the methods will be unsupported in those cases; see each method for * details. - *

          - * - * @since Android 1.0 + * + * @since 1.5 */ public final class UUID implements Serializable, Comparable { @@ -60,13 +57,11 @@ public final class UUID implements Serializable, Comparable { /** *

          * Constructs an instance with the specified bits. - *

          - * + * * @param mostSigBits * The 64 most significant bits of the UUID. * @param leastSigBits * The 64 least significant bits of the UUID. - * @since Android 1.0 */ public UUID(long mostSigBits, long leastSigBits) { super(); @@ -79,7 +74,6 @@ public final class UUID implements Serializable, Comparable { *

          * Sets up the transient fields of this instance based on the current values * of the {@code mostSigBits} and {@code leastSigBits} fields. - *

          */ private void init() { // setup hash field @@ -123,10 +117,8 @@ public final class UUID implements Serializable, Comparable { *

          * Generates a variant 2, version 4 (randomly generated number) UUID as per * RFC 4122. - *

          - * + * * @return an UUID instance. - * @since Android 1.0 */ public static UUID randomUUID() { byte[] data; @@ -135,9 +127,8 @@ public final class UUID implements Serializable, Comparable { if (rng == null) { rng = new SecureRandom(); } - rng.nextBytes(data = new byte[16]); } - + rng.nextBytes(data = new byte[16]); long msb = (data[0] & 0xFFL) << 56; msb |= (data[1] & 0xFFL) << 48; msb |= (data[2] & 0xFFL) << 40; @@ -164,12 +155,10 @@ public final class UUID implements Serializable, Comparable { *

          * Generates a variant 2, version 3 (name-based, MD5-hashed) UUID as per RFC 4122. - *

          - * + * * @param name * the name used as byte array to create an UUID. * @return an UUID instance. - * @since Android 1.0 */ public static UUID nameUUIDFromBytes(byte[] name) { if (name == null) { @@ -209,57 +198,57 @@ public final class UUID implements Serializable, Comparable { /** *

          * Parses a UUID string with the format defined by {@link #toString()}. - *

          - * + * * @param uuid * the UUID string to parse. * @return an UUID instance. + * @throws NullPointerException + * if {@code uuid} is {@code null}. * @throws IllegalArgumentException * if {@code uuid} is not formatted correctly. - * @since Android 1.0 */ public static UUID fromString(String uuid) { if (uuid == null) { throw new NullPointerException(); } - + int[] position = new int[5]; int lastPosition = 1; int startPosition = 0; - + int i = 0; - for (; i < position.length && lastPosition > 0; i++) { + for (; i < position.length && lastPosition > 0; i++) { position[i] = uuid.indexOf("-", startPosition); //$NON-NLS-1$ lastPosition = position[i]; startPosition = position[i] + 1; } // should have and only can have four "-" in UUID - if(i != position.length || lastPosition != -1) - { + if (i != position.length || lastPosition != -1) { throw new IllegalArgumentException(Msg.getString("KA014") + uuid); //$NON-NLS-1$ } long m1 = Long.parseLong(uuid.substring(0, position[0]), 16); - long m2 = Long.parseLong(uuid.substring(position[0]+ 1, position[1]), 16); - long m3 = Long.parseLong(uuid.substring(position[1] + 1, position[2]), 16); + long m2 = Long.parseLong(uuid.substring(position[0] + 1, position[1]), + 16); + long m3 = Long.parseLong(uuid.substring(position[1] + 1, position[2]), + 16); - long lsb1 = Long.parseLong(uuid.substring(position[2] + 1, position[3]), 16); - long lsb2 = Long.parseLong(uuid.substring(position[3]+ 1), 16); + long lsb1 = Long.parseLong( + uuid.substring(position[2] + 1, position[3]), 16); + long lsb2 = Long.parseLong(uuid.substring(position[3] + 1), 16); long msb = (m1 << 32) | (m2 << 16) | m3; long lsb = (lsb1 << 48) | lsb2; - + return new UUID(msb, lsb); } /** *

          * The 64 least significant bits of the UUID. - *

          - * + * * @return the 64 least significant bits. - * @since Android 1.0 */ public long getLeastSignificantBits() { return leastSigBits; @@ -268,10 +257,8 @@ public final class UUID implements Serializable, Comparable { /** *

          * The 64 most significant bits of the UUID. - *

          - * + * * @return the 64 most significant bits. - * @since Android 1.0 */ public long getMostSignificantBits() { return mostSigBits; @@ -282,7 +269,6 @@ public final class UUID implements Serializable, Comparable { * The version of the variant 2 UUID as per RFC 4122. If the variant * is not 2, then the version will be 0. - *

          *
            *
          • 1 - Time-based UUID
          • *
          • 2 - DCE Security UUID
          • @@ -290,9 +276,8 @@ public final class UUID implements Serializable, Comparable { *
          • 4 - Randomly generated UUID ({@link #randomUUID()})
          • *
          • 5 - Name-based with SHA-1 hashing UUID
          • *
          - * + * * @return an {@code int} value. - * @since Android 1.0 */ public int version() { return version; @@ -302,16 +287,14 @@ public final class UUID implements Serializable, Comparable { *

          * The variant of the UUID as per RFC 4122. - *

          *
            *
          • 0 - Reserved for NCS compatibility
          • *
          • 2 - RFC 4122/Leach-Salz
          • *
          • 6 - Reserved for Microsoft Corporation compatibility
          • *
          • 7 - Reserved for future use
          • *
          - * + * * @return an {@code int} value. - * @since Android 1.0 */ public int variant() { return variant; @@ -321,12 +304,10 @@ public final class UUID implements Serializable, Comparable { *

          * The timestamp value of the version 1, variant 2 UUID as per RFC 4122. - *

          - * + * * @return a {@code long} value. * @throws UnsupportedOperationException * if {@link #version()} is not 1. - * @since Android 1.0 */ public long timestamp() { if (version != 1) { @@ -339,12 +320,10 @@ public final class UUID implements Serializable, Comparable { *

          * The clock sequence value of the version 1, variant 2 UUID as per RFC 4122. - *

          - * + * * @return a {@code long} value. * @throws UnsupportedOperationException * if {@link #version()} is not 1. - * @since Android 1.0 */ public int clockSequence() { if (version != 1) { @@ -357,12 +336,10 @@ public final class UUID implements Serializable, Comparable { *

          * The node value of the version 1, variant 2 UUID as per RFC 4122. - *

          - * + * * @return a {@code long} value. * @throws UnsupportedOperationException * if {@link #version()} is not 1. - * @since Android 1.0 */ public long node() { if (version != 1) { @@ -376,13 +353,11 @@ public final class UUID implements Serializable, Comparable { * Compares this UUID to the specified UUID. The natural ordering of UUIDs * is based upon the value of the bits from most significant to least * significant. - *

          - * + * * @param uuid * the UUID to compare to. * @return a value of -1, 0 or 1 if this UUID is less than, equal to or * greater than {@code uuid}. - * @since Android 1.0 */ public int compareTo(UUID uuid) { if (uuid == this) { @@ -409,13 +384,11 @@ public final class UUID implements Serializable, Comparable { * Compares this UUID to another object for equality. If {@code object} * is not {@code null}, is a UUID instance, and all bits are equal, then * {@code true} is returned. - *

          - * + * * @param object * the {@code Object} to compare to. * @return {@code true} if this UUID is equal to {@code object} * or {@code false} if not. - * @since Android 1.0 */ @Override public boolean equals(Object object) { @@ -441,10 +414,8 @@ public final class UUID implements Serializable, Comparable { *

          * Returns a hash value for this UUID that is consistent with the * {@link #equals(Object)} method. - *

          - * + * * @return an {@code int} value. - * @since Android 1.0 */ @Override public int hashCode() { @@ -455,8 +426,7 @@ public final class UUID implements Serializable, Comparable { *

          * Returns a string representation of this UUID in the following format, as * per RFC 4122. - *

          - * + * *
                *            UUID                   = time-low "-" time-mid "-"
                *                                     time-high-and-version "-"
          @@ -474,9 +444,8 @@ public final class UUID implements Serializable, Comparable {
                *                "a" / "b" / "c" / "d" / "e" / "f" /
                *                "A" / "B" / "C" / "D" / "E" / "F"
                * 
          - * + * * @return a String instance. - * @since Android 1.0 */ @Override public String toString() { @@ -507,8 +476,7 @@ public final class UUID implements Serializable, Comparable { /** *

          * Resets the transient fields to match the behavior of the constructor. - *

          - * + * * @param in * the {@code InputStream} to read from. * @throws IOException diff --git a/libcore/luni/src/main/java/java/util/UnknownFormatConversionException.java b/libcore/luni/src/main/java/java/util/UnknownFormatConversionException.java index 19b9b6d41..8bb94411a 100644 --- a/libcore/luni/src/main/java/java/util/UnknownFormatConversionException.java +++ b/libcore/luni/src/main/java/java/util/UnknownFormatConversionException.java @@ -22,7 +22,6 @@ import org.apache.harmony.luni.util.Msg; * conversion is unknown. * * @see java.lang.RuntimeException - * @since Android 1.0 */ public class UnknownFormatConversionException extends IllegalFormatException { private static final long serialVersionUID = 19060418L; diff --git a/libcore/luni/src/main/java/java/util/UnknownFormatFlagsException.java b/libcore/luni/src/main/java/java/util/UnknownFormatFlagsException.java index 837328ac7..2a1b0754c 100644 --- a/libcore/luni/src/main/java/java/util/UnknownFormatFlagsException.java +++ b/libcore/luni/src/main/java/java/util/UnknownFormatFlagsException.java @@ -23,7 +23,6 @@ import org.apache.harmony.luni.util.Msg; * an unknown flag. * * @see java.lang.RuntimeException - * @since Android 1.0 */ public class UnknownFormatFlagsException extends IllegalFormatException { diff --git a/libcore/luni/src/main/java/java/util/Vector.java b/libcore/luni/src/main/java/java/util/Vector.java index 7a5c775f2..6a4060a9e 100644 --- a/libcore/luni/src/main/java/java/util/Vector.java +++ b/libcore/luni/src/main/java/java/util/Vector.java @@ -17,7 +17,6 @@ package java.util; - import java.io.IOException; import java.io.ObjectOutputStream; import java.io.Serializable; @@ -36,26 +35,21 @@ import java.lang.reflect.Array; * The capacity of a vector may be specified when the vector is created. If the * capacity of the vector is exceeded, the capacity is increased (doubled by * default). - * + * * @see java.lang.StringBuffer - * @since Android 1.0 */ -public class Vector extends AbstractList implements List, RandomAccess, - Cloneable, Serializable { - +public class Vector extends AbstractList implements List, + RandomAccess, Cloneable, Serializable { + private static final long serialVersionUID = -2767605614048989439L; /** * The number of elements or the size of the vector. - * - * @since Android 1.0 */ protected int elementCount; /** * The elements of the vector. - * - * @since Android 1.0 */ protected Object[] elementData; @@ -63,8 +57,6 @@ public class Vector extends AbstractList implements List, RandomAccess, * How many elements should be added to the vector when it is detected that * it needs to grow to accommodate extra entries. If this value is zero or * negative the size will be doubled if an increase is needed. - * - * @since Android 1.0 */ protected int capacityIncrement; @@ -72,8 +64,6 @@ public class Vector extends AbstractList implements List, RandomAccess, /** * Constructs a new vector using the default capacity. - * - * @since Android 1.0 */ public Vector() { this(DEFAULT_SIZE, 0); @@ -81,12 +71,11 @@ public class Vector extends AbstractList implements List, RandomAccess, /** * Constructs a new vector using the specified capacity. - * + * * @param capacity * the initial capacity of the new vector. * @throws IllegalArgumentException * if {@code capacity} is negative. - * @since Android 1.0 */ public Vector(int capacity) { this(capacity, 0); @@ -95,22 +84,20 @@ public class Vector extends AbstractList implements List, RandomAccess, /** * Constructs a new vector using the specified capacity and capacity * increment. - * + * * @param capacity * the initial capacity of the new vector. * @param capacityIncrement * the amount to increase the capacity when this vector is full. * @throws IllegalArgumentException * if {@code capacity} is negative. - * @since Android 1.0 */ public Vector(int capacity, int capacityIncrement) { - elementCount = 0; - try { - elementData = newElementArray(capacity); - } catch (NegativeArraySizeException e) { + if (capacity < 0) { throw new IllegalArgumentException(); } + elementData = newElementArray(capacity); + elementCount = 0; this.capacityIncrement = capacityIncrement; } @@ -118,10 +105,9 @@ public class Vector extends AbstractList implements List, RandomAccess, * Constructs a new instance of {@code Vector} containing the elements in * {@code collection}. The order of the elements in the new {@code Vector} * is dependent on the iteration order of the seed collection. - * + * * @param collection * the collection of elements to add. - * @since Android 1.0 */ public Vector(Collection collection) { this(collection.size(), 0); @@ -130,10 +116,10 @@ public class Vector extends AbstractList implements List, RandomAccess, elementData[elementCount++] = it.next(); } } - + @SuppressWarnings("unchecked") private E[] newElementArray(int size) { - return (E[])new Object[size]; + return (E[]) new Object[size]; } /** @@ -141,16 +127,15 @@ public class Vector extends AbstractList implements List, RandomAccess, * object is inserted before any element with the same or a higher index * increasing their index by 1. If the location is equal to the size of this * vector, the object is added at the end. - * + * * @param location * the index at which to insert the element. * @param object * the object to insert in this vector. - * @exception ArrayIndexOutOfBoundsException - * when {@code location < 0 || location > size()}. + * @throws ArrayIndexOutOfBoundsException + * if {@code location < 0 || location > size()}. * @see #addElement * @see #size - * @since Android 1.0 */ @Override public void add(int location, E object) { @@ -159,15 +144,18 @@ public class Vector extends AbstractList implements List, RandomAccess, /** * Adds the specified object at the end of this vector. - * + * * @param object * the object to add to the vector. * @return {@code true} - * @since Android 1.0 */ @Override - public boolean add(E object) { - addElement(object); + public synchronized boolean add(E object) { + if (elementCount == elementData.length) { + growByOne(); + } + elementData[elementCount++] = object; + modCount++; return true; } @@ -177,18 +165,18 @@ public class Vector extends AbstractList implements List, RandomAccess, * returned from the Collection iterator. The elements with an index equal * or higher than {@code location} have their index increased by the size of * the added collection. - * + * * @param location * the location to insert the objects. * @param collection * the collection of objects. * @return {@code true} if this vector is modified, {@code false} otherwise. - * @exception ArrayIndexOutOfBoundsException - * when {@code location < 0} or {@code location > size()}. - * @since Android 1.0 + * @throws ArrayIndexOutOfBoundsException + * if {@code location < 0} or {@code location > size()}. */ @Override - public synchronized boolean addAll(int location, Collection collection) { + public synchronized boolean addAll(int location, + Collection collection) { if (0 <= location && location <= elementCount) { int size = collection.size(); if (size == 0) { @@ -216,11 +204,10 @@ public class Vector extends AbstractList implements List, RandomAccess, /** * Adds the objects in the specified collection to the end of this vector. - * + * * @param collection * the collection of objects. * @return {@code true} if this vector is modified, {@code false} otherwise. - * @since Android 1.0 */ @Override public synchronized boolean addAll(Collection collection) { @@ -229,10 +216,9 @@ public class Vector extends AbstractList implements List, RandomAccess, /** * Adds the specified object at the end of this vector. - * + * * @param object * the object to add to the vector. - * @since Android 1.0 */ public synchronized void addElement(E object) { if (elementCount == elementData.length) { @@ -244,11 +230,10 @@ public class Vector extends AbstractList implements List, RandomAccess, /** * Returns the number of elements this vector can hold without growing. - * + * * @return the capacity of this vector. * @see #ensureCapacity * @see #size - * @since Android 1.0 */ public synchronized int capacity() { return elementData.length; @@ -256,10 +241,9 @@ public class Vector extends AbstractList implements List, RandomAccess, /** * Removes all elements from this vector, leaving it empty. - * + * * @see #isEmpty * @see #size - * @since Android 1.0 */ @Override public void clear() { @@ -269,10 +253,9 @@ public class Vector extends AbstractList implements List, RandomAccess, /** * Returns a new vector with the same elements, size, capacity and capacity * increment as this vector. - * + * * @return a shallow copy of this vector. * @see java.lang.Cloneable - * @since Android 1.0 */ @Override @SuppressWarnings("unchecked") @@ -288,7 +271,7 @@ public class Vector extends AbstractList implements List, RandomAccess, /** * Searches this vector for the specified object. - * + * * @param object * the object to look for in this vector. * @return {@code true} if object is an element of this vector, @@ -296,7 +279,6 @@ public class Vector extends AbstractList implements List, RandomAccess, * @see #indexOf(Object) * @see #indexOf(Object, int) * @see java.lang.Object#equals - * @since Android 1.0 */ @Override public boolean contains(Object object) { @@ -305,12 +287,11 @@ public class Vector extends AbstractList implements List, RandomAccess, /** * Searches this vector for all objects in the specified collection. - * + * * @param collection * the collection of objects. * @return {@code true} if all objects in the specified collection are * elements of this vector, {@code false} otherwise. - * @since Android 1.0 */ @Override public synchronized boolean containsAll(Collection collection) { @@ -320,14 +301,13 @@ public class Vector extends AbstractList implements List, RandomAccess, /** * Attempts to copy elements contained by this {@code Vector} into the * corresponding elements of the supplied {@code Object} array. - * + * * @param elements * the {@code Object} array into which the elements of this * vector are copied. * @throws IndexOutOfBoundsException * if {@code elements} is not big enough. * @see #clone - * @since Android 1.0 */ public synchronized void copyInto(Object[] elements) { System.arraycopy(elementData, 0, elements, 0, elementCount); @@ -335,18 +315,18 @@ public class Vector extends AbstractList implements List, RandomAccess, /** * Returns the element at the specified location in this vector. - * + * * @param location * the index of the element to return in this vector. * @return the element at the specified location. - * @exception ArrayIndexOutOfBoundsException - * when {@code location < 0 || location >= size()}. + * @throws ArrayIndexOutOfBoundsException + * if {@code location < 0 || location >= size()}. * @see #size - * @since Android 1.0 */ + @SuppressWarnings("unchecked") public synchronized E elementAt(int location) { if (location < elementCount) { - return (E)elementData[location]; + return (E) elementData[location]; } throw new ArrayIndexOutOfBoundsException(location); } @@ -354,26 +334,24 @@ public class Vector extends AbstractList implements List, RandomAccess, /** * Returns an enumeration on the elements of this vector. The results of the * enumeration may be affected if the contents of this vector is modified. - * + * * @return an enumeration of the elements of this vector. * @see #elementAt * @see Enumeration - * @since Android 1.0 */ public Enumeration elements() { return new Enumeration() { int pos = 0; public boolean hasMoreElements() { - synchronized (Vector.this) { - return pos < elementCount; - } + return pos < elementCount; } + @SuppressWarnings("unchecked") public E nextElement() { synchronized (Vector.this) { if (pos < elementCount) { - return (E)elementData[pos++]; + return (E) elementData[pos++]; } } throw new NoSuchElementException(); @@ -384,12 +362,11 @@ public class Vector extends AbstractList implements List, RandomAccess, /** * Ensures that this vector can hold the specified number of elements * without growing. - * + * * @param minimumCapacity * the minimum number of elements that this vector will hold * before growing. * @see #capacity - * @since Android 1.0 */ public synchronized void ensureCapacity(int minimumCapacity) { if (elementData.length < minimumCapacity) { @@ -404,13 +381,12 @@ public class Vector extends AbstractList implements List, RandomAccess, * Compares the specified object to this vector and returns if they are * equal. The object must be a List which contains the same objects in the * same order. - * + * * @param object * the object to compare with this object * @return {@code true} if the specified object is equal to this vector, * {@code false} otherwise. * @see #hashCode - * @since Android 1.0 */ @Override public synchronized boolean equals(Object object) { @@ -418,8 +394,8 @@ public class Vector extends AbstractList implements List, RandomAccess, return true; } if (object instanceof List) { - List list = (List) object; - if (list.size() != size()) { + List list = (List) object; + if (list.size() != elementCount) { return false; } @@ -438,35 +414,34 @@ public class Vector extends AbstractList implements List, RandomAccess, /** * Returns the first element in this vector. - * + * * @return the element at the first position. - * @exception NoSuchElementException - * when this vector is empty. + * @throws NoSuchElementException + * if this vector is empty. * @see #elementAt * @see #lastElement * @see #size - * @since Android 1.0 */ + @SuppressWarnings("unchecked") public synchronized E firstElement() { if (elementCount > 0) { - return (E)elementData[0]; + return (E) elementData[0]; } throw new NoSuchElementException(); } /** * Returns the element at the specified location in this vector. - * + * * @param location * the index of the element to return in this vector. * @return the element at the specified location. - * @exception ArrayIndexOutOfBoundsException - * when {@code location < 0 || location >= size()}. + * @throws ArrayIndexOutOfBoundsException + * if {@code location < 0 || location >= size()}. * @see #size - * @since Android 1.0 */ @Override - public synchronized E get(int location) { + public E get(int location) { return elementAt(location); } @@ -474,7 +449,7 @@ public class Vector extends AbstractList implements List, RandomAccess, E[] newData = newElementArray(newCapacity); // Assumes elementCount is <= newCapacity assert elementCount <= newCapacity; - System.arraycopy(elementData, 0, newData, 0, elementCount); + System.arraycopy(elementData, 0, newData, 0, elementCount); elementData = newData; } @@ -519,10 +494,9 @@ public class Vector extends AbstractList implements List, RandomAccess, /** * Returns an integer hash code for the receiver. Objects which are equal * return the same value for this method. - * + * * @return the receiver's hash. * @see #equals - * @since Android 1.0 */ @Override public synchronized int hashCode() { @@ -538,7 +512,7 @@ public class Vector extends AbstractList implements List, RandomAccess, * Searches in this vector for the index of the specified object. The search * for the object starts at the beginning and moves towards the end of this * vector. - * + * * @param object * the object to find in this vector. * @return the index in this vector of the specified element, -1 if the @@ -546,7 +520,6 @@ public class Vector extends AbstractList implements List, RandomAccess, * @see #contains * @see #lastIndexOf(Object) * @see #lastIndexOf(Object, int) - * @since Android 1.0 */ @Override public int indexOf(Object object) { @@ -557,19 +530,18 @@ public class Vector extends AbstractList implements List, RandomAccess, * Searches in this vector for the index of the specified object. The search * for the object starts at the specified location and moves towards the end * of this vector. - * + * * @param object * the object to find in this vector. * @param location * the index at which to start searching. * @return the index in this vector of the specified element, -1 if the * element isn't found. - * @exception ArrayIndexOutOfBoundsException - * when {@code location < 0}. + * @throws ArrayIndexOutOfBoundsException + * if {@code location < 0}. * @see #contains * @see #lastIndexOf(Object) * @see #lastIndexOf(Object, int) - * @since Android 1.0 */ public synchronized int indexOf(Object object, int location) { if (object != null) { @@ -594,16 +566,15 @@ public class Vector extends AbstractList implements List, RandomAccess, * location. All elements with an index equal or greater than * {@code location} have their index increased by 1. If the location is * equal to the size of this vector, the object is added at the end. - * + * * @param object * the object to insert in this vector. * @param location * the index at which to insert the element. - * @exception ArrayIndexOutOfBoundsException - * when {@code location < 0 || location > size()}. + * @throws ArrayIndexOutOfBoundsException + * if {@code location < 0 || location > size()}. * @see #addElement * @see #size - * @since Android 1.0 */ public synchronized void insertElementAt(E object, int location) { if (0 <= location && location <= elementCount) { @@ -625,11 +596,10 @@ public class Vector extends AbstractList implements List, RandomAccess, /** * Returns if this vector has no elements, a size of zero. - * + * * @return {@code true} if this vector has no elements, {@code false} * otherwise. * @see #size - * @since Android 1.0 */ @Override public synchronized boolean isEmpty() { @@ -638,18 +608,18 @@ public class Vector extends AbstractList implements List, RandomAccess, /** * Returns the last element in this vector. - * + * * @return the element at the last position. - * @exception NoSuchElementException - * when this vector is empty. + * @throws NoSuchElementException + * if this vector is empty. * @see #elementAt * @see #firstElement * @see #size - * @since Android 1.0 */ + @SuppressWarnings("unchecked") public synchronized E lastElement() { try { - return (E)elementData[elementCount - 1]; + return (E) elementData[elementCount - 1]; } catch (IndexOutOfBoundsException e) { throw new NoSuchElementException(); } @@ -659,7 +629,7 @@ public class Vector extends AbstractList implements List, RandomAccess, * Searches in this vector for the index of the specified object. The search * for the object starts at the end and moves towards the start of this * vector. - * + * * @param object * the object to find in this vector. * @return the index in this vector of the specified element, -1 if the @@ -667,7 +637,6 @@ public class Vector extends AbstractList implements List, RandomAccess, * @see #contains * @see #indexOf(Object) * @see #indexOf(Object, int) - * @since Android 1.0 */ @Override public synchronized int lastIndexOf(Object object) { @@ -678,19 +647,18 @@ public class Vector extends AbstractList implements List, RandomAccess, * Searches in this vector for the index of the specified object. The search * for the object starts at the specified location and moves towards the * start of this vector. - * + * * @param object * the object to find in this vector. * @param location * the index at which to start searching. * @return the index in this vector of the specified element, -1 if the * element isn't found. - * @exception ArrayIndexOutOfBoundsException - * when {@code location >= size()}. + * @throws ArrayIndexOutOfBoundsException + * if {@code location >= size()}. * @see #contains * @see #indexOf(Object) * @see #indexOf(Object, int) - * @since Android 1.0 */ public synchronized int lastIndexOf(Object object, int location) { if (location < elementCount) { @@ -716,18 +684,18 @@ public class Vector extends AbstractList implements List, RandomAccess, * Removes the object at the specified location from this vector. All * elements with an index bigger than {@code location} have their index * decreased by 1. - * + * * @param location * the index of the object to remove. * @return the removed object. - * @exception IndexOutOfBoundsException - * when {@code location < 0 || location >= size()}. - * @since Android 1.0 + * @throws IndexOutOfBoundsException + * if {@code location < 0 || location >= size()}. */ + @SuppressWarnings("unchecked") @Override public synchronized E remove(int location) { if (location < elementCount) { - E result = (E)elementData[location]; + E result = (E) elementData[location]; elementCount--; int size = elementCount - location; if (size > 0) { @@ -746,7 +714,7 @@ public class Vector extends AbstractList implements List, RandomAccess, * towards the end, of the specified object from this vector. All elements * with an index bigger than the element that gets removed have their index * decreased by 1. - * + * * @param object * the object to remove from this vector. * @return {@code true} if the specified object was found, {@code false} @@ -754,7 +722,6 @@ public class Vector extends AbstractList implements List, RandomAccess, * @see #removeAllElements * @see #removeElementAt * @see #size - * @since Android 1.0 */ @Override public boolean remove(Object object) { @@ -764,13 +731,12 @@ public class Vector extends AbstractList implements List, RandomAccess, /** * Removes all occurrences in this vector of each object in the specified * Collection. - * + * * @param collection * the collection of objects to remove. * @return {@code true} if this vector is modified, {@code false} otherwise. * @see #remove(Object) * @see #contains(Object) - * @since Android 1.0 */ @Override public synchronized boolean removeAll(Collection collection) { @@ -780,13 +746,14 @@ public class Vector extends AbstractList implements List, RandomAccess, /** * Removes all elements from this vector, leaving the size zero and the * capacity unchanged. - * + * * @see #isEmpty * @see #size - * @since Android 1.0 */ public synchronized void removeAllElements() { - Arrays.fill(elementData, 0, elementCount, null); + for (int i = 0; i < elementCount; i++) { + elementData[i] = null; + } modCount++; elementCount = 0; } @@ -796,7 +763,7 @@ public class Vector extends AbstractList implements List, RandomAccess, * towards the end, of the specified object from this vector. All elements * with an index bigger than the element that gets removed have their index * decreased by 1. - * + * * @param object * the object to remove from this vector. * @return {@code true} if the specified object was found, {@code false} @@ -804,7 +771,6 @@ public class Vector extends AbstractList implements List, RandomAccess, * @see #removeAllElements * @see #removeElementAt * @see #size - * @since Android 1.0 */ public synchronized boolean removeElement(Object object) { int index; @@ -819,15 +785,14 @@ public class Vector extends AbstractList implements List, RandomAccess, * Removes the element found at index position {@code location} from * this {@code Vector}. All elements with an index bigger than * {@code location} have their index decreased by 1. - * + * * @param location * the index of the element to remove. - * @exception ArrayIndexOutOfBoundsException - * when {@code location < 0 || location >= size()}. + * @throws ArrayIndexOutOfBoundsException + * if {@code location < 0 || location >= size()}. * @see #removeElement * @see #removeAllElements * @see #size - * @since Android 1.0 */ public synchronized void removeElementAt(int location) { if (0 <= location && location < elementCount) { @@ -848,19 +813,18 @@ public class Vector extends AbstractList implements List, RandomAccess, * Removes the objects in the specified range from the start to the, but not * including, end index. All elements with an index bigger than or equal to * {@code end} have their index decreased by {@code end - start}. - * + * * @param start * the index at which to start removing. * @param end * the index one past the end of the range to remove. - * @exception IndexOutOfBoundsException - * when {@code start < 0, start > end} or + * @throws IndexOutOfBoundsException + * if {@code start < 0, start > end} or * {@code end > size()}. - * @since Android 1.0 */ @Override protected void removeRange(int start, int end) { - if (start >= 0 && start <= end && end <= size()) { + if (start >= 0 && start <= end && end <= elementCount) { if (start == end) { return; } @@ -883,12 +847,11 @@ public class Vector extends AbstractList implements List, RandomAccess, /** * Removes all objects from this vector that are not contained in the * specified collection. - * + * * @param collection * the collection of objects to retain. * @return {@code true} if this vector is modified, {@code false} otherwise. * @see #remove(Object) - * @since Android 1.0 */ @Override public synchronized boolean retainAll(Collection collection) { @@ -898,21 +861,21 @@ public class Vector extends AbstractList implements List, RandomAccess, /** * Replaces the element at the specified location in this vector with the * specified object. - * + * * @param location * the index at which to put the specified object. * @param object * the object to add to this vector. * @return the previous element at the location. - * @exception ArrayIndexOutOfBoundsException - * when {@code location < 0 || location >= size()}. + * @throws ArrayIndexOutOfBoundsException + * if {@code location < 0 || location >= size()}. * @see #size - * @since Android 1.0 */ + @SuppressWarnings("unchecked") @Override public synchronized E set(int location, E object) { if (location < elementCount) { - E result = (E)elementData[location]; + E result = (E) elementData[location]; elementData[location] = object; return result; } @@ -922,15 +885,14 @@ public class Vector extends AbstractList implements List, RandomAccess, /** * Replaces the element at the specified location in this vector with the * specified object. - * + * * @param object * the object to add to this vector. * @param location * the index at which to put the specified object. - * @exception ArrayIndexOutOfBoundsException - * when {@code location < 0 || location >= size()}. + * @throws ArrayIndexOutOfBoundsException + * if {@code location < 0 || location >= size()}. * @see #size - * @since Android 1.0 */ public synchronized void setElementAt(E object, int location) { if (location < elementCount) { @@ -945,11 +907,10 @@ public class Vector extends AbstractList implements List, RandomAccess, * than length elements in this vector, the elements at end are lost. If * there are less than length elements in the vector, the additional * elements contain null. - * + * * @param length * the new size of this vector. * @see #size - * @since Android 1.0 */ public synchronized void setSize(int length) { if (length == elementCount) { @@ -965,11 +926,10 @@ public class Vector extends AbstractList implements List, RandomAccess, /** * Returns the number of elements in this vector. - * + * * @return the number of elements in this vector. * @see #elementCount * @see #lastElement - * @since Android 1.0 */ @Override public synchronized int size() { @@ -980,29 +940,27 @@ public class Vector extends AbstractList implements List, RandomAccess, * Returns a List of the specified portion of this vector from the start * index to one less than the end index. The returned List is backed by this * vector so changes to one are reflected by the other. - * + * * @param start * the index at which to start the sublist. * @param end * the index one past the end of the sublist. * @return a List of a portion of this vector. - * @exception IndexOutOfBoundsException - * when {@code start < 0} or {@code end > size()}. - * @exception IllegalArgumentException - * when {@code start > end}. - * @since Android 1.0 + * @throws IndexOutOfBoundsException + * if {@code start < 0} or {@code end > size()}. + * @throws IllegalArgumentException + * if {@code start > end}. */ @Override public synchronized List subList(int start, int end) { - return new Collections.SynchronizedRandomAccessList( - super.subList(start, end), this); + return new Collections.SynchronizedRandomAccessList(super.subList( + start, end), this); } /** * Returns a new array containing all elements contained in this vector. - * + * * @return an array of the elements from this vector. - * @since Android 1.0 */ @Override public synchronized Object[] toArray() { @@ -1017,14 +975,13 @@ public class Vector extends AbstractList implements List, RandomAccess, * is used, otherwise an array of the same type is created. If the specified * array is used and is larger than this vector, the array element following * the collection elements is set to null. - * + * * @param contents * the array to fill. * @return an array of the elements from this vector. - * @exception ArrayStoreException - * when the type of an element in this vector cannot be + * @throws ArrayStoreException + * if the type of an element in this vector cannot be * stored in the type of the specified array. - * @since Android 1.0 */ @Override @SuppressWarnings("unchecked") @@ -1042,29 +999,28 @@ public class Vector extends AbstractList implements List, RandomAccess, /** * Returns the string representation of this vector. - * + * * @return the string representation of this vector. * @see #elements - * @since Android 1.0 */ @Override public synchronized String toString() { if (elementCount == 0) { - return "[]"; + return "[]"; //$NON-NLS-1$ } int length = elementCount - 1; - StringBuffer buffer = new StringBuffer(size() * 16); + StringBuilder buffer = new StringBuilder(elementCount * 16); buffer.append('['); for (int i = 0; i < length; i++) { if (elementData[i] == this) { - buffer.append("(this Collection)"); + buffer.append("(this Collection)"); //$NON-NLS-1$ } else { buffer.append(elementData[i]); } - buffer.append(", "); + buffer.append(", "); //$NON-NLS-1$ } if (elementData[length] == this) { - buffer.append("(this Collection)"); + buffer.append("(this Collection)"); //$NON-NLS-1$ } else { buffer.append(elementData[length]); } @@ -1074,11 +1030,10 @@ public class Vector extends AbstractList implements List, RandomAccess, /** * Sets the capacity of this vector to be the same as the size. - * + * * @see #capacity * @see #ensureCapacity * @see #size - * @since Android 1.0 */ public synchronized void trimToSize() { if (elementData.length != elementCount) { diff --git a/libcore/luni/src/main/java/java/util/WeakHashMap.java b/libcore/luni/src/main/java/java/util/WeakHashMap.java index e0228e20b..01e15af36 100644 --- a/libcore/luni/src/main/java/java/util/WeakHashMap.java +++ b/libcore/luni/src/main/java/java/util/WeakHashMap.java @@ -26,10 +26,10 @@ import java.lang.ref.WeakReference; * optional operations (adding and removing) are supported. Keys and values can * be any objects. Note that the garbage collector acts similar to a second * thread on this collection, possibly removing keys. - * + * + * @since 1.2 * @see HashMap * @see WeakReference - * @since Android 1.0 */ public class WeakHashMap extends AbstractMap implements Map { @@ -127,7 +127,7 @@ public class WeakHashMap extends AbstractMap implements Map { } public boolean hasNext() { - if (nextEntry != null) { + if (nextEntry != null && (nextKey != null || nextEntry.isNull)) { return true; } while (true) { @@ -183,8 +183,6 @@ public class WeakHashMap extends AbstractMap implements Map { /** * Constructs a new empty {@code WeakHashMap} instance. - * - * @since Android 1.0 */ public WeakHashMap() { this(DEFAULT_SIZE); @@ -193,12 +191,11 @@ public class WeakHashMap extends AbstractMap implements Map { /** * Constructs a new {@code WeakHashMap} instance with the specified * capacity. - * + * * @param capacity * the initial capacity of this map. * @throws IllegalArgumentException * if the capacity is less than zero. - * @since Android 1.0 */ public WeakHashMap(int capacity) { if (capacity >= 0) { @@ -215,7 +212,7 @@ public class WeakHashMap extends AbstractMap implements Map { /** * Constructs a new {@code WeakHashMap} instance with the specified capacity * and load factor. - * + * * @param capacity * the initial capacity of this map. * @param loadFactor @@ -223,7 +220,6 @@ public class WeakHashMap extends AbstractMap implements Map { * @throws IllegalArgumentException * if the capacity is less than zero or the load factor is less * or equal to zero. - * @since Android 1.0 */ public WeakHashMap(int capacity, float loadFactor) { if (capacity >= 0 && loadFactor > 0) { @@ -240,10 +236,9 @@ public class WeakHashMap extends AbstractMap implements Map { /** * Constructs a new {@code WeakHashMap} instance containing the mappings * from the specified map. - * + * * @param map * the mappings to add. - * @since Android 1.0 */ public WeakHashMap(Map map) { this(map.size() < 6 ? 11 : map.size() * 2); @@ -252,10 +247,9 @@ public class WeakHashMap extends AbstractMap implements Map { /** * Removes all mappings from this map, leaving it empty. - * + * * @see #isEmpty() * @see #size() - * @since Android 1.0 */ @Override public void clear() { @@ -275,12 +269,11 @@ public class WeakHashMap extends AbstractMap implements Map { /** * Returns whether this map contains the specified key. - * + * * @param key * the key to search for. * @return {@code true} if this map contains the specified key, * {@code false} otherwise. - * @since Android 1.0 */ @Override public boolean containsKey(Object key) { @@ -292,9 +285,8 @@ public class WeakHashMap extends AbstractMap implements Map { * an instance of {@link Map.Entry}. As the set is backed by this map, * changes in one will be reflected in the other. It does not support adding * operations. - * + * * @return a set of the mappings. - * @since Android 1.0 */ @Override public Set> entrySet() { @@ -351,9 +343,8 @@ public class WeakHashMap extends AbstractMap implements Map { * Returns a set of the keys contained in this map. The set is backed by * this map so changes to one are reflected by the other. The set does not * support adding. - * + * * @return a set of the keys. - * @since Android 1.0 */ @Override public Set keySet() { @@ -392,6 +383,26 @@ public class WeakHashMap extends AbstractMap implements Map { } }); } + + @Override + public Object[] toArray() { + Collection coll = new ArrayList(size()); + + for (Iterator iter = iterator(); iter.hasNext();) { + coll.add(iter.next()); + } + return coll.toArray(); + } + + @Override + public T[] toArray(T[] contents) { + Collection coll = new ArrayList(size()); + + for (Iterator iter = iterator(); iter.hasNext();) { + coll.add(iter.next()); + } + return coll.toArray(contents); + } }; } return keySet; @@ -408,16 +419,13 @@ public class WeakHashMap extends AbstractMap implements Map { * "wrapper object" over the iterator of map's entrySet(). The size method * wraps the map's size method and the contains method wraps the map's * containsValue method. - *

          *

          * The collection is created when this method is called at first time and * returned in response to all subsequent calls. This method may return * different Collection when multiple calls to this method, since it has no * synchronization performed. - *

          - * + * * @return a collection of the values contained in this map. - * @since Android 1.0 */ @Override public Collection values() { @@ -454,12 +462,11 @@ public class WeakHashMap extends AbstractMap implements Map { /** * Returns the value of the mapping with the specified key. - * + * * @param key * the key. * @return the value of the mapping with the specified key, or {@code null} * if no mapping for the specified key is found. - * @since Android 1.0 */ @Override public V get(Object key) { @@ -510,12 +517,11 @@ public class WeakHashMap extends AbstractMap implements Map { /** * Returns whether this map contains the specified value. - * + * * @param value * the value to search for. * @return {@code true} if this map contains the specified value, * {@code false} otherwise. - * @since Android 1.0 */ @Override public boolean containsValue(Object value) { @@ -549,9 +555,8 @@ public class WeakHashMap extends AbstractMap implements Map { /** * Returns the number of elements in this map. - * + * * @return the number of elements in this map. - * @since Android 1.0 */ @Override public boolean isEmpty() { @@ -590,14 +595,13 @@ public class WeakHashMap extends AbstractMap implements Map { /** * Maps the specified key to the specified value. - * + * * @param key * the key. * @param value * the value. * @return the value of any previous mapping with the specified key or * {@code null} if there was no mapping. - * @since Android 1.0 */ @Override public V put(K key, V value) { @@ -658,10 +662,11 @@ public class WeakHashMap extends AbstractMap implements Map { * Copies all the mappings in the given map to this map. These mappings will * replace all mappings that this map had for any of the keys currently in * the given map. - * + * * @param map * the map to copy mappings from. - * @since Android 1.0 + * @throws NullPointerException + * if {@code map} is {@code null}. */ @Override public void putAll(Map map) { @@ -670,12 +675,11 @@ public class WeakHashMap extends AbstractMap implements Map { /** * Removes the mapping with the specified key from this map. - * + * * @param key * the key of the mapping to remove. * @return the value of the removed mapping or {@code null} if no mapping * for the specified key was found. - * @since Android 1.0 */ @Override public V remove(Object key) { @@ -711,9 +715,8 @@ public class WeakHashMap extends AbstractMap implements Map { /** * Returns the number of elements in this map. - * + * * @return the number of elements in this map. - * @since Android 1.0 */ @Override public int size() { diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/io/FileCanonPathCache.java b/libcore/luni/src/main/java/org/apache/harmony/luni/internal/io/FileCanonPathCache.java new file mode 100644 index 000000000..e3ea7b513 --- /dev/null +++ b/libcore/luni/src/main/java/org/apache/harmony/luni/internal/io/FileCanonPathCache.java @@ -0,0 +1,136 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.luni.internal.io; + +import java.util.HashMap; +import java.util.LinkedList; + +/** + * A simple cache implementation for file's canonical path. The cache has fixed + * size CACHE_SIZE and cached elements would be expired. If + * put method is invoked when cache is full, the oldest element will be removed. + * + */ +public class FileCanonPathCache { + + static private class CacheElement { + String canonicalPath; + + long timestamp; + + public CacheElement(String path) { + this.canonicalPath = path; + this.timestamp = System.currentTimeMillis(); + } + } + + /** + * Max elemnts could be hold in the cache. + */ + public static final int CACHE_SIZE = 256; + + private static HashMap cache = new HashMap( + CACHE_SIZE); + + /** + * FIFO queue for tracking age of elements. + */ + private static LinkedList list = new LinkedList(); + + private static Object lock = new Object(); + + /** + * Expired time. + */ + private static long timeout = 600000; + + /** + * Retrieve element from cache. + * + * @param path + * absolute path. + * @return canonical path of path if it's in cache. + * + */ + public static String get(String path) { + CacheElement element = null; + synchronized (lock) { + element = cache.get(path); + } + + if (element == null) { + return null; + } + + long time = System.currentTimeMillis(); + if (time - element.timestamp > timeout) { + // remove all elements older than this one + synchronized (lock) { + if (cache.get(path) != null) { + String oldest = null; + do { + oldest = list.removeFirst(); + cache.remove(path); + } while (!path.equals(oldest)); + } + } + return null; + } + + return element.canonicalPath; + } + + /** + * Put element to cache. + * + * @param path + * absolute path. + * @param canonicalPath + * the canonical path of path. + */ + public static void put(String path, String canonicalPath) { + CacheElement element = new CacheElement(canonicalPath); + synchronized (lock) { + if (cache.size() >= CACHE_SIZE) { + // cache is full + String oldest = list.removeFirst(); + cache.remove(oldest); + } + cache.put(path, element); + list.addLast(path); + } + } + + /** + * Remove all elements from cache. + */ + public static void clear() { + synchronized (lock) { + cache.clear(); + list.clear(); + } + } + + public static long getTimeout() { + return timeout; + } + + public static void setTimeout(long timeout) { + FileCanonPathCache.timeout = timeout; + } +} diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/MimeTable.java b/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/MimeTable.java index e288f1a71..be6da054a 100644 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/MimeTable.java +++ b/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/MimeTable.java @@ -51,7 +51,7 @@ public class MimeTable implements FileNameMap { } /** - * Contructs a MIME table using the default values defined in this class. + * Constructs a MIME table using the default values defined in this class. * * It then augments this table by reading pairs of extensions and * corresponding content types from the file "content-types.properties", diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/file/FileURLConnection.java b/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/file/FileURLConnection.java index 07357be4d..7f738d353 100644 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/file/FileURLConnection.java +++ b/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/file/FileURLConnection.java @@ -59,16 +59,9 @@ public class FileURLConnection extends URLConnection { public FileURLConnection(URL url) { super(url); fileName = url.getFile(); - if (url.getRef() != null) { - fileName += "#" + url.getRef(); //$NON-NLS-1$ - } if (fileName == null) { fileName = ""; //$NON-NLS-1$ } - String host = url.getHost(); - if (host != null && host.length() > 0) { - fileName = "//" + host + fileName; //$NON-NLS-1$ - } fileName = Util.decode(fileName, false); } @@ -131,13 +124,23 @@ public class FileURLConnection extends URLConnection { return MimeTable.UNKNOWN; } if (isDir) { - return "text/html"; //$NON-NLS-1$ + return "text/plain"; //$NON-NLS-1$ } String result = guessContentTypeFromName(url.getFile()); - if (result == null) { - return MimeTable.UNKNOWN; + if (result != null) { + return result; } - return result; + + try { + result = guessContentTypeFromStream(is); + } catch (IOException e) { + // Ignore + } + if (result != null) { + return result; + } + + return MimeTable.UNKNOWN; } /** diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/file/Handler.java b/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/file/Handler.java index 909e6112a..1b5bfb949 100644 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/file/Handler.java +++ b/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/file/Handler.java @@ -41,15 +41,15 @@ public class Handler extends URLStreamHandler { * */ @Override - public URLConnection openConnection(URL url) { - return new FileURLConnection(url); + public URLConnection openConnection(URL url) throws IOException { + return openConnection(url, null); } /** * The behaviour of this method is the same as openConnection(URL). * proxy is not used in FileURLConnection. * - * @param u + * @param url * the URL which the connection is pointing to * @param proxy * Proxy @@ -58,18 +58,28 @@ public class Handler extends URLStreamHandler { * @throws IOException * if this handler fails to establish a connection. * @throws IllegalArgumentException - * if any argument is null or of an invalid type. + * if the url argument is null. * @throws UnsupportedOperationException * if the protocol handler doesn't support this method. */ @Override public URLConnection openConnection(URL url, Proxy proxy) throws IOException { - if (null == url || null == proxy) { + if (null == url) { // K034b=url and proxy can not be null throw new IllegalArgumentException(Msg.getString("K034b")); //$NON-NLS-1$ } - return new FileURLConnection(url); + + String host = url.getHost(); + if (host == null || host.length() == 0 + || host.equalsIgnoreCase("localhost")) { //$NON-NLS-1$ + return new FileURLConnection(url); + } + + // If a hostname is specified try to get the resource using FTP + URL ftpURL = new URL("ftp", host, url.getFile()); //$NON-NLS-1$ + return (proxy == null) ? ftpURL.openConnection() : ftpURL + .openConnection(proxy); } /** diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/ftp/FtpURLConnection.java b/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/ftp/FtpURLConnection.java index 2d4ab0d2b..3c361bfbc 100644 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/ftp/FtpURLConnection.java +++ b/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/ftp/FtpURLConnection.java @@ -183,22 +183,24 @@ public class FtpURLConnection extends URLConnection { ProxySelector selector = ProxySelector.getDefault(); Iterator iter = proxyList.iterator(); boolean connectOK = false; + String failureReason = ""; //$NON-NLS-1$ while (iter.hasNext() && !connectOK) { currentProxy = iter.next(); try { connectInternal(); connectOK = true; } catch (IOException ioe) { + failureReason = ioe.getLocalizedMessage(); // If connect failed, callback "connectFailed" // should be invoked. if (null != selector && Proxy.NO_PROXY != currentProxy) { - selector - .connectFailed(uri, currentProxy.address(), ioe); + selector.connectFailed(uri, currentProxy.address(), ioe); } } } if (!connectOK) { - throw new IOException(Msg.getString("K0097")); //$NON-NLS-1$ + // K0097=Unable to connect to server\: {0} + throw new IOException(Msg.getString("K0097", failureReason)); //$NON-NLS-1$ } } } @@ -397,7 +399,7 @@ public class FtpURLConnection extends URLConnection { * Read a line of text and return it for possible parsing */ private String readLine() throws IOException { - StringBuffer sb = new StringBuffer(); + StringBuilder sb = new StringBuilder(); int c; while ((c = ctrlInput.read()) != '\n') { sb.append((char) c); diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/Header.java b/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/Header.java index 1f7589db0..24be0922e 100644 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/Header.java +++ b/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/Header.java @@ -118,7 +118,7 @@ public class Header implements Cloneable { /** * Set a field with the specified value. If the field is not found, it is * added. If the field is found, the existing value(s) are overwritten. - * + * * @param key * @param value */ @@ -148,8 +148,10 @@ public class Header implements Cloneable { * Provides an unmodifiable map with all String header names mapped to their * String values. The map keys are Strings and the values are unmodifiable * Lists of Strings. - * + * * @return an unmodifiable map of the headers + * + * @since 1.4 */ public Map> getFieldMap() { Map> result = new HashMap>( @@ -164,7 +166,7 @@ public class Header implements Cloneable { /** * Returns the element at pos, null if no such element * exist. - * + * * @return java.lang.String the value of the key * @param pos * int the position to look for @@ -179,8 +181,8 @@ public class Header implements Cloneable { /** * Returns the key of this header at pos, null if there are * fewer keys in the header - * - * + * + * * @return the key the desired position * @param pos * the position to look for @@ -193,11 +195,12 @@ public class Header implements Cloneable { } /** - * Returns the value corresponding to the specified key, null if no such key - * exists. - * + * Returns the value corresponding to the specified key. + * * @param key - * @return + * the key to look up. + * @return Answers the value for the given key, or null if no + * such key exists. */ public String get(String key) { LinkedList result = keyTable.get(key.toLowerCase()); @@ -209,8 +212,8 @@ public class Header implements Cloneable { /** * Returns the number of keys stored in this header - * - * @return + * + * @return the number of keys. */ public int length() { return props.size() / 2; diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpConfiguration.java b/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpConfiguration.java index 6a2636553..55d762d84 100644 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpConfiguration.java +++ b/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpConfiguration.java @@ -14,12 +14,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -// BEGIN android-note -// This class was copied from a newer version of harmony -// to improve reusability of URLConnections -// END android-note - package org.apache.harmony.luni.internal.net.www.protocol.http; import java.net.InetSocketAddress; @@ -141,4 +135,4 @@ public class HttpConfiguration { return uri.hashCode(); } -} +} \ No newline at end of file diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpConnection.java b/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpConnection.java index f7c8d9646..4c51bdc66 100644 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpConnection.java +++ b/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpConnection.java @@ -14,12 +14,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -// BEGIN android-note -// This class was copied from a newer version of harmony -// to improve reusability of URLConnections -// END android-note - package org.apache.harmony.luni.internal.net.www.protocol.http; import java.io.IOException; @@ -42,6 +36,7 @@ import org.apache.harmony.luni.internal.nls.Messages; * various utility methods to access that connection. */ public class HttpConnection { + private boolean usingSecureSocket = false; private Socket socket; @@ -233,7 +228,7 @@ public class HttpConnection { * Returns whether this connection is eligible to be recycled. This * is like {@link #isStale} except that it doesn't try to actually * perform any I/O. - * + * * @return true if the connection is eligible to be * recycled */ @@ -272,4 +267,5 @@ public class HttpConnection { return null; } } -} + +} \ No newline at end of file diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpConnectionManager.java b/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpConnectionManager.java index 9a906122b..61d7cbede 100644 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpConnectionManager.java +++ b/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpConnectionManager.java @@ -14,12 +14,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -// BEGIN android-note -// This class was copied from a newer version of harmony -// to improve reusability of URLConnections -// END android-note - package org.apache.harmony.luni.internal.net.www.protocol.http; import java.io.IOException; @@ -179,4 +173,4 @@ public class HttpConnectionManager { pool.clear(); } -} +} \ No newline at end of file diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpURLConnection.java b/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpURLConnection.java index 6f5a2bed2..2dea92b6f 100644 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpURLConnection.java +++ b/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/http/HttpURLConnection.java @@ -15,11 +15,6 @@ * limitations under the License. */ -// BEGIN android-note -// This class and some helper classes where copied from a newer version of harmony -// to improve reusability of URLConnections -// END android-note - package org.apache.harmony.luni.internal.net.www.protocol.http; import java.io.ByteArrayOutputStream; @@ -185,7 +180,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection { return is.skip(n); } - + public int available() throws IOException { if (closed) { throwClosed(); @@ -222,9 +217,9 @@ public class HttpURLConnection extends java.net.HttpURLConnection { private void throwClosed() throws IOException { throw new IOException("stream closed"); } - } + } // END android-added - + private class LimitedInputStream extends InputStream { int bytesRemaining; @@ -523,7 +518,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection { * size minus chunk head (which writes chunk data size in HEX and * "\r\n") size. For example, a string "abcd" use chunk whose size is 5 * must be written to socket as "2\r\nab","2\r\ncd" ... - * + * */ private int calculateChunkDataLength() { /* @@ -691,7 +686,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection { /** * Creates an instance of the HttpURLConnection using default * port 80. - * + * * @param url * URL The URL this connection is connecting */ @@ -701,7 +696,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection { /** * Creates an instance of the HttpURLConnection - * + * * @param url * URL The URL this connection is connecting * @param port @@ -711,7 +706,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection { super(url); defaultPort = port; reqHeader = (Header) defaultReqHeader.clone(); - + try { uri = url.toURI(); } catch (URISyntaxException e) { @@ -727,7 +722,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection { /** * Creates an instance of the HttpURLConnection - * + * * @param url * URL The URL this connection is connecting * @param port @@ -742,12 +737,12 @@ public class HttpURLConnection extends java.net.HttpURLConnection { /** * Establishes the connection to the remote HTTP server - * + * * Any methods that requires a valid connection to the resource will call * this method implicitly. After the connection is established, * connected is set to true. - * - * + * + * * @see #connected * @see java.io.IOException * @see URLStreamHandler @@ -762,9 +757,9 @@ public class HttpURLConnection extends java.net.HttpURLConnection { } // BEGIN android-changed // url.toURI(); throws an URISyntaxException if the url contains - // illegal characters in e.g. the query. - // Since the query is not needed for proxy selection, we just create an - // URI that only contains the necessary information. + // illegal characters in e.g. the query. + // Since the query is not needed for proxy selection, we just create an + // URI that only contains the necessary information. try { uri = new URI(url.getProtocol(), null, @@ -817,7 +812,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection { } /** - * Returns connected socket to be used for this HTTP connection. + * Returns connected socket to be used for this HTTP connection. */ protected HttpConnection getHTTPConnection(Proxy proxy) throws IOException { HttpConnection connection; @@ -832,7 +827,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection { /** * Sets up the data streams used to send request[s] and read response[s]. - * + * * @param connection * HttpConnection to be used */ @@ -882,8 +877,8 @@ public class HttpURLConnection extends java.net.HttpURLConnection { /** * Closes the connection with the HTTP server - * - * + * + * * @see URLConnection#connect() */ @Override @@ -940,7 +935,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection { *

          * If the content type is not what stated above, * FileNotFoundException is thrown. - * + * * @return InputStream the error input stream returned by the server. */ @Override @@ -978,14 +973,14 @@ public class HttpURLConnection extends java.net.HttpURLConnection { /** * Returns the value of the field corresponding to the key * Returns null if there is no such field. - * + * * If there are multiple fields with that key, the last field value is * returned. - * + * * @return java.lang.String The value of the header field * @param key * java.lang.String the name of the header field - * + * * @see #getHeaderField(int) * @see #getHeaderFieldKey */ @@ -1019,8 +1014,10 @@ public class HttpURLConnection extends java.net.HttpURLConnection { * Provides an unmodifiable map of the connection header values. The map * keys are the String header field names. Each map value is a list of the * header field values associated with that key name. - * + * * @return the mapping of header field names to values + * + * @since 1.4 */ @Override public Map> getHeaderFields() { @@ -1172,12 +1169,12 @@ public class HttpURLConnection extends java.net.HttpURLConnection { /** * Returns a line read from the input stream. Does not include the \n - * + * * @return The line that was read. */ String readln() throws IOException { boolean lastCr = false; - StringBuffer result = new StringBuffer(80); + StringBuilder result = new StringBuilder(80); int c = is.read(); if (c < 0) { return null; @@ -1215,7 +1212,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection { * Sends the request header to the remote HTTP server Not all of them are * guaranteed to have any effect on the content the server will return, * depending on if the server supports that field. - * + * * Examples : Accept: text/*, text/html, text/html;level=1, Accept-Charset: * iso-8859-5, unicode-1-1;q=0.8 */ @@ -1327,6 +1324,45 @@ public class HttpURLConnection extends java.net.HttpURLConnection { } else { output.append("1\r\n"); //$NON-NLS-1$ } + // add user-specified request headers if any + boolean hasContentLength = false; + for (int i = 0; i < reqHeader.length(); i++) { + String key = reqHeader.getKey(i); + if (key != null) { + String lKey = key.toLowerCase(); + if ((os != null && !os.isChunked()) + || (!lKey.equals("transfer-encoding") && !lKey //$NON-NLS-1$ + .equals("content-length"))) { //$NON-NLS-1$ + output.append(key); + String value = reqHeader.get(i); + /* + * duplicates are allowed under certain conditions see + * http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2 + */ + if (lKey.equals("content-length")) { //$NON-NLS-1$ + hasContentLength = true; + /* + * if both setFixedLengthStreamingMode and + * content-length are set, use fixedContentLength first + */ + if(fixedContentLength >= 0){ + value = String.valueOf(fixedContentLength); + } + } + if (value != null) { + output.append(": "); //$NON-NLS-1$ + output.append(value); + } + output.append("\r\n"); //$NON-NLS-1$ + } + } + } + if (fixedContentLength >= 0 && !hasContentLength) { + output.append("content-length: "); //$NON-NLS-1$ + output.append(String.valueOf(fixedContentLength)); + output.append("\r\n"); //$NON-NLS-1$ + } + if (reqHeader.get("User-Agent") == null) { //$NON-NLS-1$ output.append("User-Agent: "); //$NON-NLS-1$ String agent = getSystemProperty("http.agent"); //$NON-NLS-1$ @@ -1348,6 +1384,11 @@ public class HttpURLConnection extends java.net.HttpURLConnection { } output.append("\r\n"); //$NON-NLS-1$ } + if (reqHeader.get("Accept") == null) { //$NON-NLS-1$ + // BEGIN android-changed + output.append("Accept: *, */*\r\n"); //$NON-NLS-1$ + // END android-changed + } if (httpVersion > 0 && reqHeader.get("Connection") == null) { //$NON-NLS-1$ output.append("Connection: Keep-Alive\r\n"); //$NON-NLS-1$ } @@ -1367,43 +1408,6 @@ public class HttpURLConnection extends java.net.HttpURLConnection { output.append("Transfer-Encoding: chunked\r\n"); //$NON-NLS-1$ } } - - boolean hasContentLength = false; - // then the user-specified request headers, if any - for (int i = 0; i < reqHeader.length(); i++) { - String key = reqHeader.getKey(i); - if (key != null) { - String lKey = key.toLowerCase(); - if ((os != null && !os.isChunked()) - || (!lKey.equals("transfer-encoding") && !lKey //$NON-NLS-1$ - .equals("content-length"))) { //$NON-NLS-1$ - output.append(key); - output.append(": "); //$NON-NLS-1$ - /* - * duplicates are allowed under certain conditions see - * http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2 - */ - if (lKey.equals("content-length")) { //$NON-NLS-1$ - hasContentLength = true; - /* - * if both setFixedLengthStreamingMode and - * content-length are set, use fixedContentLength first - */ - output.append((fixedContentLength >= 0) ? String - .valueOf(fixedContentLength) - : reqHeader.get(i)); - } else { - output.append(reqHeader.get(i)); - } - output.append("\r\n"); //$NON-NLS-1$ - } - } - } - if (fixedContentLength >= 0 && !hasContentLength) { - output.append("content-length: "); //$NON-NLS-1$ - output.append(String.valueOf(fixedContentLength)); - output.append("\r\n"); //$NON-NLS-1$ - } // end the headers output.append("\r\n"); //$NON-NLS-1$ return output.toString().getBytes("ISO8859_1"); //$NON-NLS-1$ @@ -1412,7 +1416,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection { /** * Sets the default request header fields to be sent to the remote server. * This does not affect the current URL Connection, only newly created ones. - * + * * @param field * java.lang.String The name of the field to be changed * @param value @@ -1427,11 +1431,11 @@ public class HttpURLConnection extends java.net.HttpURLConnection { * setIfModifiedSince() Since this HTTP impl supports * IfModifiedSince as one of the header field, the request header is updated * with the new value. - * - * + * + * * @param newValue * the number of millisecond since epoch - * + * * @throws IllegalStateException * if already connected. */ @@ -1655,7 +1659,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection { /** * Returns the authorization credentials on the base of provided * authorization challenge - * + * * @param challenge * @return authorization credentials * @throws IOException @@ -1707,4 +1711,4 @@ public class HttpURLConnection extends java.net.HttpURLConnection { } } } -} +} \ No newline at end of file diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/https/HttpsURLConnection.java b/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/https/HttpsURLConnection.java index 31c119827..4d0aff7af 100644 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/https/HttpsURLConnection.java +++ b/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/https/HttpsURLConnection.java @@ -14,7 +14,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.apache.harmony.luni.internal.net.www.protocol.https; import java.io.IOException; @@ -22,10 +21,6 @@ import java.io.InputStream; import java.io.OutputStream; import java.net.ProtocolException; import java.net.Proxy; -// BEGIN andorid-removed -// copied from newer version of harmony -// import java.net.Socket; -// END android-removed import java.net.URL; import java.security.Permission; import java.security.Principal; @@ -390,10 +385,7 @@ public class HttpsURLConnection extends javax.net.ssl.HttpsURLConnection { responseMessage, responseCode)); } // if there are some remaining data in the stream - read it out - // BEGIN andorid-changed - // copied from newer version of harmony InputStream is = connection.getInputStream(); - // END android-changed while (is.available() != 0) { is.read(); } @@ -403,11 +395,8 @@ public class HttpsURLConnection extends javax.net.ssl.HttpsURLConnection { super.connect(); } if (!makingSSLTunnel) { - // BEGIN andorid-changed - // copied from newer version of harmony sslSocket = connection.getSecureSocket(getSSLSocketFactory(), getHostnameVerifier()); setUpTransportIO(connection); - // END android-changed } } @@ -430,23 +419,5 @@ public class HttpsURLConnection extends javax.net.ssl.HttpsURLConnection { return super.requestString(); } - // BEGIN android-removed - // /** - // * Create the secure socket over the connected socket and verify remote - // * hostname. - // */ - // private Socket wrapConnection(Socket socket) throws IOException { - // String hostname = url.getHost(); - // // create the wrapper over connected socket - // sslSocket = (SSLSocket) getSSLSocketFactory().createSocket(socket, - // hostname, url.getPort(), true); - // sslSocket.setUseClientMode(true); - // sslSocket.startHandshake(); - // if (!getHostnameVerifier().verify(hostname, sslSocket.getSession())) { - // throw new IOException(Messages.getString("luni.02", hostname)); //$NON-NLS-1$ - // } - // return sslSocket; - // } - // END android-removed } } diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/jar/Handler.java b/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/jar/Handler.java index da34969f6..4db9910ab 100644 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/jar/Handler.java +++ b/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/jar/Handler.java @@ -36,8 +36,9 @@ public class Handler extends URLStreamHandler { * @param u * java.net.URL The URL to which the connection is pointing to * - * @thows IOException thrown if an IO error occurs when this method tries to - * establish connection. + * @throws IOException + * thrown if an IO error occurs when this method tries to + * establish connection. */ @Override protected URLConnection openConnection(URL u) throws IOException { @@ -75,7 +76,8 @@ public class Handler extends URLStreamHandler { file = file.substring(0, file.indexOf('!') + 1) + spec; } else { int idx = file.indexOf('!'); - String tmpFile = file.substring(idx + 1, file.lastIndexOf('/') + 1) + spec; + String tmpFile = file.substring(idx + 1, file.lastIndexOf('/') + 1) + + spec; tmpFile = URLUtil.canonicalizePath(tmpFile); file = file.substring(0, idx + 1) + tmpFile; } @@ -97,7 +99,7 @@ public class Handler extends URLStreamHandler { */ @Override protected String toExternalForm(URL url) { - StringBuffer sb = new StringBuffer(); + StringBuilder sb = new StringBuilder(); sb.append("jar:"); //$NON-NLS-1$ sb.append(url.getFile()); String ref = url.getRef(); diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/jar/JarURLConnection.java b/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/jar/JarURLConnection.java index 5f4896a3e..34e7d07e0 100644 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/jar/JarURLConnection.java +++ b/libcore/luni/src/main/java/org/apache/harmony/luni/internal/net/www/protocol/jar/JarURLConnection.java @@ -23,8 +23,6 @@ import java.io.FileOutputStream; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; -import java.lang.ref.ReferenceQueue; -import java.lang.ref.WeakReference; import java.net.ContentHandler; import java.net.ContentHandlerFactory; import java.net.MalformedURLException; @@ -32,120 +30,38 @@ import java.net.URL; import java.security.AccessController; import java.security.Permission; import java.security.PrivilegedAction; -import java.util.Comparator; -import java.util.Date; -import java.util.Enumeration; -import java.util.Hashtable; -import java.util.TreeSet; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.zip.ZipFile; -// BEGIN android-removed -// import org.apache.harmony.kernel.vm.VM; -// END android-removed import org.apache.harmony.luni.util.Msg; import org.apache.harmony.luni.util.Util; /** * This subclass extends URLConnection. *

          - * + * * This class is responsible for connecting and retrieving resources from a Jar * file which can be anywhere that can be refered to by an URL. */ public class JarURLConnection extends java.net.JarURLConnection { - static Hashtable> jarCache = new Hashtable>(); + static HashMap jarCache = new HashMap(); + + private URL jarFileURL; - InputStream jarInput; + private InputStream jarInput; private JarFile jarFile; private JarEntry jarEntry; - - private boolean closed; - - ReferenceQueue cacheQueue = new ReferenceQueue(); - - static TreeSet lru = new TreeSet( - new LRUComparator()); - - static int Limit; - - static { - Limit = AccessController.doPrivileged(new PrivilegedAction() { - public Integer run() { - return Integer.getInteger("jar.cacheSize", 500); //$NON-NLS-1$ - } - }); - // BEGIN android-removed - // TODO this needs to be implemented once this is available. - // VM.closeJars(); - // END android-removed - } - - static final class CacheEntry extends WeakReference { - Object key; - - CacheEntry(T jar, String key, ReferenceQueue queue) { - super(jar, queue); - this.key = key; - } - } - - static final class LRUKey { - JarFile jar; - - long ts; - - LRUKey(JarFile file, long time) { - jar = file; - ts = time; - } - - /** - * @see java.lang.Object#equals(java.lang.Object) - */ - @Override - public boolean equals(Object obj) { - return (obj instanceof LRUKey) && - (jar == ((LRUKey) obj).jar); - } - - @Override - public int hashCode() { - return jar.hashCode(); - } - } - - static final class LRUComparator implements Comparator { - LRUComparator() { - } - - /** - * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) - */ - public int compare(LRUKey o1, LRUKey o2) { - if ((o1).ts > (o2).ts) { - return 1; - } - return (o1).ts == (o2).ts ? 0 : -1; - } + private boolean closed; - /** - * @param o1 - * an object to compare - * @param o2 - * an object to compare - * @return true if the objects are equal, - * false otherwise. - */ - public boolean equals(Object o1, Object o2) { - return o1.equals(o2); - } - } /** * @param url @@ -153,8 +69,10 @@ public class JarURLConnection extends java.net.JarURLConnection { * @throws MalformedURLException * if the URL is malformed */ - public JarURLConnection(java.net.URL url) throws MalformedURLException { + public JarURLConnection(java.net.URL url) throws MalformedURLException, IOException { super(url); + jarFileURL = getJarFileURL(); + jarFileURLConnection = jarFileURL.openConnection(); } /** @@ -162,77 +80,76 @@ public class JarURLConnection extends java.net.JarURLConnection { */ @Override public void connect() throws IOException { - jarFileURLConnection = getJarFileURL().openConnection(); - findJarFile(); // ensure the file can be found - findJarEntry(); // ensure the entry, if any, can be found - connected = true; + if (!connected) { + findJarFile(); // ensure the file can be found + findJarEntry(); // ensure the entry, if any, can be found + connected = true; + } } /** * Returns the Jar file refered by this URLConnection - * + * * @return the JAR file referenced by this connection - * + * * @throws IOException * thrown if an IO error occurs while connecting to the * resource. */ @Override public JarFile getJarFile() throws IOException { - if (!connected) { - connect(); - } + connect(); return jarFile; } /** * Returns the Jar file refered by this URLConnection - * + * * @throws IOException * if an IO error occurs while connecting to the resource. */ private void findJarFile() throws IOException { - URL jarFileURL = getJarFileURL(); - if (jarFileURL.getProtocol().equals("file")) { //$NON-NLS-1$ - String fileName = jarFileURL.getFile(); - if(!new File(Util.decode(fileName,false)).exists()){ - // KA026=JAR entry {0} not found in {1} - throw new FileNotFoundException(Msg.getString("KA026", //$NON-NLS-1$ - getEntryName(), fileName)); + JarFile jar = null; + if (getUseCaches()) { + synchronized(jarCache){ + jarFile = jarCache.get(jarFileURL); } - String host = jarFileURL.getHost(); - if (host != null && host.length() > 0) { - fileName = "//" + host + fileName; //$NON-NLS-1$ + if (jarFile == null) { + jar = openJarFile(); + synchronized(jarCache){ + jarFile = jarCache.get(jarFileURL); + if (jarFile == null){ + jarCache.put(jarFileURL, jar); + jarFile = jar; + }else{ + jar.close(); + } + } } - jarFile = openJarFile(fileName, fileName, false); - return; + }else{ + jarFile = openJarFile(); } - final String externalForm = jarFileURLConnection.getURL() - .toExternalForm(); - jarFile = AccessController - .doPrivileged(new PrivilegedAction() { - public JarFile run() { - try { - return openJarFile(null, externalForm, false); - } catch (IOException e) { - return null; - } - } - }); - if (jarFile != null) { - return; + if (jarFile == null) { + throw new IOException(); } + } - // Build a temp jar file - final InputStream is = jarFileURLConnection.getInputStream(); - try { - jarFile = AccessController + JarFile openJarFile() throws IOException { + JarFile jar = null; + if (jarFileURL.getProtocol().equals("file")) { //$NON-NLS-1$ + jar = new JarFile(new File(Util.decode(jarFileURL.getFile(), false, + "UTF-8")), true, ZipFile.OPEN_READ); + } else { + final InputStream is = jarFileURL.openConnection().getInputStream(); + try { + jar = AccessController .doPrivileged(new PrivilegedAction() { public JarFile run() { try { File tempJar = File.createTempFile("hyjar_", //$NON-NLS-1$ ".tmp", null); //$NON-NLS-1$ + tempJar.deleteOnExit(); FileOutputStream fos = new FileOutputStream( tempJar); byte[] buf = new byte[4096]; @@ -241,79 +158,33 @@ public class JarURLConnection extends java.net.JarURLConnection { fos.write(buf, 0, nbytes); } fos.close(); - String path = tempJar.getPath(); - return openJarFile(path, externalForm, true); + return new JarFile(tempJar, + true, ZipFile.OPEN_READ | ZipFile.OPEN_DELETE); } catch (IOException e) { return null; } } }); - } finally { - is.close(); - } - if (jarFile == null) { - throw new IOException(); - } - } - - JarFile openJarFile(String fileString, String key, boolean temp) - throws IOException { - - JarFile jar = null; - if (useCaches) { - CacheEntry entry; - while ((entry = (CacheEntry) cacheQueue.poll()) != null) { - jarCache.remove(entry.key); + } finally { + if (is != null) is.close(); } - entry = jarCache.get(key); - if (entry != null) { - jar = entry.get(); - } - if (jar == null && fileString != null) { - int flags = ZipFile.OPEN_READ - + (temp ? ZipFile.OPEN_DELETE : 0); - jar = new JarFile(new File(Util.decode(fileString, false)), - true, flags); - jarCache - .put(key, new CacheEntry(jar, key, cacheQueue)); - } else { - SecurityManager security = System.getSecurityManager(); - if (security != null) { - security.checkPermission(getPermission()); - } - if (temp) { - lru.remove(new LRUKey(jar, 0)); - } - } - } else if (fileString != null) { - int flags = ZipFile.OPEN_READ + (temp ? ZipFile.OPEN_DELETE : 0); - jar = new JarFile(new File(Util.decode(fileString, false)), true, - flags); } - if (temp) { - lru.add(new LRUKey(jar, new Date().getTime())); - if (lru.size() > Limit) { - lru.remove(lru.first()); - } - } return jar; } /** * Returns the JarEntry of the entry referenced by this * URLConnection. - * + * * @return java.util.jar.JarEntry the JarEntry referenced - * + * * @throws IOException * if an IO error occurs while getting the entry */ @Override public JarEntry getJarEntry() throws IOException { - if (!connected) { - connect(); - } + connect(); return jarEntry; } @@ -334,20 +205,19 @@ public class JarURLConnection extends java.net.JarURLConnection { /** * Creates an input stream for reading from this URL Connection. - * + * * @return the input stream - * + * * @throws IOException * if an IO error occurs while connecting to the resource. */ @Override public InputStream getInputStream() throws IOException { + if (closed) { throw new IllegalStateException(Msg.getString("KA027")); } - if (!connected) { - connect(); - } + connect(); if (jarInput != null) { return jarInput; } @@ -359,39 +229,56 @@ public class JarURLConnection extends java.net.JarURLConnection { } /** - * Returns the content type of the resource. Test cases reveal that only if - * the URL is refering to a Jar file, that this method returns a non-null - * value - x-java/jar. - * + * Returns the content type of the resource. + * For jar file itself "x-java/jar" should be returned, + * for jar entries the content type of the entry should be returned. + * Returns non-null results ("content/unknown" for unknown types). + * * @return the content type */ @Override public String getContentType() { - // it could also return "x-java/jar" which jdk returns but here, we get - // it from the URLConnection - try { - if (url.getFile().endsWith("!/")) { //$NON-NLS-1$ - return getJarFileURL().openConnection().getContentType(); + if (url.getFile().endsWith("!/")) { //$NON-NLS-1$ + // the type for jar file itself is always "x-java/jar" + return "x-java/jar"; //$NON-NLS-1$ + } else { + String cType = null; + String entryName = getEntryName(); + + if (entryName != null) { + // if there is an Jar Entry, get the content type from the name + cType = guessContentTypeFromName(entryName); + } else { + try { + connect(); + cType = jarFileURLConnection.getContentType(); + } catch (IOException ioe) { + // Ignore + } } - } catch (IOException ioe) { - // Ignore + if (cType == null) { + cType = "content/unknown"; //$NON-NLS-1$ + } + return cType; } - // if there is an Jar Entry, get the content type from the name - return guessContentTypeFromName(url.getFile()); } /** * Returns the content length of the resource. Test cases reveal that if the * URL is refering to a Jar file, this method returns a content-length - * returned by URLConnection. If not, it will return -1. - * + * returned by URLConnection. For jar entry it should return it's size. + * Otherwise, it will return -1. + * * @return the content length */ @Override public int getContentLength() { try { - if (url.getFile().endsWith("!/")) { //$NON-NLS-1$ - return getJarFileURL().openConnection().getContentLength(); + connect(); + if (jarEntry == null) { + return jarFileURLConnection.getContentLength(); + } else { + return (int) getJarEntry().getSize(); } } catch (IOException e) { //Ignored @@ -404,12 +291,12 @@ public class JarURLConnection extends java.net.JarURLConnection { * URLConnection is pointing to a Jar File (no Jar Entry), this method will * return a JarFile If there is a Jar Entry, it will return * the object corresponding to the Jar entry content type. - * + * * @return a non-null object - * + * * @throws IOException * if an IO error occured - * + * * @see ContentHandler * @see ContentHandlerFactory * @see java.io.IOException @@ -417,9 +304,7 @@ public class JarURLConnection extends java.net.JarURLConnection { */ @Override public Object getContent() throws IOException { - if (!connected) { - connect(); - } + connect(); // if there is no Jar Entry, return a JarFile if (jarEntry == null) { return jarFile; @@ -431,37 +316,57 @@ public class JarURLConnection extends java.net.JarURLConnection { * Returns the permission, in this case the subclass, FilePermission object * which represents the permission necessary for this URLConnection to * establish the connection. - * + * * @return the permission required for this URLConnection. - * + * * @throws IOException * thrown when an IO exception occurs while creating the * permission. */ + @Override public Permission getPermission() throws IOException { - if (jarFileURLConnection != null) { - return jarFileURLConnection.getPermission(); - } - return getJarFileURL().openConnection().getPermission(); + return jarFileURLConnection.getPermission(); + } + + @Override + public boolean getUseCaches() { + return jarFileURLConnection.getUseCaches(); + } + + @Override + public void setUseCaches(boolean usecaches) { + jarFileURLConnection.setUseCaches(usecaches); + } + + @Override + public boolean getDefaultUseCaches() { + return jarFileURLConnection.getDefaultUseCaches(); + } + + @Override + public void setDefaultUseCaches(boolean defaultusecaches) { + jarFileURLConnection.setDefaultUseCaches(defaultusecaches); } /** * Closes the cached files. */ public static void closeCachedFiles() { - Enumeration> elemEnum = jarCache - .elements(); - while (elemEnum.hasMoreElements()) { - try { - ZipFile zip = elemEnum.nextElement().get(); - if (zip != null) { - zip.close(); + Set> s = jarCache.entrySet(); + synchronized(jarCache){ + Iterator> i = s.iterator(); + while(i.hasNext()){ + try { + ZipFile zip = i.next().getValue(); + if (zip != null) { + zip.close(); + } + } catch (IOException e) { + // Ignored } - } catch (IOException e) { - // Ignored } - } + } } private class JarURLConnectionInputStream extends FilterInputStream { @@ -478,7 +383,7 @@ public class JarURLConnection extends java.net.JarURLConnection { @Override public void close() throws IOException { super.close(); - if (!useCaches) { + if (!getUseCaches()) { closed = true; jarFile.close(); } diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/nls/messages.properties b/libcore/luni/src/main/java/org/apache/harmony/luni/internal/nls/messages.properties index fe392b7dc..2ee09f703 100644 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/nls/messages.properties +++ b/libcore/luni/src/main/java/org/apache/harmony/luni/internal/nls/messages.properties @@ -21,8 +21,6 @@ luni.03=The enum constant {0}.{1} is missing luni.04=this Map luni.05=Attempt to insert {0} element into collection with element type {1} luni.06=The string argument is null -# // BEGIN android-added -# // copied from newer version of harmony +luni.07=The stream is corrupted luni.08=Invalid Unicode sequence: expected format \\uxxxx luni.09=Invalid Unicode sequence: illegal character -# // END android-added diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/reflect/ProxyCharArrayCache.java b/libcore/luni/src/main/java/org/apache/harmony/luni/internal/reflect/ProxyCharArrayCache.java deleted file mode 100644 index a359b8bb1..000000000 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/reflect/ProxyCharArrayCache.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.harmony.luni.internal.reflect; - -class ProxyCharArrayCache { - static boolean equals(char[] first, char[] second) { - if (first == second) { - return true; - } - if (first == null || second == null) { - return false; - } - if (first.length != second.length) { - return false; - } - - for (int i = first.length; --i >= 0;) { - if (first[i] != second[i]) { - return false; - } - } - return true; - } - - // to avoid using Enumerations, walk the individual tables skipping nulls - private char[] keyTable[]; - - private int valueTable[]; - - // number of elements in the table - private int elementSize; - - private int threshold; - - ProxyCharArrayCache(int initialCapacity) { - if (initialCapacity < 13) { - initialCapacity = 13; - } - this.elementSize = 0; - this.threshold = (int) (initialCapacity * 0.66f); - this.keyTable = new char[initialCapacity][]; - this.valueTable = new int[initialCapacity]; - } - - int get(char[] key) { - int index = hashCodeChar(key); - while (keyTable[index] != null) { - if (equals(keyTable[index], key)) { - return valueTable[index]; - } - index = (index + 1) % keyTable.length; - } - return -1; - } - - private int hashCodeChar(char[] val) { - int length = val.length; - int hash = 0; - int n = 2; // number of characters skipped - for (int i = 0; i < length; i += n) { - hash += val[i]; - } - return (hash & 0x7FFFFFFF) % keyTable.length; - } - - int put(char[] key, int value) { - int index = hashCodeChar(key); - while (keyTable[index] != null) { - if (equals(keyTable[index], key)) { - return valueTable[index] = value; - } - index = (index + 1) % keyTable.length; - } - keyTable[index] = key; - valueTable[index] = value; - - // assumes the threshold is never equal to the size of the table - if (++elementSize > threshold) { - rehash(); - } - return value; - } - - private void rehash() { - ProxyCharArrayCache newHashtable = new ProxyCharArrayCache( - keyTable.length * 2); - for (int i = keyTable.length; --i >= 0;) { - if (keyTable[i] != null) { - newHashtable.put(keyTable[i], valueTable[i]); - } - } - - this.keyTable = newHashtable.keyTable; - this.valueTable = newHashtable.valueTable; - this.threshold = newHashtable.threshold; - } - - int size() { - return elementSize; - } - - @Override - public String toString() { - int max = size(); - StringBuilder buf = new StringBuilder(); - buf.append("{"); //$NON-NLS-1$ - for (int i = 0; i < max; ++i) { - if (keyTable[i] != null) { - buf.append(keyTable[i]).append("->").append(valueTable[i]); //$NON-NLS-1$ - } - if (i < max) { - buf.append(", "); //$NON-NLS-1$ - } - } - buf.append("}"); //$NON-NLS-1$ - return buf.toString(); - } -} diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/reflect/ProxyClassFile.java b/libcore/luni/src/main/java/org/apache/harmony/luni/internal/reflect/ProxyClassFile.java deleted file mode 100644 index 43fa5a36f..000000000 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/reflect/ProxyClassFile.java +++ /dev/null @@ -1,937 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.harmony.luni.internal.reflect; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; -import java.lang.reflect.UndeclaredThrowableException; -import java.util.ArrayList; -import java.util.HashSet; - -public final class ProxyClassFile implements ProxyConstants { - - private static final int INITIAL_CONTENTS_SIZE = 1000; - - private static final int INITIAL_HEADER_SIZE = 500; - - private static final int INCREMENT_SIZE = 250; - - private static Method ObjectEqualsMethod; - - private static Method ObjectHashCodeMethod; - - private static Method ObjectToStringMethod; - - private static Method ClassForNameMethod; - - private static Method ClassGetMethod; - - private static Method HandlerInvokeMethod; - - private static Constructor ProxyConstructor; - - private static Constructor UndeclaredThrowableExceptionConstructor; - - private static Field ProxyHandlerField; - - public static byte[] generateBytes(String typeName, Class[] interfaces) { - ProxyClassFile classFile = new ProxyClassFile(typeName, interfaces); - classFile.addFields(); - classFile.findMethods(interfaces); - classFile.addMethods(); - classFile.addAttributes(); - return classFile.getBytes(); - } - - static char[] getConstantPoolName(Class c) { - if (c.isArray()) { - // Array classes are named/ with their signature - return c.getName().replace('.', '/').toCharArray(); - } - - if (c.isPrimitive()) { - // Special cases for each base type. - if (c == void.class) { - return new char[] { 'V' }; - } - if (c == int.class) { - return new char[] { 'I' }; - } - if (c == boolean.class) { - return new char[] { 'Z' }; - } - if (c == byte.class) { - return new char[] { 'B' }; - } - if (c == char.class) { - return new char[] { 'C' }; - } - if (c == short.class) { - return new char[] { 'S' }; - } - if (c == long.class) { - return new char[] { 'J' }; - } - if (c == float.class) { - return new char[] { 'F' }; - } - if (c == double.class) { - return new char[] { 'D' }; - } - } - return ("L" + c.getName().replace('.', '/') + ";").toCharArray(); - } - - static char[] getConstantPoolName(Constructor method) /* (ILjava/lang/Thread;)V */{ - Class[] parameters = method.getParameterTypes(); - StringBuffer buffer = new StringBuffer(parameters.length + 1 * 20); - buffer.append('('); - for (Class element : parameters) { - buffer.append(getConstantPoolName(element)); - } - buffer.append(')'); - buffer.append(getConstantPoolName(void.class)); - return buffer.toString().toCharArray(); - } - - static char[] getConstantPoolName(Method method) /* (ILjava/lang/Thread;)Ljava/lang/Object; */{ - Class[] parameters = method.getParameterTypes(); - StringBuffer buffer = new StringBuffer(parameters.length + 1 * 20); - buffer.append('('); - for (Class element : parameters) { - buffer.append(getConstantPoolName(element)); - } - buffer.append(')'); - buffer.append(getConstantPoolName(method.getReturnType())); - return buffer.toString().toCharArray(); - } - - private ProxyConstantPool constantPool; - - // the header contains all the bytes till the end of the constant pool - byte[] header; - - int headerOffset; - - // that collection contains all the remaining bytes of the .class file - private byte[] contents; - - private int contentsOffset; - - private int constantPoolOffset; - - private ProxyMethod[] proxyMethods; - - ProxyClassFile(String typeName, Class[] interfaces) { - super(); - header = new byte[INITIAL_HEADER_SIZE]; - // generate the magic numbers inside the header - header[headerOffset++] = (byte) (0xCAFEBABEL >> 24); - header[headerOffset++] = (byte) (0xCAFEBABEL >> 16); - header[headerOffset++] = (byte) (0xCAFEBABEL >> 8); - header[headerOffset++] = (byte) (0xCAFEBABEL >> 0); - // Compatible with JDK 1.2 - header[headerOffset++] = 0; - header[headerOffset++] = 0; - header[headerOffset++] = 0; - header[headerOffset++] = 46; - constantPoolOffset = headerOffset; - headerOffset += 2; - constantPool = new ProxyConstantPool(this); - contents = new byte[INITIAL_CONTENTS_SIZE]; - // now we continue to generate the bytes inside the contents array - int accessFlags = AccPublic | AccFinal | AccSuper; - contents[contentsOffset++] = (byte) (accessFlags >> 8); - contents[contentsOffset++] = (byte) accessFlags; - int classNameIndex = constantPool.typeIndex(typeName); - contents[contentsOffset++] = (byte) (classNameIndex >> 8); - contents[contentsOffset++] = (byte) classNameIndex; - int superclassNameIndex = constantPool - .typeIndex("java/lang/reflect/Proxy"); - contents[contentsOffset++] = (byte) (superclassNameIndex >> 8); - contents[contentsOffset++] = (byte) superclassNameIndex; - int interfacesCount = interfaces.length; - contents[contentsOffset++] = (byte) (interfacesCount >> 8); - contents[contentsOffset++] = (byte) interfacesCount; - for (int i = 0; i < interfacesCount; i++) { - int interfaceIndex = constantPool - .typeIndex(interfaces[i].getName()); - contents[contentsOffset++] = (byte) (interfaceIndex >> 8); - contents[contentsOffset++] = (byte) interfaceIndex; - } - } - - private void addAttributes() { - writeUnsignedShort(0); // classFile does not have attributes of its own - - // resynchronize all offsets of the classfile - header = constantPool.poolContent; - headerOffset = constantPool.currentOffset; - int constantPoolCount = constantPool.currentIndex; - header[constantPoolOffset++] = (byte) (constantPoolCount >> 8); - header[constantPoolOffset] = (byte) constantPoolCount; - } - - private void addFields() { - writeUnsignedShort(0); // we have no fields - } - - private void addMethods() { - int methodCount = proxyMethods.length; - writeUnsignedShort(methodCount + 1); - - // save constructor - writeUnsignedShort(AccPublic); - writeUnsignedShort(constantPool.literalIndex(Init)); - if (ProxyConstructor == null) { - try { - ProxyConstructor = Proxy.class - .getDeclaredConstructor(new Class[] { InvocationHandler.class }); - } catch (NoSuchMethodException e) { - throw new InternalError(); - } - } - writeUnsignedShort(constantPool - .literalIndex(getConstantPoolName(ProxyConstructor))); - writeUnsignedShort(1); // store just the code attribute - writeUnsignedShort(constantPool.literalIndex(CodeName)); - // save attribute_length(4), max_stack(2), max_locals(2), code_length(4) - int codeLength = 6; - writeUnsignedWord(12 + codeLength); // max_stack(2), max_locals(2), - // code_length(4), 2 zero shorts - writeUnsignedShort(2); - writeUnsignedShort(2); - writeUnsignedWord(codeLength); - writeUnsignedByte(OPC_aload_0); - writeUnsignedByte(OPC_aload_1); - writeUnsignedByte(OPC_invokespecial); - writeUnsignedShort(constantPool.literalIndex(ProxyConstructor)); - writeUnsignedByte(OPC_return); - writeUnsignedShort(0); // no exceptions table - writeUnsignedShort(0); // there are no attributes for the code - // attribute - - for (int i = 0; i < methodCount; i++) { - ProxyMethod pMethod = proxyMethods[i]; - Method method = pMethod.method; - writeUnsignedShort(AccPublic | AccFinal); - writeUnsignedShort(constantPool.literalIndex(method.getName() - .toCharArray())); - writeUnsignedShort(constantPool - .literalIndex(getConstantPoolName(method))); - Class[] thrownsExceptions = pMethod.commonExceptions; - int eLength = thrownsExceptions.length; - if (eLength > 0) { - writeUnsignedShort(2); // store the exception & code attributes - // The method has a throw clause. So we need to add an exception - // attribute - writeUnsignedShort(constantPool.literalIndex(ExceptionsName)); - // The attribute length = length * 2 + 2 in case of a exception - // attribute - writeUnsignedWord(eLength * 2 + 2); - writeUnsignedShort(eLength); - for (int e = 0; e < eLength; e++) { - writeUnsignedShort(constantPool - .typeIndex(thrownsExceptions[e].getName())); - } - } else { - writeUnsignedShort(1); // store just the code attribute - } - generateCodeAttribute(pMethod); - } - } - - private void findMethods(Class[] interfaces) { - /* - * find all methods defined by the interfaces (including inherited - * interfaces) plus hashCode, equals & toString from Object build an - * array with the methods... no duplicates - check up to the array size - * when the interface's first method was added - */ - if (ObjectEqualsMethod == null) { - try { - ObjectEqualsMethod = Object.class.getMethod("equals", - new Class[] { Object.class }); - ObjectHashCodeMethod = Object.class.getMethod("hashCode", - new Class[0]); - ObjectToStringMethod = Object.class.getMethod("toString", - new Class[0]); - } catch (NoSuchMethodException ex) { - throw new InternalError(); - } - } - - ArrayList allMethods = new ArrayList(25); - allMethods.add(new ProxyMethod(ObjectEqualsMethod)); - allMethods.add(new ProxyMethod(ObjectHashCodeMethod)); - allMethods.add(new ProxyMethod(ObjectToStringMethod)); - - HashSet> interfacesSeen = new HashSet>(); - for (Class element : interfaces) { - findMethods(element, allMethods, interfacesSeen); - } - - proxyMethods = new ProxyMethod[allMethods.size()]; - allMethods.toArray(proxyMethods); - } - - private void findMethods(Class nextInterface, - ArrayList allMethods, HashSet> interfacesSeen) { - /* - * add the nextInterface's methods to allMethods if an equivalent method - * already exists then return types must be identical... don't replace - * it - */ - if (interfacesSeen.contains(nextInterface)) { - return; // already walked it - } - interfacesSeen.add(nextInterface); - - int existingMethodCount = allMethods.size(); - Method[] methods = nextInterface.getMethods(); - nextMethod: for (Method method : methods) { - for (int j = 0; j < existingMethodCount; j++) { - if (allMethods.get(j).matchMethod(method)) { - continue nextMethod; - } - } - allMethods.add(new ProxyMethod(method)); - } - - Class[] superInterfaces = nextInterface.getInterfaces(); - for (Class element : superInterfaces) { - // recursion should be minimal - findMethods(element, allMethods, interfacesSeen); - } - } - - private void generateCodeAttribute(ProxyMethod pMethod) { - int codeAttributeOffset = contentsOffset; - int contentsLength = contents.length; - if (contentsOffset + 20 + 100 >= contentsLength) { - System.arraycopy(contents, 0, (contents = new byte[contentsLength - + INCREMENT_SIZE]), 0, contentsLength); - } - writeUnsignedShort(constantPool.literalIndex(CodeName)); - // leave space for attribute_length(4), max_stack(2), max_locals(2), - // code_length(4) - contentsOffset += 12; - - /* - * to push the args for the call to invoke push the receiver field h 0 - * aload0 1 getfield 33 java.lang.reflect.Proxy.h - * Ljava.lang.reflect.InvocationHandler; push the receiver as the first - * arg 4 aload0 push the method push the array of args call invoke 89 - * invokeinterface 67 - * java.lang.reflect.InvocationHandler.invoke(Ljava.lang.Object;Ljava.lang.reflect.Method;[Ljava.lang.Object;)Ljava.lang.Object; - * cast return result catch & convert exceptions if necessary - */ - - int codeStartOffset = contentsOffset; - writeUnsignedByte(OPC_aload_0); - writeUnsignedByte(OPC_getfield); - if (ProxyHandlerField == null) { - try { - ProxyHandlerField = Proxy.class.getDeclaredField("h"); - } catch (NoSuchFieldException e) { - throw new InternalError(); - } - } - writeUnsignedShort(constantPool.literalIndex(ProxyHandlerField)); - writeUnsignedByte(OPC_aload_0); - Method method = pMethod.method; - Class[] argTypes = method.getParameterTypes(); - genCallGetMethod(method.getDeclaringClass(), method.getName(), argTypes); - int maxLocals = genInvokeArgs(argTypes); - writeUnsignedByte(OPC_invokeinterface); - if (HandlerInvokeMethod == null) { - try { - HandlerInvokeMethod = InvocationHandler.class.getMethod( - "invoke", new Class[] { Object.class, Method.class, - Object[].class }); - } catch (NoSuchMethodException e) { - throw new InternalError(); - } - } - writeUnsignedShort(constantPool.literalIndex(HandlerInvokeMethod)); - writeUnsignedByte(4); // invoke has 3 args - writeUnsignedByte(0); // 4th operand must be 0 - genCastReturnType(method.getReturnType()); - int codeLength = contentsOffset - codeStartOffset; - - Class[] checkedExceptions = pMethod.getCheckedExceptions(); - int checkedLength = checkedExceptions.length; - if (checkedLength > 0) { - int codeEndIndex = contentsOffset - codeStartOffset; - writeUnsignedByte(OPC_athrow); // re-throw the caught exception - - genStoreArg(maxLocals); - writeUnsignedByte(OPC_new); - writeUnsignedShort(constantPool - .typeIndex("java/lang/reflect/UndeclaredThrowableException")); - writeUnsignedByte(OPC_dup); - genLoadArg(maxLocals); - maxLocals++; // now expecting the exception - writeUnsignedByte(OPC_invokespecial); - if (UndeclaredThrowableExceptionConstructor == null) { - try { - UndeclaredThrowableExceptionConstructor = UndeclaredThrowableException.class - .getConstructor(new Class[] { Throwable.class }); - } catch (NoSuchMethodException e) { - throw new InternalError(); - } - } - writeUnsignedShort(constantPool - .literalIndex(UndeclaredThrowableExceptionConstructor)); - writeUnsignedByte(OPC_athrow); - - codeLength = contentsOffset - codeStartOffset; - - // write the exception table - writeUnsignedShort(checkedLength + 1); - for (int i = 0; i < checkedLength; i++) { - writeUnsignedShort(0); - writeUnsignedShort(codeEndIndex); - writeUnsignedShort(codeEndIndex); - writeUnsignedShort(constantPool.typeIndex(checkedExceptions[i] - .getName())); - } - writeUnsignedShort(0); - writeUnsignedShort(codeEndIndex); - writeUnsignedShort(codeEndIndex + 1); // starts after the first - // throw - writeUnsignedShort(constantPool.typeIndex("java/lang/Throwable")); - } else { - writeUnsignedShort(0); // no exceptions table - } - // there are no attributes for the code attribute - writeUnsignedShort(0); - - /* - * Complete the creation of the code attribute by setting the - * attribute_length, max_stack max_locals, code_length & exception table - * codeAttributeOffset is the position inside contents byte array before - * we started to write That means that to write the attribute_length you - * need to offset by 2 the value of codeAttributeOffset to get the right - * position, 6 for the max_stack etc... - */ - int codeAttributeLength = contentsOffset - (codeAttributeOffset + 6); - contents[codeAttributeOffset + 2] = (byte) (codeAttributeLength >> 24); - contents[codeAttributeOffset + 3] = (byte) (codeAttributeLength >> 16); - contents[codeAttributeOffset + 4] = (byte) (codeAttributeLength >> 8); - contents[codeAttributeOffset + 5] = (byte) codeAttributeLength; - - int maxStack = maxLocals + 10; // larger than the exact amount - contents[codeAttributeOffset + 6] = (byte) (maxStack >> 8); - contents[codeAttributeOffset + 7] = (byte) maxStack; - contents[codeAttributeOffset + 8] = (byte) (maxLocals >> 8); - contents[codeAttributeOffset + 9] = (byte) maxLocals; - contents[codeAttributeOffset + 10] = (byte) (codeLength >> 24); - contents[codeAttributeOffset + 11] = (byte) (codeLength >> 16); - contents[codeAttributeOffset + 12] = (byte) (codeLength >> 8); - contents[codeAttributeOffset + 13] = (byte) codeLength; - } - - /** - * Perform call to Class.getMethod(String, Class[]) receiver 13 ldc 37 - * (java.lang.String) "java.lang.Object" 15 invokestatic 43 - * java.lang.Class.forName(Ljava.lang.String;)Ljava.lang.Class; selector 37 - * ldc 55 (java.lang.String) "equals" plus method args 39 iconst0 40 - * anewarray 39 java.lang.Class or 39 iconst1 40 anewarray 39 - * java.lang.Class 43 dup 44 iconst0 53 ldc 37 (java.lang.String) - * "java.lang.Object" 55 invokestatic 43 - * java.lang.Class.forName(Ljava.lang.String;)Ljava.lang.Class; 77 aastore - * or 39 iconst2 40 anewarray 39 java.lang.Class 43 dup 44 iconst0 45 - * getstatic 102 java.lang.Integer.TYPE Ljava.lang.Class; 48 aastore 49 dup - * 50 iconst1 51 getstatic 104 java.lang.Boolean.TYPE Ljava.lang.Class; 54 - * aastore then 78 invokevirtual 59 - * java.lang.Class.getMethod(Ljava.lang.String;[Ljava.lang.Class;)Ljava.lang.reflect.Method; - */ - private void genCallGetMethod(Class receiverType, String selector, - Class[] argTypes) { - genCallClassForName(receiverType.getName()); - writeLdc(selector); - int length = argTypes.length; - writeIntConstant(length); - writeUnsignedByte(OPC_anewarray); - writeUnsignedShort(constantPool.typeIndex("java/lang/Class")); - for (int i = 0; i < length; i++) { - writeUnsignedByte(OPC_dup); - writeIntConstant(i); - Class type = argTypes[i]; - if (type.isPrimitive()) { - writeUnsignedByte(OPC_getstatic); - writeUnsignedShort(constantPool.literalIndex(typeField(type))); - } else { - genCallClassForName(type.getName()); - } - writeUnsignedByte(OPC_aastore); - } - writeUnsignedByte(OPC_invokevirtual); - if (ClassGetMethod == null) { - try { - ClassGetMethod = Class.class.getMethod("getMethod", - new Class[] { String.class, Class[].class }); - } catch (NoSuchMethodException e) { - throw new InternalError(); - } - } - writeUnsignedShort(constantPool.literalIndex(ClassGetMethod)); - } - - /** - * Add argument array for call to InvocationHandler.invoke - * - * 46 aconstnull or 81 iconst1 82 anewarray 61 java.lang.Object 85 dup 86 - * iconst0 87 aload1 88 aastore or 58 iconst2 59 anewarray 61 - * java.lang.Object 62 dup 63 iconst0 64 new 84 java.lang.Integer 67 dup 68 - * iload1 69 invokespecial 107 java.lang.Integer.(I)V 72 aastore 73 - * dup 74 iconst1 75 new 69 java.lang.Boolean 78 dup 79 iload2 80 - * invokespecial 110 java.lang.Boolean.(Z)V 83 aastore - */ - private int genInvokeArgs(Class[] argTypes) { - int argByteOffset = 1; // remember h is at position 0 - int length = argTypes.length; - if (length == 0) { - writeUnsignedByte(OPC_aconst_null); - } else { - writeIntConstant(length); - writeUnsignedByte(OPC_anewarray); - writeUnsignedShort(constantPool.typeIndex("java/lang/Object")); - for (int i = 0; i < length; i++) { - writeUnsignedByte(OPC_dup); - writeIntConstant(i); - argByteOffset = genInvokeArg(argTypes[i], argByteOffset); - writeUnsignedByte(OPC_aastore); - } - } - return argByteOffset; - } - - private int genInvokeArg(Class type, int argByteOffset) { - // offset represents maxLocals in bytes - if (type.isPrimitive()) { - writeUnsignedByte(OPC_new); - writeUnsignedShort(constantPool.typeIndex(typeWrapperName(type))); - writeUnsignedByte(OPC_dup); - if (argByteOffset > 255) { - writeUnsignedByte(OPC_wide); - } - if (type == long.class) { - switch (argByteOffset) { - case 0: - writeUnsignedByte(OPC_lload_0); - break; - case 1: - writeUnsignedByte(OPC_lload_1); - break; - case 2: - writeUnsignedByte(OPC_lload_2); - break; - case 3: - writeUnsignedByte(OPC_lload_3); - break; - default: - writeUnsignedByte(OPC_lload); - if (argByteOffset > 255) { - writeUnsignedShort(argByteOffset); - } else { - writeUnsignedByte(argByteOffset); - } - } - argByteOffset += 2; - } else if (type == float.class) { - switch (argByteOffset) { - case 0: - writeUnsignedByte(OPC_fload_0); - break; - case 1: - writeUnsignedByte(OPC_fload_1); - break; - case 2: - writeUnsignedByte(OPC_fload_2); - break; - case 3: - writeUnsignedByte(OPC_fload_3); - break; - default: - writeUnsignedByte(OPC_fload); - if (argByteOffset > 255) { - writeUnsignedShort(argByteOffset); - } else { - writeUnsignedByte(argByteOffset); - } - } - argByteOffset++; - } else if (type == double.class) { - switch (argByteOffset) { - case 0: - writeUnsignedByte(OPC_dload_0); - break; - case 1: - writeUnsignedByte(OPC_dload_1); - break; - case 2: - writeUnsignedByte(OPC_dload_2); - break; - case 3: - writeUnsignedByte(OPC_dload_3); - break; - default: - writeUnsignedByte(OPC_dload); - if (argByteOffset > 255) { - writeUnsignedShort(argByteOffset); - } else { - writeUnsignedByte(argByteOffset); - } - } - argByteOffset += 2; - } else { // handles int, short, byte, boolean & char - switch (argByteOffset) { - case 0: - writeUnsignedByte(OPC_iload_0); - break; - case 1: - writeUnsignedByte(OPC_iload_1); - break; - case 2: - writeUnsignedByte(OPC_iload_2); - break; - case 3: - writeUnsignedByte(OPC_iload_3); - break; - default: - writeUnsignedByte(OPC_iload); - if (argByteOffset > 255) { - writeUnsignedShort(argByteOffset); - } else { - writeUnsignedByte(argByteOffset); - } - } - argByteOffset++; - } - writeUnsignedByte(OPC_invokespecial); - writeUnsignedShort(constantPool.literalIndex(typeInitMethod(type))); - } else { - genLoadArg(argByteOffset); - argByteOffset++; - } - return argByteOffset; - } - - /** - * 94 checkcast 69 java.lang.Boolean 97 invokevirtual 73 - * java.lang.Boolean.booleanValue()Z 100 ireturn or 52 checkcast 91 - * java.lang.String 55 areturn - */ - private void genCastReturnType(Class type) { - if (type.isPrimitive()) { - if (type == void.class) { - writeUnsignedByte(OPC_pop); - writeUnsignedByte(OPC_return); - } else { - writeUnsignedByte(OPC_checkcast); - writeUnsignedShort(constantPool - .typeIndex(typeWrapperName(type))); - writeUnsignedByte(OPC_invokevirtual); - writeUnsignedShort(constantPool - .literalIndex(typeAccessMethod(type))); - if (type == long.class) { - writeUnsignedByte(OPC_lreturn); - } else if (type == float.class) { - writeUnsignedByte(OPC_freturn); - } else if (type == double.class) { - writeUnsignedByte(OPC_dreturn); - } else { // handles int, short, byte, boolean & char - writeUnsignedByte(OPC_ireturn); - } - } - } else { - writeUnsignedByte(OPC_checkcast); - writeUnsignedShort(constantPool.typeIndex(type.getName())); - writeUnsignedByte(OPC_areturn); - } - } - - private void genCallClassForName(String typeName) { - writeLdc(typeName); - writeUnsignedByte(OPC_invokestatic); - if (ClassForNameMethod == null) { - try { - ClassForNameMethod = Class.class.getMethod("forName", - new Class[] { String.class }); - } catch (NoSuchMethodException e) { - throw new InternalError(); - } - } - writeUnsignedShort(constantPool.literalIndex(ClassForNameMethod)); - } - - private void genLoadArg(int argByteOffset) { - if (argByteOffset > 255) { - writeUnsignedByte(OPC_wide); - writeUnsignedByte(OPC_aload); - writeUnsignedShort(argByteOffset); - } else { - switch (argByteOffset) { - case 0: - writeUnsignedByte(OPC_aload_0); - break; - case 1: - writeUnsignedByte(OPC_aload_1); - break; - case 2: - writeUnsignedByte(OPC_aload_2); - break; - case 3: - writeUnsignedByte(OPC_aload_3); - break; - default: - writeUnsignedByte(OPC_aload); - writeUnsignedByte(argByteOffset); - } - } - } - - private void genStoreArg(int argByteOffset) { - if (argByteOffset > 255) { - writeUnsignedByte(OPC_wide); - writeUnsignedByte(OPC_astore); - writeUnsignedShort(argByteOffset); - } else { - switch (argByteOffset) { - case 0: - writeUnsignedByte(OPC_astore_0); - break; - case 1: - writeUnsignedByte(OPC_astore_1); - break; - case 2: - writeUnsignedByte(OPC_astore_2); - break; - case 3: - writeUnsignedByte(OPC_astore_3); - break; - default: - writeUnsignedByte(OPC_astore); - writeUnsignedByte(argByteOffset); - } - } - } - - private byte[] getBytes() { - byte[] fullContents = new byte[headerOffset + contentsOffset]; - System.arraycopy(header, 0, fullContents, 0, headerOffset); - System.arraycopy(contents, 0, fullContents, headerOffset, - contentsOffset); - return fullContents; - } - - private Method typeAccessMethod(Class baseType) { - try { - if (baseType == int.class) { - return Integer.class.getMethod("intValue", (Class[]) null); - } - if (baseType == short.class) { - return Short.class.getMethod("shortValue", (Class[]) null); - } - if (baseType == byte.class) { - return Byte.class.getMethod("byteValue", (Class[]) null); - } - if (baseType == boolean.class) { - return Boolean.class.getMethod("booleanValue", (Class[]) null); - } - if (baseType == char.class) { - return Character.class.getMethod("charValue", (Class[]) null); - } - if (baseType == long.class) { - return Long.class.getMethod("longValue", (Class[]) null); - } - if (baseType == float.class) { - return Float.class.getMethod("floatValue", (Class[]) null); - } - if (baseType == double.class) { - return Double.class.getMethod("doubleValue", (Class[]) null); - } - } catch (NoSuchMethodException e) { - throw new InternalError(); - } - return null; - } - - private Field typeField(Class baseType) { - try { - if (baseType == int.class) { - return Integer.class.getField("TYPE"); - } - if (baseType == short.class) { - return Short.class.getField("TYPE"); - } - if (baseType == byte.class) { - return Byte.class.getField("TYPE"); - } - if (baseType == boolean.class) { - return Boolean.class.getField("TYPE"); - } - if (baseType == char.class) { - return Character.class.getField("TYPE"); - } - if (baseType == long.class) { - return Long.class.getField("TYPE"); - } - if (baseType == float.class) { - return Float.class.getField("TYPE"); - } - if (baseType == double.class) { - return Double.class.getField("TYPE"); - } - } catch (NoSuchFieldException e) { - throw new InternalError(); - } - return null; - } - - private Constructor typeInitMethod(Class baseType) { - try { - if (baseType == int.class) { - return Integer.class.getConstructor(new Class[] { int.class }); - } - if (baseType == short.class) { - return Short.class.getConstructor(new Class[] { short.class }); - } - if (baseType == byte.class) { - return Byte.class.getConstructor(new Class[] { byte.class }); - } - if (baseType == boolean.class) { - return Boolean.class - .getConstructor(new Class[] { boolean.class }); - } - if (baseType == char.class) { - return Character.class - .getConstructor(new Class[] { char.class }); - } - if (baseType == long.class) { - return Long.class.getConstructor(new Class[] { long.class }); - } - if (baseType == float.class) { - return Float.class.getConstructor(new Class[] { float.class }); - } - if (baseType == double.class) { - return Double.class - .getConstructor(new Class[] { double.class }); - } - } catch (NoSuchMethodException e) { - throw new InternalError(); - } - return null; - } - - private String typeWrapperName(Class baseType) { - if (baseType == int.class) { - return "java/lang/Integer"; - } - if (baseType == short.class) { - return "java/lang/Short"; - } - if (baseType == byte.class) { - return "java/lang/Byte"; - } - if (baseType == boolean.class) { - return "java/lang/Boolean"; - } - if (baseType == char.class) { - return "java/lang/Character"; - } - if (baseType == long.class) { - return "java/lang/Long"; - } - if (baseType == float.class) { - return "java/lang/Float"; - } - if (baseType == double.class) { - return "java/lang/Double"; - } - return null; - } - - private void writeIntConstant(int b) { - switch (b) { - case 0: - writeUnsignedByte(OPC_iconst_0); - break; - case 1: - writeUnsignedByte(OPC_iconst_1); - break; - case 2: - writeUnsignedByte(OPC_iconst_2); - break; - case 3: - writeUnsignedByte(OPC_iconst_3); - break; - case 4: - writeUnsignedByte(OPC_iconst_4); - break; - case 5: - writeUnsignedByte(OPC_iconst_5); - break; - default: - writeUnsignedByte(OPC_bipush); - writeUnsignedByte(b); - } - } - - private void writeLdc(String constant) { - int index = constantPool.literalIndexForLdc(constant.toCharArray()); - if (index <= 0) { - throw new InternalError(); - } - if (index > 255) { - writeUnsignedByte(OPC_ldc_w); - writeUnsignedShort(index); - } else { - writeUnsignedByte(OPC_ldc); - writeUnsignedByte(index); - } - } - - private void writeUnsignedByte(int b) { - try { - contents[contentsOffset++] = (byte) b; - } catch (IndexOutOfBoundsException e) { - int actualLength = contents.length; - System.arraycopy(contents, 0, (contents = new byte[actualLength - + INCREMENT_SIZE]), 0, actualLength); - contents[contentsOffset - 1] = (byte) b; - } - } - - private void writeUnsignedShort(int b) { - writeUnsignedByte(b >>> 8); - writeUnsignedByte(b); - } - - private void writeUnsignedWord(int b) { - writeUnsignedByte(b >>> 24); - writeUnsignedByte(b >>> 16); - writeUnsignedByte(b >>> 8); - writeUnsignedByte(b); - } -} diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/reflect/ProxyConstantPool.java b/libcore/luni/src/main/java/org/apache/harmony/luni/internal/reflect/ProxyConstantPool.java deleted file mode 100644 index 3789730f5..000000000 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/reflect/ProxyConstantPool.java +++ /dev/null @@ -1,312 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.harmony.luni.internal.reflect; - - -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.Method; - -class ProxyConstantPool implements ProxyConstants { - public static final int UTF8_INITIAL_SIZE = 50; - - public static final int STRING_INITIAL_SIZE = 21; - - public static final int FIELD_INITIAL_SIZE = 7; - - public static final int METHOD_INITIAL_SIZE = 21; - - public static final int INTERFACE_INITIAL_SIZE = 21; - - public static final int CLASS_INITIAL_SIZE = 21; - - public static final int NAMEANDTYPE_INITIAL_SIZE = 21; - - public static final int CONSTANTPOOL_INITIAL_SIZE = 500; - - public static final int CONSTANTPOOL_GROW_SIZE = 1000; - - ProxyCharArrayCache UTF8Cache; - - ProxyCharArrayCache stringCache; - - ProxyCharArrayCache classNameCache; - - ProxyObjectCache fieldCache; - - ProxyObjectCache methodCache; - - ProxyObjectCache interfaceMethodCache; - - ProxyNameAndTypeCache nameAndTypeCache; - - byte[] poolContent; - - int currentIndex; - - int currentOffset; - - ProxyConstantPool(ProxyClassFile classFile) { - UTF8Cache = new ProxyCharArrayCache(UTF8_INITIAL_SIZE); - stringCache = new ProxyCharArrayCache(STRING_INITIAL_SIZE); - classNameCache = new ProxyCharArrayCache(CLASS_INITIAL_SIZE); - fieldCache = new ProxyObjectCache(FIELD_INITIAL_SIZE); - methodCache = new ProxyObjectCache(METHOD_INITIAL_SIZE); - interfaceMethodCache = new ProxyObjectCache(INTERFACE_INITIAL_SIZE); - nameAndTypeCache = new ProxyNameAndTypeCache(NAMEANDTYPE_INITIAL_SIZE); - poolContent = classFile.header; - currentOffset = classFile.headerOffset; - currentIndex = 1; - } - - int literalIndex(char[] utf8Constant) { - int index; - if ((index = UTF8Cache.get(utf8Constant)) < 0) { - writeU1(Utf8Tag); - int savedCurrentOffset = currentOffset; - if (currentOffset + 2 >= poolContent.length) { - int length = poolContent.length; - System.arraycopy(poolContent, 0, (poolContent = new byte[length - + CONSTANTPOOL_GROW_SIZE]), 0, length); - } - currentOffset += 2; - int length = 0; - for (int i = 0; i < utf8Constant.length; i++) { - char current = utf8Constant[i]; - if ((current >= 0x0001) && (current <= 0x007F)) { - // we only need one byte: ASCII table - writeU1(current); - length++; - } else if (current > 0x07FF) { - // we need 3 bytes - length += 3; - writeU1(0xE0 | ((current >> 12) & 0x0F)); // 0xE0 = 1110 - // 0000 - writeU1(0x80 | ((current >> 6) & 0x3F)); // 0x80 = 1000 - // 0000 - writeU1(0x80 | (current & 0x3F)); // 0x80 = 1000 0000 - } else { - // we can be 0 or between 0x0080 and 0x07FF - // In that case we only need 2 bytes - length += 2; - writeU1(0xC0 | ((current >> 6) & 0x1F)); // 0xC0 = 1100 - // 0000 - writeU1(0x80 | (current & 0x3F)); // 0x80 = 1000 0000 - } - } - if (length >= 65535) { - currentOffset = savedCurrentOffset - 1; - return -1; - } - index = UTF8Cache.put(utf8Constant, currentIndex++); - // Now we know the length that we have to write in the constant pool - // we use savedCurrentOffset to do that - poolContent[savedCurrentOffset] = (byte) (length >> 8); - poolContent[savedCurrentOffset + 1] = (byte) length; - } - return index; - } - - int literalIndex(Field aField) { - int index; - if ((index = fieldCache.get(aField)) < 0) { - int classIndex = typeIndex(aField.getDeclaringClass().getName()); - int nameAndTypeIndex = literalIndexForNameAndType( - literalIndex(aField.getName().toCharArray()), - literalIndex(ProxyClassFile.getConstantPoolName(aField - .getType()))); - index = fieldCache.put(aField, currentIndex++); - writeU1(FieldRefTag); - writeU2(classIndex); - writeU2(nameAndTypeIndex); - } - return index; - } - - int literalIndex(Constructor aMethod) { - int index; - if ((index = methodCache.get(aMethod)) < 0) { - int classIndex = typeIndex(aMethod.getDeclaringClass().getName()); - int nameAndTypeIndex = literalIndexForNameAndType( - literalIndex(Init), literalIndex(ProxyClassFile - .getConstantPoolName(aMethod))); - index = methodCache.put(aMethod, currentIndex++); - writeU1(MethodRefTag); - writeU2(classIndex); - writeU2(nameAndTypeIndex); - } - return index; - } - - int literalIndex(Method aMethod) { - int index; - if (aMethod.getDeclaringClass().isInterface()) { - if ((index = interfaceMethodCache.get(aMethod)) < 0) { - int classIndex = typeIndex(aMethod.getDeclaringClass() - .getName()); - int nameAndTypeIndex = literalIndexForNameAndType( - literalIndex(aMethod.getName().toCharArray()), - literalIndex(ProxyClassFile - .getConstantPoolName(aMethod))); - index = interfaceMethodCache.put(aMethod, currentIndex++); - writeU1(InterfaceMethodRefTag); - writeU2(classIndex); - writeU2(nameAndTypeIndex); - } - } else if ((index = methodCache.get(aMethod)) < 0) { - int classIndex = typeIndex(aMethod.getDeclaringClass().getName()); - int nameAndTypeIndex = literalIndexForNameAndType( - literalIndex(aMethod.getName().toCharArray()), - literalIndex(ProxyClassFile.getConstantPoolName(aMethod))); - index = methodCache.put(aMethod, currentIndex++); - writeU1(MethodRefTag); - writeU2(classIndex); - writeU2(nameAndTypeIndex); - } - return index; - } - - int literalIndex(String stringConstant) { - int index; - char[] stringCharArray = stringConstant.toCharArray(); - if ((index = stringCache.get(stringCharArray)) < 0) { - int stringIndex = literalIndex(stringCharArray); - index = stringCache.put(stringCharArray, currentIndex++); - writeU1(StringTag); - writeU2(stringIndex); - } - return index; - } - - int literalIndexForLdc(char[] stringCharArray) { - int index; - if ((index = stringCache.get(stringCharArray)) < 0) { - int stringIndex; - if ((stringIndex = UTF8Cache.get(stringCharArray)) < 0) { - writeU1(Utf8Tag); - int savedCurrentOffset = currentOffset; - if (currentOffset + 2 >= poolContent.length) { - int length = poolContent.length; - System.arraycopy(poolContent, 0, - (poolContent = new byte[length - + CONSTANTPOOL_GROW_SIZE]), 0, length); - } - currentOffset += 2; - int length = 0; - for (int i = 0; i < stringCharArray.length; i++) { - char current = stringCharArray[i]; - if ((current >= 0x0001) && (current <= 0x007F)) { - // we only need one byte: ASCII table - writeU1(current); - length++; - } else if (current > 0x07FF) { - // we need 3 bytes - length += 3; - writeU1(0xE0 | ((current >> 12) & 0x0F)); // 0xE0 = - // 1110 0000 - writeU1(0x80 | ((current >> 6) & 0x3F)); // 0x80 = - // 1000 0000 - writeU1(0x80 | (current & 0x3F)); // 0x80 = 1000 0000 - } else { - // we can be 0 or between 0x0080 and 0x07FF - // In that case we only need 2 bytes - length += 2; - writeU1(0xC0 | ((current >> 6) & 0x1F)); // 0xC0 = - // 1100 0000 - writeU1(0x80 | (current & 0x3F)); // 0x80 = 1000 0000 - } - } - if (length >= 65535) { - currentOffset = savedCurrentOffset - 1; - return -1; - } - stringIndex = UTF8Cache.put(stringCharArray, currentIndex++); - // Now we know the length that we have to write in the constant - // pool - // we use savedCurrentOffset to do that - if (length > 65535) - return 0; - poolContent[savedCurrentOffset] = (byte) (length >> 8); - poolContent[savedCurrentOffset + 1] = (byte) length; - } - index = stringCache.put(stringCharArray, currentIndex++); - writeU1(StringTag); - writeU2(stringIndex); - } - return index; - } - - private int literalIndexForNameAndType(int nameIndex, int typeIndex) { - int index; - int[] key = new int[] { nameIndex, typeIndex }; - if ((index = nameAndTypeCache.get(key)) == -1) { - index = nameAndTypeCache.put(key, currentIndex++); - writeU1(NameAndTypeTag); - writeU2(nameIndex); - writeU2(typeIndex); - } - return index; - } - - int typeIndex(String typeName) { - int index; - if (typeName.indexOf('.') != -1) - typeName = typeName.replace('.', '/'); - char[] charArray = typeName.toCharArray(); - if ((index = classNameCache.get(charArray)) < 0) { - int nameIndex = literalIndex(charArray); - index = classNameCache.put(charArray, currentIndex++); - writeU1(ClassTag); - writeU2(nameIndex); - } - return index; - } - - private final void writeU1(int value) { - try { - poolContent[currentOffset++] = (byte) value; - } catch (IndexOutOfBoundsException e) { - // currentOffset has been ++ already (see the -1) - int length = poolContent.length; - System.arraycopy(poolContent, 0, (poolContent = new byte[length - + CONSTANTPOOL_GROW_SIZE]), 0, length); - poolContent[currentOffset - 1] = (byte) value; - } - } - - private final void writeU2(int value) { - try { - poolContent[currentOffset++] = (byte) (value >> 8); - } catch (IndexOutOfBoundsException e) { - // currentOffset has been ++ already (see the -1) - int length = poolContent.length; - System.arraycopy(poolContent, 0, (poolContent = new byte[length - + CONSTANTPOOL_GROW_SIZE]), 0, length); - poolContent[currentOffset - 1] = (byte) (value >> 8); - } - try { - poolContent[currentOffset++] = (byte) value; - } catch (IndexOutOfBoundsException e) { - // currentOffset has been ++ already (see the -1) - int length = poolContent.length; - System.arraycopy(poolContent, 0, (poolContent = new byte[length - + CONSTANTPOOL_GROW_SIZE]), 0, length); - poolContent[currentOffset - 1] = (byte) value; - } - } -} diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/reflect/ProxyConstants.java b/libcore/luni/src/main/java/org/apache/harmony/luni/internal/reflect/ProxyConstants.java deleted file mode 100644 index 93db39443..000000000 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/reflect/ProxyConstants.java +++ /dev/null @@ -1,464 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.harmony.luni.internal.reflect; - - -interface ProxyConstants { - final char[] CodeName = new char[] { 'C', 'o', 'd', 'e' }; - - final char[] ExceptionsName = new char[] { 'E', 'x', 'c', 'e', 'p', 't', - 'i', 'o', 'n', 's' }; - - final char[] Init = new char[] { '<', 'i', 'n', 'i', 't', '>' }; - - final int AccPublic = 0x0001; - - final int AccPrivate = 0x0002; - - final int AccProtected = 0x0004; - - final int AccStatic = 0x0008; - - final int AccFinal = 0x0010; - - final int AccSuper = 0x0020; - - final int Utf8Tag = 1; - - final int IntegerTag = 3; - - final int FloatTag = 4; - - final int LongTag = 5; - - final int DoubleTag = 6; - - final int ClassTag = 7; - - final int StringTag = 8; - - final int FieldRefTag = 9; - - final int MethodRefTag = 10; - - final int InterfaceMethodRefTag = 11; - - final int NameAndTypeTag = 12; - - final int OPC_nop = 0; - - final int OPC_aconst_null = 1; - - final int OPC_iconst_m1 = 2; - - final int OPC_iconst_0 = 3; - - final int OPC_iconst_1 = 4; - - final int OPC_iconst_2 = 5; - - final int OPC_iconst_3 = 6; - - final int OPC_iconst_4 = 7; - - final int OPC_iconst_5 = 8; - - final int OPC_lconst_0 = 9; - - final int OPC_lconst_1 = 10; - - final int OPC_fconst_0 = 11; - - final int OPC_fconst_1 = 12; - - final int OPC_fconst_2 = 13; - - final int OPC_dconst_0 = 14; - - final int OPC_dconst_1 = 15; - - final int OPC_bipush = 16; - - final int OPC_sipush = 17; - - final int OPC_ldc = 18; - - final int OPC_ldc_w = 19; - - final int OPC_ldc2_w = 20; - - final int OPC_iload = 21; - - final int OPC_lload = 22; - - final int OPC_fload = 23; - - final int OPC_dload = 24; - - final int OPC_aload = 25; - - final int OPC_iload_0 = 26; - - final int OPC_iload_1 = 27; - - final int OPC_iload_2 = 28; - - final int OPC_iload_3 = 29; - - final int OPC_lload_0 = 30; - - final int OPC_lload_1 = 31; - - final int OPC_lload_2 = 32; - - final int OPC_lload_3 = 33; - - final int OPC_fload_0 = 34; - - final int OPC_fload_1 = 35; - - final int OPC_fload_2 = 36; - - final int OPC_fload_3 = 37; - - final int OPC_dload_0 = 38; - - final int OPC_dload_1 = 39; - - final int OPC_dload_2 = 40; - - final int OPC_dload_3 = 41; - - final int OPC_aload_0 = 42; - - final int OPC_aload_1 = 43; - - final int OPC_aload_2 = 44; - - final int OPC_aload_3 = 45; - - final int OPC_iaload = 46; - - final int OPC_laload = 47; - - final int OPC_faload = 48; - - final int OPC_daload = 49; - - final int OPC_aaload = 50; - - final int OPC_baload = 51; - - final int OPC_caload = 52; - - final int OPC_saload = 53; - - final int OPC_istore = 54; - - final int OPC_lstore = 55; - - final int OPC_fstore = 56; - - final int OPC_dstore = 57; - - final int OPC_astore = 58; - - final int OPC_istore_0 = 59; - - final int OPC_istore_1 = 60; - - final int OPC_istore_2 = 61; - - final int OPC_istore_3 = 62; - - final int OPC_lstore_0 = 63; - - final int OPC_lstore_1 = 64; - - final int OPC_lstore_2 = 65; - - final int OPC_lstore_3 = 66; - - final int OPC_fstore_0 = 67; - - final int OPC_fstore_1 = 68; - - final int OPC_fstore_2 = 69; - - final int OPC_fstore_3 = 70; - - final int OPC_dstore_0 = 71; - - final int OPC_dstore_1 = 72; - - final int OPC_dstore_2 = 73; - - final int OPC_dstore_3 = 74; - - final int OPC_astore_0 = 75; - - final int OPC_astore_1 = 76; - - final int OPC_astore_2 = 77; - - final int OPC_astore_3 = 78; - - final int OPC_iastore = 79; - - final int OPC_lastore = 80; - - final int OPC_fastore = 81; - - final int OPC_dastore = 82; - - final int OPC_aastore = 83; - - final int OPC_bastore = 84; - - final int OPC_castore = 85; - - final int OPC_sastore = 86; - - final int OPC_pop = 87; - - final int OPC_pop2 = 88; - - final int OPC_dup = 89; - - final int OPC_dup_x1 = 90; - - final int OPC_dup_x2 = 91; - - final int OPC_dup2 = 92; - - final int OPC_dup2_x1 = 93; - - final int OPC_dup2_x2 = 94; - - final int OPC_swap = 95; - - final int OPC_iadd = 96; - - final int OPC_ladd = 97; - - final int OPC_fadd = 98; - - final int OPC_dadd = 99; - - final int OPC_isub = 100; - - final int OPC_lsub = 101; - - final int OPC_fsub = 102; - - final int OPC_dsub = 103; - - final int OPC_imul = 104; - - final int OPC_lmul = 105; - - final int OPC_fmul = 106; - - final int OPC_dmul = 107; - - final int OPC_idiv = 108; - - final int OPC_ldiv = 109; - - final int OPC_fdiv = 110; - - final int OPC_ddiv = 111; - - final int OPC_irem = 112; - - final int OPC_lrem = 113; - - final int OPC_frem = 114; - - final int OPC_drem = 115; - - final int OPC_ineg = 116; - - final int OPC_lneg = 117; - - final int OPC_fneg = 118; - - final int OPC_dneg = 119; - - final int OPC_ishl = 120; - - final int OPC_lshl = 121; - - final int OPC_ishr = 122; - - final int OPC_lshr = 123; - - final int OPC_iushr = 124; - - final int OPC_lushr = 125; - - final int OPC_iand = 126; - - final int OPC_land = 127; - - final int OPC_ior = 128; - - final int OPC_lor = 129; - - final int OPC_ixor = 130; - - final int OPC_lxor = 131; - - final int OPC_iinc = 132; - - final int OPC_i2l = 133; - - final int OPC_i2f = 134; - - final int OPC_i2d = 135; - - final int OPC_l2i = 136; - - final int OPC_l2f = 137; - - final int OPC_l2d = 138; - - final int OPC_f2i = 139; - - final int OPC_f2l = 140; - - final int OPC_f2d = 141; - - final int OPC_d2i = 142; - - final int OPC_d2l = 143; - - final int OPC_d2f = 144; - - final int OPC_i2b = 145; - - final int OPC_i2c = 146; - - final int OPC_i2s = 147; - - final int OPC_lcmp = 148; - - final int OPC_fcmpl = 149; - - final int OPC_fcmpg = 150; - - final int OPC_dcmpl = 151; - - final int OPC_dcmpg = 152; - - final int OPC_ifeq = 153; - - final int OPC_ifne = 154; - - final int OPC_iflt = 155; - - final int OPC_ifge = 156; - - final int OPC_ifgt = 157; - - final int OPC_ifle = 158; - - final int OPC_if_icmpeq = 159; - - final int OPC_if_icmpne = 160; - - final int OPC_if_icmplt = 161; - - final int OPC_if_icmpge = 162; - - final int OPC_if_icmpgt = 163; - - final int OPC_if_icmple = 164; - - final int OPC_if_acmpeq = 165; - - final int OPC_if_acmpne = 166; - - final int OPC_goto = 167; - - final int OPC_jsr = 168; - - final int OPC_ret = 169; - - final int OPC_tableswitch = 170; - - final int OPC_lookupswitch = 171; - - final int OPC_ireturn = 172; - - final int OPC_lreturn = 173; - - final int OPC_freturn = 174; - - final int OPC_dreturn = 175; - - final int OPC_areturn = 176; - - final int OPC_return = 177; - - final int OPC_getstatic = 178; - - final int OPC_putstatic = 179; - - final int OPC_getfield = 180; - - final int OPC_putfield = 181; - - final int OPC_invokevirtual = 182; - - final int OPC_invokespecial = 183; - - final int OPC_invokestatic = 184; - - final int OPC_invokeinterface = 185; - - final int OPC_new = 187; - - final int OPC_newarray = 188; - - final int OPC_anewarray = 189; - - final int OPC_arraylength = 190; - - final int OPC_athrow = 191; - - final int OPC_checkcast = 192; - - final int OPC_instanceof = 193; - - final int OPC_monitorenter = 194; - - final int OPC_monitorexit = 195; - - final int OPC_wide = 196; - - final int OPC_multianewarray = 197; - - final int OPC_ifnull = 198; - - final int OPC_ifnonnull = 199; - - final int OPC_goto_w = 200; - - final int OPC_jsr_w = 201; -} diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/reflect/ProxyMethod.java b/libcore/luni/src/main/java/org/apache/harmony/luni/internal/reflect/ProxyMethod.java deleted file mode 100644 index cd112b57d..000000000 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/reflect/ProxyMethod.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.harmony.luni.internal.reflect; - -import java.lang.reflect.Method; - -import org.apache.harmony.luni.util.Msg; - -class ProxyMethod { - Method method; - - Class[] commonExceptions; - - ProxyMethod(Method method) { - this.method = method; - this.commonExceptions = method.getExceptionTypes(); - } - - Class[] getCheckedExceptions() { - Class[] newExceptions = commonExceptions.clone(); - int cLength = newExceptions.length; - for (int c = 0, cL = cLength; c < cL; c++) { - Class ex = newExceptions[c]; - if (Throwable.class == ex) { - // if Throwable is included then treat as if no exceptions are - // checked - return new Class[0]; - } - if (Error.class.isAssignableFrom(ex) || RuntimeException.class.isAssignableFrom(ex)) { - newExceptions[c] = null; - cLength--; - } - } - - // All errors & runtime exceptions are passed back without being wrapped - Class[] result = new Class[cLength + 2]; - int index = 0; - result[index++] = Error.class; - result[index++] = RuntimeException.class; - for (Class ex : newExceptions) { - if (ex != null) { - result[index++] = ex; - } - } - return result; - } - - boolean matchMethod(Method otherMethod) { - if (!method.getName().equals(otherMethod.getName())) { - return false; - } - - Class[] params1 = method.getParameterTypes(); - Class[] params2 = otherMethod.getParameterTypes(); - int p = params1.length; - if (p != params2.length) { - return false; - } - while (--p >= 0) { - if (params1[p] != params2[p]) { - return false; - } - } - - Class thisMethodReturnType = method.getReturnType(); - Class otherMethodReturnType = otherMethod.getReturnType(); - if (!thisMethodReturnType.isAssignableFrom(otherMethodReturnType)) { - if (otherMethodReturnType.isAssignableFrom(thisMethodReturnType)) { - // substitute returnType of method with that of otherMethod - method = otherMethod; - } else { - throw new IllegalArgumentException(Msg.getString("K00f2", - method.getName())); - } - } - - if (commonExceptions.length != 0) { - Class[] otherExceptions = otherMethod.getExceptionTypes(); - if (otherExceptions.length == 0) { - commonExceptions = otherExceptions; - } else { - int cLength = commonExceptions.length; - nextException: for (int c = 0, cL = cLength, oL = otherExceptions.length; c < cL; c++) { - Class cException = commonExceptions[c]; - for (int o = 0; o < oL; o++) { - Class oException = otherExceptions[o]; - if (cException == oException) { - continue nextException; - } - if (oException.isAssignableFrom(cException)) { - continue nextException; // cException is a subclass - } - if (cException.isAssignableFrom(oException)) { - // oException is a subclass, keep it instead - commonExceptions[c] = cException = oException; - continue nextException; - } - } - commonExceptions[c] = null; - cLength--; - } - if (cLength != commonExceptions.length) { - Class[] newExceptions = new Class[cLength]; - for (int i = 0, j = 0, length = commonExceptions.length; i < length; i++) { - Class ex = commonExceptions[i]; - if (ex != null) { - newExceptions[j++] = ex; - } - } - commonExceptions = newExceptions; - } - } - } - return true; - } - -} diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/reflect/ProxyNameAndTypeCache.java b/libcore/luni/src/main/java/org/apache/harmony/luni/internal/reflect/ProxyNameAndTypeCache.java deleted file mode 100644 index 09eb23e64..000000000 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/reflect/ProxyNameAndTypeCache.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.harmony.luni.internal.reflect; - -class ProxyNameAndTypeCache { - int[][] keyTable; - - int[] valueTable; - - int elementSize; - - int threshold; - - ProxyNameAndTypeCache(int initialCapacity) { - if (initialCapacity < 13) { - initialCapacity = 13; - } - this.elementSize = 0; - this.threshold = (int) (initialCapacity * 0.66f); - this.keyTable = new int[initialCapacity][]; - this.valueTable = new int[initialCapacity]; - } - - int get(int[] key) { - int index = hashCode(key); - while (keyTable[index] != null) { - if (keyTable[index][0] == key[0] && keyTable[index][1] == key[1]) { - return valueTable[index]; - } - index = (index + 1) % keyTable.length; - } - return -1; - } - - int hashCode(int[] key) { - return (key[0] + key[1]) % keyTable.length; - } - - int put(int[] key, int value) { - int index = hashCode(key); - while (keyTable[index] != null) { - if (keyTable[index][0] == key[0] && keyTable[index][1] == key[1]) { - return valueTable[index] = value; - } - index = (index + 1) % keyTable.length; - } - keyTable[index] = key; - valueTable[index] = value; - - // assumes the threshold is never equal to the size of the table - if (++elementSize > threshold) { - rehash(); - } - return value; - } - - private void rehash() { - ProxyNameAndTypeCache newHashtable = new ProxyNameAndTypeCache(keyTable.length * 2); - for (int i = keyTable.length; --i >= 0;) { - if (keyTable[i] != null) { - newHashtable.put(keyTable[i], valueTable[i]); - } - } - - this.keyTable = newHashtable.keyTable; - this.valueTable = newHashtable.valueTable; - this.threshold = newHashtable.threshold; - } - - int size() { - return elementSize; - } - - @Override - public String toString() { - int max = size(); - StringBuilder buf = new StringBuilder(); - buf.append("{"); - for (int i = 0; i < max; ++i) { - if (keyTable[i] != null) { - buf.append(keyTable[i]).append("->").append(valueTable[i]); - } - if (i < max) { - buf.append(", "); - } - } - buf.append("}"); - return buf.toString(); - } -} diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/reflect/ProxyObjectCache.java b/libcore/luni/src/main/java/org/apache/harmony/luni/internal/reflect/ProxyObjectCache.java deleted file mode 100644 index b3089c5e2..000000000 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/internal/reflect/ProxyObjectCache.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.harmony.luni.internal.reflect; - -class ProxyObjectCache { - Object keyTable[]; - - int valueTable[]; - - int elementSize; - - int threshold; - - ProxyObjectCache(int initialCapacity) { - if (initialCapacity < 13) { - initialCapacity = 13; - } - this.elementSize = 0; - this.threshold = (int) (initialCapacity * 0.66f); - this.keyTable = new Object[initialCapacity]; - this.valueTable = new int[initialCapacity]; - } - - int get(Object key) { - int index = hashCode(key); - while (keyTable[index] != null) { - if (keyTable[index].equals(key)) { - return valueTable[index]; - } - index = (index + 1) % keyTable.length; - } - return -1; - } - - int hashCode(Object key) { - return (key.hashCode() & 0x7FFFFFFF) % keyTable.length; - } - - int put(Object key, int value) { - int index = hashCode(key); - while (keyTable[index] != null) { - if (keyTable[index].equals(key)) { - return valueTable[index] = value; - } - index = (index + 1) % keyTable.length; - } - keyTable[index] = key; - valueTable[index] = value; - - // assumes the threshold is never equal to the size of the table - if (++elementSize > threshold) { - rehash(); - } - return value; - } - - private void rehash() { - ProxyObjectCache newHashtable = new ProxyObjectCache( - keyTable.length * 2); - for (int i = keyTable.length; --i >= 0;) { - if (keyTable[i] != null) { - newHashtable.put(keyTable[i], valueTable[i]); - } - } - - this.keyTable = newHashtable.keyTable; - this.valueTable = newHashtable.valueTable; - this.threshold = newHashtable.threshold; - } - - int size() { - return elementSize; - } - - @Override - public String toString() { - int max = size(); - StringBuilder buf = new StringBuilder(); - buf.append("{"); //$NON-NLS-1$ - for (int i = 0; i < max; ++i) { - if (keyTable[i] != null) { - buf.append(keyTable[i]).append("->").append(valueTable[i]); //$NON-NLS-1$ - } - if (i < max) { - buf.append(", "); //$NON-NLS-1$ - } - } - buf.append("}"); //$NON-NLS-1$ - return buf.toString(); - } -} diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/net/PlainDatagramSocketImpl.java b/libcore/luni/src/main/java/org/apache/harmony/luni/net/PlainDatagramSocketImpl.java index 230df1363..ac0c877fd 100644 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/net/PlainDatagramSocketImpl.java +++ b/libcore/luni/src/main/java/org/apache/harmony/luni/net/PlainDatagramSocketImpl.java @@ -42,7 +42,7 @@ import org.apache.harmony.luni.util.PriviAction; * support security checks. Alternative types of DatagramSocketImpl's may be * used by setting the impl.prefix system property. */ -class PlainDatagramSocketImpl extends DatagramSocketImpl { +public class PlainDatagramSocketImpl extends DatagramSocketImpl { static final int MULTICAST_IF = 1; @@ -70,7 +70,8 @@ class PlainDatagramSocketImpl extends DatagramSocketImpl { */ static final int REUSEADDR_AND_REUSEPORT = 10001; - private boolean bindToDevice; + // Ignored in native code + private boolean bindToDevice = false; private byte[] ipaddress = { 0, 0, 0, 0 }; @@ -116,7 +117,7 @@ class PlainDatagramSocketImpl extends DatagramSocketImpl { public void bind(int port, InetAddress addr) throws SocketException { String prop = AccessController.doPrivileged(new PriviAction("bindToDevice")); //$NON-NLS-1$ boolean useBindToDevice = prop != null && prop.toLowerCase().equals("true"); //$NON-NLS-1$ - bindToDevice = netImpl.bind2(fd, port, useBindToDevice, addr); + netImpl.bind(fd, addr, port); if (0 != port) { localPort = port; } else { diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/net/PlainMulticastSocketImpl.java b/libcore/luni/src/main/java/org/apache/harmony/luni/net/PlainMulticastSocketImpl.java deleted file mode 100644 index 7f5ac5044..000000000 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/net/PlainMulticastSocketImpl.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.harmony.luni.net; - -import java.net.SocketException; - -import org.apache.harmony.luni.platform.Platform; - -/** - * This class was added so we can create sockets with options that are needed - * for server sockets. It just overrides create so that we call new natives - * which only set the options required for server sockets. In order to preserve - * behaviour of older versions the create PlainSocketImpl was left as is and - * this new class was added. For newer versions an instance of this class is - * used, for earlier versions the original PlainSocketImpl is used. - */ -class PlainMulticastSocketImpl extends PlainDatagramSocketImpl { - - @Override - public void create() throws SocketException { - Platform.getNetworkSystem().createMulticastSocket(fd, NetUtil.preferIPv4Stack()); - } -} diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/net/PlainServerSocketImpl.java b/libcore/luni/src/main/java/org/apache/harmony/luni/net/PlainServerSocketImpl.java index de6cb8f47..4343ffb11 100644 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/net/PlainServerSocketImpl.java +++ b/libcore/luni/src/main/java/org/apache/harmony/luni/net/PlainServerSocketImpl.java @@ -24,14 +24,10 @@ import org.apache.harmony.luni.net.NetUtil; import org.apache.harmony.luni.net.PlainSocketImpl; /** - * This class was added so we can create sockets with options that are needed - * for server sockets. It just overrides create so that we call new natives - * which only set the options required for server sockets. In order to preserve - * behaviour of older versions the create PlainSocketImpl was left as is and - * this new class was added. For newer versions an instance of this class is - * used, for earlier versions the original PlainSocketImpl is used. + * This class overrides create to call natives that set the options required + * for server sockets. */ -class PlainServerSocketImpl extends PlainSocketImpl { +public class PlainServerSocketImpl extends PlainSocketImpl { public PlainServerSocketImpl() { super(); diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/net/PlainSocketImpl.java b/libcore/luni/src/main/java/org/apache/harmony/luni/net/PlainSocketImpl.java index 4fb8b91b0..9a3ae6e31 100644 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/net/PlainSocketImpl.java +++ b/libcore/luni/src/main/java/org/apache/harmony/luni/net/PlainSocketImpl.java @@ -43,7 +43,7 @@ import org.apache.harmony.luni.util.Msg; /** * A concrete connected-socket implementation. */ -class PlainSocketImpl extends SocketImpl { +public class PlainSocketImpl extends SocketImpl { // Const copy from socket @@ -89,6 +89,27 @@ class PlainSocketImpl extends SocketImpl { fd = new FileDescriptor(); } + public PlainSocketImpl(FileDescriptor fd) { + super(); + this.fd = fd; + } + + /** + * creates an instance with specified proxy. + */ + public PlainSocketImpl(Proxy proxy) { + this(); + this.proxy = proxy; + } + + public PlainSocketImpl(FileDescriptor fd, int localport, InetAddress addr, int port) { + super(); + this.fd = fd; + this.localport = localport; + this.address = addr; + this.port = port; + } + @Override protected void accept(SocketImpl newImpl) throws IOException { if (NetUtil.usingSocks(proxy)) { @@ -160,7 +181,7 @@ class PlainSocketImpl extends SocketImpl { @Override protected void bind(InetAddress anAddr, int aPort) throws IOException { - netImpl.bind(fd, aPort, anAddr); + netImpl.bind(fd, anAddr, aPort); // PlainSocketImpl2.socketBindImpl2(fd, aPort, anAddr); address = anAddr; if (0 != aPort) { @@ -201,7 +222,7 @@ class PlainSocketImpl extends SocketImpl { /** * Connects this socket to the specified remote host address/port. - * + * * @param anAddr * the remote host address to connect to * @param aPort @@ -211,13 +232,10 @@ class PlainSocketImpl extends SocketImpl { * @throws IOException * if an error occurs while connecting */ - // BEGIN android-changed - // copied from a newer harmony revision private void connect(InetAddress anAddr, int aPort, int timeout) throws IOException { - InetAddress normalAddr = anAddr.isAnyLocalAddress() ? InetAddress - .getLocalHost() : anAddr; + InetAddress normalAddr = anAddr.isAnyLocalAddress() ? InetAddress.getLocalHost() : anAddr; try { if (streaming) { if (NetUtil.usingSocks(proxy)) { @@ -231,7 +249,7 @@ class PlainSocketImpl extends SocketImpl { } } } else { - netImpl.connectDatagram(fd, aPort, trafficClass, normalAddr); + netImpl.connectDatagram(fd, aPort, trafficClass, normalAddr); } } catch (ConnectException e) { throw new ConnectException(anAddr + ":" + aPort + " - " @@ -240,16 +258,12 @@ class PlainSocketImpl extends SocketImpl { super.address = normalAddr; super.port = aPort; } - // END android-changed @Override protected void create(boolean streaming) throws IOException { this.streaming = streaming; if (streaming) { - // BEGIN android-changed - // call createSocket instead of createStreamSocket - netImpl.createSocket(fd, NetUtil.preferIPv4Stack()); - // END android-changed + netImpl.createStreamSocket(fd, NetUtil.preferIPv4Stack()); } else { netImpl.createDatagramSocket(fd, NetUtil.preferIPv4Stack()); } @@ -546,16 +560,19 @@ class PlainSocketImpl extends SocketImpl { if (shutdownInput) { return -1; } - try { - int read = netImpl.receiveStream(fd, buffer, offset, count, - receiveTimeout); - if (read == -1) { - shutdownInput = true; - } - return read; - } catch (InterruptedIOException e) { - throw new SocketTimeoutException(e.getMessage()); + // BEGIN android-changed + // call receiveStream() instead of read() + int read = netImpl.receiveStream(fd, buffer, offset, count, receiveTimeout); + // END android-changed + // Return of zero bytes for a blocking socket means a timeout occurred + if (read == 0) { + throw new SocketTimeoutException(); + } + // Return of -1 indicates the peer was closed + if (read == -1) { + shutdownInput = true; } + return read; } int write(byte[] buffer, int offset, int count) throws IOException { @@ -563,6 +580,9 @@ class PlainSocketImpl extends SocketImpl { return netImpl.sendDatagram2(fd, buffer, offset, count, port, address); } + // BEGIN android-changed + // call sendStream() instead of write() return netImpl.sendStream(fd, buffer, offset, count); + // END android-changed } } diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/net/PlainSocketImpl2.java b/libcore/luni/src/main/java/org/apache/harmony/luni/net/PlainSocketImpl2.java deleted file mode 100644 index 160de7cfe..000000000 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/net/PlainSocketImpl2.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.harmony.luni.net; - -import java.io.FileDescriptor; -import java.io.IOException; -import java.net.InetAddress; -import java.net.Proxy; - -/** - * This class was added so we can create sockets without options that were - * needed for server sockets. It just overrides create so that we call new - * natives which only set the options required for plain sockets. In order to - * preserve behaviour of older versions the create PlainSocketImpl was left as - * is and this new class was added. For newer versions an instance of this class - * is used, for earlier versions the original PlainSocketImpl is used. - */ -class PlainSocketImpl2 extends PlainSocketImpl { - - public PlainSocketImpl2(FileDescriptor fd, int localport, InetAddress addr, int port) { - super(); - super.fd = fd; - super.localport = localport; - super.address = addr; - super.port = port; - } - - public PlainSocketImpl2() { - super(); - } - - /** - * creates an instance with specified proxy. - */ - public PlainSocketImpl2(Proxy proxy) { - super(); - this.proxy = proxy; - } - - @Override - protected void create(boolean isStreaming) throws IOException { - streaming = isStreaming; - if (isStreaming) { - netImpl.createSocket(fd, NetUtil.preferIPv4Stack()); - } else { - netImpl.createDatagramSocket(fd, NetUtil.preferIPv4Stack()); - } - } -} diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/net/SocketImplProvider.java b/libcore/luni/src/main/java/org/apache/harmony/luni/net/SocketImplProvider.java deleted file mode 100644 index e8e7dc3f3..000000000 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/net/SocketImplProvider.java +++ /dev/null @@ -1,62 +0,0 @@ -/* Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.harmony.luni.net; - -import java.io.FileDescriptor; -import java.net.DatagramSocketImpl; -import java.net.InetAddress; -import java.net.Proxy; -import java.net.SocketImpl; - -public class SocketImplProvider { - - public static SocketImpl getSocketImpl() { - return new PlainSocketImpl2(); - } - - /** - * gets a SocketImpl with specified proxy. - */ - public static SocketImpl getSocketImpl(Proxy proxy) { - return new PlainSocketImpl2(proxy); - } - - public static SocketImpl getSocketImpl(FileDescriptor fd, int localport, InetAddress addr, - int port) { - return new PlainSocketImpl2(fd, localport, addr, port); - } - - public static SocketImpl getServerSocketImpl() { - return new PlainServerSocketImpl(); - } - - public static SocketImpl getServerSocketImpl(FileDescriptor fd) { - return new PlainServerSocketImpl(fd); - } - - public static DatagramSocketImpl getDatagramSocketImpl() { - return new PlainDatagramSocketImpl(); - } - - public static DatagramSocketImpl getMulticastSocketImpl() { - return new PlainMulticastSocketImpl(); - } - - public static DatagramSocketImpl getDatagramSocketImpl(FileDescriptor fd, int localPort) { - return new PlainDatagramSocketImpl(fd, localPort); - } - -} diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/platform/AbstractMemorySpy.java b/libcore/luni/src/main/java/org/apache/harmony/luni/platform/AbstractMemorySpy.java index 130454fe7..5462cf08b 100644 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/platform/AbstractMemorySpy.java +++ b/libcore/luni/src/main/java/org/apache/harmony/luni/platform/AbstractMemorySpy.java @@ -30,84 +30,82 @@ import java.util.Map; */ abstract class AbstractMemorySpy implements IMemorySpy { - // TODO: figure out how to prevent this being a synchronization bottleneck - protected Map memoryInUse = new HashMap(); // Shadow to Wrapper + // TODO: figure out how to prevent this being a synchronization bottleneck + protected Map memoryInUse = new HashMap(); // Shadow to Wrapper - protected Map refToShadow = new HashMap(); // Reference to Shadow + protected Map refToShadow = new HashMap(); // Reference to Shadow - protected ReferenceQueue notifyQueue = new ReferenceQueue(); + protected ReferenceQueue notifyQueue = new ReferenceQueue(); - final class AddressWrapper { - final PlatformAddress shadow; + final class AddressWrapper { + final PlatformAddress shadow; - final PhantomReference wrAddress; + final PhantomReference wrAddress; - volatile boolean autoFree = false; + volatile boolean autoFree = false; - AddressWrapper(PlatformAddress address) { - super(); - this.shadow = address.duplicate(); - this.wrAddress = new PhantomReference(address, notifyQueue); - } - } + AddressWrapper(PlatformAddress address) { + super(); + this.shadow = address.duplicate(); + this.wrAddress = new PhantomReference(address, notifyQueue); + } + } - public AbstractMemorySpy() { - super(); - } + public AbstractMemorySpy() { + super(); + } - public void alloc(PlatformAddress address) { - AddressWrapper wrapper = new AddressWrapper(address); - synchronized (this) { - memoryInUse.put(wrapper.shadow, wrapper); - refToShadow.put(wrapper.wrAddress, wrapper.shadow); - } - } + public void alloc(PlatformAddress address) { + AddressWrapper wrapper = new AddressWrapper(address); + synchronized (this) { + memoryInUse.put(wrapper.shadow, wrapper); + refToShadow.put(wrapper.wrAddress, wrapper.shadow); + } + } - public boolean free(PlatformAddress address) { - AddressWrapper wrapper; - synchronized (this) { - wrapper = memoryInUse.remove(address); + public boolean free(PlatformAddress address) { + AddressWrapper wrapper; + synchronized (this) { + wrapper = memoryInUse.remove(address); // BEGIN android-added if (wrapper != null) { refToShadow.remove(wrapper.wrAddress); } // END android-added - } - if (wrapper == null) { - // Attempt to free memory we didn't alloc - System.err - .println("Memory Spy! Fixed attempt to free memory that was not allocated " + address); //$NON-NLS-1$ - } - return wrapper != null; - } - - public void rangeCheck(PlatformAddress address, int offset, int length) - throws IndexOutOfBoundsException { - // Do nothing - } - - /* - * (non-Javadoc) - * - * @see org.apache.harmony.luni.platform.struct.IMemorySpy#autoFree(org.apache.harmony.luni.platform.struct.PlatformAddress) - */ - public void autoFree(PlatformAddress address) { - AddressWrapper wrapper; - synchronized (this) { - wrapper = memoryInUse.get(address); - } - if (wrapper != null) { - wrapper.autoFree = true; - } - } - - protected void orphanedMemory(Reference ref) { - AddressWrapper wrapper; - // BEGIN android-changed - // copied from newer version of harmony - synchronized (this) { - PlatformAddress shadow = refToShadow.remove(ref); - wrapper = memoryInUse.get(shadow); + } + if (wrapper == null) { + // Attempt to free memory we didn't alloc + System.err + .println("Memory Spy! Fixed attempt to free memory that was not allocated " + address); //$NON-NLS-1$ + } + return wrapper != null; + } + + public void rangeCheck(PlatformAddress address, int offset, int length) + throws IndexOutOfBoundsException { + // Do nothing + } + + /* + * (non-Javadoc) + * + * @see org.apache.harmony.luni.platform.struct.IMemorySpy#autoFree(org.apache.harmony.luni.platform.struct.PlatformAddress) + */ + public void autoFree(PlatformAddress address) { + AddressWrapper wrapper; + synchronized (this) { + wrapper = memoryInUse.get(address); + } + if (wrapper != null) { + wrapper.autoFree = true; + } + } + + protected void orphanedMemory(Reference ref) { + AddressWrapper wrapper; + synchronized (this) { + PlatformAddress shadow = refToShadow.remove(ref); + wrapper = memoryInUse.get(shadow); if (wrapper != null) { // There is a leak if we were not auto-freeing this memory. if (!wrapper.autoFree) { @@ -116,8 +114,7 @@ abstract class AbstractMemorySpy implements IMemorySpy { } wrapper.shadow.free(); } - } + } ref.clear(); - // END android-changed - } + } } diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/platform/AdapterManager.java b/libcore/luni/src/main/java/org/apache/harmony/luni/platform/AdapterManager.java deleted file mode 100644 index 2380fbcc3..000000000 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/platform/AdapterManager.java +++ /dev/null @@ -1,79 +0,0 @@ -/* Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.harmony.luni.platform; - - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; - -/** - * Adapter Manager - * - */ -final class AdapterManager implements IAdapterManager { - - /* - * key is adaptable type, and value is list of adapter factories for that - * type. - */ - private final HashMap> factories = new HashMap>(); - - public Object getAdapter(IAdaptable adaptable, Class adapterType) { - List factoryList = factories.get(adapterType); - if (factoryList != null) { - for (Iterator factoryItr = factoryList.iterator(); factoryItr - .hasNext();) { - IAdapterFactory factory = (IAdapterFactory) factoryItr.next(); - Object adapter = factory.getAdapter(adaptable, adapterType); - if (adapter != null) { - return adapter; - } - } - } - return null; - } - - public boolean hasAdapter(IAdaptable adaptable, Class adapterType) { - return null == getAdapter(adaptable, adapterType); - } - - public void registerAdapters(IAdapterFactory factory, Class adaptable) { - List factoryList = factories.get(adaptable); - if (factoryList == null) { - factoryList = new ArrayList(); - factories.put(adaptable, factoryList); - } - factoryList.add(factory); - } - - public void unregisterAdapters(IAdapterFactory factory, Class adaptable) { - List factoryList = factories.get(adaptable); - if (factoryList != null) { - factoryList.remove(factory); - } - } - - public void unregisterAdapters(IAdapterFactory factory) { - for (Iterator knownAdaptablesItr = factories.keySet().iterator(); knownAdaptablesItr - .hasNext();) { - Class adaptable = knownAdaptablesItr.next(); - unregisterAdapters(factory, adaptable); - } - } -} diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/platform/IAdaptable.java b/libcore/luni/src/main/java/org/apache/harmony/luni/platform/IAdaptable.java deleted file mode 100644 index e4c72ea6b..000000000 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/platform/IAdaptable.java +++ /dev/null @@ -1,54 +0,0 @@ -/* Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.harmony.luni.platform; - - -/** - * The interface to extensible objects. - *

          - * Classes can implement this interface (a single method) to provide interfaces - * that are not API -- each interface type has to be handled in the - * implementation of getAdapter(Class). This is a good way to - * extend the class without breaking existing API. - *

          - *

          - * In addition, classes can be augmented by interfaces that are defined by other - * classes (which requires the getAdapter(Class) to be - * implemented by a factory. - * - */ -public interface IAdaptable { - - /** - * Returns the adapter corresponding to the given class. - *

          - * The adapter is typically obtained using the class literal, like this: - * - *

          -     *    ...
          -     *    IAdaptable = (IAdaptable) foo;
          -     *    IMyInterface bar = (IMyInterface)foo.getAdapter(IMyInterface.class);
          -     *    bar.doMyThing();
          -     *    ...
          -     * 
          - * - * @param adapter - * the type of adapter requested - * @return the adapter - */ - public Object getAdapter(Class adapter); -} diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/platform/IAdapterFactory.java b/libcore/luni/src/main/java/org/apache/harmony/luni/platform/IAdapterFactory.java deleted file mode 100644 index 49fcd15e9..000000000 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/platform/IAdapterFactory.java +++ /dev/null @@ -1,43 +0,0 @@ -/* Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.harmony.luni.platform; - - -/** - * IAdapterFactory - * - */ -public interface IAdapterFactory { - - /** - * Answer the adapter of the given type for the given object. - * - * @param adaptableObject - * the object that implements IAdaptable. - * @param adapterType - * the type of adapter to return. - * @return the adapter of the requested type. - */ - public Object getAdapter(IAdaptable adaptableObject, Class adapterType); - - /** - * Returns the adapters that this factory provides. - * - * @return the list of adapters as an array of classes. - */ - public Class[] getAdapterList(); -} diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/platform/IFileSystem.java b/libcore/luni/src/main/java/org/apache/harmony/luni/platform/IFileSystem.java index fb2ff9d3b..7613f0e8d 100644 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/platform/IFileSystem.java +++ b/libcore/luni/src/main/java/org/apache/harmony/luni/platform/IFileSystem.java @@ -29,7 +29,7 @@ import java.io.IOException; * TODO Type description * */ -public interface IFileSystem extends ISystemComponent { +public interface IFileSystem { public final int SHARED_LOCK_TYPE = 1; diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/platform/IMemorySystem.java b/libcore/luni/src/main/java/org/apache/harmony/luni/platform/IMemorySystem.java index b765c7d6c..6e8028ad5 100644 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/platform/IMemorySystem.java +++ b/libcore/luni/src/main/java/org/apache/harmony/luni/platform/IMemorySystem.java @@ -30,10 +30,22 @@ import java.io.IOException; */ public interface IMemorySystem { + /** + * The constant representing read-only access to data in a memory map + * request. + */ public final int MMAP_READ_ONLY = 1; + /** + * The constant representing read-write access to data in a memory map + * request. + */ public final int MMAP_READ_WRITE = 2; + /** + * The constant representing copy-on-write access to data in a memory map + * request. + */ public final int MMAP_WRITE_COPY = 4; /** @@ -243,7 +255,17 @@ public interface IMemorySystem { throws NullPointerException, IndexOutOfBoundsException; // END android-added - // Primitive get & set methods + /** + * Returns the value of a single byte at the given address. + *

          + * The behavior is unspecified if address is not in the range + * that was previously allocated using malloc(). + *

          + * + * @param address + * the address at which to get the byte value. + * @return the value of the byte. + */ public byte getByte(int address); /** @@ -275,6 +297,21 @@ public interface IMemorySystem { */ public short getShort(int address); + /** + * Gets the value of the signed two-byte integer stored in the given byte + * order at the given address. + *

          + * The behavior is unspecified if (address ... address + 2) is + * not wholly within the range that was previously allocated using + * malloc(). + *

          + * + * @param address + * the platform address of the start of the two-byte value. + * @param endianness + * the required interpretation of the short endianness. + * @return the value of the two-byte integer as a Java short. + */ public short getShort(int address, Endianness endianness); /** @@ -474,17 +511,25 @@ public interface IMemorySystem { public void setAddress(int address, int value); /** - * TODO: JavaDoc - * + * Map file content into memory. + * * @param fileDescriptor + * a handle to the file that is to be memory mapped. * @param alignment + * the offset in the file where the mapping should begin. * @param size + * the number of bytes that are requested to map. * @param mapMode - * @return + * the desired access mode as defined by one of the constants + * {@link IMemorySystem#MMAP_READ_ONLY}, + * {@link IMemorySystem#MMAP_READ_WRITE}, + * {@link IMemorySystem#MMAP_WRITE_COPY} + * @return the start address of the mapped memory area. * @throws IOException + * if an exception occurs mapping the file into memory. */ - public int mmap(int fileDescriptor, long alignment, long size, - int mapMode) throws IOException; + public int mmap(int fileDescriptor, long alignment, long size, int mapMode) + throws IOException; /** * TODO: JavaDoc diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/platform/INetworkSystem.java b/libcore/luni/src/main/java/org/apache/harmony/luni/platform/INetworkSystem.java index 9f70515d6..fb47f0d9b 100644 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/platform/INetworkSystem.java +++ b/libcore/luni/src/main/java/org/apache/harmony/luni/platform/INetworkSystem.java @@ -29,17 +29,12 @@ import java.net.SocketException; import java.net.SocketImpl; import java.net.UnknownHostException; import java.nio.channels.Channel; -import java.nio.channels.SelectableChannel; /* * The interface for network methods. */ public interface INetworkSystem { - // ----------------------------------------------- - // Class Const - // ----------------------------------------------- - /* * Socket connect Step start */ @@ -50,34 +45,24 @@ public interface INetworkSystem { */ public final int SOCKET_CONNECT_STEP_CHECK = 1; - // ----------------------------------------------- - // Methods - // ----------------------------------------------- - /* * socket accept */ public void accept(FileDescriptor fdServer, SocketImpl newSocket, FileDescriptor fdnewSocket, int timeout) throws IOException; - public void bind(FileDescriptor aFD, int port, InetAddress inetAddress) + public void bind(FileDescriptor aFD, InetAddress inetAddress, int port) throws SocketException; - public boolean bind2(FileDescriptor aFD, int port, boolean bindToDevice, - InetAddress inetAddress) throws SocketException; - - public void createSocket(FileDescriptor fd, boolean preferIPv4Stack) - throws IOException; - public int read(FileDescriptor aFD, byte[] data, int offset, int count, int timeout) throws IOException; - + public int readDirect(FileDescriptor aFD, int address, int offset, int count, int timeout) throws IOException; public int write(FileDescriptor fd, byte[] data, int offset, int count) throws IOException; - + public int writeDirect(FileDescriptor fd, int address, int offset, int count) throws IOException; @@ -96,7 +81,7 @@ public interface INetworkSystem { public int sendDatagram(FileDescriptor fd, byte[] data, int offset, int length, int port, boolean bindToDevice, int trafficClass, InetAddress inetAddress) throws IOException; - + public int sendDatagramDirect(FileDescriptor fd, int address, int offset, int length, int port, boolean bindToDevice, int trafficClass, InetAddress inetAddress) throws IOException; @@ -104,7 +89,7 @@ public interface INetworkSystem { public int receiveDatagram(FileDescriptor aFD, DatagramPacket packet, byte[] data, int offset, int length, int receiveTimeout, boolean peek) throws IOException; - + public int receiveDatagramDirect(FileDescriptor aFD, DatagramPacket packet, int address, int offset, int length, int receiveTimeout, boolean peek) throws IOException; @@ -112,17 +97,17 @@ public interface INetworkSystem { public int recvConnectedDatagram(FileDescriptor aFD, DatagramPacket packet, byte[] data, int offset, int length, int receiveTimeout, boolean peek) throws IOException; - + public int recvConnectedDatagramDirect(FileDescriptor aFD, DatagramPacket packet, int address, int offset, int length, int receiveTimeout, boolean peek) throws IOException; - + public int peekDatagram(FileDescriptor aFD, InetAddress sender, int receiveTimeout) throws IOException; public int sendConnectedDatagram(FileDescriptor fd, byte[] data, int offset, int length, boolean bindToDevice) throws IOException; - + public int sendConnectedDatagramDirect(FileDescriptor fd, int address, int offset, int length, boolean bindToDevice) throws IOException; @@ -134,17 +119,17 @@ public interface INetworkSystem { public void connectDatagram(FileDescriptor aFD, int port, int trafficClass, InetAddress inetAddress) throws SocketException; - public void createMulticastSocket(FileDescriptor aFD, - boolean preferIPv4Stack) throws SocketException; - - public void createServerStreamSocket(FileDescriptor aFD, - boolean preferIPv4Stack) throws SocketException; - + /** + * @deprecated Use {@link #read(FileDescriptor, byte[], int, int, int)} + */ + @Deprecated public int receiveStream(FileDescriptor aFD, byte[] data, int offset, int count, int timeout) throws IOException; + // BEGIN android-added public int sendStream(FileDescriptor fd, byte[] data, int offset, int count) throws IOException; + // END android-added public void shutdownInput(FileDescriptor descriptor) throws IOException; @@ -160,11 +145,14 @@ public interface INetworkSystem { // public void acceptStreamSocket(FileDescriptor fdServer, // SocketImpl newSocket, FileDescriptor fdnewSocket, int timeout) // throws IOException; - // - // public void createStreamSocket(FileDescriptor aFD, boolean preferIPv4Stack) - // throws SocketException; // END android-removed + public void createServerStreamSocket(FileDescriptor aFD, boolean preferIPv4Stack) + throws SocketException; + + public void createStreamSocket(FileDescriptor aFD, boolean preferIPv4Stack) + throws SocketException; + public void listenStreamSocket(FileDescriptor aFD, int backlog) throws SocketException; @@ -184,7 +172,7 @@ public interface INetworkSystem { /* * Query the IP stack for the local port to which this socket is bound. - * + * * @param aFD the socket descriptor @param preferIPv6Addresses address * preference for nodes that support both IPv4 and IPv6 @return int the * local port to which the socket is bound @@ -194,10 +182,10 @@ public interface INetworkSystem { /* * Query the IP stack for the nominated socket option. - * + * * @param aFD the socket descriptor @param opt the socket option type * @return the nominated socket option value - * + * * @throws SocketException if the option is invalid */ public Object getSocketOption(FileDescriptor aFD, int opt) @@ -205,10 +193,10 @@ public interface INetworkSystem { /* * Set the nominated socket option in the IP stack. - * + * * @param aFD the socket descriptor @param opt the option selector @param * optVal the nominated option value - * + * * @throws SocketException if the option is invalid or cannot be set */ public void setSocketOption(FileDescriptor aFD, int opt, Object optVal) @@ -218,7 +206,7 @@ public interface INetworkSystem { /* * Close the socket in the IP stack. - * + * * @param aFD the socket descriptor */ public void socketClose(FileDescriptor aFD) throws IOException; @@ -229,12 +217,20 @@ public interface INetworkSystem { throws UnknownHostException; public void setInetAddress(InetAddress sender, byte[] address); - + + // BEGIN android-added + public String byteArrayToIpString(byte[] address) + throws UnknownHostException; + + public byte[] ipStringToByteArray(String address) + throws UnknownHostException; + // END android-added + // BEGIN android-removed // public boolean isReachableByICMP(InetAddress dest,InetAddress source,int ttl,int timeout); // END android-removed - + public Channel inheritedChannel(); - + public void oneTimeInitialization(boolean jcl_supports_ipv6); } diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/platform/OSFileSystem.java b/libcore/luni/src/main/java/org/apache/harmony/luni/platform/OSFileSystem.java index 2759d6b1f..08bdac6db 100644 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/platform/OSFileSystem.java +++ b/libcore/luni/src/main/java/org/apache/harmony/luni/platform/OSFileSystem.java @@ -24,17 +24,21 @@ package org.apache.harmony.luni.platform; import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.IOException; +import java.io.UnsupportedEncodingException; /** * This is the portable implementation of the file system interface. - * + * */ -class OSFileSystem extends OSComponent implements IFileSystem { +class OSFileSystem implements IFileSystem { - /** - * - */ - public OSFileSystem() { + private static final OSFileSystem singleton = new OSFileSystem(); + + public static OSFileSystem getOSFileSystem() { + return singleton; + } + + private OSFileSystem() { super(); } @@ -153,6 +157,14 @@ class OSFileSystem extends OSComponent implements IFileSystem { } long bytesRead = readImpl(fileDescriptor, bytes, offset, length); if (bytesRead < -1) { + /* + * TODO: bytesRead is never less than -1 so this code + * does nothing? + * The native code throws an exception in only one case + * so perhaps this should be 'bytesRead < 0' to handle + * any other cases. But the other cases have been + * ignored until now so fixing this could break things + */ throw new IOException(); } return bytesRead; @@ -203,7 +215,7 @@ class OSFileSystem extends OSComponent implements IFileSystem { /* * (non-Javadoc) - * + * * @see org.apache.harmony.luni.platform.IFileSystem#close(long) */ public void close(int fileDescriptor) throws IOException { @@ -228,7 +240,14 @@ class OSFileSystem extends OSComponent implements IFileSystem { } int handler = openImpl(fileName, mode); if (handler < 0) { - throw new FileNotFoundException(new String(fileName)); + try { + throw new FileNotFoundException(new String(fileName, "UTF-8")); + } catch (java.io.UnsupportedEncodingException e) { + // UTF-8 should always be supported, so throw an assertion + FileNotFoundException fnfe = new FileNotFoundException(new String(fileName)); + e.initCause(fnfe); + throw new AssertionError(e); + } } return handler; } @@ -238,7 +257,8 @@ class OSFileSystem extends OSComponent implements IFileSystem { public long transfer(int fileHandler, FileDescriptor socketDescriptor, long offset, long count) throws IOException { long result = transferImpl(fileHandler, socketDescriptor, offset, count); - if (result < 0) throw new IOException(); + if (result < 0) + throw new IOException(); return result; } @@ -268,7 +288,7 @@ class OSFileSystem extends OSComponent implements IFileSystem { } private native long ttyReadImpl(byte[] bytes, int offset, int length); - + // BEGIN android-added public native int ioctlAvailable(int fileDescriptor) throws IOException; // END android-added diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/platform/OSMemory.java b/libcore/luni/src/main/java/org/apache/harmony/luni/platform/OSMemory.java index e3e7c4687..0061d2a4a 100644 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/platform/OSMemory.java +++ b/libcore/luni/src/main/java/org/apache/harmony/luni/platform/OSMemory.java @@ -43,7 +43,7 @@ import java.io.IOException; * above. *

          */ -final class OSMemory extends OSComponent implements IMemorySystem { +final class OSMemory implements IMemorySystem { /** * Defines the size, in bytes, of a native pointer type for the underlying @@ -74,142 +74,150 @@ final class OSMemory extends OSComponent implements IMemorySystem { } /** - * This class is not designed to be publically instantiated. - * + * This class is not designed to be publicly instantiated. + * * @see #getOSMemory() */ - OSMemory() { + private OSMemory() { super(); } - // BEGIN android-note - // changed to private - // END android-note /** * Returns whether the byte order of this machine is little endian or not.. - * - * @return false for Big Endian, and - * truefalse for Big Endian, and + * truelength bytes. The space is uninitialized and may be larger * than the number of bytes requested; however, the guaranteed usable memory * block is exactly length bytes int. - * - * @param length - * number of bytes requested. + * + * @param length + * number of bytes requested. * @return the address of the start of the memory block. - * @throws OutOfMemoryError - * if the request cannot be satisfied. + * @throws OutOfMemoryError + * if the request cannot be satisfied. */ + // BEGIN android-changed + // public long malloc(long length) throws OutOfMemoryError + // { + // OSResourcesMonitor.ensurePhysicalMemoryCapacity(); + // return mallocNative(length); + // } + // private native long mallocNative(long length) throws OutOfMemoryError; public native int malloc(int length) throws OutOfMemoryError; + // END android-changed /** * Deallocates space for a memory block that was previously allocated by a * call to {@link #malloc(int) malloc(int)}. The number of bytes freed is * identical to the number of bytes acquired when the memory block was - * allocated. If address is zero the method does nothing. - *

          + * allocated. If address is zero the method does nothing. + *

          * Freeing a pointer to a memory block that was not allocated by - * malloc() has unspecified effect. - *

          - * - * @param address - * the address of the memory block to deallocate. + * malloc() has unspecified effect. + *

          + * + * @param address + * the address of the memory block to deallocate. */ public native void free(int address); /** * Places value into first length bytes of the - * memory block starting at address. - *

          - * The behavior is unspecified if - * (address ... address + length) is not wholly within the - * range that was previously allocated using malloc(). - *

          - * - * @param address - * the address of the first memory location. - * @param value - * the byte value to set at each location. - * @param length - * the number of byte-length locations to set. + * memory block starting at address. + *

          + * The behavior is unspecified if + * (address ... address + length) is not wholly within the + * range that was previously allocated using malloc(). + *

          + * + * @param address + * the address of the first memory location. + * @param value + * the byte value to set at each location. + * @param length + * the number of byte-length locations to set. */ public native void memset(int address, byte value, long length); /** * Copies length bytes from srcAddress to - * destAddress. Where any part of the source memory block - * and the destination memory block overlap memmove() ensures - * that the original source bytes in the overlapping region are copied - * before being overwritten. - *

          - * The behavior is unspecified if - * (srcAddress ... srcAddress + length) and - * (destAddress ... destAddress + length) are not both wholly - * within the range that was previously allocated using - * malloc(). - *

          - * - * @param destAddress - * the address of the destination memory block. - * @param srcAddress - * the address of the source memory block. - * @param length - * the number of bytes to move. + * destAddress. Where any part of the source memory block + * and the destination memory block overlap memmove() ensures + * that the original source bytes in the overlapping region are copied + * before being overwritten. + *

          + * The behavior is unspecified if + * (srcAddress ... srcAddress + length) and + * (destAddress ... destAddress + length) are not both wholly + * within the range that was previously allocated using + * malloc(). + *

          + * + * @param destAddress + * the address of the destination memory block. + * @param srcAddress + * the address of the source memory block. + * @param length + * the number of bytes to move. */ public native void memmove(int destAddress, int srcAddress, long length); /** * Copies length bytes from the memory block at - * address into the byte array bytes starting - * at element offset within the byte array. - *

          - * The behavior of this method is undefined if the range - * (address ... address + length) is not within a memory + * address into the byte array bytes starting + * at element offset within the byte array. + *

          + * The behavior of this method is undefined if the range + * (address ... address + length) is not within a memory * block that was allocated using {@link #malloc(int) malloc(int)}. - *

          - * - * @param address - * the address of the OS memory block from which to copy bytes. - * @param bytes - * the byte array into which to copy the bytes. - * @param offset - * the index of the first element in bytes that - * will be overwritten. - * @param length - * the total number of bytes to copy into the byte array. - * @throws NullPointerException - * if bytes is null. - * @throws IndexOutOfBoundsException - * if offset + length > bytes.length. + *

          + * + * @param address + * the address of the OS memory block from which to copy bytes. + * @param bytes + * the byte array into which to copy the bytes. + * @param offset + * the index of the first element in bytes that + * will be overwritten. + * @param length + * the total number of bytes to copy into the byte array. + * @throws NullPointerException + * if bytes is null. + * @throws IndexOutOfBoundsException + * if offset + length > bytes.length. */ public native void getByteArray(int address, byte[] bytes, int offset, int length) throws NullPointerException, IndexOutOfBoundsException; @@ -217,32 +225,32 @@ final class OSMemory extends OSComponent implements IMemorySystem { /** * Copies length bytes from the byte array bytes * into the memory block at address, starting at element - * offset within the byte array. - *

          - * The behavior of this method is undefined if the range - * (address ... address + length) is not within a memory + * offset within the byte array. + *

          + * The behavior of this method is undefined if the range + * (address ... address + length) is not within a memory * block that was allocated using {@link #malloc(int) malloc(int)}. - *

          - * - * @param address - * the address of the OS memory block into which to copy the - * bytes. - * @param bytes - * the byte array from which to copy the bytes. - * @param offset - * the index of the first element in bytes that - * will be read. - * @param length - * the total number of bytes to copy from bytes - * into the memory block. - * @throws NullPointerException - * if bytes is null. - * @throws IndexOutOfBoundsException - * if offset + length > bytes.length. + *

          + * + * @param address + * the address of the OS memory block into which to copy the + * bytes. + * @param bytes + * the byte array from which to copy the bytes. + * @param offset + * the index of the first element in bytes that + * will be read. + * @param length + * the total number of bytes to copy from bytes + * into the memory block. + * @throws NullPointerException + * if bytes is null. + * @throws IndexOutOfBoundsException + * if offset + length > bytes.length. */ public native void setByteArray(int address, byte[] bytes, int offset, int length) throws NullPointerException, IndexOutOfBoundsException; - + // BEGIN android-added /** * Copies length shorts from the short array shorts @@ -253,7 +261,7 @@ final class OSMemory extends OSComponent implements IMemorySystem { * (address ... address + 2*length) is not within a memory * block that was allocated using {@link #malloc(int) malloc(int)}. *

          - * + * * @param address * the address of the OS memory block into which to copy the * shorts. @@ -285,7 +293,7 @@ final class OSMemory extends OSComponent implements IMemorySystem { * (address ... address + 2*length) is not within a memory * block that was allocated using {@link #malloc(int) malloc(int)}. *

          - * + * * @param address * the address of the OS memory block into which to copy the * ints. @@ -312,43 +320,43 @@ final class OSMemory extends OSComponent implements IMemorySystem { // Primitive get & set methods /** - * Gets the value of the single byte at the given address. - *

          - * The behavior is unspecified if address is not in the range - * that was previously allocated using malloc(). - *

          - * - * @param address - * the platform address of the byte. + * Gets the value of the single byte at the given address. + *

          + * The behavior is unspecified if address is not in the range + * that was previously allocated using malloc(). + *

          + * + * @param address + * the platform address of the byte. * @return the byte value. */ public native byte getByte(int address); /** - * Sets the given single byte value at the given address. - *

          - * The behavior is unspecified if address is not in the range - * that was previously allocated using malloc(). - *

          - * - * @param address - * the address at which to set the byte value. - * @param value - * the value to set. + * Sets the given single byte value at the given address. + *

          + * The behavior is unspecified if address is not in the range + * that was previously allocated using malloc(). + *

          + * + * @param address + * the address at which to set the byte value. + * @param value + * the value to set. */ public native void setByte(int address, byte value); /** * Gets the value of the signed two-byte integer stored in platform byte - * order at the given address. - *

          - * The behavior is unspecified if (address ... address + 2) - * is not wholly within the range that was previously allocated using - * malloc(). - *

          - * - * @param address - * the platform address of the start of the two-byte value. + * order at the given address. + *

          + * The behavior is unspecified if (address ... address + 2) + * is not wholly within the range that was previously allocated using + * malloc(). + *

          + * + * @param address + * the platform address of the start of the two-byte value. * @return the value of the two-byte integer as a Java short. */ public native short getShort(int address); @@ -360,17 +368,17 @@ final class OSMemory extends OSComponent implements IMemorySystem { /** * Sets the value of the signed two-byte integer at the given address in - * platform byte order. - *

          - * The behavior is unspecified if (address ... address + 2) - * is not wholly within the range that was previously allocated using - * malloc(). - *

          - * - * @param address - * the platform address of the start of the two-byte value. - * @param value - * the value of the two-byte integer as a Java short. + * platform byte order. + *

          + * The behavior is unspecified if (address ... address + 2) + * is not wholly within the range that was previously allocated using + * malloc(). + *

          + * + * @param address + * the platform address of the start of the two-byte value. + * @param value + * the value of the two-byte integer as a Java short. */ public native void setShort(int address, short value); @@ -384,15 +392,15 @@ final class OSMemory extends OSComponent implements IMemorySystem { /** * Gets the value of the signed four-byte integer stored in platform - * byte-order at the given address. - *

          - * The behavior is unspecified if (address ... address + 4) - * is not wholly within the range that was previously allocated using - * malloc(). - *

          - * - * @param address - * the platform address of the start of the four-byte value. + * byte-order at the given address. + *

          + * The behavior is unspecified if (address ... address + 4) + * is not wholly within the range that was previously allocated using + * malloc(). + *

          + * + * @param address + * the platform address of the start of the four-byte value. * @return the value of the four-byte integer as a Java int. */ public native int getInt(int address); @@ -404,17 +412,17 @@ final class OSMemory extends OSComponent implements IMemorySystem { /** * Sets the value of the signed four-byte integer at the given address in - * platform byte order. - *

          - * The behavior is unspecified if (address ... address + 4) - * is not wholly within the range that was previously allocated using - * malloc(). - *

          - * - * @param address - * the platform address of the start of the four-byte value. - * @param value - * the value of the four-byte integer as a Java int. + * platform byte order. + *

          + * The behavior is unspecified if (address ... address + 4) + * is not wholly within the range that was previously allocated using + * malloc(). + *

          + * + * @param address + * the platform address of the start of the four-byte value. + * @param value + * the value of the four-byte integer as a Java int. */ public native void setInt(int address, int value); @@ -428,15 +436,15 @@ final class OSMemory extends OSComponent implements IMemorySystem { /** * Gets the value of the signed eight-byte integer stored in platform byte - * order at the given address. - *

          - * The behavior is unspecified if (address ... address + 8) - * is not wholly within the range that was previously allocated using - * malloc(). - *

          - * - * @param address - * the platform address of the start of the eight-byte value. + * order at the given address. + *

          + * The behavior is unspecified if (address ... address + 8) + * is not wholly within the range that was previously allocated using + * malloc(). + *

          + * + * @param address + * the platform address of the start of the eight-byte value. * @return the value of the eight-byte integer as a Java long. */ public native long getLong(int address); @@ -448,17 +456,17 @@ final class OSMemory extends OSComponent implements IMemorySystem { /** * Sets the value of the signed eight-byte integer at the given address in - * the platform byte order. - *

          - * The behavior is unspecified if (address ... address + 8) - * is not wholly within the range that was previously allocated using - * malloc(). - *

          - * - * @param address - * the platform address of the start of the eight-byte value. - * @param value - * the value of the eight-byte integer as a Java + * the platform byte order. + *

          + * The behavior is unspecified if (address ... address + 8) + * is not wholly within the range that was previously allocated using + * malloc(). + *

          + * + * @param address + * the platform address of the start of the eight-byte value. + * @param value + * the value of the eight-byte integer as a Java * long. */ public native void setLong(int address, long value); @@ -473,15 +481,15 @@ final class OSMemory extends OSComponent implements IMemorySystem { /** * Gets the value of the IEEE754-format four-byte float stored in platform - * byte order at the given address. - *

          - * The behavior is unspecified if (address ... address + 4) - * is not wholly within the range that was previously allocated using - * malloc(). - *

          - * - * @param address - * the platform address of the start of the eight-byte value. + * byte order at the given address. + *

          + * The behavior is unspecified if (address ... address + 4) + * is not wholly within the range that was previously allocated using + * malloc(). + *

          + * + * @param address + * the platform address of the start of the eight-byte value. * @return the value of the four-byte float as a Java float. */ public native float getFloat(int address); @@ -496,17 +504,17 @@ final class OSMemory extends OSComponent implements IMemorySystem { /** * Sets the value of the IEEE754-format four-byte float stored in platform - * byte order at the given address. - *

          - * The behavior is unspecified if (address ... address + 4) - * is not wholly within the range that was previously allocated using - * malloc(). - *

          - * - * @param address - * the platform address of the start of the eight-byte value. - * @param value - * the value of the four-byte float as a Java float. + * byte order at the given address. + *

          + * The behavior is unspecified if (address ... address + 4) + * is not wholly within the range that was previously allocated using + * malloc(). + *

          + * + * @param address + * the platform address of the start of the eight-byte value. + * @param value + * the value of the four-byte float as a Java float. */ public native void setFloat(int address, float value); @@ -521,15 +529,15 @@ final class OSMemory extends OSComponent implements IMemorySystem { /** * Gets the value of the IEEE754-format eight-byte float stored in platform - * byte order at the given address. - *

          - * The behavior is unspecified if (address ... address + 8) - * is not wholly within the range that was previously allocated using - * malloc(). - *

          - * - * @param address - * the platform address of the start of the eight-byte value. + * byte order at the given address. + *

          + * The behavior is unspecified if (address ... address + 8) + * is not wholly within the range that was previously allocated using + * malloc(). + *

          + * + * @param address + * the platform address of the start of the eight-byte value. * @return the value of the eight-byte float as a Java double. */ public native double getDouble(int address); @@ -544,17 +552,17 @@ final class OSMemory extends OSComponent implements IMemorySystem { /** * Sets the value of the IEEE754-format eight-byte float store in platform - * byte order at the given address. - *

          - * The behavior is unspecified if (address ... address + 8) - * is not wholly within the range that was previously allocated using - * malloc(). - *

          - * - * @param address - * the platform address of the start of the eight-byte value. - * @param value - * the value of the eight-byte float as a Java + * byte order at the given address. + *

          + * The behavior is unspecified if (address ... address + 8) + * is not wholly within the range that was previously allocated using + * malloc(). + *

          + * + * @param address + * the platform address of the start of the eight-byte value. + * @param value + * the value of the eight-byte float as a Java * double. */ public native void setDouble(int address, double value); @@ -569,44 +577,44 @@ final class OSMemory extends OSComponent implements IMemorySystem { } /** - * Gets the value of the platform pointer at the given address. - *

          - * The length of the platform pointer is defined by - * POINTER_SIZE. - *

          - * The behavior is unspecified if - * (address ... address + POINTER_SIZE) is not wholly within - * the range that was previously allocated using malloc(). - *

          - * - * @param address - * the platform address of the start of the platform pointer. + * Gets the value of the platform pointer at the given address. + *

          + * The length of the platform pointer is defined by + * POINTER_SIZE. + *

          + * The behavior is unspecified if + * (address ... address + POINTER_SIZE) is not wholly within + * the range that was previously allocated using malloc(). + *

          + * + * @param address + * the platform address of the start of the platform pointer. * @return the value of the platform pointer as a Java int. */ public native int getAddress(int address); /** - * Sets the value of the platform pointer at the given address. - *

          - * The length of the platform pointer is defined by - * POINTER_SIZE. This method only sets - * POINTER_SIZE bytes at the given address. - *

          - * The behavior is unspecified if - * (address ... address + POINTER_SIZE) is not wholly within - * the range that was previously allocated using malloc(). - *

          - * - * @param address - * the platform address of the start of the platform pointer. - * @param value + * Sets the value of the platform pointer at the given address. + *

          + * The length of the platform pointer is defined by + * POINTER_SIZE. This method only sets + * POINTER_SIZE bytes at the given address. + *

          + * The behavior is unspecified if + * (address ... address + POINTER_SIZE) is not wholly within + * the range that was previously allocated using malloc(). + *

          + * + * @param address + * the platform address of the start of the platform pointer. + * @param value * the value of the platform pointer as a Java int. */ public native void setAddress(int address, int value); /* - * Memory mapped file - */ + * Memory mapped file + */ private native int mmapImpl(int fileDescriptor, long alignment, long size, int mapMode); @@ -632,39 +640,48 @@ final class OSMemory extends OSComponent implements IMemorySystem { private native int loadImpl(int l, long size); public boolean isLoaded(int addr, long size) { - return size == 0 ? true : isLoadedImpl(addr, size); - } + return size == 0 ? true : isLoadedImpl(addr, size); + } private native boolean isLoadedImpl(int l, long size); public void flush(int addr, long size) { - flushImpl(addr, size); - } + flushImpl(addr, size); + } private native int flushImpl(int l, long size); - /* - * Helper methods to change byte order. - */ - private short swap(short value) { - int topEnd = value << 8; - int btmEnd = (value >> 8) & 0xFF; - return (short) (topEnd | btmEnd); - } - - private int swap(int value) { - short left = (short) (value >> 16); - short right = (short) value; - int topEnd = swap(right) << 16; - int btmEnd = swap(left) & 0xFFFF; - return topEnd | btmEnd; - } - - private long swap(long value) { - int left = (int) (value >> 32); - int right = (int) value; - long topEnd = ((long) swap(right)) << 32; - long btmEnd = swap(left) & 0xFFFFFFFFL; - return topEnd | btmEnd; - } + /* + * Helper methods to change byte order. + */ + private short swap(short value) { + // BEGIN android-note + // is Integer.reverseBytes() >>> 16 be faster? + // END android-note + int topEnd = value << 8; + int btmEnd = (value >> 8) & 0xFF; + return (short) (topEnd | btmEnd); + } + + private int swap(int value) { + // BEGIN android-note + // is Integer.reverseBytes() be faster? + // END android-note + short left = (short) (value >> 16); + short right = (short) value; + int topEnd = swap(right) << 16; + int btmEnd = swap(left) & 0xFFFF; + return topEnd | btmEnd; + } + + private long swap(long value) { + // BEGIN android-note + // is Long.reverseBytes() be faster? + // END android-note + int left = (int) (value >> 32); + int right = (int) value; + long topEnd = ((long) swap(right)) << 32; + long btmEnd = swap(left) & 0xFFFFFFFFL; + return topEnd | btmEnd; + } } diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/platform/OSNetworkSystem.java b/libcore/luni/src/main/java/org/apache/harmony/luni/platform/OSNetworkSystem.java index c153428a4..bd6a6097d 100644 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/platform/OSNetworkSystem.java +++ b/libcore/luni/src/main/java/org/apache/harmony/luni/platform/OSNetworkSystem.java @@ -16,7 +16,10 @@ */ // BEGIN android-note -// address length was changed from long to int for performance reasons. +// Address length was changed from long to int for performance reasons. +// Harmony implements INetworkSystem's methods with native methods; Android +// implements them with Java that call through to native wrappers. +// TODO: change the native code to eliminate the wrappers // END android-note package org.apache.harmony.luni.platform; @@ -32,715 +35,798 @@ import java.nio.channels.Channel; // BEGIN android-removed // import java.nio.channels.SelectableChannel; // END android-removed -/* - * - * This Class is used for native code wrap, the implement class of - * INetworkSystem. - * + +/** + * This wraps native code that implements the INetworkSystem interface. */ final class OSNetworkSystem implements INetworkSystem { - // ---------------------------------------------------- - // Class Variables - // ---------------------------------------------------- - private static final int ERRORCODE_SOCKET_TIMEOUT = -209; + private static final int ERRORCODE_SOCKET_INTERRUPTED = -208; - private static OSNetworkSystem ref = new OSNetworkSystem(); - private static final int INETADDR_REACHABLE = 0; - - private static boolean isNetworkInited = false; - - // ---------------------------------------------------- - // Class Constructor - // ---------------------------------------------------- + private static boolean isNetworkInited = false; - // can not be instantiated. - private OSNetworkSystem() { - super(); - } + private static OSNetworkSystem singleton = new OSNetworkSystem(); - /* - * @return a static ref of this class + /** + * Answers the unique instance of the OSNetworkSystem. + * + * @return the network system interface instance */ public static OSNetworkSystem getOSNetworkSystem() { - return ref; - } - - // Useing when cache set/get is OK - // public static native void oneTimeInitializationDatagram( - // boolean jcl_IPv6_support); - // - // public static native void oneTimeInitializationSocket( - // boolean jcl_IPv6_support); - - // -------------------------------------------------- - // java codes that wrap native codes - // -------------------------------------------------- - - public void createSocket(FileDescriptor fd, boolean preferIPv4Stack) - throws IOException { - createSocketImpl(fd, preferIPv4Stack); - } - - public void createDatagramSocket(FileDescriptor fd, boolean preferIPv4Stack) - throws SocketException { - createDatagramSocketImpl(fd, preferIPv4Stack); + return singleton; } - public int read(FileDescriptor aFD, byte[] data, int offset, int count, - int timeout) throws IOException { - return readSocketImpl(aFD, data, offset, count, timeout); - } - - public int readDirect(FileDescriptor aFD, int address, int offset, int count, - int timeout) throws IOException { - return readSocketDirectImpl(aFD, address, offset, count, timeout); + // Can not be instantiated. + private OSNetworkSystem() { + super(); } - public int write(FileDescriptor aFD, byte[] data, int offset, int count) - throws IOException { - return writeSocketImpl(aFD, data, offset, count); - } - - public int writeDirect(FileDescriptor aFD, int address, int offset, - int count) throws IOException { - return writeSocketDirectImpl(aFD, address, offset, count); + public void accept(FileDescriptor fdServer, SocketImpl newSocket, + FileDescriptor fdnewSocket, int timeout) throws IOException { + acceptSocketImpl(fdServer, newSocket, fdnewSocket, timeout); } - public void setNonBlocking(FileDescriptor aFD, boolean block) - throws IOException { - setNonBlockingImpl(aFD, block); - } + static native void acceptSocketImpl(FileDescriptor fdServer, + SocketImpl newSocket, FileDescriptor fdnewSocket, int timeout) + throws IOException; - public void connectDatagram(FileDescriptor aFD, int port, int trafficClass, - InetAddress inetAddress) throws SocketException { - connectDatagramImpl2(aFD, port, trafficClass, inetAddress); - } + // BEGIN android-removed + // public void acceptStreamSocket(FileDescriptor fdServer, + // SocketImpl newSocket, FileDescriptor fdnewSocket, int timeout) + // throws IOException { + // acceptStreamSocketImpl(fdServer, newSocket, fdnewSocket, timeout); + // } - public int connect(FileDescriptor aFD, int trafficClass, - InetAddress inetAddress, int port) throws IOException{ - return connectSocketImpl(aFD, trafficClass, inetAddress, port); - } + // static native void acceptStreamSocketImpl(FileDescriptor fdServer, + // SocketImpl newSocket, FileDescriptor fdnewSocket, int timeout) + // throws IOException; + // END android-removed - // BEGIN android-changed - public int connectWithTimeout(FileDescriptor aFD, int timeout, - int trafficClass, InetAddress inetAddress, int port, int step, - byte[] context) throws IOException{ - return connectWithTimeoutSocketImpl(aFD, timeout, trafficClass, - inetAddress, port, step, context); + public int availableStream(FileDescriptor fd) throws SocketException { + return availableStreamImpl(fd); } - // END android-changed - public void connectStreamWithTimeoutSocket(FileDescriptor aFD, int aport, - int timeout, int trafficClass, InetAddress inetAddress) - throws IOException { - connectStreamWithTimeoutSocketImpl(aFD, aport, timeout, trafficClass, - inetAddress); - } + static native int availableStreamImpl(FileDescriptor aFD) throws SocketException; - public void bind(FileDescriptor aFD, int port, InetAddress inetAddress) - throws SocketException { - socketBindImpl(aFD, port, inetAddress); + /** + * Associates a local address with a socket. + * + * @param fd + * the socket descriptor + * @param port + * the port number + * @param inetAddress + * address to bind + * @throws SocketException + * thrown if bind operation fails + */ + public void bind(FileDescriptor fd, InetAddress inetAddress, int port) throws SocketException { + socketBindImpl(fd, port, inetAddress); } - public boolean bind2(FileDescriptor aFD, int port, boolean bindToDevice, - InetAddress inetAddress) throws SocketException { - return socketBindImpl2(aFD, port, bindToDevice, inetAddress); - } + static native void socketBindImpl(FileDescriptor aFD, int port, InetAddress inetAddress) throws SocketException; - public void accept(FileDescriptor fdServer, SocketImpl newSocket, - FileDescriptor fdnewSocket, int timeout) throws IOException { - acceptSocketImpl(fdServer, newSocket, fdnewSocket, timeout); + public int connect(FileDescriptor fd, int trafficClass, + InetAddress inetAddress, int port) throws IOException{ + return connectSocketImpl(fd, trafficClass, inetAddress, port); } - public int sendDatagram(FileDescriptor fd, byte[] data, int offset, - int length, int port, boolean bindToDevice, int trafficClass, - InetAddress inetAddress) throws IOException { - return sendDatagramImpl(fd, data, offset, length, port, bindToDevice, - trafficClass, inetAddress); - } - - public int sendDatagramDirect(FileDescriptor fd, int address, int offset, - int length, int port, boolean bindToDevice, int trafficClass, - InetAddress inetAddress) throws IOException { - return sendDatagramDirectImpl(fd, address, offset, length, port, bindToDevice, - trafficClass, inetAddress); - } + static native int connectSocketImpl(FileDescriptor aFD, + int trafficClass, InetAddress inetAddress, int port); - public int sendDatagram2(FileDescriptor fd, byte[] data, int offset, - int length, int port, InetAddress inetAddress) throws IOException { - return sendDatagramImpl2(fd, data, offset, length, port, inetAddress); + public void connectDatagram(FileDescriptor fd, int port, + int trafficClass, InetAddress inetAddress) throws SocketException { + connectDatagramImpl2(fd, port, trafficClass, inetAddress); } - public int receiveDatagram(FileDescriptor aFD, DatagramPacket packet, - byte[] data, int offset, int length, int receiveTimeout, - boolean peek) throws IOException { - return receiveDatagramImpl(aFD, packet, data, offset, length, - receiveTimeout, peek); - } - - public int receiveDatagramDirect(FileDescriptor aFD, DatagramPacket packet, - int address, int offset, int length, int receiveTimeout, - boolean peek) throws IOException { - return receiveDatagramDirectImpl(aFD, packet, address, offset, length, - receiveTimeout, peek); - } + static native void connectDatagramImpl2(FileDescriptor aFD, int port, + int trafficClass, InetAddress inetAddress) throws SocketException; - public int recvConnectedDatagram(FileDescriptor aFD, DatagramPacket packet, - byte[] data, int offset, int length, int receiveTimeout, - boolean peek) throws IOException { - return recvConnectedDatagramImpl(aFD, packet, data, offset, length, - receiveTimeout, peek); - } - - public int recvConnectedDatagramDirect(FileDescriptor aFD, DatagramPacket packet, int address, - int offset, int length, int receiveTimeout, boolean peek) + public void connectStreamWithTimeoutSocket(FileDescriptor aFD, + int aport, int timeout, int trafficClass, InetAddress inetAddress) throws IOException { - return recvConnectedDatagramDirectImpl(aFD, packet, address, offset, length, receiveTimeout, peek); + connectStreamWithTimeoutSocketImpl(aFD, aport, timeout, trafficClass, + inetAddress); } - public int peekDatagram(FileDescriptor aFD, InetAddress sender, - int receiveTimeout) throws IOException { - return peekDatagramImpl(aFD, sender, receiveTimeout); - } + static native void connectStreamWithTimeoutSocketImpl(FileDescriptor aFD, + int aport, int timeout, int trafficClass, InetAddress inetAddress) + throws IOException; - public int sendConnectedDatagram(FileDescriptor fd, byte[] data, - int offset, int length, boolean bindToDevice) throws IOException { - return sendConnectedDatagramImpl(fd, data, offset, length, bindToDevice); - } - - public int sendConnectedDatagramDirect(FileDescriptor fd, int address, - int offset, int length, boolean bindToDevice) throws IOException { - return sendConnectedDatagramDirectImpl(fd, address, offset, length, bindToDevice); + // BEGIN android-changed + // changed context from Long to byte[] + public int connectWithTimeout(FileDescriptor fd, int timeout, + int trafficClass, InetAddress inetAddress, int port, int step, + byte[] context) throws IOException { + return connectWithTimeoutSocketImpl(fd, timeout, trafficClass, + inetAddress, port, step, context); } - public void disconnectDatagram(FileDescriptor aFD) throws SocketException { - disconnectDatagramImpl(aFD); - } + static native int connectWithTimeoutSocketImpl(FileDescriptor aFD, + int timeout, int trafficClass, InetAddress hostname, int port, int step, + byte[] context); + // END android-changed - public void createMulticastSocket(FileDescriptor aFD, + public void createDatagramSocket(FileDescriptor fd, boolean preferIPv4Stack) throws SocketException { - createMulticastSocketImpl(aFD, preferIPv4Stack); + createDatagramSocketImpl(fd, preferIPv4Stack); } - public void createServerStreamSocket(FileDescriptor aFD, - boolean preferIPv4Stack) throws SocketException { - createServerStreamSocketImpl(aFD, preferIPv4Stack); - } + /* + * Allocate a datagram socket in the IP stack. The socket is associated with + * the aFD. + * + * @param aFD the FileDescriptor to associate with the socket @param + * preferIPv4Stack IP stack preference if underlying platform is V4/V6 + * @exception SocketException upon an allocation error + */ + static native void createDatagramSocketImpl(FileDescriptor aFD, + boolean preferIPv4Stack) throws SocketException; - public int receiveStream(FileDescriptor aFD, byte[] data, int offset, - int count, int timeout) throws IOException { - return receiveStreamImpl(aFD, data, offset, count, timeout); + public void createServerStreamSocket(FileDescriptor fd, + boolean preferIPv4Stack) throws SocketException { + createServerStreamSocketImpl(fd, preferIPv4Stack); } - public int sendStream(FileDescriptor fd, byte[] data, int offset, int count) - throws IOException { - return sendStreamImpl(fd, data, offset, count); - } + /* + * Answer the result of attempting to create a server stream socket in the + * IP stack. Any special options required for server sockets will be set by + * this method. + * + * @param aFD the socket FileDescriptor @param preferIPv4Stack if use IPV4 + * @exception SocketException if an error occurs while creating the socket + */ + static native void createServerStreamSocketImpl(FileDescriptor aFD, + boolean preferIPv4Stack) throws SocketException; - public void shutdownInput(FileDescriptor descriptor) throws IOException { - shutdownInputImpl(descriptor); + public void createStreamSocket(FileDescriptor fd, + boolean preferIPv4Stack) throws SocketException { + createStreamSocketImpl(fd, preferIPv4Stack); } - public void shutdownOutput(FileDescriptor descriptor) throws IOException { - shutdownOutputImpl(descriptor); - } + static native void createStreamSocketImpl(FileDescriptor aFD, + boolean preferIPv4Stack) throws SocketException; - public boolean supportsUrgentData(FileDescriptor fd) { - return supportsUrgentDataImpl(fd); + /** + * Disconnect the socket to a port and address + *a + * @param fd + * the FileDescriptor associated with the socket + * + * @throws SocketException + * if the disconnect fails + */ + public void disconnectDatagram(FileDescriptor fd) + throws SocketException { + disconnectDatagramImpl(fd); } - public void sendUrgentData(FileDescriptor fd, byte value) { - sendUrgentDataImpl(fd, value); - } + static native void disconnectDatagramImpl(FileDescriptor aFD) + throws SocketException; - public int availableStream(FileDescriptor aFD) throws SocketException { - return availableStreamImpl(aFD); + public InetAddress getHostByAddr(byte[] ipAddress) + throws UnknownHostException { + // BEGIN android-changed + // Wallpaper fix for http://b/1851257. This is a layering violation, + // but at least the method has the right return type. + // TODO: Fix the socket code to remove this method altogether. + return InetAddress.getByAddress(ipAddress); + // END android-changed } - // BEGIN android-removed - // public void acceptStreamSocket(FileDescriptor fdServer, - // SocketImpl newSocket, FileDescriptor fdnewSocket, int timeout) - // throws IOException { - // acceptStreamSocketImpl(fdServer, newSocket, fdnewSocket, timeout); - // } - // - // public void createStreamSocket(FileDescriptor aFD, boolean preferIPv4Stack) - // throws SocketException { - // createStreamSocketImpl(aFD, preferIPv4Stack); - // } + // static native InetAddress getHostByAddrImpl(byte[] addr) + // throws UnknownHostException; // END android-removed - public void listenStreamSocket(FileDescriptor aFD, int backlog) - throws SocketException { - listenStreamSocketImpl(aFD, backlog); + // BEGIN android-removed + public InetAddress getHostByName(String hostName, + boolean preferIPv6Addresses) throws UnknownHostException { + // BEGIN android-changed + // Wallpaper fix for http://b/1851257. + return InetAddress.getByName(hostName); + // END android-changed } - + // BEGIN android-removed - // public boolean isReachableByICMP(final InetAddress dest, - // InetAddress source, final int ttl, final int timeout) { - // return INETADDR_REACHABLE == isReachableByICMPImpl(dest, source, ttl, - // timeout); - // } + // static native InetAddress getHostByNameImpl(String addr, + // boolean preferIPv6Addresses) throws UnknownHostException; // END android-removed - /* - * - * @param - * readChannels all channels interested in read and accept - * @param - * writeChannels all channels interested in write and connect - * @param timeout - * timeout in millis @return a set of channels that are ready for operation - * @throws - * SocketException @return int array, each int approve one of the * channel if OK - */ - - public int[] select(FileDescriptor[] readFDs, - FileDescriptor[] writeFDs, long timeout) - throws SocketException { - int countRead = readFDs.length; - int countWrite = writeFDs.length; - int result = 0; - if (0 == countRead + countWrite) { - return (new int[0]); - } - int[] flags = new int[countRead + countWrite]; + public int getSocketFlags() { + return getSocketFlagsImpl(); + } - // handle timeout in native - result = selectImpl(readFDs, writeFDs, countRead, countWrite, flags, - timeout); + public native String byteArrayToIpString(byte[] address) + throws UnknownHostException; - if (0 <= result) { - return flags; - } - if (ERRORCODE_SOCKET_TIMEOUT == result) { - return new int[0]; - } - throw new SocketException(); + public native byte[] ipStringToByteArray(String address) + throws UnknownHostException; - } + static native int getSocketFlagsImpl(); - public InetAddress getSocketLocalAddress(FileDescriptor aFD, + public InetAddress getSocketLocalAddress(FileDescriptor fd, boolean preferIPv6Addresses) { - return getSocketLocalAddressImpl(aFD, preferIPv6Addresses); + return getSocketLocalAddressImpl(fd, preferIPv6Addresses); } + static native InetAddress getSocketLocalAddressImpl(FileDescriptor aFD, + boolean preferIPv6Addresses); - /* + /** * Query the IP stack for the local port to which this socket is bound. - * - * @param aFD the socket descriptor @param preferIPv6Addresses address - * preference for nodes that support both IPv4 and IPv6 @return int the - * local port to which the socket is bound + * + * @param aFD + * the socket descriptor + * @param preferIPv6Addresses + * address preference for nodes that support both IPv4 and IPv6 + * @return the local port to which the socket is bound */ public int getSocketLocalPort(FileDescriptor aFD, boolean preferIPv6Addresses) { return getSocketLocalPortImpl(aFD, preferIPv6Addresses); } - /* + static native int getSocketLocalPortImpl(FileDescriptor aFD, + boolean preferIPv6Addresses); + + /** * Query the IP stack for the nominated socket option. - * - * @param aFD the socket descriptor @param opt the socket option type + * + * @param fd + * the socket descriptor + * @param opt + * the socket option type * @return the nominated socket option value - * - * @throws SocketException if the option is invalid + * @throws SocketException + * if the option is invalid */ - public Object getSocketOption(FileDescriptor aFD, int opt) + public Object getSocketOption(FileDescriptor fd, int opt) throws SocketException { - return getSocketOptionImpl(aFD, opt); + return getSocketOptionImpl(fd, opt); } - /* - * Set the nominated socket option in the IP stack. - * - * @param aFD the socket descriptor @param opt the option selector @param - * optVal the nominated option value - * - * @throws SocketException if the option is invalid or cannot be set - */ - public void setSocketOption(FileDescriptor aFD, int opt, Object optVal) - throws SocketException { - setSocketOptionImpl(aFD, opt, optVal); - } + static native Object getSocketOptionImpl(FileDescriptor aFD, int opt) + throws SocketException; - public int getSocketFlags() { - return getSocketFlagsImpl(); + public Channel inheritedChannel() { + return inheritedChannelImpl(); } - /* - * Close the socket in the IP stack. - * - * @param aFD the socket descriptor - */ - public void socketClose(FileDescriptor aFD) throws IOException { - socketCloseImpl(aFD); - } + native Channel inheritedChannelImpl(); - public InetAddress getHostByAddr(byte[] addr) throws UnknownHostException { - return getHostByAddrImpl(addr); - } + // BEGIN android-removed + // public boolean isReachableByICMP(final InetAddress dest, + // InetAddress source, final int ttl, final int timeout) { + // return INETADDR_REACHABLE == isReachableByICMPImpl(dest, source, ttl, + // timeout); + // } - public InetAddress getHostByName(String addr, boolean preferIPv6Addresses) - throws UnknownHostException { - return getHostByNameImpl(addr, preferIPv6Addresses); + // native int isReachableByICMPImpl(InetAddress addr, + // InetAddress local, int ttl, int timeout); + // END android-removed + + public void listenStreamSocket(FileDescriptor aFD, int backlog) + throws SocketException { + listenStreamSocketImpl(aFD, backlog); } - public void setInetAddress(InetAddress sender, byte[] address) { - setInetAddressImpl(sender, address); + static native void listenStreamSocketImpl(FileDescriptor aFD, int backlog) + throws SocketException; + + public void oneTimeInitialization(boolean jcl_supports_ipv6) { + if (!isNetworkInited) { + oneTimeInitializationImpl(jcl_supports_ipv6); + isNetworkInited = true; + } } - // --------------------------------------------------- - // Native Codes - // --------------------------------------------------- + native void oneTimeInitializationImpl (boolean jcl_supports_ipv6); + + /** + * Peek on the socket, update sender address and answer the + * sender port. + * + * @param fd + * the socket FileDescriptor + * @param sender + * an InetAddress, to be updated with the sender's address + * @param receiveTimeout + * the maximum length of time the socket should block, reading + * @return the sender port + * + * @throws IOException + * upon an read error or timeout + */ + public int peekDatagram(FileDescriptor fd, InetAddress sender, + int receiveTimeout) throws IOException { + return peekDatagramImpl(fd, sender, receiveTimeout); + } - static native void createSocketImpl(FileDescriptor fd, - boolean preferIPv4Stack); + static native int peekDatagramImpl(FileDescriptor aFD, + InetAddress sender, int receiveTimeout) throws IOException; - /* - * Allocate a datagram socket in the IP stack. The socket is associated with - * the aFD. - * - * @param aFD the FileDescriptor to associate with the socket @param - * preferIPv4Stack IP stack preference if underlying platform is V4/V6 - * @exception SocketException upon an allocation error + /** + * Read available bytes from the given file descriptor into a byte array. + * + * The read has an optional timeout parameter, which if non-zero is the + * length of time that the read will wait on a select call to see if any + * bytes are available for reading. If the timeout expires the method + * returns zero to indicate no bytes were read. + * + * @param fd + * the socket file descriptor to read + * @param data + * the byte array in which to store the results + * @param offset + * the offset into the byte array in which to start reading the + * results + * @param count + * the maximum number of bytes to read + * @param timeout + * the length of time to wait for the bytes, in milliseconds; or + * zero to indicate no timeout applied. When there is no timeout + * applied the read may block based upon socket options. + * @return number of bytes read, or zero if there were no bytes available + * before the timeout occurred, or -1 to indicate the socket is + * closed + * @throws IOException + * if an underlying socket exception occurred */ - static native void createDatagramSocketImpl(FileDescriptor aFD, - boolean preferIPv4Stack) throws SocketException; + public int read(FileDescriptor fd, byte[] data, int offset, int count, + int timeout) throws IOException { + return readSocketImpl(fd, data, offset, count, timeout); + } static native int readSocketImpl(FileDescriptor aFD, byte[] data, int offset, int count, int timeout) throws IOException; - - static native int readSocketDirectImpl(FileDescriptor aFD, int address, - int offset, int count, int timeout) throws IOException; - - static native int writeSocketImpl(FileDescriptor fd, byte[] data, - int offset, int count) throws IOException; - - static native int writeSocketDirectImpl(FileDescriptor fd, int address, - int offset, int count) throws IOException; - static native void setNonBlockingImpl(FileDescriptor aFD, - boolean block); - - static native int connectSocketImpl(FileDescriptor aFD, - int trafficClass, InetAddress inetAddress, int port); - - // BEGIN android-changed - static native int connectWithTimeoutSocketImpl( - FileDescriptor aFD, int timeout, int trafficClass, - InetAddress hostname, int port, int step, byte[] context); - // END android-changed - - static native void connectStreamWithTimeoutSocketImpl(FileDescriptor aFD, - int aport, int timeout, int trafficClass, InetAddress inetAddress) - throws IOException; - - static native void socketBindImpl(FileDescriptor aFD, int port, - InetAddress inetAddress) throws SocketException; - - static native void listenStreamSocketImpl(FileDescriptor aFD, int backlog) - throws SocketException; - - static native int availableStreamImpl(FileDescriptor aFD) - throws SocketException; - - static native void acceptSocketImpl(FileDescriptor fdServer, - SocketImpl newSocket, FileDescriptor fdnewSocket, int timeout) - throws IOException; - - static native boolean supportsUrgentDataImpl(FileDescriptor fd); - - static native void sendUrgentDataImpl(FileDescriptor fd, byte value); - - /* - * Connect the socket to a port and address - * - * @param aFD the FileDescriptor to associate with the socket @param port - * the port to connect to @param trafficClass the traffic Class to be used - * then the connection is made @param inetAddress address to connect to. - * - * @exception SocketException if the connect fails - */ - static native void connectDatagramImpl2(FileDescriptor aFD, - int port, int trafficClass, InetAddress inetAddress) - throws SocketException; - - /* - * Disconnect the socket to a port and address - * - * @param aFD the FileDescriptor to associate with the socket - * - * @exception SocketException if the disconnect fails - */ - static native void disconnectDatagramImpl(FileDescriptor aFD) - throws SocketException; - - /* - * Allocate a datagram socket in the IP stack. The socket is associated with - * the aFD. - * - * @param aFD the FileDescriptor to associate with the socket @param - * preferIPv4Stack IP stack preference if underlying platform is V4/V6 - * @exception SocketException upon an allocation error - */ - - /* - * Bind the socket to the port/localhost in the IP stack. - * - * @param aFD the socket descriptor @param port the option selector @param - * bindToDevice bind the socket to the specified interface @param - * inetAddress address to connect to. @return if bind successful @exception - * SocketException thrown if bind operation fails + /** + * Read available bytes from the given file descriptor into OS memory at a + * given address. + * + * @param fd + * the socket file descriptor to read + * @param address + * the address of the memory in which to store the results + * @param count + * the maximum number of bytes to read + * @param timeout + * the length of time to wait for the bytes, in milliseconds + * @return number of bytes read, or zero if there were no bytes available + * before the timeout occurred, or -1 to indicate the socket is + * closed + * @throws IOException + * if an underlying socket exception occurred */ - static native boolean socketBindImpl2(FileDescriptor aFD, - int port, boolean bindToDevice, InetAddress inetAddress) - throws SocketException; + public int readDirect(FileDescriptor fd, int address, int offset, int count, + int timeout) throws IOException { + return readSocketDirectImpl(fd, address, offset, count, timeout); + } - /* - * Peek on the socket, update sender address and answer the - * sender port. - * - * @param aFD the socket FileDescriptor @param sender an InetAddress, to be - * updated with the sender's address @param receiveTimeout the maximum - * length of time the socket should block, reading @return int the sender - * port - * - * @exception IOException upon an read error or timeout - */ - static native int peekDatagramImpl(FileDescriptor aFD, - InetAddress sender, int receiveTimeout) throws IOException; + static native int readSocketDirectImpl(FileDescriptor aFD, int address, int offset, int count, + int timeout) throws IOException; - /* - * Recieve data on the socket into the specified buffer. The packet fields + /** + * Receive data on the socket into the specified buffer. The packet fields * data & length are passed in addition to * packet to eliminate the JNI field access calls. - * - * @param aFD the socket FileDescriptor @param packet the DatagramPacket to - * receive into @param data the data buffer of the packet @param offset the - * offset in the data buffer @param length the length of the data buffer in - * the packet @param receiveTimeout the maximum length of time the socket - * should block, reading @param peek indicates to peek at the data @return - * number of data received @exception IOException upon an read error or - * timeout + * + * @param fd + * the socket FileDescriptor + * @param packet + * the DatagramPacket to receive into + * @param data + * the data buffer of the packet + * @param offset + * the offset in the data buffer + * @param length + * the length of the data buffer in the packet + * @param receiveTimeout + * the maximum length of time the socket should block, reading + * @param peek + * indicates to peek at the data + * @return number of data received + * @throws IOException + * upon an read error or timeout */ + public int receiveDatagram(FileDescriptor fd, DatagramPacket packet, + byte[] data, int offset, int length, int receiveTimeout, + boolean peek) throws IOException { + return receiveDatagramImpl(fd, packet, data, offset, length, + receiveTimeout, peek); + } + static native int receiveDatagramImpl(FileDescriptor aFD, DatagramPacket packet, byte[] data, int offset, int length, int receiveTimeout, boolean peek) throws IOException; - + + public int receiveDatagramDirect(FileDescriptor fd, + DatagramPacket packet, int address, int offset, int length, + int receiveTimeout, boolean peek) throws IOException { + return receiveDatagramDirectImpl(fd, packet, address, offset, length, + receiveTimeout, peek); + } + static native int receiveDatagramDirectImpl(FileDescriptor aFD, DatagramPacket packet, int address, int offset, int length, int receiveTimeout, boolean peek) throws IOException; - /* + /** + * Receive at most count bytes into the buffer + * data at the offset on the socket. + * + * @param aFD + * the socket FileDescriptor + * @param data + * the receive buffer + * @param offset + * the offset into the buffer + * @param count + * the max number of bytes to receive + * @param timeout + * the max time the read operation should block waiting for data + * @return the actual number of bytes read + * @throws IOException + * @throws SocketException + * if an error occurs while reading + * @deprecated use {@link #read(FileDescriptor, byte[], int, int, int)} + */ + public int receiveStream(FileDescriptor aFD, byte[] data, + int offset, int count, int timeout) throws IOException { + return receiveStreamImpl(aFD, data, offset, count, timeout); + } + + static native int receiveStreamImpl(FileDescriptor aFD, byte[] data, + int offset, int count, int timeout) throws IOException; + + // BEGIN android-added + /** + * Send count bytes from the buffer data at + * the offset, on the socket. + * + * @param fd + * + * @param data the send buffer @param offset the offset into the buffer + * @param count the number of bytes to receive @return int the actual number + * of bytes sent @throws IOException @exception SocketException if an error + * occurs while writing + */ + public int sendStream(FileDescriptor fd, byte[] data, int offset, int count) + throws IOException { + return sendStreamImpl(fd, data, offset, count); + } + + static native int sendStreamImpl(FileDescriptor fd, byte[] data, + int offset, int count) throws IOException; + // END android-added + + /** * Recieve data on the connected socket into the specified buffer. The - * packet fields data & length are passed in - * addition to packet to eliminate the JNI field access - * calls. - * - * @param aFD the socket FileDescriptor @param packet the DatagramPacket to - * receive into @param data the data buffer of the packet @param offset the - * offset in the data buffer @param length the length of the data buffer in - * the packet @param receiveTimeout the maximum length of time the socket - * should block, reading @param peek indicates to peek at the data @return - * number of data received @exception IOException upon an read error or - * timeout + * packet fields data and length are passed in + * addition to packet to eliminate the JNI field access calls. + * + * @param fd + * the socket FileDescriptor + * @param packet + * the DatagramPacket to receive into + * @param data + * the data buffer of the packet + * @param offset + * the offset in the data buffer + * @param length + * the length of the data buffer in the packet + * @param receiveTimeout + * the maximum length of time the socket should block, reading + * @param peek + * indicates to peek at the data + * @return number of data received + * @throws IOException + * upon an read error or timeout */ + public int recvConnectedDatagram(FileDescriptor fd, + DatagramPacket packet, byte[] data, int offset, int length, + int receiveTimeout, boolean peek) throws IOException { + return recvConnectedDatagramImpl(fd, packet, data, offset, length, + receiveTimeout, peek); + } + static native int recvConnectedDatagramImpl(FileDescriptor aFD, DatagramPacket packet, byte[] data, int offset, int length, int receiveTimeout, boolean peek) throws IOException; - + + public int recvConnectedDatagramDirect(FileDescriptor aFD, DatagramPacket packet, int address, + int offset, int length, int receiveTimeout, boolean peek) + throws IOException { + return recvConnectedDatagramDirectImpl(aFD, packet, address, offset, length, receiveTimeout, peek); + } + static native int recvConnectedDatagramDirectImpl(FileDescriptor aFD, DatagramPacket packet, int address, int offset, int length, int receiveTimeout, boolean peek) throws IOException; - /* - * Send the data to the nominated target address - * and port. These values are derived from the - * DatagramPacket to reduce the field calls within JNI. - * - * @param fd the socket FileDescriptor @param data the data buffer of the - * packet @param offset the offset in the data buffer @param length the - * length of the data buffer in the packet @param port the target host port - * @param bindToDevice if bind to device @param trafficClass the traffic - * class to be used when the datagram is sent @param inetAddress address to - * connect to. @return number of data send - * - * @exception IOException upon an read error or timeout + /** + * Select the given file descriptors for read and write operations. + * + * The file descriptors passed in as readFDs will be selected for read-ready + * operations, and those in the writeFDs will be selected for write-ready + * operations. A file descriptor can appear in either or both array, and + * must not be null. If the file descriptor is closed during + * the select the behavior depends upon the underlying OS. + * + * Upon return the result is a single array of length + * readFDs.length + writeFDs.length laid out as + * the result of the select operation on the corresponding file descriptors. + * + * @param readFDs + * all sockets interested in read and accept + * @param writeFDs + * all sockets interested in write and connect + * @param timeout + * timeout in milliseconds + * @return each element describes the corresponding state of the descriptor + * in the read and write arrays. + * @throws SocketException */ - static native int sendDatagramImpl(FileDescriptor fd, - byte[] data, int offset, int length, int port, - boolean bindToDevice, int trafficClass, InetAddress inetAddress) - throws IOException; - - static native int sendDatagramDirectImpl(FileDescriptor fd, - int address, int offset, int length, int port, - boolean bindToDevice, int trafficClass, InetAddress inetAddress) - throws IOException; + public int[] select(FileDescriptor[] readFDs, FileDescriptor[] writeFDs, + long timeout) throws SocketException { + int countRead = readFDs.length; + int countWrite = writeFDs.length; + int result = 0; + if (0 == countRead + countWrite) { + return (new int[0]); + } + int[] flags = new int[countRead + countWrite]; - /* + assert validateFDs(readFDs, writeFDs) : "Invalid file descriptor arrays"; //$NON-NLS-1$ + + // handle timeout in native + result = selectImpl(readFDs, writeFDs, countRead, countWrite, flags, + timeout); + + if (0 <= result) { + return flags; + } + if (ERRORCODE_SOCKET_TIMEOUT == result || + ERRORCODE_SOCKET_INTERRUPTED == result) { + return new int[0]; + } + throw new SocketException(); + } + + static native int selectImpl(FileDescriptor[] readfd, + FileDescriptor[] writefd, int cread, int cwirte, int[] flags, + long timeout); + + /** * Send the data to the address and port to which the was - * connnected and port. - * - * @param fd the socket FileDescriptor @param data the data buffer of the - * packet @param offset the offset in the data buffer @param length the - * length of the data buffer in the packet @param bindToDevice not used, - * current kept in case needed as was the case for sendDatagramImpl @return - * number of data send @exception IOException upon an read error or timeout + * connected and port. + * + * @param fd + * the socket FileDescriptor + * @param data + * the data buffer of the packet + * @param offset + * the offset in the data buffer + * @param length + * the length of the data buffer in the packet + * @param bindToDevice + * not used, current kept in case needed as was the case for + * sendDatagramImpl + * @return number of data send + * @throws IOException + * upon an read error or timeout */ + public int sendConnectedDatagram(FileDescriptor fd, byte[] data, + int offset, int length, boolean bindToDevice) throws IOException { + return sendConnectedDatagramImpl(fd, data, offset, length, bindToDevice); + } + static native int sendConnectedDatagramImpl(FileDescriptor fd, byte[] data, int offset, int length, boolean bindToDevice) throws IOException; - + + public int sendConnectedDatagramDirect(FileDescriptor fd, + int address, int offset, int length, boolean bindToDevice) + throws IOException { + return sendConnectedDatagramDirectImpl(fd, address, offset, length, bindToDevice); + } static native int sendConnectedDatagramDirectImpl(FileDescriptor fd, int address, int offset, int length, boolean bindToDevice) throws IOException; - /* - * Answer the result of attempting to create a server stream socket in the - * IP stack. Any special options required for server sockets will be set by - * this method. - * - * @param aFD the socket FileDescriptor @param preferIPv4Stack if use IPV4 - * @exception SocketException if an error occurs while creating the socket + /** + * Send the data to the nominated target address + * and port. These values are derived from the DatagramPacket + * to reduce the field calls within JNI. + * + * @param fd + * the socket FileDescriptor + * @param data + * the data buffer of the packet + * @param offset + * the offset in the data buffer + * @param length + * the length of the data buffer in the packet + * @param port + * the target host port + * @param bindToDevice + * if bind to device + * @param trafficClass + * the traffic class to be used when the datagram is sent + * @param inetAddress + * address to connect to. + * @return number of data send + * + * @throws IOException + * upon an read error or timeout */ - static native void createServerStreamSocketImpl(FileDescriptor aFD, - boolean preferIPv4Stack) throws SocketException; + public int sendDatagram(FileDescriptor fd, byte[] data, int offset, + int length, int port, boolean bindToDevice, int trafficClass, + InetAddress inetAddress) throws IOException { + return sendDatagramImpl(fd, data, offset, length, port, bindToDevice, + trafficClass, inetAddress); + } - /* - * Answer the result of attempting to create a multicast socket in the IP - * stack. Any special options required for server sockets will be set by - * this method. - * - * @param aFD the socket FileDescriptor @param preferIPv4Stack if use IPV4 - * @exception SocketException if an error occurs while creating the socket - */ - static native void createMulticastSocketImpl(FileDescriptor aFD, - boolean preferIPv4Stack) throws SocketException; + static native int sendDatagramImpl(FileDescriptor fd, byte[] data, int offset, + int length, int port, boolean bindToDevice, int trafficClass, + InetAddress inetAddress) throws IOException; - /* - * Recieve at most count bytes into the buffer data - * at the offset on the socket. - * - * @param aFD the socket FileDescriptor @param data the receive buffer - * @param offset the offset into the buffer @param count the max number of - * bytes to receive @param timeout the max time the read operation should - * block waiting for data @return int the actual number of bytes read - * @throws IOException @exception SocketException if an error occurs while - * reading - */ - static native int receiveStreamImpl(FileDescriptor aFD, byte[] data, - int offset, int count, int timeout) throws IOException; + public int sendDatagram2(FileDescriptor fd, byte[] data, int offset, + int length, int port, InetAddress inetAddress) throws IOException { + return sendDatagramImpl2(fd, data, offset, length, port, inetAddress); + } - /* - * Send count bytes from the buffer data at - * the offset, on the socket. - * - * @param fd - * - * @param data the send buffer @param offset the offset into the buffer - * @param count the number of bytes to receive @return int the actual number - * of bytes sent @throws IOException @exception SocketException if an error - * occurs while writing - */ - static native int sendStreamImpl(FileDescriptor fd, byte[] data, - int offset, int count) throws IOException; + static native int sendDatagramImpl2(FileDescriptor fd, byte[] data, + int offset, int length, int port, InetAddress inetAddress) throws IOException; - private native void shutdownInputImpl(FileDescriptor descriptor) - throws IOException; - private native void shutdownOutputImpl(FileDescriptor descriptor) - throws IOException; + public int sendDatagramDirect(FileDescriptor fd, int address, + int offset, int length, int port, boolean bindToDevice, + int trafficClass, InetAddress inetAddress) throws IOException { + return sendDatagramDirectImpl(fd, address, offset, length, port, bindToDevice, + trafficClass, inetAddress); + } - // BEGIN android-removed - // static native void acceptStreamSocketImpl(FileDescriptor fdServer, - // SocketImpl newSocket, FileDescriptor fdnewSocket, int timeout) - // throws IOException; - // - // static native void createStreamSocketImpl(FileDescriptor aFD, - // boolean preferIPv4Stack) throws SocketException; - // END android-removed + static native int sendDatagramDirectImpl(FileDescriptor fd, int address, + int offset, int length, int port, boolean bindToDevice, + int trafficClass, InetAddress inetAddress) throws IOException; - static native int sendDatagramImpl2(FileDescriptor fd, byte[] data, - int offset, int length, int port, InetAddress inetAddress) - throws IOException; + public void sendUrgentData(FileDescriptor fd, byte value) { + sendUrgentDataImpl(fd, value); + } - static native int selectImpl(FileDescriptor[] readfd, - FileDescriptor[] writefd, int cread, int cwirte, int[] flags, - long timeout); + static native void sendUrgentDataImpl(FileDescriptor fd, byte value); - static native InetAddress getSocketLocalAddressImpl(FileDescriptor aFD, - boolean preferIPv6Addresses); + public void setInetAddress(InetAddress sender, byte[] address) { + setInetAddressImpl(sender, address); + } - /* - * Query the IP stack for the local port to which this socket is bound. - * - * @param aFD the socket descriptor @param preferIPv6Addresses address - * preference for nodes that support both IPv4 and IPv6 @return int the - * local port to which the socket is bound - */ - static native int getSocketLocalPortImpl(FileDescriptor aFD, - boolean preferIPv6Addresses); + native void setInetAddressImpl(InetAddress sender, byte[] address); - /* - * Query the IP stack for the nominated socket option. - * - * @param aFD the socket descriptor @param opt the socket option type - * @return the nominated socket option value - * - * @throws SocketException if the option is invalid - */ - static native Object getSocketOptionImpl(FileDescriptor aFD, int opt) - throws SocketException; + public void setNonBlocking(FileDescriptor fd, boolean block) + throws IOException { + setNonBlockingImpl(fd, block); + } - /* + static native void setNonBlockingImpl(FileDescriptor aFD, boolean block); + + /** * Set the nominated socket option in the IP stack. - * - * @param aFD the socket descriptor @param opt the option selector @param - * optVal the nominated option value - * - * @throws SocketException if the option is invalid or cannot be set + * + * @param aFD + * the socket descriptor @param opt the option selector @param + * optVal the nominated option value + * + * @throws SocketException + * if the option is invalid or cannot be set */ + public void setSocketOption(FileDescriptor aFD, int opt, + Object optVal) throws SocketException { + setSocketOptionImpl(aFD, opt, optVal); + } + static native void setSocketOptionImpl(FileDescriptor aFD, int opt, Object optVal) throws SocketException; - static native int getSocketFlagsImpl(); + public void shutdownInput(FileDescriptor descriptor) throws IOException { + shutdownInputImpl(descriptor); + } - /* + private native void shutdownInputImpl(FileDescriptor descriptor) + throws IOException; + + public void shutdownOutput(FileDescriptor fd) throws IOException { + shutdownOutputImpl(fd); + } + + private native void shutdownOutputImpl(FileDescriptor descriptor) + throws IOException; + /** * Close the socket in the IP stack. - * - * @param aFD the socket descriptor + * + * @param fd + * the socket descriptor */ - static native void socketCloseImpl(FileDescriptor aFD); + public void socketClose(FileDescriptor fd) throws IOException { + socketCloseImpl(fd); + } - static native InetAddress getHostByAddrImpl(byte[] addr) - throws UnknownHostException; + static native void socketCloseImpl(FileDescriptor fD); - static native InetAddress getHostByNameImpl(String addr, - boolean preferIPv6Addresses) throws UnknownHostException; + public boolean supportsUrgentData(FileDescriptor fd) { + return supportsUrgentDataImpl(fd); + } - native void setInetAddressImpl(InetAddress sender, byte[] address); + static native boolean supportsUrgentDataImpl(FileDescriptor fd); - // BEGIN android-removed - // native int isReachableByICMPImpl(InetAddress addr, InetAddress local, - // int ttl, int timeout); - // END android-removed - - native Channel inheritedChannelImpl(); + /* + * Used to check if the file descriptor arrays are valid before passing them + * into the select native call. + */ + private boolean validateFDs(FileDescriptor[] readFDs, + FileDescriptor[] writeFDs) { + for (FileDescriptor fd : readFDs) { + // Also checks fd not null + if (!fd.valid()) { + return false; + } + } + for (FileDescriptor fd : writeFDs) { + if (!fd.valid()) { + return false; + } + } + return true; + } - public Channel inheritedChannel() { - return inheritedChannelImpl(); + /** + * Write bytes from a byte array to a socket. + * + * @param fd + * the socket on which to write the bytes + * @param data + * the array containing the bytes to be written + * @param offset + * the offset in the byte array from which to take the bytes + * @param count + * the maximum number of bytes to be written. Callers are trusted + * not to send values of length+count that are larger than + * data.length + * @return the actual number of bytes written, which will be between zero + * and count + * @throws IOException + * if there is an underlying socket problem + */ + public int write(FileDescriptor fd, byte[] data, int offset, int count) + throws IOException { + return writeSocketImpl(fd, data, offset, count); } - - public void oneTimeInitialization(boolean jcl_supports_ipv6){ - if (!isNetworkInited){ - oneTimeInitializationImpl(jcl_supports_ipv6); - isNetworkInited = true; - } + + static native int writeSocketImpl(FileDescriptor fd, byte[] data, int offset, + int count) throws IOException; + + + /** + * Write bytes from the given address to a socket. + * + * @param fd + * the socket on which to write the bytes + * @param address + * the start address of the bytes to be written + * @param count + * the maximum number of bytes to be written + * @return the actual number of bytes written, which will be between zero + * and count + * @throws IOException + * if there is an underlying socket problem + */ + public int writeDirect(FileDescriptor fd, int address, int offset, int count) + throws IOException { + return writeSocketDirectImpl(fd, address, offset, count); } - - native void oneTimeInitializationImpl (boolean jcl_supports_ipv6); + + static native int writeSocketDirectImpl(FileDescriptor fd, int address, int offset, int count) + throws IOException; } diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/platform/Platform.java b/libcore/luni/src/main/java/org/apache/harmony/luni/platform/Platform.java index 68d2515b2..ab71a0059 100644 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/platform/Platform.java +++ b/libcore/luni/src/main/java/org/apache/harmony/luni/platform/Platform.java @@ -21,25 +21,33 @@ import dalvik.system.VMStack; // END android-changed /** - * Platform - * + * The Platform class gives access to the low-level underlying capabilities of + * the operating system. + * + * The platform is structured into operations on the process heap memory, + * network subsystem, and file system through different OS components. + * + * OS components are 'dangerous' in that they pass through the calls and + * arguments to the OS with very little checking, and as such may cause fatal + * exceptions in the runtime. Access to the OS components is restricted to + * trusted code running on the system classpath. + * + * @see IFileSystem + * @see INetworkSystem + * @see IMemorySystem */ public class Platform { - static final IAdapterManager ADAPTER_MANAGER = new AdapterManager(); + static final IFileSystem FILE_SYSTEM = OSFileSystem.getOSFileSystem(); - static final IFileSystem FILE_SYSTEM = OSComponentFactory.getFileSystem(); + static final IMemorySystem MEMORY_SYSTEM = OSMemory.getOSMemory(); - static final IMemorySystem MEMORY_SYSTEM = OSComponentFactory - .getMemorySystem(); - - static final INetworkSystem NETWORK_SYSTEM = OSComponentFactory - .getNetworkSystem(); - - public static IAdapterManager getAdapterManager() { - return ADAPTER_MANAGER; - } + static final INetworkSystem NETWORK_SYSTEM = OSNetworkSystem.getOSNetworkSystem(); + /** + * Checks to ensure that whoever is asking for the OS component is running + * on the system classpath. + */ private static final void accessCheck() { // BEGIN android-changed if (VMStack.getCallingClassLoader() != null) { @@ -48,16 +56,31 @@ public class Platform { // END android-changed } + /** + * Answers the instance that interacts directly with the OS file system. + * + * @return a low-level file system interface. + */ public static IFileSystem getFileSystem() { accessCheck(); return FILE_SYSTEM; } + /** + * Answers the instance that interacts directly with the OS memory system. + * + * @return a low-level memory system interface. + */ public static IMemorySystem getMemorySystem() { accessCheck(); return MEMORY_SYSTEM; } + /** + * Answers the instance that interacts directly with the OS network system. + * + * @return a low-level network system interface. + */ public static INetworkSystem getNetworkSystem() { accessCheck(); return NETWORK_SYSTEM; diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/platform/RuntimeMemorySpy.java b/libcore/luni/src/main/java/org/apache/harmony/luni/platform/RuntimeMemorySpy.java index e897506a3..3692819e3 100644 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/platform/RuntimeMemorySpy.java +++ b/libcore/luni/src/main/java/org/apache/harmony/luni/platform/RuntimeMemorySpy.java @@ -20,17 +20,18 @@ import java.lang.ref.Reference; final class RuntimeMemorySpy extends AbstractMemorySpy { - public RuntimeMemorySpy() { - super(); - } + public RuntimeMemorySpy() { + super(); + } + + public void alloc(PlatformAddress address) { + // Pay a tax on the allocation to see if there are any frees pending. + Reference ref = notifyQueue.poll(); // non-blocking check + while (ref != null) { + orphanedMemory(ref); + ref = notifyQueue.poll(); + } - public void alloc(PlatformAddress address) { super.alloc(address); - // Pay a tax on the allocation to see if there are any frees pending. - Reference ref = notifyQueue.poll(); // non-blocking check - while (ref != null) { - orphanedMemory(ref); - ref = notifyQueue.poll(); - } - } + } } diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/util/DeleteOnExit.java b/libcore/luni/src/main/java/org/apache/harmony/luni/util/DeleteOnExit.java new file mode 100644 index 000000000..ac3275ea2 --- /dev/null +++ b/libcore/luni/src/main/java/org/apache/harmony/luni/util/DeleteOnExit.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.luni.util; + + +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; + +// BEGIN android-changed +/** + * Implements the actual DeleteOnExit mechanism. Is registered as a shutdown + * hook in the Runtime, once it is actually being used. + */ +public class DeleteOnExit extends Thread { + + /** + * Our singleton instance. + */ + private static DeleteOnExit instance; + + /** + * Our list of files scheduled for deletion. + */ + private ArrayList files = new ArrayList(); + + /** + * Returns our singleton instance, creating it if necessary. + */ + public static synchronized DeleteOnExit getInstance() { + if (instance == null) { + instance = new DeleteOnExit(); + Runtime.getRuntime().addShutdownHook(instance); + } + + return instance; + } + + /** + * Schedules a file for deletion. + * + * @param filename The file to delete. + */ + public void addFile(String filename) { + synchronized(files) { + if (!files.contains(filename)) { + files.add(filename); + } + } + } + + /** + * Does the actual work. Note we (a) first sort the files lexicographically + * and then (b) delete them in reverse order. This is to make sure files + * get deleted before their parent directories. + */ + @Override + public void run() { + Collections.sort(files); + for (int i = files.size() - 1; i >= 0; i--) { + new File(files.get(i)).delete(); + } + } +} +// END android-changed diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/util/ExposedByteArrayInputStream.java b/libcore/luni/src/main/java/org/apache/harmony/luni/util/ExposedByteArrayInputStream.java new file mode 100644 index 000000000..0ff073cd6 --- /dev/null +++ b/libcore/luni/src/main/java/org/apache/harmony/luni/util/ExposedByteArrayInputStream.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.luni.util; + +import java.io.ByteArrayInputStream; + +/** + * The extension of ByteArrayInputStream which exposes an + * underlying buffer. + */ +public class ExposedByteArrayInputStream extends ByteArrayInputStream { + + /** + * @see java.io.ByteArrayInputStream(byte[]) + */ + public ExposedByteArrayInputStream(byte buf[]) { + super(buf); + } + + /** + * @see java.io.ByteArrayInputStream(byte[], int, int) + */ + public ExposedByteArrayInputStream(byte buf[], int offset, int length) { + super(buf, offset, length); + } + + /** + * Reads the whole stream and returns the stream snapshot. + */ + public synchronized byte[] expose() { + if (pos == 0 && count == buf.length) { + skip(count); + return buf; + } + + final int available = available(); + final byte[] buffer = new byte[available]; + System.arraycopy(buf, pos, buffer, 0, available); + skip(available); + return buffer; + } +} diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/util/ExternalMessages.properties b/libcore/luni/src/main/java/org/apache/harmony/luni/util/ExternalMessages.properties index bc1688fa0..280a8f565 100644 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/util/ExternalMessages.properties +++ b/libcore/luni/src/main/java/org/apache/harmony/luni/util/ExternalMessages.properties @@ -61,8 +61,8 @@ K0045=Attempted to set a negative SoLinger K0046=Local port declared out of range K0047=buffer is null K0048=Invalid action specified\: {0} -K0049=MinPort is greater than MaxPort -K004a=Invalid port number specified +K0049=MinPort is greater than MaxPort\: {0} +K004a=Invalid port number specified\: {0} K004b=Attempt to set factory more than once. K004c=Package is sealed K004d=Does not support writing to the input stream @@ -288,6 +288,7 @@ K034c=proxy should not be null K034d=method has not been implemented yet K034e=Build rules empty K0351=format is null +K0352=package is sealed KA000=Line too long KA001=Argument must not be null KA002=Unshared read of back reference @@ -322,3 +323,6 @@ KA024=One of urls is null KA025=Method has not been implemented KA026=JAR entry {0} not found in {1} KA027=Inputstream of the JarURLConnection has been closed +KA028=Cannot set protocol version when stream in use +KA029=Can't find resource for bundle {0}, key {1} +KA030=Write end dead diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/util/FloatingPointParser.java b/libcore/luni/src/main/java/org/apache/harmony/luni/util/FloatingPointParser.java index 820de0f0a..873403976 100644 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/util/FloatingPointParser.java +++ b/libcore/luni/src/main/java/org/apache/harmony/luni/util/FloatingPointParser.java @@ -17,8 +17,6 @@ package org.apache.harmony.luni.util; -import java.util.regex.Matcher; -import java.util.regex.Pattern; /** * Used to parse a string and return either a single or double precision @@ -26,94 +24,94 @@ import java.util.regex.Pattern; */ public final class FloatingPointParser { - private static final class StringExponentPair { - String s; - - int e; - - boolean negative; - - StringExponentPair(String s, int e, boolean negative) { - this.s = s; - this.e = e; - this.negative = negative; - } - } - - /** - * Takes a String and an integer exponent. The String should hold a positive - * integer value (or zero). The exponent will be used to calculate the - * floating point number by taking the positive integer the String - * represents and multiplying by 10 raised to the power of the of the - * exponent. Returns the closest double value to the real number - * - * @param s - * the String that will be parsed to a floating point - * @param e - * an int represent the 10 to part - * @return the double closest to the real number - * - * @exception NumberFormatException - * if the String doesn't represent a positive integer value - */ - private static native double parseDblImpl(String s, int e); - - /** - * Takes a String and an integer exponent. The String should hold a positive - * integer value (or zero). The exponent will be used to calculate the - * floating point number by taking the positive integer the String - * represents and multiplying by 10 raised to the power of the of the - * exponent. Returns the closest float value to the real number - * - * @param s - * the String that will be parsed to a floating point - * @param e - * an int represent the 10 to part - * @return the float closest to the real number - * - * @exception NumberFormatException - * if the String doesn't represent a positive integer value - */ - private static native float parseFltImpl(String s, int e); - - /** - * Takes a String and does some initial parsing. Should return a - * StringExponentPair containing a String with no leading or trailing white - * space and trailing zeroes eliminated. The exponent of the - * StringExponentPair will be used to calculate the floating point number by - * taking the positive integer the String represents and multiplying by 10 - * raised to the power of the of the exponent. - * - * @param s - * the String that will be parsed to a floating point - * @param length - * the length of s - * @return a StringExponentPair with necessary values - * - * @exception NumberFormatException - * if the String doesn't pass basic tests - */ - private static StringExponentPair initialParse(String s, int length) { - boolean negative = false; - char c; - int start, end, decimal; - int e = 0; - - start = 0; - if (length == 0) - throw new NumberFormatException(s); - - c = s.charAt(length - 1); - if (c == 'D' || c == 'd' || c == 'F' || c == 'f') { - length--; - if (length == 0) - throw new NumberFormatException(s); - } - - end = Math.max(s.indexOf('E'), s.indexOf('e')); - if (end > -1) { - if (end + 1 == length) - throw new NumberFormatException(s); + private static final class StringExponentPair { + String s; + + int e; + + boolean negative; + + StringExponentPair(String s, int e, boolean negative) { + this.s = s; + this.e = e; + this.negative = negative; + } + } + + /** + * Takes a String and an integer exponent. The String should hold a positive + * integer value (or zero). The exponent will be used to calculate the + * floating point number by taking the positive integer the String + * represents and multiplying by 10 raised to the power of the of the + * exponent. Returns the closest double value to the real number + * + * @param s + * the String that will be parsed to a floating point + * @param e + * an int represent the 10 to part + * @return the double closest to the real number + * + * @exception NumberFormatException + * if the String doesn't represent a positive integer value + */ + private static native double parseDblImpl(String s, int e); + + /** + * Takes a String and an integer exponent. The String should hold a positive + * integer value (or zero). The exponent will be used to calculate the + * floating point number by taking the positive integer the String + * represents and multiplying by 10 raised to the power of the of the + * exponent. Returns the closest float value to the real number + * + * @param s + * the String that will be parsed to a floating point + * @param e + * an int represent the 10 to part + * @return the float closest to the real number + * + * @exception NumberFormatException + * if the String doesn't represent a positive integer value + */ + private static native float parseFltImpl(String s, int e); + + /** + * Takes a String and does some initial parsing. Should return a + * StringExponentPair containing a String with no leading or trailing white + * space and trailing zeroes eliminated. The exponent of the + * StringExponentPair will be used to calculate the floating point number by + * taking the positive integer the String represents and multiplying by 10 + * raised to the power of the of the exponent. + * + * @param s + * the String that will be parsed to a floating point + * @param length + * the length of s + * @return a StringExponentPair with necessary values + * + * @exception NumberFormatException + * if the String doesn't pass basic tests + */ + private static StringExponentPair initialParse(String s, int length) { + boolean negative = false; + char c; + int start, end, decimal; + int e = 0; + + start = 0; + if (length == 0) + throw new NumberFormatException(s); + + c = s.charAt(length - 1); + if (c == 'D' || c == 'd' || c == 'F' || c == 'f') { + length--; + if (length == 0) + throw new NumberFormatException(s); + } + + end = Math.max(s.indexOf('E'), s.indexOf('e')); + if (end > -1) { + if (end + 1 == length) + throw new NumberFormatException(s); int exponent_offset = end + 1; if (s.charAt(exponent_offset) == '+') { @@ -122,197 +120,208 @@ public final class FloatingPointParser { } exponent_offset++; // skip the plus sign } - try { - e = Integer.parseInt(s.substring(exponent_offset, + try { + e = Integer.parseInt(s.substring(exponent_offset, length)); } catch (NumberFormatException ex) { // ex contains the exponent substring // only so throw a new exception with // the correct string - throw new NumberFormatException(s); + throw new NumberFormatException(s); } - } else { - end = length; - } - if (length == 0) - throw new NumberFormatException(s); - - c = s.charAt(start); - if (c == '-') { - ++start; - --length; - negative = true; - } else if (c == '+') { - ++start; - --length; - } - if (length == 0) - throw new NumberFormatException(s); - - decimal = s.indexOf('.'); - if (decimal > -1) { - e -= end - decimal - 1; - s = s.substring(start, decimal) + s.substring(decimal + 1, end); - } else { - s = s.substring(start, end); - } - - if ((length = s.length()) == 0) - throw new NumberFormatException(); - - end = length; - while (end > 1 && s.charAt(end - 1) == '0') - --end; - - start = 0; - while (start < end - 1 && s.charAt(start) == '0') - start++; - - if (end != length || start != 0) { - e += length - end; - s = s.substring(start, end); + } else { + end = length; + } + if (length == 0) + throw new NumberFormatException(s); + + c = s.charAt(start); + if (c == '-') { + ++start; + --length; + negative = true; + } else if (c == '+') { + ++start; + --length; + } + if (length == 0) + throw new NumberFormatException(s); + + decimal = s.indexOf('.'); + if (decimal > -1) { + e -= end - decimal - 1; + s = s.substring(start, decimal) + s.substring(decimal + 1, end); + } else { + s = s.substring(start, end); + } + + if ((length = s.length()) == 0) + throw new NumberFormatException(); + + end = length; + while (end > 1 && s.charAt(end - 1) == '0') + --end; + + start = 0; + while (start < end - 1 && s.charAt(start) == '0') + start++; + + if (end != length || start != 0) { + e += length - end; + s = s.substring(start, end); + } + + // Trim the length of very small numbers, natives can only handle down + // to E-309 + final int APPROX_MIN_MAGNITUDE = -359; + final int MAX_DIGITS = 52; + length = s.length(); + if (length > MAX_DIGITS && e < APPROX_MIN_MAGNITUDE) { + int d = Math.min(APPROX_MIN_MAGNITUDE - e, length - 1); + s = s.substring(0, length - d); + e += d; } - return new StringExponentPair(s, e, negative); - } - - /* - * Assumes the string is trimmed. - */ - private static double parseDblName(String namedDouble, int length) { - // Valid strings are only +Nan, NaN, -Nan, +Infinity, Infinity, - // -Infinity. - if ((length != 3) && (length != 4) && (length != 8) && (length != 9)) { - throw new NumberFormatException(); - } - - boolean negative = false; - int cmpstart = 0; - switch (namedDouble.charAt(0)) { - case '-': - negative = true; // fall through - case '+': - cmpstart = 1; - default: - } - - if (namedDouble.regionMatches(false, cmpstart, "Infinity", 0, 8)) { - return negative ? Double.NEGATIVE_INFINITY - : Float.POSITIVE_INFINITY; - } - - if (namedDouble.regionMatches(false, cmpstart, "NaN", 0, 3)) { - return Double.NaN; - } - - throw new NumberFormatException(); - } - - /* - * Assumes the string is trimmed. - */ - private static float parseFltName(String namedFloat, int length) { - // Valid strings are only +Nan, NaN, -Nan, +Infinity, Infinity, - // -Infinity. - if ((length != 3) && (length != 4) && (length != 8) && (length != 9)) { - throw new NumberFormatException(); - } - - boolean negative = false; - int cmpstart = 0; - switch (namedFloat.charAt(0)) { - case '-': - negative = true; // fall through - case '+': - cmpstart = 1; - default: - } - - if (namedFloat.regionMatches(false, cmpstart, "Infinity", 0, 8)) { - return negative ? Float.NEGATIVE_INFINITY : Float.POSITIVE_INFINITY; - } - - if (namedFloat.regionMatches(false, cmpstart, "NaN", 0, 3)) { - return Float.NaN; - } - - throw new NumberFormatException(); - } - - /** - * Returns the closest double value to the real number in the string. - * - * @param s - * the String that will be parsed to a floating point - * @return the double closest to the real number - * - * @exception NumberFormatException - * if the String doesn't represent a double - */ - public static double parseDouble(String s) { - s = s.trim(); - int length = s.length(); - - if (length == 0) { - throw new NumberFormatException(s); - } - - // See if this could be a named double - char last = s.charAt(length - 1); - if ((last == 'y') || (last == 'N')) { - return parseDblName(s, length); - } + return new StringExponentPair(s, e, negative); + } + + /* + * Assumes the string is trimmed. + */ + private static double parseDblName(String namedDouble, int length) { + // Valid strings are only +Nan, NaN, -Nan, +Infinity, Infinity, + // -Infinity. + if ((length != 3) && (length != 4) && (length != 8) && (length != 9)) { + throw new NumberFormatException(); + } + + boolean negative = false; + int cmpstart = 0; + switch (namedDouble.charAt(0)) { + case '-': + negative = true; // fall through + case '+': + cmpstart = 1; + default: + } + + if (namedDouble.regionMatches(false, cmpstart, "Infinity", 0, 8)) { + return negative ? Double.NEGATIVE_INFINITY + : Float.POSITIVE_INFINITY; + } + + if (namedDouble.regionMatches(false, cmpstart, "NaN", 0, 3)) { + return Double.NaN; + } + + throw new NumberFormatException(); + } + + /* + * Assumes the string is trimmed. + */ + private static float parseFltName(String namedFloat, int length) { + // Valid strings are only +Nan, NaN, -Nan, +Infinity, Infinity, + // -Infinity. + if ((length != 3) && (length != 4) && (length != 8) && (length != 9)) { + throw new NumberFormatException(); + } + + boolean negative = false; + int cmpstart = 0; + switch (namedFloat.charAt(0)) { + case '-': + negative = true; // fall through + case '+': + cmpstart = 1; + default: + } + + if (namedFloat.regionMatches(false, cmpstart, "Infinity", 0, 8)) { + return negative ? Float.NEGATIVE_INFINITY : Float.POSITIVE_INFINITY; + } + + if (namedFloat.regionMatches(false, cmpstart, "NaN", 0, 3)) { + return Float.NaN; + } + + throw new NumberFormatException(); + } + + /** + * Returns the closest double value to the real number in the string. + * + * @param s + * the String that will be parsed to a floating point + * @return the double closest to the real number + * + * @exception NumberFormatException + * if the String doesn't represent a double + */ + public static double parseDouble(String s) { + s = s.trim(); + int length = s.length(); + + if (length == 0) { + throw new NumberFormatException(s); + } + + // See if this could be a named double + char last = s.charAt(length - 1); + if ((last == 'y') || (last == 'N')) { + return parseDblName(s, length); + } // See if it could be a hexadecimal representation if (s.toLowerCase().indexOf("0x") != -1) { //$NON-NLS-1$ return HexStringParser.parseDouble(s); } - StringExponentPair info = initialParse(s, length); - - double result = parseDblImpl(info.s, info.e); - if (info.negative) - result = -result; - - return result; - } - - /** - * Returns the closest float value to the real number in the string. - * - * @param s - * the String that will be parsed to a floating point - * @return the float closest to the real number - * - * @exception NumberFormatException - * if the String doesn't represent a float - */ - public static float parseFloat(String s) { - s = s.trim(); - int length = s.length(); - - if (length == 0) { - throw new NumberFormatException(s); - } - - // See if this could be a named float - char last = s.charAt(length - 1); - if ((last == 'y') || (last == 'N')) { - return parseFltName(s, length); - } + StringExponentPair info = initialParse(s, length); + + double result = parseDblImpl(info.s, info.e); + if (info.negative) + result = -result; + + return result; + } + + /** + * Returns the closest float value to the real number in the string. + * + * @param s + * the String that will be parsed to a floating point + * @return the float closest to the real number + * + * @exception NumberFormatException + * if the String doesn't represent a float + */ + public static float parseFloat(String s) { + s = s.trim(); + int length = s.length(); + + if (length == 0) { + throw new NumberFormatException(s); + } + + // See if this could be a named float + char last = s.charAt(length - 1); + if ((last == 'y') || (last == 'N')) { + return parseFltName(s, length); + } // See if it could be a hexadecimal representation if (s.toLowerCase().indexOf("0x") != -1) { //$NON-NLS-1$ return HexStringParser.parseFloat(s); } - StringExponentPair info = initialParse(s, length); + StringExponentPair info = initialParse(s, length); - float result = parseFltImpl(info.s, info.e); - if (info.negative) - result = -result; + float result = parseFltImpl(info.s, info.e); + if (info.negative) + result = -result; - return result; - } + return result; + } } diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/util/HexStringParser.java b/libcore/luni/src/main/java/org/apache/harmony/luni/util/HexStringParser.java index 93bd9da05..80721651c 100644 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/util/HexStringParser.java +++ b/libcore/luni/src/main/java/org/apache/harmony/luni/util/HexStringParser.java @@ -304,13 +304,13 @@ final class HexStringParser { private int getOffset(String strIntegerPart, String strDecimalPart) { strIntegerPart = strIntegerPart.replaceFirst("^0+", ""); //$NON-NLS-1$ //$NON-NLS-2$ - //If the Interger part is a nonzero number. + //If the Integer part is a nonzero number. if (strIntegerPart.length() != 0) { String leadingNumber = strIntegerPart.substring(0, 1); return (strIntegerPart.length() - 1) * 4 + countBitsLength(Long.parseLong(leadingNumber,HEX_RADIX)) - 1; } - //If the Interger part is a zero number. + //If the Integer part is a zero number. int i; for (i = 0; i < strDecimalPart.length() && strDecimalPart.charAt(i) == '0'; i++); if (i == strDecimalPart.length()) { diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/util/HistoricalNamesUtil.java b/libcore/luni/src/main/java/org/apache/harmony/luni/util/HistoricalNamesUtil.java new file mode 100644 index 000000000..f32f9768b --- /dev/null +++ b/libcore/luni/src/main/java/org/apache/harmony/luni/util/HistoricalNamesUtil.java @@ -0,0 +1,170 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.harmony.luni.util; + +import java.util.HashMap; + +public class HistoricalNamesUtil { + private static HashMap historicalNames = new HashMap(); + + static { + historicalNames.put("Big5-HKSCS", "Big5_HKSCS"); + historicalNames.put("EUC-JP", "EUC_JP"); + historicalNames.put("EUC-KR", "EUC_KR"); + historicalNames.put("GB2312", "EUC_CN"); + historicalNames.put("IBM-Thai", "Cp838"); + historicalNames.put("IBM00858", "Cp858"); + historicalNames.put("IBM01140", "Cp1140"); + historicalNames.put("IBM01141", "Cp1141"); + historicalNames.put("IBM01142", "Cp1142"); + historicalNames.put("IBM01143", "Cp1143"); + historicalNames.put("IBM01144", "Cp1144"); + historicalNames.put("IBM01145", "Cp1145"); + historicalNames.put("IBM01146", "Cp1146"); + historicalNames.put("IBM01147", "Cp1147"); + historicalNames.put("IBM01148", "Cp1148"); + historicalNames.put("IBM01149", "Cp1149"); + historicalNames.put("IBM037", "Cp037"); + historicalNames.put("IBM1026", "Cp1026"); + historicalNames.put("IBM1047", "Cp1047"); + historicalNames.put("IBM273", "Cp273"); + historicalNames.put("IBM277", "Cp277"); + historicalNames.put("IBM278", "Cp278"); + historicalNames.put("IBM280", "Cp280"); + historicalNames.put("IBM284", "Cp284"); + historicalNames.put("IBM285", "Cp285"); + historicalNames.put("IBM297", "Cp297"); + historicalNames.put("IBM420", "Cp420"); + historicalNames.put("IBM424", "Cp424"); + historicalNames.put("IBM437", "Cp437"); + historicalNames.put("IBM500", "Cp500"); + historicalNames.put("IBM775", "Cp775"); + historicalNames.put("IBM850", "Cp850"); + historicalNames.put("IBM852", "Cp852"); + historicalNames.put("IBM855", "Cp855"); + historicalNames.put("IBM857", "Cp857"); + historicalNames.put("IBM860", "Cp860"); + historicalNames.put("IBM861", "Cp861"); + historicalNames.put("IBM862", "Cp862"); + historicalNames.put("IBM863", "Cp863"); + historicalNames.put("IBM864", "Cp864"); + historicalNames.put("IBM865", "Cp865"); + historicalNames.put("IBM866", "Cp866"); + historicalNames.put("IBM868", "Cp868"); + historicalNames.put("IBM869", "Cp869"); + historicalNames.put("IBM870", "Cp870"); + historicalNames.put("IBM871", "Cp871"); + historicalNames.put("IBM918", "Cp918"); + historicalNames.put("ISO-2022-CN", "ISO2022CN"); + historicalNames.put("ISO-2022-JP", "ISO2022JP"); + historicalNames.put("ISO-2022-KR", "ISO2022KR"); + historicalNames.put("ISO-8859-1", "ISO8859_1"); + historicalNames.put("ISO-8859-13", "ISO8859_13"); + historicalNames.put("ISO-8859-15", "ISO8859_15"); + historicalNames.put("ISO-8859-2", "ISO8859_2"); + historicalNames.put("ISO-8859-3", "ISO8859_3"); + historicalNames.put("ISO-8859-4", "ISO8859_4"); + historicalNames.put("ISO-8859-5", "ISO8859_5"); + historicalNames.put("ISO-8859-6", "ISO8859_6"); + historicalNames.put("ISO-8859-7", "ISO8859_7"); + historicalNames.put("ISO-8859-8", "ISO8859_8"); + historicalNames.put("ISO-8859-9", "ISO8859_9"); + historicalNames.put("KOI8-R", "KOI8_R"); + historicalNames.put("Shift_JIS", "SJIS"); + historicalNames.put("TIS-620", "TIS620"); + historicalNames.put("US-ASCII", "ASCII"); + historicalNames.put("UTF-16BE", "UnicodeBigUnmarked"); + historicalNames.put("UTF-16LE", "UnicodeLittleUnmarked"); + historicalNames.put("UTF-8", "UTF8"); + historicalNames.put("windows-1250", "Cp1250"); + historicalNames.put("windows-1251", "Cp1251"); + historicalNames.put("windows-1252", "Cp1252"); + historicalNames.put("windows-1253", "Cp1253"); + historicalNames.put("windows-1254", "Cp1254"); + historicalNames.put("windows-1255", "Cp1255"); + historicalNames.put("windows-1256", "Cp1256"); + historicalNames.put("windows-1257", "Cp1257"); + historicalNames.put("windows-1258", "Cp1258"); + historicalNames.put("windows-31j", "MS932"); + historicalNames.put("x-Big5-Solaris", "Big5_Solaris"); + historicalNames.put("x-euc-jp-linux", "EUC_JP_LINUX"); + historicalNames.put("x-EUC-TW", "EUC_TW"); + historicalNames.put("x-eucJP-Open", "EUC_JP_Solaris"); + historicalNames.put("x-IBM1006", "Cp1006"); + historicalNames.put("x-IBM1025", "Cp1025"); + historicalNames.put("x-IBM1046", "Cp1046"); + historicalNames.put("x-IBM1097", "Cp1097"); + historicalNames.put("x-IBM1098", "Cp1098"); + historicalNames.put("x-IBM1112", "Cp1112"); + historicalNames.put("x-IBM1122", "Cp1122"); + historicalNames.put("x-IBM1123", "Cp1123"); + historicalNames.put("x-IBM1124", "Cp1124"); + historicalNames.put("x-IBM1381", "Cp1381"); + historicalNames.put("x-IBM1383", "Cp1383"); + historicalNames.put("x-IBM33722", "Cp33722"); + historicalNames.put("x-IBM737", "Cp737"); + historicalNames.put("x-IBM856", "Cp856"); + historicalNames.put("x-IBM874", "Cp874"); + historicalNames.put("x-IBM875", "Cp875"); + historicalNames.put("x-IBM921", "Cp921"); + historicalNames.put("x-IBM922", "Cp922"); + historicalNames.put("x-IBM930", "Cp930"); + historicalNames.put("x-IBM933", "Cp933"); + historicalNames.put("x-IBM935", "Cp935"); + historicalNames.put("x-IBM937", "Cp937"); + historicalNames.put("x-IBM939", "Cp939"); + historicalNames.put("x-IBM942", "Cp942"); + historicalNames.put("x-IBM942C", "Cp942C"); + historicalNames.put("x-IBM943", "Cp943"); + historicalNames.put("x-IBM943C", "Cp943C"); + historicalNames.put("x-IBM948", "Cp948"); + historicalNames.put("x-IBM949", "Cp949"); + historicalNames.put("x-IBM949C", "Cp949C"); + historicalNames.put("x-IBM950", "Cp950"); + historicalNames.put("x-IBM964", "Cp964"); + historicalNames.put("x-IBM970", "Cp970"); + historicalNames.put("x-ISCII91", "ISCII91"); + historicalNames.put("x-ISO-2022-CN-CNS", "ISO2022CN"); + historicalNames.put("x-ISO-2022-CN-GB", "ISO2022CN"); + historicalNames.put("x-JISAutoDetect", "JISAutoDetect"); + historicalNames.put("x-MacArabic", "MacArabic"); + historicalNames.put("x-MacCentralEurope", "MacCentralEurope"); + historicalNames.put("x-MacCroatian", "MacCroatian"); + historicalNames.put("x-MacCyrillic", "MacCyrillic"); + historicalNames.put("x-MacDingbat", "MacDingbat"); + historicalNames.put("x-MacGreek", "MacGreek"); + historicalNames.put("x-MacHebrew", "MacHebrew"); + historicalNames.put("x-MacIceland", "MacIceland"); + historicalNames.put("x-MacRoman", "MacRoman"); + historicalNames.put("x-MacRomania", "MacRomania"); + historicalNames.put("x-MacSymbol", "MacSymbol"); + historicalNames.put("x-MacThai", "MacThai"); + historicalNames.put("x-MacTurkish", "MacTurkish"); + historicalNames.put("x-MacUkraine", "MacUkraine"); + historicalNames.put("x-MS950-HKSCS", "MS950_HKSCS"); + historicalNames.put("x-mswin-936", "MS936"); + historicalNames.put("x-PCK", "PCK"); + historicalNames.put("x-windows-874", "MS874"); + historicalNames.put("x-windows-949", "MS949"); + historicalNames.put("x-windows-950", "MS950"); + } + + public static String getHistoricalName(String name) { + return (!historicalNames.containsKey(name) ? name : historicalNames + .get(name)); + } +} diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/util/Inet6Util.java b/libcore/luni/src/main/java/org/apache/harmony/luni/util/Inet6Util.java index 8c8257bee..55914de92 100644 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/util/Inet6Util.java +++ b/libcore/luni/src/main/java/org/apache/harmony/luni/util/Inet6Util.java @@ -24,296 +24,7 @@ import java.util.StringTokenizer; */ public class Inet6Util { - /** - * Creates an byte[] based on an ipAddressString. No error handling is - * performed here. - */ - public static byte[] createByteArrayFromIPAddressString( - String ipAddressString) { - if (isValidIPV4Address(ipAddressString)) { - StringTokenizer tokenizer = new StringTokenizer(ipAddressString, "."); - String token = ""; - int tempInt = 0; - byte[] byteAddress = new byte[4]; - for (int i = 0; i < 4; i++) { - token = tokenizer.nextToken(); - tempInt = Integer.parseInt(token); - byteAddress[i] = (byte) tempInt; - } - - return byteAddress; - } - - if (ipAddressString.charAt(0) == '[') { - ipAddressString = ipAddressString.substring(1, ipAddressString.length() - 1); - } - - StringTokenizer tokenizer = new StringTokenizer(ipAddressString, ":.", true); - ArrayList hexStrings = new ArrayList(); - ArrayList decStrings = new ArrayList(); - String token = ""; - String prevToken = ""; - // If a double colon exists, we need to insert 0s. - int doubleColonIndex = -1; - - /* - * Go through the tokens, including the separators ':' and '.' When we - * hit a : or . the previous token will be added to either the hex list - * or decimal list. In the case where we hit a :: we will save the index - * of the hexStrings so we can add zeros in to fill out the string - */ - while (tokenizer.hasMoreTokens()) { - prevToken = token; - token = tokenizer.nextToken(); - - if (token.equals(":")) { - if (prevToken.equals(":")) { - doubleColonIndex = hexStrings.size(); - } else if (!prevToken.equals("")) { - hexStrings.add(prevToken); - } - } else if (token.equals(".")) { - decStrings.add(prevToken); - } - } - - if (prevToken.equals(":")) { - if (token.equals(":")) { - doubleColonIndex = hexStrings.size(); - } else { - hexStrings.add(token); - } - } else if (prevToken.equals(".")) { - decStrings.add(token); - } - - // figure out how many hexStrings we should have - // also check if it is a IPv4 address - int hexStringsLength = 8; - - // If we have an IPv4 address tagged on at the end, subtract - // 4 bytes, or 2 hex words from the total - if (decStrings.size() > 0) { - hexStringsLength -= 2; - } - - // if we hit a double Colon add the appropriate hex strings - if (doubleColonIndex != -1) { - int numberToInsert = hexStringsLength - hexStrings.size(); - for (int i = 0; i < numberToInsert; i++) { - hexStrings.add(doubleColonIndex, "0"); - } - } - - byte ipByteArray[] = new byte[16]; - - // Finally convert these strings to bytes... - for (int i = 0; i < hexStrings.size(); i++) { - convertToBytes(hexStrings.get(i), ipByteArray, i * 2); - } - - // Now if there are any decimal values, we know where they go... - for (int i = 0; i < decStrings.size(); i++) { - ipByteArray[i + 12] = (byte) (Integer.parseInt(decStrings.get(i)) & 255); - } - - // now check to see if this guy is actually and IPv4 address - // an ipV4 address is ::FFFF:d.d.d.d - boolean ipV4 = true; - for (int i = 0; i < 10; i++) { - if (ipByteArray[i] != 0) { - ipV4 = false; - break; - } - } - - if (ipByteArray[10] != -1 || ipByteArray[11] != -1) { - ipV4 = false; - } - - if (ipV4) { - byte ipv4ByteArray[] = new byte[4]; - for (int i = 0; i < 4; i++) { - ipv4ByteArray[i] = ipByteArray[i + 12]; - } - return ipv4ByteArray; - } - - return ipByteArray; - - } - - // BEGIN android-changed - static char[] hexCharacters = {'0', '1', '2', '3', '4', '5', '6', '7', '8', - '9', 'a', 'b', 'c', 'd', 'e', 'f'}; - // END android-changed - - public static String createIPAddrStringFromByteArray(byte ipByteArray[]) { - if (ipByteArray.length == 4) { - return addressToString(bytesToInt(ipByteArray, 0)); - } - - if (ipByteArray.length == 16) { - if (isIPv4MappedAddress(ipByteArray)) { - byte ipv4ByteArray[] = new byte[4]; - for (int i = 0; i < 4; i++) { - ipv4ByteArray[i] = ipByteArray[i + 12]; - } - return addressToString(bytesToInt(ipv4ByteArray, 0)); - } - StringBuilder buffer = new StringBuilder(); - // BEGIN android-changed - for (int i = 0; i < 8; i++) { // ipByteArray.length / 2 - - int num = (ipByteArray[2 * i] & 0xff) << 8; - num ^= ipByteArray[2 * i + 1] & 0xff; - - // count the digits to display without leading 0 - int count = 1, j = num; - while ((j >>>= 4) != 0) { - count++; - } - - char[] buf = new char[count]; - do { - int t = num & 0x0f; - buf[--count] = hexCharacters[t]; - num >>>= 4; - } while (count > 0); - - buffer.append(buf); - if ((i + 1) < 8) { // ipByteArray.length / 2 - buffer.append(":"); - } - } - // END android-changed - return buffer.toString(); - } - return null; - } - - /** Converts a 4 character hex word into a 2 byte word equivalent */ - public static void convertToBytes(String hexWord, byte ipByteArray[], - int byteIndex) { - - int hexWordLength = hexWord.length(); - int hexWordIndex = 0; - ipByteArray[byteIndex] = 0; - ipByteArray[byteIndex + 1] = 0; - int charValue; - - // high order 4 bits of first byte - if (hexWordLength > 3) { - charValue = getIntValue(hexWord.charAt(hexWordIndex++)); - ipByteArray[byteIndex] = (byte) (ipByteArray[byteIndex] | (charValue << 4)); - } - - // low order 4 bits of the first byte - if (hexWordLength > 2) { - charValue = getIntValue(hexWord.charAt(hexWordIndex++)); - ipByteArray[byteIndex] = (byte) (ipByteArray[byteIndex] | charValue); - } - - // high order 4 bits of second byte - if (hexWordLength > 1) { - charValue = getIntValue(hexWord.charAt(hexWordIndex++)); - ipByteArray[byteIndex + 1] = (byte) (ipByteArray[byteIndex + 1] | (charValue << 4)); - } - - // low order 4 bits of the first byte - charValue = getIntValue(hexWord.charAt(hexWordIndex)); - ipByteArray[byteIndex + 1] = (byte) (ipByteArray[byteIndex + 1] | charValue & 15); - } - - static int getIntValue(char c) { - - switch (c) { - case '0': - return 0; - case '1': - return 1; - case '2': - return 2; - case '3': - return 3; - case '4': - return 4; - case '5': - return 5; - case '6': - return 6; - case '7': - return 7; - case '8': - return 8; - case '9': - return 9; - } - - c = Character.toLowerCase(c); - switch (c) { - case 'a': - return 10; - case 'b': - return 11; - case 'c': - return 12; - case 'd': - return 13; - case 'e': - return 14; - case 'f': - return 15; - } - return 0; - } - - private static boolean isIPv4MappedAddress(byte ipAddress[]) { - - // Check if the address matches ::FFFF:d.d.d.d - // The first 10 bytes are 0. The next to are -1 (FF). - // The last 4 bytes are varied. - for (int i = 0; i < 10; i++) { - if (ipAddress[i] != 0) { - return false; - } - } - - if (ipAddress[10] != -1 || ipAddress[11] != -1) { - return false; - } - - return true; - - } - - /** - * Takes the byte array and creates an integer out of four bytes starting at - * start as the high-order byte. This method makes no checks on the validity - * of the parameters. - */ - public static int bytesToInt(byte bytes[], int start) { - // First mask the byte with 255, as when a negative - // signed byte converts to an integer, it has bits - // on in the first 3 bytes, we are only concerned - // about the right-most 8 bits. - // Then shift the rightmost byte to align with its - // position in the integer. - int value = ((bytes[start + 3] & 255)) - | ((bytes[start + 2] & 255) << 8) - | ((bytes[start + 1] & 255) << 16) - | ((bytes[start] & 255) << 24); - return value; - } - - public static String addressToString(int value) { - return ((value >> 24) & 0xff) + "." + ((value >> 16) & 0xff) + "." - + ((value >> 8) & 0xff) + "." + (value & 0xff); - } - - // BEGIN android-added - // copied from a newer version of harmony public static boolean isIP6AddressInFullForm(String ipAddress) { if (isValidIP6Address(ipAddress)) { int doubleColonIndex = ipAddress.indexOf("::"); @@ -325,215 +36,214 @@ public class Inet6Util { } return false; } - // END android-added - public static boolean isValidIP6Address(String ipAddress) { - int length = ipAddress.length(); - boolean doubleColon = false; - int numberOfColons = 0; - int numberOfPeriods = 0; - int numberOfPercent = 0; - String word = ""; - char c = 0; - char prevChar = 0; - int offset = 0; // offset for [] IP addresses - - if (length < 2) { + public static boolean isValidIP6Address(String ipAddress) { + int length = ipAddress.length(); + boolean doubleColon = false; + int numberOfColons = 0; + int numberOfPeriods = 0; + int numberOfPercent = 0; + String word = ""; + char c = 0; + char prevChar = 0; + int offset = 0; // offset for [] IP addresses + + if (length < 2) { return false; } - for (int i = 0; i < length; i++) { - prevChar = c; - c = ipAddress.charAt(i); - switch (c) { + for (int i = 0; i < length; i++) { + prevChar = c; + c = ipAddress.charAt(i); + switch (c) { - // case for an open bracket [x:x:x:...x] - case '[': - if (i != 0) { + // case for an open bracket [x:x:x:...x] + case '[': + if (i != 0) { return false; // must be first character } - if (ipAddress.charAt(length - 1) != ']') { + if (ipAddress.charAt(length - 1) != ']') { return false; // must have a close ] } - offset = 1; - if (length < 4) { + offset = 1; + if (length < 4) { return false; } - break; + break; - // case for a closed bracket at end of IP [x:x:x:...x] - case ']': - if (i != length - 1) { + // case for a closed bracket at end of IP [x:x:x:...x] + case ']': + if (i != length - 1) { return false; // must be last character } - if (ipAddress.charAt(0) != '[') { + if (ipAddress.charAt(0) != '[') { return false; // must have a open [ } - break; + break; - // case for the last 32-bits represented as IPv4 x:x:x:x:x:x:d.d.d.d - case '.': - numberOfPeriods++; - if (numberOfPeriods > 3) { + // case for the last 32-bits represented as IPv4 x:x:x:x:x:x:d.d.d.d + case '.': + numberOfPeriods++; + if (numberOfPeriods > 3) { return false; } - if (!isValidIP4Word(word)) { + if (!isValidIP4Word(word)) { return false; } - if (numberOfColons != 6 && !doubleColon) { + if (numberOfColons != 6 && !doubleColon) { return false; } - // a special case ::1:2:3:4:5:d.d.d.d allows 7 colons with an - // IPv4 ending, otherwise 7 :'s is bad - if (numberOfColons == 7 && ipAddress.charAt(0 + offset) != ':' - && ipAddress.charAt(1 + offset) != ':') { + // a special case ::1:2:3:4:5:d.d.d.d allows 7 colons with an + // IPv4 ending, otherwise 7 :'s is bad + if (numberOfColons == 7 && ipAddress.charAt(0 + offset) != ':' + && ipAddress.charAt(1 + offset) != ':') { return false; } - word = ""; - break; + word = ""; + break; - case ':': - numberOfColons++; - if (numberOfColons > 7) { + case ':': + numberOfColons++; + if (numberOfColons > 7) { return false; } - if (numberOfPeriods > 0) { + if (numberOfPeriods > 0) { return false; } - if (prevChar == ':') { - if (doubleColon) { + if (prevChar == ':') { + if (doubleColon) { return false; } - doubleColon = true; - } - word = ""; - break; - case '%': - if (numberOfColons == 0) { - return false; - } - numberOfPercent++; - - // validate that the stuff after the % is valid - if ((i + 1) >= length) { - // in this case the percent is there but no number is - // available - return false; - } - try { - Integer.parseInt(ipAddress.substring(i + 1)); - } catch (NumberFormatException e) { - // right now we just support an integer after the % so if - // this is not - // what is there then return + doubleColon = true; + } + word = ""; + break; + case '%': + if (numberOfColons == 0) { return false; } - break; - - default: - if (numberOfPercent == 0) { - if (word.length() > 3) { + numberOfPercent++; + + // validate that the stuff after the % is valid + if ((i + 1) >= length) { + // in this case the percent is there but no number is + // available + return false; + } + try { + Integer.parseInt(ipAddress.substring(i + 1)); + } catch (NumberFormatException e) { + // right now we just support an integer after the % so if + // this is not + // what is there then return + return false; + } + break; + + default: + if (numberOfPercent == 0) { + if (word.length() > 3) { return false; } - if (!isValidHexChar(c)) { + if (!isValidHexChar(c)) { return false; } - } - word += c; - } - } - - // Check if we have an IPv4 ending - if (numberOfPeriods > 0) { - if (numberOfPeriods != 3 || !isValidIP4Word(word)) { + } + word += c; + } + } + + // Check if we have an IPv4 ending + if (numberOfPeriods > 0) { + if (numberOfPeriods != 3 || !isValidIP4Word(word)) { return false; } - } else { - // If we're at then end and we haven't had 7 colons then there is a - // problem unless we encountered a doubleColon - if (numberOfColons != 7 && !doubleColon) { - return false; - } - - // If we have an empty word at the end, it means we ended in either - // a : or a . - // If we did not end in :: then this is invalid - if (numberOfPercent == 0) { - if (word == "" && ipAddress.charAt(length - 1 - offset) == ':' - && ipAddress.charAt(length - 2 - offset) != ':') { - return false; - } - } - } - - return true; - } - - public static boolean isValidIP4Word(String word) { - char c; - if (word.length() < 1 || word.length() > 3) { + } else { + // If we're at then end and we haven't had 7 colons then there is a + // problem unless we encountered a doubleColon + if (numberOfColons != 7 && !doubleColon) { + return false; + } + + // If we have an empty word at the end, it means we ended in either + // a : or a . + // If we did not end in :: then this is invalid + if (numberOfPercent == 0) { + if (word == "" && ipAddress.charAt(length - 1 - offset) == ':' + && ipAddress.charAt(length - 2 - offset) != ':') { + return false; + } + } + } + + return true; + } + + public static boolean isValidIP4Word(String word) { + char c; + if (word.length() < 1 || word.length() > 3) { return false; } - for (int i = 0; i < word.length(); i++) { - c = word.charAt(i); - if (!(c >= '0' && c <= '9')) { + for (int i = 0; i < word.length(); i++) { + c = word.charAt(i); + if (!(c >= '0' && c <= '9')) { return false; } - } - if (Integer.parseInt(word) > 255) { + } + if (Integer.parseInt(word) > 255) { return false; } - return true; - } + return true; + } - static boolean isValidHexChar(char c) { + static boolean isValidHexChar(char c) { - return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') - || (c >= 'a' && c <= 'f'); - } + return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') + || (c >= 'a' && c <= 'f'); + } - /** - * Takes a string and parses it to see if it is a valid IPV4 address. - * - * @return true, if the string represents an IPV4 address in dotted - * notation, false otherwise - */ - public static boolean isValidIPV4Address(String value) { - // BEGIN android-changed - // general test - if (!value.matches("\\p{Digit}+(\\.\\p{Digit}+)*")) { - return false; - } + /** + * Takes a string and parses it to see if it is a valid IPV4 address. + * + * @return true, if the string represents an IPV4 address in dotted + * notation, false otherwise + */ + public static boolean isValidIPV4Address(String value) { + // BEGIN android-changed + // general test + if (!value.matches("\\p{Digit}+(\\.\\p{Digit}+)*")) { + return false; + } - String[] parts = value.split("\\."); - int length = parts.length; - if (length < 1 || length > 4) { - return false; - } + String[] parts = value.split("\\."); + int length = parts.length; + if (length < 1 || length > 4) { + return false; + } - if (length == 1) { - // One part decimal numeric address - long longValue = Long.parseLong(parts[0]); - return longValue <= 0xFFFFFFFFL; - } else { - // Test each part for inclusion in the correct range - for (int i = 0; i < length; i++) { - // For two part addresses, the second part expresses - // a 24-bit quantity; for three part addresses, the third - // part expresses a 16-bit quantity. - int max = 0xff; - if ((length == 2) && (i == 1)) { - max = 0xffffff; - } else if ((length == 3) && (i == 2)) { - max = 0xffff; - } - if (Integer.parseInt(parts[i]) > max) { - return false; + if (length == 1) { + // One part decimal numeric address + long longValue = Long.parseLong(parts[0]); + return longValue <= 0xFFFFFFFFL; + } else { + // Test each part for inclusion in the correct range + for (int i = 0; i < length; i++) { + // For two part addresses, the second part expresses + // a 24-bit quantity; for three part addresses, the third + // part expresses a 16-bit quantity. + int max = 0xff; + if ((length == 2) && (i == 1)) { + max = 0xffffff; + } else if ((length == 3) && (i == 2)) { + max = 0xffff; + } + if (Integer.parseInt(parts[i]) > max) { + return false; + } } + return true; } - return true; - } - // END android-changed - } + // END android-changed + } } diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/util/InputStreamExposer.java b/libcore/luni/src/main/java/org/apache/harmony/luni/util/InputStreamExposer.java new file mode 100644 index 000000000..51a2d28ba --- /dev/null +++ b/libcore/luni/src/main/java/org/apache/harmony/luni/util/InputStreamExposer.java @@ -0,0 +1,118 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.luni.util; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Field; +import java.security.AccessController; +import java.security.PrivilegedAction; + +/** + * The class contains static {@link java.io.InputStream} utilities. + */ +public class InputStreamExposer { + + /** + * Provides access to a protected underlying buffer of + * ByteArrayInputStream. + */ + private static final Field BAIS_BUF; + + /** + * Provides access to a protected position in the underlying buffer of + * ByteArrayInputStream. + */ + private static final Field BAIS_POS; + + static { + final Field[] f = new Field[2]; + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + try { + f[0] = ByteArrayInputStream.class.getDeclaredField("buf"); + f[0].setAccessible(true); + f[1] = ByteArrayInputStream.class.getDeclaredField("pos"); + f[1].setAccessible(true); + } catch (NoSuchFieldException nsfe) { + throw new InternalError(nsfe.getLocalizedMessage()); + } + return null; + } + }); + BAIS_BUF = f[0]; + BAIS_POS = f[1]; + } + + /** + * Reads all bytes from {@link java.io.ByteArrayInputStream} using its + * underlying buffer directly. + * + * @return an underlying buffer, if a current position is at the buffer + * beginning, and an end position is at the buffer end, or a copy of + * the underlying buffer part. + */ + private static byte[] expose(ByteArrayInputStream bais) { + byte[] buffer, buf; + int pos; + synchronized (bais) { + int available = bais.available(); + try { + buf = (byte[]) BAIS_BUF.get(bais); + pos = BAIS_POS.getInt(bais); + } catch (IllegalAccessException iae) { + throw new InternalError(iae.getLocalizedMessage()); + } + if (pos == 0 && available == buf.length) { + buffer = buf; + } else { + buffer = new byte[available]; + System.arraycopy(buf, pos, buffer, 0, available); + } + bais.skip(available); + } + return buffer; + } + + /** + * The utility method for reading the whole input stream into a snapshot + * buffer. To speed up the access it works with an underlying buffer for a + * given {@link java.io.ByteArrayInputStream}. + * + * @param is + * the stream to be read. + * @return the snapshot wrapping the buffer where the bytes are read to. + * @throws UnsupportedOperationException if the input stream data cannot be exposed + */ + public static byte[] expose(InputStream is) throws IOException, UnsupportedOperationException { + // BEGIN android-changed + // if (is instanceof ExposedByteArrayInputStream) { + // return ((ExposedByteArrayInputStream) is).expose(); + // } + // END android-changed + + if (is.getClass().equals(ByteArrayInputStream.class)) { + return expose((ByteArrayInputStream) is); + } + + // We don't know how to do this + throw new UnsupportedOperationException(); + } +} diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/util/Sorter.java b/libcore/luni/src/main/java/org/apache/harmony/luni/util/Sorter.java deleted file mode 100644 index 1a994b206..000000000 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/util/Sorter.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.harmony.luni.util; - -import java.util.Arrays; - -/** - * Helper class with methods for sorting arrays. - * @deprecated Use {@link Comparator} and {@link Arrays.sort()} - */ -@Deprecated -public final class Sorter { - public interface Comparator extends java.util.Comparator { - } - - /** - * Sorts the array of objects using the default sorting algorithm. - * - * @param objs - * array of objects to be sorted - * @param comp - * A Comparator to be used to sort the elements - * - */ - public static void sort(T[] objs, Comparator comp) { - Arrays.sort(objs, comp); - } - -} diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/util/ThreadLocalCache.java b/libcore/luni/src/main/java/org/apache/harmony/luni/util/ThreadLocalCache.java new file mode 100644 index 000000000..cdfe0c820 --- /dev/null +++ b/libcore/luni/src/main/java/org/apache/harmony/luni/util/ThreadLocalCache.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.luni.util; + +import java.lang.ref.SoftReference; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; + +/** + * The class extends the functionality of {@link java.lang.ThreadLocal} with + * possibility of discarding the thread local storage content when a heap is + * exhausted. + */ +public class ThreadLocalCache { + + private SoftReference> storage = new SoftReference>( + null); + + private ThreadLocal getThreadLocal() { + ThreadLocal tls = storage.get(); + if (tls == null) { + tls = new ThreadLocal() { + public T initialValue() { + return ThreadLocalCache.this.initialValue(); + } + }; + storage = new SoftReference>(tls); + } + return tls; + } + + /** + * Returns the initial value for the cache for the current thread. + */ + protected T initialValue() { + return null; + } + + /** + * Returns the thread local value of this object. + */ + public T get() { + return getThreadLocal().get(); + } + + /** + * Sets the value of this variable for the current thread. Might be useful + * for expanding the thread local cache. + */ + public void set(T value) { + getThreadLocal().set(value); + } + + /** + * Discards the cache for all threads. + */ + public void remove() { + storage.clear(); + } + + public static ThreadLocalCache utf8Decoder = new ThreadLocalCache() { + protected CharsetDecoder initialValue() { + return Charset.forName("UTF-8").newDecoder(); + } + }; + + public static ThreadLocalCache utf8Encoder = new ThreadLocalCache() { + protected CharsetEncoder initialValue() { + return Charset.forName("UTF-8").newEncoder(); + } + }; + + public static ThreadLocalCache byteBuffer = new ThreadLocalCache() { + protected java.nio.ByteBuffer initialValue() { + return java.nio.ByteBuffer.allocate(72); // >= + // Manifest.LINE_LENGTH_LIMIT + } + }; + + public static ThreadLocalCache charBuffer = new ThreadLocalCache() { + protected CharBuffer initialValue() { + return CharBuffer.allocate(72); // no specific requirement + } + }; + +} diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/util/TwoKeyHashMap.java b/libcore/luni/src/main/java/org/apache/harmony/luni/util/TwoKeyHashMap.java index 26e8d0122..655d15654 100644 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/util/TwoKeyHashMap.java +++ b/libcore/luni/src/main/java/org/apache/harmony/luni/util/TwoKeyHashMap.java @@ -29,7 +29,7 @@ import java.util.NoSuchElementException; import java.util.Set; /** - * + * * Reductive hash with two keys * */ @@ -37,13 +37,13 @@ public class TwoKeyHashMap extends AbstractMap { static final float DEFAULT_LOAD_FACTOR = 0.75f; static final int DEFAULT_INITIAL_SIZE = 16; - + private Set> entrySet; private Collection values; private int size; private int arrSize; private int modCount; - + private Entry[] arr; private float loadFactor; @@ -55,9 +55,10 @@ public class TwoKeyHashMap extends AbstractMap { public TwoKeyHashMap() { this(DEFAULT_INITIAL_SIZE, DEFAULT_LOAD_FACTOR); } - + /** * Constructs an empty HashMap + * * @param initialCapacity */ public TwoKeyHashMap(int initialCapacity) { @@ -65,16 +66,19 @@ public class TwoKeyHashMap extends AbstractMap { } /** - * Constructs an empty HashMap + * Constructs an empty HashMap + * * @param initialCapacity * @param initialLoadFactor */ + @SuppressWarnings("unchecked") public TwoKeyHashMap(int initialCapacity, float initialLoadFactor) { - if (initialCapacity < 0) { + if (initialCapacity < 0) { throw new IllegalArgumentException("initialCapacity should be >= 0"); } if (initialLoadFactor <= 0) { - throw new IllegalArgumentException("initialLoadFactor should be > 0"); + throw new IllegalArgumentException( + "initialLoadFactor should be > 0"); } loadFactor = initialLoadFactor; if (initialCapacity == Integer.MAX_VALUE) { @@ -84,9 +88,9 @@ public class TwoKeyHashMap extends AbstractMap { threshold = (int) (arrSize * loadFactor); arr = new Entry[arrSize + 1]; } - + /** - * Returns a collection view of the values + * Returns a collection view of the values */ public Collection values() { if (values == null) { @@ -94,9 +98,9 @@ public class TwoKeyHashMap extends AbstractMap { } return values; } - + /** - * Returns a collection view of the mappings + * Returns a collection view of the mappings */ public Set> entrySet() { if (entrySet == null) { @@ -104,7 +108,7 @@ public class TwoKeyHashMap extends AbstractMap { } return entrySet; } - + /** * Clears the map */ @@ -116,6 +120,7 @@ public class TwoKeyHashMap extends AbstractMap { /** * Removes the mapping for the keys + * * @param key1 * @param key2 * @return @@ -124,9 +129,10 @@ public class TwoKeyHashMap extends AbstractMap { Entry e = removeEntry(key1, key2); return null != e ? e.value : null; } - + /** * Associates the specified value with the specified keys in this map + * * @param key1 * @param key2 * @param value @@ -152,7 +158,8 @@ public class TwoKeyHashMap extends AbstractMap { Entry e = arr[index]; while (e != null) { - if (hash == e.hash && key1.equals(e.getKey1()) && key2.equals(e.getKey2())) { + if (hash == e.hash && key1.equals(e.getKey1()) + && key2.equals(e.getKey2())) { V oldValue = e.value; e.value = value; return oldValue; @@ -172,8 +179,9 @@ public class TwoKeyHashMap extends AbstractMap { /** * Rehash the map - * + * */ + @SuppressWarnings("unchecked") void rehash() { int newArrSize = (arrSize + 1) * 2 + 1; if (newArrSize < 0) { @@ -206,10 +214,12 @@ public class TwoKeyHashMap extends AbstractMap { } /** - * Returns true if this map contains a mapping for the specified keys - * @param key1 - * @param key2 - * @return + * Answers whether this map contains a mapping for the specified keys. + * + * @param key1 first key + * @param key2 second key + * @return true if this map contains a mapping for the specified keys, and + * false otherwise. */ public boolean containsKey(Object key1, Object key2) { return findEntry(key1, key2) != null; @@ -217,6 +227,7 @@ public class TwoKeyHashMap extends AbstractMap { /** * Return the value by keys + * * @param key1 * @param key2 * @return @@ -237,14 +248,15 @@ public class TwoKeyHashMap extends AbstractMap { } /** - * Returns the number of mappings + * Returns the number of mappings */ public int size() { return size; } - + /** - * Creates new entry + * Creates new entry + * * @param hashCode * @param key1 * @param key2 @@ -252,13 +264,14 @@ public class TwoKeyHashMap extends AbstractMap { * @param next * @return */ - Entry createEntry(int hashCode, E key1, K key2, - V value, Entry next) { + Entry createEntry(int hashCode, E key1, K key2, V value, + Entry next) { return new Entry(hashCode, key1, key2, value, next); } /** * Creates entries iterator + * * @return */ Iterator> createEntrySetIterator() { @@ -267,18 +280,18 @@ public class TwoKeyHashMap extends AbstractMap { /** * Creates values iterator + * * @return */ Iterator createValueCollectionIterator() { return new ValueIteratorImpl(); } - /** * Entry implementation for the TwoKeyHashMap class * */ - public static class Entry implements Map.Entry { + public static class Entry implements Map.Entry { int hash; E key1; K key2; @@ -292,7 +305,7 @@ public class TwoKeyHashMap extends AbstractMap { this.value = value; this.next = next; } - + public String getKey() { return key1.toString() + key2.toString(); } @@ -300,7 +313,7 @@ public class TwoKeyHashMap extends AbstractMap { public E getKey1() { return key1; } - + public K getKey2() { return key2; } @@ -324,12 +337,11 @@ public class TwoKeyHashMap extends AbstractMap { Object getKey1 = e.getKey1(); Object getKey2 = e.getKey2(); Object getValue = e.getValue(); - if ((key1 == null && getKey1 != null) || - (key2 == null && getKey2 != null) || - (value == null && getValue != null) || - !key1.equals(e.getKey1()) || - !key2.equals(e.getKey2()) || - !value.equals(getValue)) { + if ((key1 == null && getKey1 != null) + || (key2 == null && getKey2 != null) + || (value == null && getValue != null) + || !key1.equals(e.getKey1()) || !key2.equals(e.getKey2()) + || !value.equals(getValue)) { return false; } return true; @@ -340,9 +352,9 @@ public class TwoKeyHashMap extends AbstractMap { int hash2 = (key2 == null ? 0 : key2.hashCode()); return (hash1 + hash2) ^ (value == null ? 0 : value.hashCode()); } - + } - + class EntrySetImpl extends AbstractSet> { public int size() { return size; @@ -375,7 +387,7 @@ public class TwoKeyHashMap extends AbstractMap { if (!(obj instanceof Entry)) { return false; } - return removeEntry(((Entry) obj).getKey1(), ((Entry)obj).getKey2()) != null; + return removeEntry(((Entry) obj).getKey1(), ((Entry) obj).getKey2()) != null; } public Iterator> iterator() { @@ -425,14 +437,14 @@ public class TwoKeyHashMap extends AbstractMap { found = false; returned_index = curr; returned_entry = curr_entry; - return (Map.Entry)curr_entry; + return (Map.Entry) curr_entry; } public void remove() { if (returned_index == -1) { throw new IllegalStateException(); } - + if (modCount != startModCount) { throw new ConcurrentModificationException(); } @@ -454,7 +466,7 @@ public class TwoKeyHashMap extends AbstractMap { returned_index = -1; } } - + private final Entry findEntry(Object key1, Object key2) { if (key1 == null && key2 == null) { return arr[arrSize]; @@ -465,14 +477,15 @@ public class TwoKeyHashMap extends AbstractMap { Entry e = arr[index]; while (e != null) { - if (hash == e.hash && key1.equals(e.getKey1()) && key2.equals(e.getKey2())) { + if (hash == e.hash && key1.equals(e.getKey1()) + && key2.equals(e.getKey2())) { return e; } e = e.next; } return null; } - + // Removes entry private final Entry removeEntry(Object key1, Object key2) { if (key1 == null && key2 == null) { @@ -493,7 +506,8 @@ public class TwoKeyHashMap extends AbstractMap { Entry e = arr[index]; Entry prev = e; while (e != null) { - if (hash == e.hash && key1.equals(e.getKey1()) && key2.equals(e.getKey2())) { + if (hash == e.hash && key1.equals(e.getKey1()) + && key2.equals(e.getKey2())) { if (prev == e) { arr[index] = e.next; } else { @@ -529,12 +543,12 @@ public class TwoKeyHashMap extends AbstractMap { public Iterator iterator() { return createValueCollectionIterator(); } - + public boolean contains(Object obj) { return containsValue(obj); } } - + class ValueIteratorImpl implements Iterator { private EntryIteratorImpl itr; diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/util/Util.java b/libcore/luni/src/main/java/org/apache/harmony/luni/util/Util.java index bfb01499d..e4515aedc 100644 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/util/Util.java +++ b/libcore/luni/src/main/java/org/apache/harmony/luni/util/Util.java @@ -20,216 +20,262 @@ package org.apache.harmony.luni.util; import java.io.ByteArrayOutputStream; import java.io.UTFDataFormatException; +import java.io.UnsupportedEncodingException; import java.util.Calendar; import java.util.TimeZone; public final class Util { - private static String[] WEEKDAYS = new String[] { "", "Sunday", "Monday", - "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; + private static String[] WEEKDAYS = new String[] { "", "Sunday", "Monday", + "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; - private static String[] MONTHS = new String[] { "January", "February", - "March", "April", "May", "June", "July", "August", "September", - "October", "November", "December" }; + private static String[] MONTHS = new String[] { "January", "February", + "March", "April", "May", "June", "July", "August", "September", + "October", "November", "December" }; - private static final String defaultEncoding; + private static final String defaultEncoding; - static { + static { // BEGIN android-changed String encoding = System.getProperty("file.encoding"); // END android-changed - if (encoding != null) { - try { - "".getBytes(encoding); - } catch (java.io.UnsupportedEncodingException e) { + if (encoding != null) { + try { + "".getBytes(encoding); + } catch (Throwable t) { encoding = null; - } - } + } + } defaultEncoding = encoding; - } + } + /** + * Get bytes from String using default encoding; default encoding can + * be changed via "os.encoding" property + * @param name input String + * @return byte array + */ public static byte[] getBytes(String name) { - if (defaultEncoding != null) { - try { - return name.getBytes(defaultEncoding); - } catch (java.io.UnsupportedEncodingException e) { - } - } + if (defaultEncoding != null) { + try { + return name.getBytes(defaultEncoding); + } catch (java.io.UnsupportedEncodingException e) { + } + } return name.getBytes(); - } - - public static String toString(byte[] bytes) { - if (defaultEncoding != null) { - try { - return new String(bytes, 0, bytes.length, defaultEncoding); - } catch (java.io.UnsupportedEncodingException e) { - } + } + + /** + * Get bytes from String with UTF8 encoding + * @param name + * input String + * @return byte array + */ + public static byte[] getUTF8Bytes(String name) { + try { + return name.getBytes("UTF-8"); + } catch (java.io.UnsupportedEncodingException e) { + return getBytes(name); } - return new String(bytes, 0, bytes.length); } - public static String toString(byte[] bytes, int offset, int length) { - if (defaultEncoding != null) { - try { - return new String(bytes, offset, length, defaultEncoding); - } catch (java.io.UnsupportedEncodingException e) { - } + public static String toString(byte[] bytes) { + if (defaultEncoding != null) { + try { + return new String(bytes, 0, bytes.length, defaultEncoding); + } catch (java.io.UnsupportedEncodingException e) { + } + } + return new String(bytes, 0, bytes.length); + } + + public static String toUTF8String(byte[] bytes) { + return toUTF8String(bytes, 0, bytes.length); + } + + public static String toString(byte[] bytes, int offset, int length) { + if (defaultEncoding != null) { + try { + return new String(bytes, offset, length, defaultEncoding); + } catch (java.io.UnsupportedEncodingException e) { + } + } + return new String(bytes, offset, length); + } + + public static String toUTF8String(byte[] bytes, int offset, int length) { + try { + return new String(bytes, offset, length, "UTF-8"); + } catch (java.io.UnsupportedEncodingException e) { + return toString(bytes, offset, length); } - return new String(bytes, offset, length); } + + /** + * Answers the millisecond value of the date and time parsed from the + * specified String. Many date/time formats are recognized + * + * @param string + * the String to parse + * @return the millisecond value parsed from the String + */ + public static long parseDate(String string) { + int offset = 0, length = string.length(), state = 0; + int year = -1, month = -1, date = -1; + int hour = -1, minute = -1, second = -1; + final int PAD = 0, LETTERS = 1, NUMBERS = 2; + StringBuilder buffer = new StringBuilder(); - /** - * Returns the millisecond value of the date and time parsed from the - * specified String. Many date/time formats are recognized - * - * @param string - * the String to parse - * @return the millisecond value parsed from the String - */ - public static long parseDate(String string) { - int offset = 0, length = string.length(), state = 0; - int year = -1, month = -1, date = -1; - int hour = -1, minute = -1, second = -1; - final int PAD = 0, LETTERS = 1, NUMBERS = 2; - StringBuffer buffer = new StringBuffer(); - - while (offset <= length) { - char next = offset < length ? string.charAt(offset) : '\r'; - offset++; + while (offset <= length) { + char next = offset < length ? string.charAt(offset) : '\r'; + offset++; - int nextState; - if ((next >= 'a' && next <= 'z') || (next >= 'A' && next <= 'Z')) - nextState = LETTERS; - else if (next >= '0' && next <= '9') - nextState = NUMBERS; - else if (" ,-:\r\t".indexOf(next) == -1) - throw new IllegalArgumentException(); - else - nextState = PAD; + int nextState; + if ((next >= 'a' && next <= 'z') || (next >= 'A' && next <= 'Z')) + nextState = LETTERS; + else if (next >= '0' && next <= '9') + nextState = NUMBERS; + else if (" ,-:\r\t".indexOf(next) == -1) + throw new IllegalArgumentException(); + else + nextState = PAD; - if (state == NUMBERS && nextState != NUMBERS) { - int digit = Integer.parseInt(buffer.toString()); - buffer.setLength(0); - if (digit >= 70) { - if (year != -1 - || (next != ' ' && next != ',' && next != '\r')) - throw new IllegalArgumentException(); - year = digit; - } else if (next == ':') { - if (hour == -1) - hour = digit; - else if (minute == -1) - minute = digit; - else - throw new IllegalArgumentException(); - } else if (next == ' ' || next == ',' || next == '-' - || next == '\r') { - if (hour != -1 && minute == -1) - minute = digit; - else if (minute != -1 && second == -1) - second = digit; - else if (date == -1) - date = digit; - else if (year == -1) - year = digit; - else - throw new IllegalArgumentException(); - } else if (year == -1 && month != -1 && date != -1) - year = digit; - else - throw new IllegalArgumentException(); - } else if (state == LETTERS && nextState != LETTERS) { - String text = buffer.toString().toUpperCase(); - buffer.setLength(0); - if (text.length() < 3) - throw new IllegalArgumentException(); - if (parse(text, WEEKDAYS) != -1) { - } else if (month == -1 && (month = parse(text, MONTHS)) != -1) { - } else if (text.equals("GMT")) { - } else - throw new IllegalArgumentException(); - } + if (state == NUMBERS && nextState != NUMBERS) { + int digit = Integer.parseInt(buffer.toString()); + buffer.setLength(0); + if (digit >= 70) { + if (year != -1 + || (next != ' ' && next != ',' && next != '\r')) + throw new IllegalArgumentException(); + year = digit; + } else if (next == ':') { + if (hour == -1) + hour = digit; + else if (minute == -1) + minute = digit; + else + throw new IllegalArgumentException(); + } else if (next == ' ' || next == ',' || next == '-' + || next == '\r') { + if (hour != -1 && minute == -1) + minute = digit; + else if (minute != -1 && second == -1) + second = digit; + else if (date == -1) + date = digit; + else if (year == -1) + year = digit; + else + throw new IllegalArgumentException(); + } else if (year == -1 && month != -1 && date != -1) + year = digit; + else + throw new IllegalArgumentException(); + } else if (state == LETTERS && nextState != LETTERS) { + String text = buffer.toString().toUpperCase(); + buffer.setLength(0); + if (text.length() < 3) + throw new IllegalArgumentException(); + if (parse(text, WEEKDAYS) != -1) { + } else if (month == -1 && (month = parse(text, MONTHS)) != -1) { + } else if (text.equals("GMT")) { + } else + throw new IllegalArgumentException(); + } - if (nextState == LETTERS || nextState == NUMBERS) - buffer.append(next); - state = nextState; - } + if (nextState == LETTERS || nextState == NUMBERS) + buffer.append(next); + state = nextState; + } - if (year != -1 && month != -1 && date != -1) { - if (hour == -1) - hour = 0; - if (minute == -1) - minute = 0; - if (second == -1) - second = 0; - Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); - int current = cal.get(Calendar.YEAR) - 80; - if (year < 100) { - year += current / 100 * 100; - if (year < current) - year += 100; - } - cal.set(Calendar.YEAR, year); - cal.set(Calendar.MONTH, month); - cal.set(Calendar.DATE, date); - cal.set(Calendar.HOUR_OF_DAY, hour); - cal.set(Calendar.MINUTE, minute); - cal.set(Calendar.SECOND, second); - cal.set(Calendar.MILLISECOND, 0); - return cal.getTime().getTime(); - } - throw new IllegalArgumentException(); - } + if (year != -1 && month != -1 && date != -1) { + if (hour == -1) + hour = 0; + if (minute == -1) + minute = 0; + if (second == -1) + second = 0; + Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); + int current = cal.get(Calendar.YEAR) - 80; + if (year < 100) { + year += current / 100 * 100; + if (year < current) + year += 100; + } + cal.set(Calendar.YEAR, year); + cal.set(Calendar.MONTH, month); + cal.set(Calendar.DATE, date); + cal.set(Calendar.HOUR_OF_DAY, hour); + cal.set(Calendar.MINUTE, minute); + cal.set(Calendar.SECOND, second); + cal.set(Calendar.MILLISECOND, 0); + return cal.getTime().getTime(); + } + throw new IllegalArgumentException(); + } - private static int parse(String string, String[] array) { - int length = string.length(); - for (int i = 0; i < array.length; i++) { - if (string.regionMatches(true, 0, array[i], 0, length)) - return i; - } - return -1; - } + private static int parse(String string, String[] array) { + int length = string.length(); + for (int i = 0; i < array.length; i++) { + if (string.regionMatches(true, 0, array[i], 0, length)) + return i; + } + return -1; + } - public static String convertFromUTF8(byte[] buf, int offset, int utfSize) - throws UTFDataFormatException { - return convertUTF8WithBuf(buf, new char[utfSize], offset, utfSize); - } + public static String convertFromUTF8(byte[] buf, int offset, int utfSize) + throws UTFDataFormatException { + return convertUTF8WithBuf(buf, new char[utfSize], offset, utfSize); + } - public static String convertUTF8WithBuf(byte[] buf, char[] out, int offset, - int utfSize) throws UTFDataFormatException { - int count = 0, s = 0, a; - while (count < utfSize) { - if ((out[s] = (char) buf[offset + count++]) < '\u0080') - s++; - else if (((a = out[s]) & 0xe0) == 0xc0) { - if (count >= utfSize) - throw new UTFDataFormatException(Msg.getString("K0062", - count)); + public static String convertUTF8WithBuf(byte[] buf, char[] out, int offset, + int utfSize) throws UTFDataFormatException { + int count = 0, s = 0, a; + while (count < utfSize) { + if ((out[s] = (char) buf[offset + count++]) < '\u0080') + s++; + else if (((a = out[s]) & 0xe0) == 0xc0) { + if (count >= utfSize) + throw new UTFDataFormatException(Msg.getString("K0062", + count)); // BEGIN android-changed int b = buf[offset + count++]; // END android-changed - if ((b & 0xC0) != 0x80) - throw new UTFDataFormatException(Msg.getString("K0062", - (count - 1))); - out[s++] = (char) (((a & 0x1F) << 6) | (b & 0x3F)); - } else if ((a & 0xf0) == 0xe0) { - if (count + 1 >= utfSize) - throw new UTFDataFormatException(Msg.getString("K0063", - (count + 1))); + if ((b & 0xC0) != 0x80) + throw new UTFDataFormatException(Msg.getString("K0062", + (count - 1))); + out[s++] = (char) (((a & 0x1F) << 6) | (b & 0x3F)); + } else if ((a & 0xf0) == 0xe0) { + if (count + 1 >= utfSize) + throw new UTFDataFormatException(Msg.getString("K0063", + (count + 1))); // BEGIN android-changed int b = buf[offset + count++]; int c = buf[offset + count++]; // END android-changed - if (((b & 0xC0) != 0x80) || ((c & 0xC0) != 0x80)) - throw new UTFDataFormatException(Msg.getString("K0064", - (count - 2))); - out[s++] = (char) (((a & 0x0F) << 12) | ((b & 0x3F) << 6) | (c & 0x3F)); - } else { - throw new UTFDataFormatException(Msg.getString("K0065", - (count - 1))); - } - } - return new String(out, 0, s); + if (((b & 0xC0) != 0x80) || ((c & 0xC0) != 0x80)) + throw new UTFDataFormatException(Msg.getString("K0064", + (count - 2))); + out[s++] = (char) (((a & 0x0F) << 12) | ((b & 0x3F) << 6) | (c & 0x3F)); + } else { + throw new UTFDataFormatException(Msg.getString("K0065", + (count - 1))); + } + } + return new String(out, 0, s); + } + + /** + * '%' and two following hex digit characters are converted to the + * equivalent byte value. All other characters are passed through + * unmodified. e.g. "ABC %24%25" -> "ABC $%" + * + * @param s + * java.lang.String The encoded string. + * @return java.lang.String The decoded version. + */ + public static String decode(String s, boolean convertPlus) { + return decode(s, convertPlus, null); } /** @@ -239,12 +285,14 @@ public final class Util { * * @param s * java.lang.String The encoded string. + * @param encoding + * the specified encoding * @return java.lang.String The decoded version. */ - public static String decode(String s, boolean convertPlus) { + public static String decode(String s, boolean convertPlus, String encoding) { if (!convertPlus && s.indexOf('%') == -1) return s; - StringBuffer result = new StringBuffer(s.length()); + StringBuilder result = new StringBuilder(s.length()); ByteArrayOutputStream out = new ByteArrayOutputStream(); for (int i = 0; i < s.length();) { char c = s.charAt(i); @@ -265,7 +313,15 @@ public final class Util { out.write((byte) ((d1 << 4) + d2)); i += 3; } while (i < s.length() && s.charAt(i) == '%'); - result.append(out.toString()); + if (encoding == null) { + result.append(out.toString()); + } else { + try { + result.append(out.toString(encoding)); + } catch (UnsupportedEncodingException e) { + throw new IllegalArgumentException(e); + } + } continue; } else result.append(c); @@ -273,32 +329,32 @@ public final class Util { } return result.toString(); } - - public static String toASCIILowerCase(String s) { + + public static String toASCIILowerCase(String s) { int len = s.length(); - StringBuilder buffer = new StringBuilder(len); - for (int i = 0; i < len; i++) { - char c = s.charAt(i); - if ('A' <= c && c <= 'Z') { - buffer.append((char) (c + ('a' - 'A'))); - } else { - buffer.append(c); - } - } - return buffer.toString(); - } - - public static String toASCIIUpperCase(String s) { + StringBuilder buffer = new StringBuilder(len); + for (int i = 0; i < len; i++) { + char c = s.charAt(i); + if ('A' <= c && c <= 'Z') { + buffer.append((char) (c + ('a' - 'A'))); + } else { + buffer.append(c); + } + } + return buffer.toString(); + } + + public static String toASCIIUpperCase(String s) { int len = s.length(); StringBuilder buffer = new StringBuilder(len); for (int i = 0; i < len; i++) { - char c = s.charAt(i); - if ('a' <= c && c <= 'z') { - buffer.append((char) (c - ('a' - 'A'))); - } else { - buffer.append(c); - } - } - return buffer.toString(); - } + char c = s.charAt(i); + if ('a' <= c && c <= 'z') { + buffer.append((char) (c - ('a' - 'A'))); + } else { + buffer.append(c); + } + } + return buffer.toString(); + } } diff --git a/libcore/luni/src/main/native/cbigint.c b/libcore/luni/src/main/native/cbigint.c index 0f65ca0c0..d3279401e 100644 --- a/libcore/luni/src/main/native/cbigint.c +++ b/libcore/luni/src/main/native/cbigint.c @@ -18,7 +18,7 @@ #include #include "cbigint.h" -#if defined(LINUX) || defined(FREEBSD) +#if defined(LINUX) || defined(FREEBSD) || defined(ZOS) || defined(MACOSX) || defined(AIX) #define USE_LL #endif @@ -665,7 +665,7 @@ tenToTheEHighPrecision (U_64 * result, IDATA length, jint e) do { overflow = simpleAppendDecimalDigitHighPrecision(result, length, 0); if (overflow) - result[length++] = overflow; + result[length++] = overflow; } while (--e); */ return length; @@ -686,12 +686,12 @@ timesTenToTheEHighPrecision (U_64 * result, IDATA length, jint e) do { overflow = simpleAppendDecimalDigitHighPrecision(result, length, 0); if (overflow) - result[length++] = overflow; + result[length++] = overflow; } while (--e); */ /* Replace the current implementaion which performs a * "multiplication" by 10 e number of times with an actual - * mulitplication. 10e19 is the largest exponent to the power of ten + * multiplication. 10e19 is the largest exponent to the power of ten * that will fit in a 64-bit integer, and 10e9 is the largest exponent to * the power of ten that will fit in a 64-bit integer. Not sure where the * break-even point is between an actual multiplication and a diff --git a/libcore/luni/src/main/native/java_io_FileDescriptor.c b/libcore/luni/src/main/native/java_io_FileDescriptor.c index a5ec8b7a8..897e688ff 100644 --- a/libcore/luni/src/main/native/java_io_FileDescriptor.c +++ b/libcore/luni/src/main/native/java_io_FileDescriptor.c @@ -103,20 +103,6 @@ static void fd_sync(JNIEnv* env, jobject obj) { } } -/* - * public native boolean valid() - */ -static jboolean fd_valid(JNIEnv* env, jobject obj) { - int fd = getFd(env, obj); - struct stat sb; - - if(fstat(fd, &sb) == 0) { - return JNI_TRUE; - } else { - return JNI_FALSE; - } -} - /* checks to see if class is inited and inits if needed, returning -1 * on fail and 0 on success */ @@ -205,8 +191,7 @@ void jniSetFileDescriptorOfFD (JNIEnv* env, jobject fileDescriptor, int value) { static JNINativeMethod gMethods[] = { /* name, signature, funcPtr */ { "oneTimeInitialization", "()V", nativeClassInit }, - { "syncImpl", "()V", fd_sync }, - { "valid", "()Z", fd_valid } + { "syncImpl", "()V", fd_sync } }; int register_java_io_FileDescriptor(JNIEnv* env) { return jniRegisterNativeMethods(env, "java/io/FileDescriptor", diff --git a/libcore/luni/src/main/native/java_net_InetAddress.cpp b/libcore/luni/src/main/native/java_net_InetAddress.cpp index 508656f42..d7b493171 100644 --- a/libcore/luni/src/main/native/java_net_InetAddress.cpp +++ b/libcore/luni/src/main/native/java_net_InetAddress.cpp @@ -100,7 +100,10 @@ static jobjectArray getAllByNameUsingAdb(JNIEnv* env, const char* name) env->SetByteArrayRegion(byteArray, 0, 4, (jbyte*) &outaddr.s_addr); env->SetObjectArrayElement(addressArray, 1, byteArray); } + } else { + jniThrowException(env, "java/net/UnknownHostException", "adb error"); } + return addressArray; } @@ -111,12 +114,8 @@ static jobjectArray getAllByNameUsingDns(JNIEnv* env, const char* name, jobjectArray addressArray = NULL; memset(&hints, 0, sizeof(hints)); - /* - * IPv4 only for now until the socket code supports IPv6; otherwise, the - * resolver will create two separate requests, one for IPv4 and one, - * currently unnecessary, for IPv6. - */ - hints.ai_family = AF_INET; + hints.ai_family = AF_UNSPEC; + hints.ai_flags = AI_ADDRCONFIG; /* * If we don't specify a socket type, every address will appear twice, once * for SOCK_STREAM and one for SOCK_DGRAM. Since we do not return the family @@ -193,8 +192,8 @@ static jobjectArray getAllByNameUsingDns(JNIEnv* env, const char* name, env, "java/lang/SecurityException", "Permission denied (maybe missing INTERNET permission)"); } else { - // Do nothing. Return value will be null and the caller will throw an - // UnknownHostExeption. + jniThrowException(env, "java/net/UnknownHostException", + gai_strerror(result)); } if (addressList) { @@ -231,15 +230,19 @@ jobjectArray InetAddress_getallbyname(JNIEnv* env, jobject obj, out = getAllByNameUsingDns(env, name, preferIPv4Stack); } - if (!out) { - LOGI("Unknown host %s, throwing UnknownHostException", name); - jniThrowException(env, "java/net/UnknownHostException", name); - } env->ReleaseStringUTFChars(javaName, name); return out; } +/** + * Looks up the name corresponding to an IP address. + * + * @param javaAddress: a byte array containing the raw IP address bytes. Must be + * 4 or 16 bytes long. + * @return the hostname. + * @throws UnknownHostException: the IP address has no associated hostname. + */ static jstring InetAddress_gethostbyaddr(JNIEnv* env, jobject obj, jbyteArray javaAddress) { @@ -256,57 +259,45 @@ static jstring InetAddress_gethostbyaddr(JNIEnv* env, jobject obj, } // Convert the raw address bytes into a socket address structure. + int ret = 0; struct sockaddr_storage ss; struct sockaddr_in *sin = (struct sockaddr_in *) &ss; struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &ss; size_t socklen; + memset(&ss, 0, sizeof(ss)); switch (addrlen) { case 4: socklen = sizeof(struct sockaddr_in); - memset(sin, 0, sizeof(struct sockaddr_in)); sin->sin_family = AF_INET; - memcpy(&sin->sin_addr.s_addr, rawAddress, 4); + memcpy(&sin->sin_addr.s_addr, rawAddress, addrlen); env->ReleaseByteArrayElements(javaAddress, rawAddress, JNI_ABORT); break; case 16: socklen = sizeof(struct sockaddr_in6); - memset(sin6, 0, sizeof(struct sockaddr_in6)); sin6->sin6_family = AF_INET6; - memcpy(&sin6->sin6_addr.s6_addr, rawAddress, 16); + memcpy(&sin6->sin6_addr.s6_addr, rawAddress, addrlen); env->ReleaseByteArrayElements(javaAddress, rawAddress, JNI_ABORT); break; default: + // The caller already throws an exception in this case. Don't worry + // about it here. env->ReleaseByteArrayElements(javaAddress, rawAddress, JNI_ABORT); - jniThrowException(env, "java/net/UnknownHostException", - "Invalid address length"); return NULL; } - - // Convert the socket address structure to an IP string for logging. - int ret; - char ipstr[INET6_ADDRSTRLEN]; - ret = getnameinfo((struct sockaddr *) &ss, socklen, ipstr, sizeof(ipstr), - NULL, 0, NI_NUMERICHOST); - if (ret) { - LOGE("gethostbyaddr: getnameinfo: %s", gai_strerror(ret)); - return NULL; + // Look up the host name from the IP address. + char name[NI_MAXHOST]; + if (ret == 0) { + ret = getnameinfo((struct sockaddr *) &ss, socklen, name, sizeof(name), + NULL, 0, NI_NAMEREQD); } - // Look up the IP address from the socket address structure. - jstring result = NULL; - char name[NI_MAXHOST]; - ret = getnameinfo((struct sockaddr *) &ss, socklen, name, sizeof(name), - NULL, 0, 0); if (ret == 0) { - LOGI("gethostbyaddr: getnameinfo: %s = %s", ipstr, name); - result = env->NewStringUTF(name); - } else { - LOGE("gethostbyaddr: getnameinfo: unknown host %s: %s", ipstr, - gai_strerror(ret)); + return env->NewStringUTF(name); } - return result; + jniThrowException(env, "java/net/UnknownHostException", gai_strerror(ret)); + return NULL; } /* diff --git a/libcore/luni/src/main/native/java_net_NetworkInterface.c b/libcore/luni/src/main/native/java_net_NetworkInterface.c index 379ccdfba..db6d5032a 100644 --- a/libcore/luni/src/main/native/java_net_NetworkInterface.c +++ b/libcore/luni/src/main/native/java_net_NetworkInterface.c @@ -546,8 +546,9 @@ int sockGetNetworkInterfaces(struct NetworkInterfaceArray_struct * array) { /* since this function can return multiple entries for the same name, only do it for the first one with any given name */ if((NULL == lastName) || (strncmp(lastName, ifc.ifc_req[counter].ifr_name, IFNAMSIZ) != 0)) { - /* get the index for the interface. This is only truely necessary on platforms that support IPV6 */ - interfaces[currentAdapterIndex].index = 0; + /* get the index for the interface */ + interfaces[currentAdapterIndex].index = + ifc.ifc_req[counter].ifr_ifindex; /* get the name and display name for the adapter */ /* there only seems to be one name so use it for both the name and the display name */ nameLength = strlen(ifc.ifc_req[counter].ifr_name); diff --git a/libcore/luni/src/main/native/org_apache_harmony_luni_platform_OSMemory.cpp b/libcore/luni/src/main/native/org_apache_harmony_luni_platform_OSMemory.cpp index c5e92bbd8..2e814ccd1 100644 --- a/libcore/luni/src/main/native/org_apache_harmony_luni_platform_OSMemory.cpp +++ b/libcore/luni/src/main/native/org_apache_harmony_luni_platform_OSMemory.cpp @@ -14,14 +14,26 @@ * limitations under the License. */ +#define LOG_TAG "OSMemory" #include "JNIHelp.h" #include "AndroidSystemNatives.h" #include "utils/misc.h" +#include "utils/Log.h" #include #include #include #include +/* + * Cached dalvik.system.VMRuntime pieces. + */ +static struct { + jmethodID method_trackExternalAllocation; + jmethodID method_trackExternalFree; + + jobject runtimeInstance; +} gIDCache; + #undef MMAP_READ_ONLY #define MMAP_READ_ONLY 1L #undef MMAP_READ_WRITE @@ -55,11 +67,28 @@ static jint harmony_nio_getPointerSizeImpl(JNIEnv *_env, jclass _this) { * Signature: (I)I */ static jint harmony_nio_mallocImpl(JNIEnv *_env, jobject _this, jint size) { - void *returnValue = malloc(size); - if(returnValue == NULL) { - jniThrowException(_env, "java.lang.OutOfMemoryError", ""); + jboolean allowed = _env->CallBooleanMethod(gIDCache.runtimeInstance, + gIDCache.method_trackExternalAllocation, (jlong) size); + if (!allowed) { + LOGW("External allocation of %d bytes was rejected\n", size); + jniThrowException(_env, "java/lang/OutOfMemoryError", NULL); + return 0; + } + + LOGV("OSMemory alloc %d\n", size); + void *returnValue = malloc(size + sizeof(jlong)); + if (returnValue == NULL) { + jniThrowException(_env, "java/lang/OutOfMemoryError", NULL); + return 0; } - return (jint)returnValue; + + /* + * Tuck a copy of the size at the head of the buffer. We need this + * so harmony_nio_freeImpl() knows how much memory is being freed. + */ + jlong* adjptr = (jlong*) returnValue; + *adjptr++ = size; + return (jint)adjptr; } /* @@ -68,7 +97,12 @@ static jint harmony_nio_mallocImpl(JNIEnv *_env, jobject _this, jint size) { * Signature: (I)V */ static void harmony_nio_freeImpl(JNIEnv *_env, jobject _this, jint pointer) { - free((void *)pointer); + jlong* adjptr = (jlong*) pointer; + jint size = *--adjptr; + LOGV("OSMemory free %d\n", size); + _env->CallVoidMethod(gIDCache.runtimeInstance, + gIDCache.method_trackExternalFree, (jlong) size); + free((void *)adjptr); } /* @@ -138,12 +172,10 @@ static void harmony_nio_putBytesImpl(JNIEnv *_env, jobject _this, } static void -swapShorts(jshort *shorts, int numBytes) { +swapShorts(jshort *shorts, int count) { jbyte *src = (jbyte *) shorts; jbyte *dst = src; - int i; - - for (i = 0; i < numBytes; i+=2) { + for (int i = 0; i < count; ++i) { jbyte b0 = *src++; jbyte b1 = *src++; *dst++ = b1; @@ -152,11 +184,10 @@ swapShorts(jshort *shorts, int numBytes) { } static void -swapInts(jint *ints, int numBytes) { +swapInts(jint *ints, int count) { jbyte *src = (jbyte *) ints; jbyte *dst = src; - int i; - for (i = 0; i < numBytes; i+=4) { + for (int i = 0; i < count; ++i) { jbyte b0 = *src++; jbyte b1 = *src++; jbyte b2 = *src++; @@ -170,48 +201,30 @@ swapInts(jint *ints, int numBytes) { /* * Class: org_apache_harmony_luni_platform_OSMemory - * Method: putShortsImpl + * Method: setShortArrayImpl * Signature: (I[SIIZ)V */ -static void harmony_nio_putShortsImpl(JNIEnv *_env, jobject _this, +static void harmony_nio_setShortArrayImpl(JNIEnv *_env, jobject _this, jint pointer, jshortArray src, jint offset, jint length, jboolean swap) { - - offset = offset << 1; - length = length << 1; - - jshort *src_ = - (jshort *)_env->GetPrimitiveArrayCritical(src, (jboolean *)0); - if (swap) { - swapShorts(src_ + offset, length); - } - memcpy((jbyte *)pointer, (jbyte *)src_ + offset, length); + jshort* dst = reinterpret_cast(static_cast(pointer)); + _env->GetShortArrayRegion(src, offset, length, dst); if (swap) { - swapShorts(src_ + offset, length); + swapShorts(dst, length); } - _env->ReleasePrimitiveArrayCritical(src, src_, JNI_ABORT); } /* * Class: org_apache_harmony_luni_platform_OSMemory - * Method: putIntsImpl + * Method: setIntArrayImpl * Signature: (I[IIIZ)V */ -static void harmony_nio_putIntsImpl(JNIEnv *_env, jobject _this, +static void harmony_nio_setIntArrayImpl(JNIEnv *_env, jobject _this, jint pointer, jintArray src, jint offset, jint length, jboolean swap) { - - offset = offset << 2; - length = length << 2; - - jint *src_ = - (jint *)_env->GetPrimitiveArrayCritical(src, (jboolean *)0); + jint* dst = reinterpret_cast(static_cast(pointer)); + _env->GetIntArrayRegion(src, offset, length, dst); if (swap) { - swapInts(src_ + offset, length); + swapInts(dst, length); } - memcpy((jbyte *)pointer, (jbyte *)src_ + offset, length); - if (swap) { - swapInts(src_ + offset, length); - } - _env->ReleasePrimitiveArrayCritical(src, src_, JNI_ABORT); } /* @@ -554,8 +567,8 @@ static JNINativeMethod gMethods[] = { { "memmove", "(IIJ)V", (void*) harmony_nio_memmove }, { "getByteArray", "(I[BII)V",(void*) harmony_nio_getBytesImpl }, { "setByteArray", "(I[BII)V",(void*) harmony_nio_putBytesImpl }, - { "setShortArray", "(I[SIIZ)V",(void*) harmony_nio_putShortsImpl }, - { "setIntArray", "(I[IIIZ)V",(void*) harmony_nio_putIntsImpl }, + { "setShortArray", "(I[SIIZ)V",(void*) harmony_nio_setShortArrayImpl }, + { "setIntArray", "(I[IIIZ)V",(void*) harmony_nio_setIntArrayImpl }, { "getByte", "(I)B", (void*) harmony_nio_getByteImpl }, { "setByte", "(IB)V", (void*) harmony_nio_putByteImpl }, { "getShort", "(I)S", (void*) harmony_nio_getShortImpl }, @@ -577,6 +590,45 @@ static JNINativeMethod gMethods[] = { { "flushImpl", "(IJ)I", (void*) harmony_nio_flushImpl } }; int register_org_apache_harmony_luni_platform_OSMemory(JNIEnv *_env) { - return jniRegisterNativeMethods(_env, "org/apache/harmony/luni/platform/OSMemory", + /* + * We need to call VMRuntime.trackExternal{Allocation,Free}. Cache + * method IDs and a reference to the singleton. + */ + static const char* kVMRuntimeName = "dalvik/system/VMRuntime"; + jmethodID method_getRuntime; + jclass clazz; + + clazz = _env->FindClass(kVMRuntimeName); + if (clazz == NULL) { + LOGE("Unable to find class %s\n", kVMRuntimeName); + return -1; + } + gIDCache.method_trackExternalAllocation = _env->GetMethodID(clazz, + "trackExternalAllocation", "(J)Z"); + gIDCache.method_trackExternalFree = _env->GetMethodID(clazz, + "trackExternalFree", "(J)V"); + method_getRuntime = _env->GetStaticMethodID(clazz, + "getRuntime", "()Ldalvik/system/VMRuntime;"); + + if (gIDCache.method_trackExternalAllocation == NULL || + gIDCache.method_trackExternalFree == NULL || + method_getRuntime == NULL) + { + LOGE("Unable to find VMRuntime methods\n"); + return -1; + } + + jobject instance = _env->CallStaticObjectMethod(clazz, method_getRuntime); + if (instance == NULL) { + LOGE("Unable to obtain VMRuntime instance\n"); + return -1; + } + gIDCache.runtimeInstance = _env->NewGlobalRef(instance); + + /* + * Register methods. + */ + return jniRegisterNativeMethods(_env, + "org/apache/harmony/luni/platform/OSMemory", gMethods, NELEM(gMethods)); } diff --git a/libcore/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp b/libcore/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp old mode 100755 new mode 100644 index f79c019ab..7b6023a64 --- a/libcore/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp +++ b/libcore/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp @@ -14,6 +14,13 @@ * limitations under the License. */ +// BEGIN android-changed +// +// This file has been substantially reworked in order to provide more IPv6 +// support and to move functionality from Java to native code where it made +// sense (e.g. when converting between IP addresses, socket structures, and +// strings, for which there exist fast and robust native implementations). + #define LOG_TAG "OSNetworkSystem" #include "JNIHelp.h" @@ -26,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -33,9 +41,13 @@ #include #include -#include #include "AndroidSystemNatives.h" +// Temporary hack to build on systems that don't have up-to-date libc headers. +#ifndef IPV6_TCLASS +#define IPV6_TCLASS 67 +#endif + /** * @name Socket Errors * Error codes for socket operations @@ -145,12 +157,16 @@ // wait for 500000 usec = 0.5 second #define SEND_RETRY_TIME 500000 +// Local constants for getOrSetSocketOption +#define SOCKOPT_GET 1 +#define SOCKOPT_SET 2 struct CachedFields { jfieldID fd_descriptor; jclass iaddr_class; - jmethodID iaddr_class_init; jmethodID iaddr_getbyaddress; + jclass i4addr_class; + jmethodID i4addr_class_init; jfieldID iaddr_ipaddress; jclass genericipmreq_class; jclass integer_class; @@ -164,6 +180,7 @@ struct CachedFields { jfieldID byte_class_value; jclass string_class; jmethodID string_class_init; + jclass socketimpl_class; jfieldID socketimpl_address; jfieldID socketimpl_port; jclass dpack_class; @@ -208,60 +225,87 @@ static void throwNullPointerException(JNIEnv *env) { } /** - * Converts a 4-byte array to a native address structure. Throws a + * Converts a native address structure to a Java byte array. Throws a * NullPointerException or an IOException in case of error. This is * signaled by a return value of -1. The normal return value is 0. + * + * @param address the sockaddr_storage structure to convert + * + * @exception SocketException the address family is unknown, or out of memory + * */ -static int javaAddressToStructIn( - JNIEnv *env, jbyteArray java_address, struct in_addr *address) { - - memset(address, 0, sizeof(address)); - - if (java_address == NULL) { - return -1; +static jbyteArray socketAddressToByteArray(JNIEnv *env, + struct sockaddr_storage *address) { + + void *rawAddress; + size_t addressLength; + if (address->ss_family == AF_INET) { + struct sockaddr_in *sin = (struct sockaddr_in *) address; + rawAddress = &sin->sin_addr.s_addr; + addressLength = 4; + } else if (address->ss_family == AF_INET6) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) address; + rawAddress = &sin6->sin6_addr.s6_addr; + addressLength = 16; + } else { + throwSocketException(env, SOCKERR_BADAF); + return NULL; } - if (env->GetArrayLength(java_address) != sizeof(address->s_addr)) { - return -1; + jbyteArray byteArray = env->NewByteArray(addressLength); + if (byteArray == NULL) { + throwSocketException(env, SOCKERR_NOBUFFERS); + return NULL; } + env->SetByteArrayRegion(byteArray, 0, addressLength, (jbyte *) rawAddress); - jbyte * java_address_bytes - = env->GetByteArrayElements(java_address, NULL); - - memcpy(&(address->s_addr), - java_address_bytes, - sizeof(address->s_addr)); - - env->ReleaseByteArrayElements(java_address, java_address_bytes, JNI_ABORT); - - return 0; + return byteArray; } /** - * Converts a native address structure to a 4-byte array. Throws a - * NullPointerException or an IOException in case of error. This is - * signaled by a return value of -1. The normal return value is 0. + * Returns the port number in a sockaddr_storage structure. + * + * @param address the sockaddr_storage structure to get the port from + * + * @return the port number, or -1 if the address family is unknown. */ -static int structInToJavaAddress( - JNIEnv *env, struct in_addr *address, jbyteArray java_address) { - - if (java_address == NULL) { - return -1; +static int getSocketAddressPort(struct sockaddr_storage *address) { + switch (address->ss_family) { + case AF_INET: + return ntohs(((struct sockaddr_in *) address)->sin_port); + case AF_INET6: + return ntohs(((struct sockaddr_in6 *) address)->sin6_port); + default: + return -1; } +} - if (env->GetArrayLength(java_address) != sizeof(address->s_addr)) { - return -1; +/** + * Checks whether a socket address structure contains an IPv4-mapped address. + * + * @param address the socket address structure to check + * @return true if address contains an IPv4-mapped address, false otherwise. + */ +static bool isMappedAddress(sockaddr *address) { + if (! address || address->sa_family != AF_INET6) { + return false; } + in6_addr addr = ((sockaddr_in6 *) address)->sin6_addr; + return (addr.s6_addr32[0] == 0 && + addr.s6_addr32[1] == 0 && + addr.s6_addr32[2] == htonl(0xffff)); +} - jbyte *java_address_bytes; - - java_address_bytes = env->GetByteArrayElements(java_address, NULL); - - memcpy(java_address_bytes, &(address->s_addr), sizeof(address->s_addr)); - - env->ReleaseByteArrayElements(java_address, java_address_bytes, 0); - - return 0; +/** + * Checks whether a 16-byte array represents an IPv4-mapped IPv6 address. + * + * @param addressBytes the address to check. Must be 16 bytes long. + * @return true if address contains an IPv4-mapped address, false otherwise. + */ +static bool isJavaMappedAddress(jbyte *addressBytes) { + static const unsigned char mappedBytes[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}; + return !memcmp(mappedBytes, addressBytes, sizeof(mappedBytes)); } /** @@ -269,68 +313,289 @@ static int structInToJavaAddress( * Throws a NullPointerException or an IOException in case of * error. This is signaled by a return value of -1. The normal * return value is 0. + * + * @param sockaddress the sockaddr_storage structure to convert + * + * @return a jobject representing an InetAddress */ -static int socketAddressToInetAddress(JNIEnv *env, - struct sockaddr_in *sockaddress, jobject inetaddress, int *port) { +static jobject socketAddressToInetAddress(JNIEnv *env, + struct sockaddr_storage *sockaddress) { - jbyteArray ipaddress; - int result; + jbyteArray byteArray = socketAddressToByteArray(env, sockaddress); + if (byteArray == NULL) // Exception has already been thrown. + return NULL; - ipaddress = (jbyteArray)env->GetObjectField(inetaddress, - gCachedFields.iaddr_ipaddress); + return env->CallStaticObjectMethod(gCachedFields.iaddr_class, + gCachedFields.iaddr_getbyaddress, byteArray); +} - if (structInToJavaAddress(env, &sockaddress->sin_addr, ipaddress) < 0) { - return -1; - } +/** + * Converts an IPv4-mapped IPv6 address to an IPv4 address. Performs no error + * checking. + * + * @param address the address to convert. Must contain an IPv4-mapped address. + * @param outputAddress the converted address. Will contain an IPv4 address. + */ +static void convertMappedToIpv4(sockaddr_in6 *sin6, sockaddr_in *sin) { + memset(sin, 0, sizeof(*sin)); + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = sin6->sin6_addr.s6_addr32[3]; + sin->sin_port = sin6->sin6_port; +} - *port = ntohs(sockaddress->sin_port); +/** + * Converts an IPv4 address to an IPv4-mapped IPv6 address. Performs no error + * checking. + * + * @param address the address to convert. Must contain an IPv4 address. + * @param outputAddress the converted address. Will contain an IPv6 address. + * @param mapUnspecified if true, convert 0.0.0.0 to ::ffff:0:0; if false, to :: + */ +static void convertIpv4ToMapped(struct sockaddr_in *sin, + struct sockaddr_in6 *sin6, bool mapUnspecified) { + memset(sin6, 0, sizeof(*sin6)); + sin6->sin6_family = AF_INET6; + sin6->sin6_addr.s6_addr32[3] = sin->sin_addr.s_addr; + if (sin->sin_addr.s_addr != 0 || mapUnspecified) { + sin6->sin6_addr.s6_addr32[2] = htonl(0xffff); + } + sin6->sin6_port = sin->sin_port; +} +/** + * Converts an InetAddress object and port number to a native address structure. + * Throws a NullPointerException or a SocketException in case of + * error. This is signaled by a return value of -1. The normal + * return value is 0. + * + * @param inetaddress the InetAddress object to convert + * @param port the port number + * @param sockaddress the sockaddr_storage structure to write to + * + * @return 0 on success, a system error code on failure + * + * @exception SocketError if the address family is unknown + */ +static int byteArrayToSocketAddress(JNIEnv *env, + jbyteArray addressByteArray, int port, sockaddr_storage *sockaddress) { + if (addressByteArray == NULL) { + throwNullPointerException(env); + return EFAULT; + } + size_t addressLength = env->GetArrayLength(addressByteArray); + + // Convert the IP address bytes to the proper IP address type. + if (addressLength == 4) { + // IPv4 address. + sockaddr_in *sin = (sockaddr_in *) sockaddress; + memset(sin, 0, sizeof(sockaddr_in)); + sin->sin_family = AF_INET; + sin->sin_port = htons(port); + jbyte *rawBytes = (jbyte *) &sin->sin_addr.s_addr; + env->GetByteArrayRegion(addressByteArray, 0, 4, rawBytes); + } else if (addressLength == 16) { + // IPv6 address. + sockaddr_in6 *sin6 = (sockaddr_in6 *) sockaddress; + memset(sin6, 0, sizeof(sockaddr_in6)); + sin6->sin6_family = AF_INET6; + sin6->sin6_port = htons(port); + jbyte *rawBytes = (jbyte *) &sin6->sin6_addr.s6_addr; + env->GetByteArrayRegion(addressByteArray, 0, 16, rawBytes); + } else { + // Unknown address family. + throwSocketException(env, SOCKERR_BADAF); + return EAFNOSUPPORT; + } return 0; } /** - * Converts an InetAddress object to a native address structure. - * Throws a NullPointerException or an IOException in case of + * Converts an InetAddress object and port number to a native address structure. + * Throws a NullPointerException or a SocketException in case of * error. This is signaled by a return value of -1. The normal * return value is 0. + * + * @param inetaddress the InetAddress object to convert + * @param port the port number + * @param sockaddress the sockaddr_storage structure to write to + * + * @return 0 on success, -1 on failure + * @throw UnknownHostException if any error occurs + * + * @exception SocketError if the address family is unknown */ static int inetAddressToSocketAddress(JNIEnv *env, - jobject inetaddress, int port, struct sockaddr_in *sockaddress) { + jobject inetaddress, int port, sockaddr_storage *sockaddress) { - jbyteArray ipaddress; - int result; - - ipaddress = (jbyteArray)env->GetObjectField(inetaddress, + // Get the byte array that stores the IP address bytes in the InetAddress. + jbyteArray addressByteArray; + addressByteArray = (jbyteArray)env->GetObjectField(inetaddress, gCachedFields.iaddr_ipaddress); - memset(sockaddress, 0, sizeof(sockaddress)); + return byteArrayToSocketAddress(env, addressByteArray, port, sockaddress); +} - sockaddress->sin_family = AF_INET; - sockaddress->sin_port = htons(port); +/** + * Convert a sockaddr_storage structure to a Java string. + * + * @param address pointer to sockaddr_storage structure to convert. + * @param withPort whether to include the port number in the output as well. + * + * @return 0 on success, a getnameinfo return code on failure. + * + * @throws SocketException the address family was unknown. + */ +static int socketAddressToString(sockaddr_storage *address, char *ipString, + int len, bool withPort) { + // TODO: getnameinfo seems to want its length parameter to be exactly + // sizeof(sockaddr_in) for an IPv4 address and sizeof (sockaddr_in6) for an + // IPv6 address. Fix getnameinfo so it accepts sizeof(sockaddr_storage), and + // then remove this hack. + int size; + if (address->ss_family == AF_INET) { + size = sizeof(sockaddr_in); + } else if (address->ss_family == AF_INET6) { + size = sizeof(sockaddr_in6); + } else { + errno = EAFNOSUPPORT; + return EAI_SYSTEM; + } - if (javaAddressToStructIn(env, ipaddress, &(sockaddress->sin_addr)) < 0) { - return -1; + char tmp[INET6_ADDRSTRLEN]; + int result = getnameinfo((sockaddr *)address, size, tmp, sizeof(tmp), NULL, + 0, NI_NUMERICHOST); + if (result != 0) { + return result; } + int port; + if (withPort) { + if (address->ss_family == AF_INET6) { + sockaddr_in6 *sin6 = (sockaddr_in6 *) address; + port = ntohs(sin6->sin6_port); + snprintf(ipString, len, "[%s]:%d", tmp, port); + } else { + sockaddr_in *sin = (sockaddr_in *) address; + port = ntohs(sin->sin_port); + snprintf(ipString, len, "%s:%d", tmp, port); + } + } else { + strncpy(ipString, tmp, len); + } return 0; } -static jobject structInToInetAddress(JNIEnv *env, struct in_addr *address) { - jbyteArray bytes; - int success; +/** + * Convert a Java byte array representing an IP address to a Java string. + * + * @param addressByteArray the byte array to convert. + * + * @return a string with the textual representation of the address. + * + * @throws SocketException the address family was unknown. + */ +static jstring osNetworkSystem_byteArrayToIpString(JNIEnv *env, jclass clazz, + jbyteArray byteArray) { + // For compatibility, ensure that an UnknownHostException is thrown if the + // address is null. + if (byteArray == NULL) { + jniThrowException(env, "java/net/UnknownHostException", + strerror(EFAULT)); + return NULL; + } + struct sockaddr_storage ss; + int ret = byteArrayToSocketAddress(env, byteArray, 0, &ss); + if (ret) { + jniThrowException(env, "java/net/UnknownHostException", strerror(ret)); + return NULL; + } + char ipString[INET6_ADDRSTRLEN]; + ret = socketAddressToString(&ss, ipString, sizeof(ipString), false); + if (ret) { + env->ExceptionClear(); + jniThrowException(env, "java/net/UnknownHostException", + gai_strerror(ret)); + return NULL; + } + return env->NewStringUTF(ipString); +} + +/** + * Convert a Java string representing an IP address to a Java byte array. + * The formats accepted are: + * - IPv4: + * - 1.2.3.4 + * - 1.2.4 + * - 1.4 + * - 4 + * - IPv6 + * - Compressed form (2001:db8::1) + * - Uncompressed form (2001:db8:0:0:0:0:0:1) + * - IPv4-compatible (::192.0.2.0) + * - With an embedded IPv4 address (2001:db8::192.0.2.0). + * IPv6 addresses may appear in square brackets. + * + * @param addressByteArray the byte array to convert. + * + * @return a string with the textual representation of the address. + * + * @throws UnknownHostException the IP address was invalid. + */ +static jbyteArray osNetworkSystem_ipStringToByteArray(JNIEnv *env, jclass clazz, + jstring javaString) { + if (javaString == NULL) { + throwNullPointerException(env); + } - bytes = env->NewByteArray(4); + char ipString[INET6_ADDRSTRLEN]; + int stringLength = env->GetStringUTFLength(javaString); + env->GetStringUTFRegion(javaString, 0, stringLength, ipString); + + // Accept IPv6 addresses (only) in square brackets for compatibility. + if (ipString[0] == '[' && ipString[stringLength - 1] == ']' && + index(ipString, ':') != NULL) { + memmove(ipString, ipString + 1, stringLength - 2); + ipString[stringLength - 2] = '\0'; + } + + jbyteArray result = NULL; + sockaddr_in sin; + addrinfo hints, *res; + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_NUMERICHOST; + int ret = getaddrinfo(ipString, NULL, &hints, &res); + if (ret == 0 && res) { + // Convert mapped addresses to IPv4 addresses if necessary. + if (res->ai_family == AF_INET6 && isMappedAddress(res->ai_addr)) { + convertMappedToIpv4((sockaddr_in6 *) res->ai_addr, &sin); + result = socketAddressToByteArray(env, (sockaddr_storage *) &sin); + } else { + result = socketAddressToByteArray(env, + (sockaddr_storage *) res->ai_addr); + } + } else { + // For backwards compatibility, deal with address formats that + // getaddrinfo does not support. For example, 1.2.3, 1.3, and even 3 are + // valid IPv4 addresses according to the Java API. If getaddrinfo fails, + // try to use inet_aton. + if (inet_aton(ipString, &sin.sin_addr)) { + sin.sin_port = 0; + sin.sin_family = AF_INET; + result = socketAddressToByteArray(env, (sockaddr_storage *) &sin); + } + } - if (bytes == NULL) { - return NULL; + if (res) { + freeaddrinfo(res); } - if (structInToJavaAddress(env, address, bytes) < 0) { - return NULL; + if (! result) { + env->ExceptionClear(); + jniThrowException(env, "java/net/UnknownHostException", + gai_strerror(ret)); } - return env->CallStaticObjectMethod(gCachedFields.iaddr_class, - gCachedFields.iaddr_getbyaddress, bytes); + return result; } /** @@ -427,15 +692,34 @@ static int time_msec_clock() { } /** - * check if the passed sockaddr_in struct contains a localhost address + * Check if the passed sockaddr_storage struct contains a localhost address * - * @param[in] address pointer to the address to check + * @param address address pointer to the address to check * * @return 0 if the passed address isn't a localhost address */ -static int isLocalhost(struct sockaddr_in *address) { - // return address == 127.0.0.1 - return (unsigned int) address->sin_addr.s_addr == 16777343; +static int isLocalHost(struct sockaddr_storage *address) { + if (address->ss_family == AF_INET) { + struct sockaddr_in *sin = (struct sockaddr_in *) address; + return (sin->sin_addr.s_addr == htonl(INADDR_LOOPBACK)); + } else if (address->ss_family == AF_INET6) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) address; + return IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr); + } else { + return 0; + } +} + +/** + * Decide whether to use ADB networking for the given socket address. + * + * @param address pointer to sockaddr_storage structure to check + * + * @return true if ADB networking should be used, false otherwise. + */ +static bool useAdbNetworkingForAddress(struct sockaddr_storage *address) { + return useAdbNetworking && !isLocalHost(address) && + address->ss_family == AF_INET; } /** @@ -728,7 +1012,6 @@ static int pollSelectWait(JNIEnv *env, jobject fileDescriptor, int timeout, int } } else if (0 > result) { - log_socket_close(handle, result); throwSocketException(env, result); } poll = 0; @@ -745,7 +1028,6 @@ static int pollSelectWait(JNIEnv *env, jobject fileDescriptor, int timeout, int continue; // try again } else if (0 > result) { - log_socket_close(handle, result); throwSocketException(env, result); } poll = 0; @@ -755,6 +1037,24 @@ static int pollSelectWait(JNIEnv *env, jobject fileDescriptor, int timeout, int return result; } +/** + * Obtain the socket address family from an existing socket. + * + * @param socket the filedescriptor of the socket to examine + * + * @return an integer, the address family of the socket + */ +static int getSocketAddressFamily(int socket) { + struct sockaddr_storage ss; + socklen_t namelen = sizeof(ss); + int ret = getsockname(socket, (struct sockaddr*) &ss, &namelen); + if (ret != 0) { + return AF_UNSPEC; + } else { + return ss.ss_family; + } +} + /** * A helper method, to set the connect context to a Long object. * @@ -802,6 +1102,59 @@ unsigned short ip_checksum(unsigned short* buffer, int size) { return (unsigned short )(~sum); } + +/** + * Wrapper for connect() that converts IPv4 addresses to IPv4-mapped IPv6 + * addresses if necessary. + * + * @param socket the filedescriptor of the socket to connect + * @param socketAddress the address to connect to + */ +static int doConnect(int socket, struct sockaddr_storage *socketAddress) { + sockaddr_storage mappedAddress; + sockaddr_storage *realAddress; + if (socketAddress->ss_family == AF_INET && + getSocketAddressFamily(socket) == AF_INET6) { + convertIpv4ToMapped((sockaddr_in *) socketAddress, + (sockaddr_in6 *) &mappedAddress, true); + realAddress = &mappedAddress; + } else { + realAddress = socketAddress; + } + int ret; + do { + ret = connect(socket, (struct sockaddr *) realAddress, + sizeof(struct sockaddr_storage)); + } while (ret < 0 && errno == EINTR); + return ret; +} + +/** + * Wrapper for bind() that converts IPv4 addresses to IPv4-mapped IPv6 + * addresses if necessary. + * + * @param socket the filedescriptor of the socket to connect + * @param socketAddress the address to connect to + */ +static int doBind(int socket, struct sockaddr_storage *socketAddress) { + struct sockaddr_storage mappedAddress; + struct sockaddr_storage *realAddress; + if (socketAddress->ss_family == AF_INET && + getSocketAddressFamily(socket) == AF_INET6) { + convertIpv4ToMapped((sockaddr_in *) socketAddress, + (sockaddr_in6 *) &mappedAddress, false); + realAddress = &mappedAddress; + } else { + realAddress = socketAddress; + } + int ret; + do { + ret = bind(socket, (struct sockaddr *) realAddress, + sizeof(struct sockaddr_storage)); + } while (ret < 0 && errno == EINTR); + return ret; +} + /** * Establish a connection to a peer with a timeout. This function is called * repeatedly in order to carry out the connect and to allow other tasks to @@ -823,7 +1176,7 @@ unsigned short ip_checksum(unsigned short* buffer, int size) { * * @return 0, if no errors occurred, otherwise the (negative) error code. */ -static int sockConnectWithTimeout(int handle, struct sockaddr_in addr, +static int sockConnectWithTimeout(int handle, struct sockaddr_storage addr, unsigned int timeout, unsigned int step, jbyte *ctxt) { int rc = 0; struct timeval passedTimeout; @@ -838,16 +1191,14 @@ static int sockConnectWithTimeout(int handle, struct sockaddr_in addr, context->sock = handle; context->nfds = handle + 1; - if (useAdbNetworking && !isLocalhost(&addr)) { + if (useAdbNetworkingForAddress(&addr)) { // LOGD("+connect to address 0x%08x (via adb)", // addr.sin_addr.s_addr); - rc = adb_networking_connect_fd(handle, &addr); + rc = adb_networking_connect_fd(handle, (struct sockaddr_in *) &addr); // LOGD("-connect ret %d errno %d (via adb)", rc, errno); } else { - log_socket_connect(handle, ntohl(addr.sin_addr.s_addr), - ntohs(addr.sin_port)); /* set the socket to non-blocking */ int block = JNI_TRUE; rc = ioctl(handle, FIONBIO, &block); @@ -857,10 +1208,7 @@ static int sockConnectWithTimeout(int handle, struct sockaddr_in addr, // LOGD("+connect to address 0x%08x (via normal) on handle %d", // addr.sin_addr.s_addr, handle); - do { - rc = connect(handle, (struct sockaddr *) &addr, - sizeof(struct sockaddr)); - } while (rc < 0 && errno == EINTR); + rc = doConnect(handle, &addr); // LOGD("-connect to address 0x%08x (via normal) returned %d", // addr.sin_addr.s_addr, (int) rc); @@ -971,6 +1319,123 @@ static int sockConnectWithTimeout(int handle, struct sockaddr_in addr, return SOCKERR_ARGSINVALID; } + +#if LOG_SOCKOPT +/** + * Helper method to log getsockopt/getsockopt calls. + */ +static const char *sockoptLevelToString(int level) { + switch(level) { + case SOL_SOCKET: + return "SOL_SOCKET"; + case SOL_IP: + return "SOL_IP"; + case SOL_IPV6: + return "SOL_IPV6"; + default: + return "SOL_???"; + } +} +#endif + +/** + * Helper method to get or set socket options + * + * @param action SOCKOPT_GET to get an option, SOCKOPT_SET to set it + * @param socket the file descriptor of the socket to use + * @param ipv4Option the option value to use for an IPv4 socket + * @param ipv6Option the option value to use for an IPv6 socket + * @param optionValue the value of the socket option to get or set + * @param optionLength the length of the socket option to get or set + * + * @return the value of the socket call, or -1 on failure inside this function + * + * @note on internal failure, the errno variable will be set appropriately + */ +static int getOrSetSocketOption(int action, int socket, int ipv4Option, + int ipv6Option, void *optionValue, socklen_t *optionLength) { + int option; + int protocol; + int family = getSocketAddressFamily(socket); + switch (family) { + case AF_INET: + option = ipv4Option; + protocol = SOL_IP; + break; + case AF_INET6: + option = ipv6Option; + protocol = SOL_IPV6; + break; + default: + // TODO: throw Java exceptions from this method instead of just + // returning error codes. + errno = EAFNOSUPPORT; + return -1; + } + + int ret; + if (action == SOCKOPT_GET) { + ret = getsockopt(socket, protocol, option, optionValue, optionLength); +#if LOG_SOCKOPT + LOGI("getsockopt(%d, %s, %d, %p, [%d]) = %d %s", + socket, sockoptLevelToString(protocol), option, optionValue, + *optionLength, ret, (ret == -1) ? strerror(errno) : ""); +#endif + } else if (action == SOCKOPT_SET) { + ret = setsockopt(socket, protocol, option, optionValue, *optionLength); +#if LOG_SOCKOPT + LOGI("setsockopt(%d, %s, %d, [%d], %d) = %d %s", + socket, sockoptLevelToString(protocol), option, + // Note: this only works for integer options. + // TODO: Use dvmPrintHexDump() to log non-integer options. + *(int *)optionValue, *optionLength, ret, + (ret == -1) ? strerror(errno) : ""); +#endif + } else { + errno = EINVAL; + ret = -1; + } + return ret; +} + +/* + * Find the interface index that was set for this socket by the IP_MULTICAST_IF + * or IPV6_MULTICAST_IF socket option. + * + * @param socket the socket to examine + * + * @return the interface index, or -1 on failure + * + * @note on internal failure, the errno variable will be set appropriately + */ +static int interfaceIndexFromMulticastSocket(int socket) { + int family = getSocketAddressFamily(socket); + socklen_t requestLength; + int interfaceIndex; + int result; + if (family == AF_INET) { + // IP_MULTICAST_IF returns a pointer to a struct ip_mreqn. + struct ip_mreqn tempRequest; + requestLength = sizeof(tempRequest); + result = getsockopt(socket, SOL_IP, IP_MULTICAST_IF, &tempRequest, + &requestLength); + interfaceIndex = tempRequest.imr_ifindex; + } else if (family == AF_INET6) { + // IPV6_MULTICAST_IF returns a pointer to an integer. + requestLength = sizeof(interfaceIndex); + result = getsockopt(socket, SOL_IPV6, IPV6_MULTICAST_IF, + &interfaceIndex, &requestLength); + } else { + errno = EAFNOSUPPORT; + return -1; + } + + if (result == 0) + return interfaceIndex; + else + return -1; +} + /** * Join/Leave the nominated multicast group on the specified socket. * Implemented by setting the multicast 'add membership'/'drop membership' @@ -994,103 +1459,125 @@ static int sockConnectWithTimeout(int handle, struct sockaddr_in addr, */ static void mcastAddDropMembership (JNIEnv * env, int handle, jobject optVal, int ignoreIF, int setSockOptVal) { + struct sockaddr_storage sockaddrP; int result; - struct ip_mreq ipmreqP; - struct sockaddr_in sockaddrP; - int length = sizeof(struct ip_mreq); - socklen_t lengthIF = sizeof(struct sockaddr_in); - - /* - * JNI objects needed to access the information in the optVal oject - * passed in. The object passed in is a GenericIPMreq object - */ - jclass cls; - jfieldID multiaddrID; - jfieldID interfaceAddrID; - jobject multiaddr; - jobject interfaceAddr; + // By default, let the system decide which interface to use. + int interfaceIndex = 0; /* - * check whether we are getting an InetAddress or an Generic IPMreq, for now - * we support both so that we will not break the tests + * Check whether we are getting an InetAddress or an Generic IPMreq. For now + * we support both so that we will not break the tests. If an InetAddress + * is passed in, only support IPv4 as obtaining an interface from an + * InetAddress is complex and should be done by the Java caller. */ if (env->IsInstanceOf (optVal, gCachedFields.iaddr_class)) { + /* + * optVal is an InetAddress. Construct a multicast request structure + * from this address. Support IPv4 only. + */ + struct ip_mreqn multicastRequest; + socklen_t length = sizeof(multicastRequest); + memset(&multicastRequest, 0, length); - ipmreqP.imr_interface.s_addr = htonl(INADDR_ANY); + // If ignoreIF is false, determine the index of the interface to use. if (!ignoreIF) { - - result = getsockopt(handle, IPPROTO_IP, IP_MULTICAST_IF, &sockaddrP, - &lengthIF); - - if (0 != result) { - throwSocketException (env, convertError(errno)); + interfaceIndex = interfaceIndexFromMulticastSocket(handle); + multicastRequest.imr_ifindex = interfaceIndex; + if (interfaceIndex == -1) { + throwSocketException(env, convertError(errno)); return; } - - memcpy(&(ipmreqP.imr_interface.s_addr), &(sockaddrP.sin_addr), 4); } + // Convert the inetAddress to an IPv4 address structure. result = inetAddressToSocketAddress(env, optVal, 0, &sockaddrP); - - if (result < 0) { - throwSocketException(env, SOCKERR_BADSOCKET); + if (result < 0) // Exception has already been thrown. + return; + if (sockaddrP.ss_family != AF_INET) { + throwSocketException(env, SOCKERR_BADAF); return; } + struct sockaddr_in *sin = (struct sockaddr_in *) &sockaddrP; + multicastRequest.imr_multiaddr = sin->sin_addr; - memcpy(&(ipmreqP.imr_multiaddr.s_addr), &(sockaddrP.sin_addr), 4); - - result = setsockopt(handle, IPPROTO_IP, setSockOptVal, &ipmreqP, length); + result = setsockopt(handle, SOL_IP, setSockOptVal, + &multicastRequest, length); if (0 != result) { throwSocketException (env, convertError(errno)); return; } - } else { + /* + * optVal is a GenericIPMreq object. Extract the relevant fields from + * it and construct a multicast request structure from these. Support + * both IPv4 and IPv6. + */ + jclass cls; + jfieldID multiaddrID; + jfieldID interfaceIdxID; + jobject multiaddr; - /* we need the multicast address regardless of the type of address */ + // Get the multicast address to join or leave. cls = env->GetObjectClass(optVal); multiaddrID = env->GetFieldID(cls, "multiaddr", "Ljava/net/InetAddress;"); multiaddr = env->GetObjectField(optVal, multiaddrID); - result = inetAddressToSocketAddress(env, multiaddr, 0, &sockaddrP); - - if (result < 0) { - throwSocketException(env, SOCKERR_BADSOCKET); - return; + // Get the interface index to use. + if (! ignoreIF) { + interfaceIdxID = env->GetFieldID(cls, "interfaceIdx", "I"); + interfaceIndex = env->GetIntField(optVal, interfaceIdxID); } - memcpy(&(ipmreqP.imr_multiaddr.s_addr), &(sockaddrP.sin_addr), 4); - - /* we need to use an IP_MREQ as it is an IPV4 address */ - interfaceAddrID = env->GetFieldID(cls, "interfaceAddr", - "Ljava/net/InetAddress;"); - interfaceAddr = env->GetObjectField(optVal, interfaceAddrID); - - ipmreqP.imr_interface.s_addr = htonl(INADDR_ANY); - - /* - * if an interfaceAddr was passed then use that value, otherwise set the - * interface to all 0 to indicate the system should select the interface - * used - */ - if (!ignoreIF) { - if (NULL != interfaceAddr) { - - result = inetAddressToSocketAddress(env, interfaceAddr, 0, - &sockaddrP); + result = inetAddressToSocketAddress(env, multiaddr, 0, &sockaddrP); + if (result < 0) // Exception has already been thrown. + return; - if (result < 0) { - throwSocketException(env, SOCKERR_BADSOCKET); - return; + int family = getSocketAddressFamily(handle); + + // Handle IPv4 multicast on an IPv6 socket. + if (family == AF_INET6 && sockaddrP.ss_family == AF_INET) { + family = AF_INET; + } + + struct ip_mreqn ipv4Request; + struct ipv6_mreq ipv6Request; + void *multicastRequest; + socklen_t requestLength; + int level; + switch (family) { + case AF_INET: + requestLength = sizeof(ipv4Request); + memset(&ipv4Request, 0, requestLength); + ipv4Request.imr_multiaddr = + ((struct sockaddr_in *) &sockaddrP)->sin_addr; + ipv4Request.imr_ifindex = interfaceIndex; + multicastRequest = &ipv4Request; + level = SOL_IP; + break; + case AF_INET6: + // setSockOptVal is passed in by the caller and may be IPv4-only + if (setSockOptVal == IP_ADD_MEMBERSHIP) { + setSockOptVal = IPV6_ADD_MEMBERSHIP; } - - memcpy(&(ipmreqP.imr_interface.s_addr), &(sockaddrP.sin_addr), 4); - - } + if (setSockOptVal == IP_DROP_MEMBERSHIP) { + setSockOptVal == IPV6_DROP_MEMBERSHIP; + } + requestLength = sizeof(ipv6Request); + memset(&ipv6Request, 0, requestLength); + ipv6Request.ipv6mr_multiaddr = + ((struct sockaddr_in6 *) &sockaddrP)->sin6_addr; + ipv6Request.ipv6mr_interface = interfaceIndex; + multicastRequest = &ipv6Request; + level = SOL_IPV6; + break; + default: + throwSocketException (env, SOCKERR_BADAF); + return; } /* join/drop the multicast address */ - result = setsockopt(handle, IPPROTO_IP, setSockOptVal, &ipmreqP, length); + result = setsockopt(handle, level, setSockOptVal, multicastRequest, + requestLength); if (0 != result) { throwSocketException (env, convertError(errno)); return; @@ -1114,263 +1601,123 @@ static void osNetworkSystem_oneTimeInitializationImpl(JNIEnv* env, jobject obj, } memset(&gCachedFields, 0, sizeof(gCachedFields)); - - // initializing InetAddress - - jclass iaddrclass = env->FindClass("java/net/InetAddress"); - - if (iaddrclass == NULL) { - jniThrowException(env, "java/lang/ClassNotFoundException", - "java.net.InetAddress"); - return; - } - - gCachedFields.iaddr_class = (jclass) env->NewGlobalRef(iaddrclass); - - jmethodID iaddrclassinit = env->GetMethodID(iaddrclass, "", "()V"); - - if (iaddrclassinit == NULL) { - jniThrowException(env, "java/lang/NoSuchMethodError", "InetAddress.()"); - return; - } - - gCachedFields.iaddr_class_init = iaddrclassinit; - - jmethodID iaddrgetbyaddress = env->GetStaticMethodID(iaddrclass, - "getByAddress", "([B)Ljava/net/InetAddress;"); - - if (iaddrgetbyaddress == NULL) { - jniThrowException(env, "java/lang/NoSuchMethodError", - "InetAddress.getByAddress(byte[] val)"); - return; - } - - gCachedFields.iaddr_getbyaddress = iaddrgetbyaddress; - - jfieldID iaddripaddress = env->GetFieldID(iaddrclass, "ipaddress", "[B"); - - if (iaddripaddress == NULL) { - jniThrowException(env, "java/lang/NoSuchFieldError", - "Can't find field InetAddress.ipaddress"); - return; - } - - gCachedFields.iaddr_ipaddress = iaddripaddress; - - // get the GenericIPMreq class - - jclass genericipmreqclass = env->FindClass("org/apache/harmony/luni/net/GenericIPMreq"); - - if (genericipmreqclass == NULL) { - jniThrowException(env, "java/lang/ClassNotFoundException", - "org.apache.harmony.luni.net.GenericIPMreq"); - return; - } - - gCachedFields.genericipmreq_class = (jclass) env->NewGlobalRef(genericipmreqclass); - - // initializing Integer - - jclass integerclass = env->FindClass("java/lang/Integer"); - - if (integerclass == NULL) { - jniThrowException(env, "java/lang/ClassNotFoundException", - "java.lang.Integer"); - return; - } - - jmethodID integerclassinit = env->GetMethodID(integerclass, "", "(I)V"); - - if (integerclassinit == NULL) { - jniThrowException(env, "java/lang/NoSuchMethodError", - "Integer.(int val)"); - return; - } - - jfieldID integerclassvalue = env->GetFieldID(integerclass, "value", "I"); - - if (integerclassvalue == NULL) { - jniThrowException(env, "java/lang/NoSuchMethodError", "Integer.value"); - return; - } - - gCachedFields.integer_class = (jclass) env->NewGlobalRef(integerclass); - gCachedFields.integer_class_init = integerclassinit; - gCachedFields.integer_class_value = integerclassvalue; - - // initializing Boolean - - jclass booleanclass = env->FindClass("java/lang/Boolean"); - - if (booleanclass == NULL) { - jniThrowException(env, "java/lang/ClassNotFoundException", - "java.lang.Boolean"); - return; - } - - jmethodID booleanclassinit = env->GetMethodID(booleanclass, "", "(Z)V"); - - if (booleanclassinit == NULL) { - jniThrowException(env, "java/lang/NoSuchMethodError", - "Boolean.(boolean val)"); - return; - } - - jfieldID booleanclassvalue = env->GetFieldID(booleanclass, "value", "Z"); - - if (booleanclassvalue == NULL) { - jniThrowException(env, "java/lang/NoSuchMethodError", "Boolean.value"); - return; - } - - gCachedFields.boolean_class = (jclass) env->NewGlobalRef(booleanclass); - gCachedFields.boolean_class_init = booleanclassinit; - gCachedFields.boolean_class_value = booleanclassvalue; - - // initializing Byte - - jclass byteclass = env->FindClass("java/lang/Byte"); - - if (byteclass == NULL) { - jniThrowException(env, "java/lang/ClassNotFoundException", - "java.lang.Byte"); - return; - } - - jmethodID byteclassinit = env->GetMethodID(byteclass, "", "(B)V"); - - if (byteclassinit == NULL) { - jniThrowException(env, "java/lang/NoSuchMethodError", - "Byte.(byte val)"); - return; - } - - jfieldID byteclassvalue = env->GetFieldID(byteclass, "value", "B"); - - if (byteclassvalue == NULL) { - jniThrowException(env, "java/lang/NoSuchMethodError", "Byte.value"); - return; - } - - gCachedFields.byte_class = (jclass) env->NewGlobalRef(byteclass); - gCachedFields.byte_class_init = byteclassinit; - gCachedFields.byte_class_value = byteclassvalue; - - // initializing String - - jclass stringclass = env->FindClass("java/lang/String"); - - if (stringclass == NULL) { - jniThrowException(env, "java/lang/ClassNotFoundException", - "java.lang.String"); - return; - } - - jmethodID stringclassinit = env->GetMethodID(stringclass, "", "([B)V"); - - if (stringclassinit == NULL) { - jniThrowException(env, "java/lang/NoSuchMethodError", - "String.(byte[] val)"); - return; - } - - gCachedFields.string_class = (jclass) env->NewGlobalRef(stringclass); - gCachedFields.string_class_init = stringclassinit; - - // initializing ScoketImpl - - jclass socketimplclass = env->FindClass("java/net/SocketImpl"); - - if (socketimplclass == NULL) { - jniThrowException(env, "java/lang/ClassNotFoundException", - "java.net.SocketImpl"); - return; - } - - jfieldID socketimplport = env->GetFieldID(socketimplclass, "port", "I"); - - if (socketimplport == NULL) { - jniThrowException(env, "java/lang/NoSuchFieldError", "SocketImpl.port"); - return; - } - - jfieldID socketimpladdress = env->GetFieldID(socketimplclass, "address", - "Ljava/net/InetAddress;"); - - if (socketimpladdress == NULL) { - jniThrowException(env, "java/lang/NoSuchFieldError", - "SocketImpl.address"); - return; - } - - gCachedFields.socketimpl_address = socketimpladdress; - gCachedFields.socketimpl_port = socketimplport; - - gCachedFields.dpack_class = env->FindClass("java/net/DatagramPacket"); - if (gCachedFields.dpack_class == NULL) { - jniThrowException(env, "java/lang/ClassNotFoundException", - "java.net.DatagramPacket"); - return; + struct CachedFields *c = &gCachedFields; + + struct classInfo { + jclass *clazz; + const char *name; + } classes[] = { + {&c->iaddr_class, "java/net/InetAddress"}, + {&c->i4addr_class, "java/net/Inet4Address"}, + {&c->genericipmreq_class, "org/apache/harmony/luni/net/GenericIPMreq"}, + {&c->integer_class, "java/lang/Integer"}, + {&c->boolean_class, "java/lang/Boolean"}, + {&c->byte_class, "java/lang/Byte"}, + {&c->string_class, "java/lang/String"}, + {&c->socketimpl_class, "java/net/SocketImpl"}, + {&c->dpack_class, "java/net/DatagramPacket"} + }; + for (unsigned i = 0; i < sizeof(classes) / sizeof(classes[0]); i++) { + classInfo c = classes[i]; + jclass tempClass = env->FindClass(c.name); + if (tempClass == NULL) return; + *c.clazz = (jclass) env->NewGlobalRef(tempClass); + } + + struct methodInfo { + jmethodID *method; + jclass clazz; + const char *name; + const char *signature; + bool isStatic; + } methods[] = { + {&c->i4addr_class_init, c->i4addr_class, "", "([B)V", false}, + {&c->integer_class_init, c->integer_class, "", "(I)V", false}, + {&c->boolean_class_init, c->boolean_class, "", "(Z)V", false}, + {&c->byte_class_init, c->byte_class, "", "(B)V", false}, + {&c->string_class_init, c->string_class, "", "([B)V", false}, + {&c->iaddr_getbyaddress, c->iaddr_class, "getByAddress", + "([B)Ljava/net/InetAddress;", true} + }; + for (unsigned i = 0; i < sizeof(methods) / sizeof(methods[0]); i++) { + methodInfo m = methods[i]; + if (m.isStatic) { + *m.method = env->GetStaticMethodID(m.clazz, m.name, m.signature); + } else { + *m.method = env->GetMethodID(m.clazz, m.name, m.signature); + } + if (*m.method == NULL) return; + } + + struct fieldInfo { + jfieldID *field; + jclass clazz; + const char *name; + const char *type; + } fields[] = { + {&c->iaddr_ipaddress, c->iaddr_class, "ipaddress", "[B"}, + {&c->integer_class_value, c->integer_class, "value", "I"}, + {&c->boolean_class_value, c->boolean_class, "value", "Z"}, + {&c->byte_class_value, c->byte_class, "value", "B"}, + {&c->socketimpl_port, c->socketimpl_class, "port", "I"}, + {&c->socketimpl_address, c->socketimpl_class, "address", + "Ljava/net/InetAddress;"}, + {&c->dpack_address, c->dpack_class, "address", + "Ljava/net/InetAddress;"}, + {&c->dpack_port, c->dpack_class, "port", "I"}, + {&c->dpack_length, c->dpack_class, "length", "I"} + }; + for (unsigned i = 0; i < sizeof(fields) / sizeof(fields[0]); i++) { + fieldInfo f = fields[i]; + *f.field = env->GetFieldID(f.clazz, f.name, f.type); + if (*f.field == NULL) return; } +} - gCachedFields.dpack_address = env->GetFieldID(gCachedFields.dpack_class, - "address", "Ljava/net/InetAddress;"); - if (gCachedFields.dpack_address == NULL) { - jniThrowException(env, "java/lang/NoSuchFieldError", - "DatagramPacket.address"); - return; +/** + * Helper function to create a socket of the specified type and bind it to a + * Java file descriptor. + * + * @param fileDescriptor the file descriptor to bind the socket to + * @param type the socket type to create, e.g., SOCK_STREAM + * @throws SocketException an error occurred when creating the socket + * + * @return the socket file descriptor. On failure, an exception is thrown and + * a negative value is returned. + * + */ +static int createSocketFileDescriptor(JNIEnv* env, jobject fileDescriptor, + int type) { + if (fileDescriptor == NULL) { + throwNullPointerException(env); + errno = EBADF; + return -1; } - gCachedFields.dpack_port = env->GetFieldID(gCachedFields.dpack_class, - "port", "I"); - if (gCachedFields.dpack_port == NULL) { - jniThrowException(env, "java/lang/NoSuchFieldError", - "DatagramPacket.port"); - return; + int sock; + sock = socket(PF_INET6, type, 0); + if (sock < 0 && errno == EAFNOSUPPORT) { + sock = socket(PF_INET, type, 0); } - - gCachedFields.dpack_length = env->GetFieldID(gCachedFields.dpack_class, - "length", "I"); - if (gCachedFields.dpack_length == NULL) { - jniThrowException(env, "java/lang/NoSuchFieldError", - "DatagramPacket.length"); - return; + if (sock < 0) { + int err = convertError(errno); + throwSocketException(env, err); + return sock; } - + jniSetFileDescriptorOfFD(env, fileDescriptor, sock); + return sock; } -static void osNetworkSystem_createSocketImpl(JNIEnv* env, jclass clazz, +static void osNetworkSystem_createStreamSocketImpl(JNIEnv* env, jclass clazz, jobject fileDescriptor, jboolean preferIPv4Stack) { // LOGD("ENTER createSocketImpl"); - - int ret = socket(PF_INET, SOCK_STREAM, 0); - - if (ret < 0) { - int err = convertError(errno); - throwSocketException(env, err); - return; - } - - jniSetFileDescriptorOfFD(env, fileDescriptor, ret); - - return; + createSocketFileDescriptor(env, fileDescriptor, SOCK_STREAM); } static void osNetworkSystem_createDatagramSocketImpl(JNIEnv* env, jclass clazz, jobject fileDescriptor, jboolean preferIPv4Stack) { // LOGD("ENTER createDatagramSocketImpl"); - - int ret = socket(PF_INET, SOCK_DGRAM, 0); - - if (ret < 0) { - int err = convertError(errno); - throwSocketException(env, err); - return; - } - - jniSetFileDescriptorOfFD(env, fileDescriptor, ret); - - return; + createSocketFileDescriptor(env, fileDescriptor, SOCK_DGRAM); } static jint osNetworkSystem_readSocketDirectImpl(JNIEnv* env, jclass clazz, @@ -1379,7 +1726,7 @@ static jint osNetworkSystem_readSocketDirectImpl(JNIEnv* env, jclass clazz, // LOGD("ENTER readSocketDirectImpl"); int handle; - jbyte *message = (jbyte *)address; + jbyte *message = (jbyte *)address + offset; int result, ret, localCount; handle = jniGetFDFromFileDescriptor(env, fileDescriptor); @@ -1405,11 +1752,9 @@ static jint osNetworkSystem_readSocketDirectImpl(JNIEnv* env, jclass clazz, return -1; } else if (ret == -1) { int err = convertError(errno); - log_socket_close(handle, err); throwSocketException(env, err); return 0; } - add_recv_stats(handle, ret); return ret; } @@ -1437,7 +1782,7 @@ static jint osNetworkSystem_readSocketImpl(JNIEnv* env, jclass clazz, } result = osNetworkSystem_readSocketDirectImpl(env, clazz, fileDescriptor, - (jint) message, offset, count, timeout); + (jint) message, 0, localCount, timeout); if (result > 0) { env->SetByteArrayRegion(data, offset, result, (jbyte *)message); @@ -1455,7 +1800,7 @@ static jint osNetworkSystem_writeSocketDirectImpl(JNIEnv* env, jclass clazz, // LOGD("ENTER writeSocketDirectImpl"); int handle; - jbyte *message = (jbyte *)address; + jbyte *message = (jbyte *)address + offset; int result = 0, sent = 0; if (count <= 0) { @@ -1472,7 +1817,6 @@ static jint osNetworkSystem_writeSocketDirectImpl(JNIEnv* env, jclass clazz, result = send(handle, (jbyte *) message, (int) count, SOCKET_NOFLAGS); if (result < 0) { int err = convertError(errno); - log_socket_close(handle, err); if (SOCKERR_WOULDBLOCK == err){ jclass socketExClass,errorCodeExClass; @@ -1499,7 +1843,7 @@ static jint osNetworkSystem_writeSocketDirectImpl(JNIEnv* env, jclass clazz, if (!socketExConstructor) { return 0; } - socketEx = env->NewObject(socketExClass, socketExConstructor, errorMessageString); + socketEx = env->NewObject(socketExClass, socketExConstructor, errorMessageString); socketExCauseMethod = env->GetMethodID(socketExClass,"initCause","(Ljava/lang/Throwable;)Ljava/lang/Throwable;"); env->CallObjectMethod(socketEx,socketExCauseMethod,errorCodeEx); env->Throw((jthrowable)socketEx); @@ -1509,7 +1853,6 @@ static jint osNetworkSystem_writeSocketDirectImpl(JNIEnv* env, jclass clazz, return 0; } - add_send_stats(handle, result); return result; } @@ -1539,13 +1882,13 @@ static jint osNetworkSystem_writeSocketImpl(JNIEnv* env, jclass clazz, env->GetByteArrayRegion(data, offset, count, message); result = osNetworkSystem_writeSocketDirectImpl(env, clazz, fileDescriptor, - (jint) message, offset, count); + (jint) message, 0, count); if (( jbyte *)message != internalBuffer) { - free(( jbyte *)message); + free(( jbyte *)message); } #undef INTERNAL_SEND_BUFFER_MAX - return result; + return result; } static void osNetworkSystem_setNonBlockingImpl(JNIEnv* env, jclass clazz, @@ -1581,36 +1924,25 @@ static jint osNetworkSystem_connectWithTimeoutSocketImpl(JNIEnv* env, int handle; int result = 0; - struct sockaddr_in address; + struct sockaddr_storage address; jbyte *context = NULL; - memset(&address, 0, sizeof(address)); - - address.sin_family = AF_INET; - - result = inetAddressToSocketAddress(env, inetAddr, port, - (struct sockaddr_in *) &address); - - if (result < 0) { - throwSocketException(env, SOCKERR_BADSOCKET); + result = inetAddressToSocketAddress(env, inetAddr, port, &address); + if (result < 0) return result; - } // Check if we're using adb networking and redirect in case it is used. - if (useAdbNetworking && !isLocalhost(&address)) { + if (useAdbNetworkingForAddress(&address)) { return osNetworkSystem_connectSocketImpl(env, clazz, fileDescriptor, trafficClass, inetAddr, port); } handle = jniGetFDFromFileDescriptor(env, fileDescriptor); - if (handle == 0 || handle == -1) { - throwSocketException(env, SOCKERR_BADSOCKET); + throwSocketException(env, SOCKERR_BADDESC); return -1; } - address.sin_port = htons(port); - context = (jbyte *)env->GetPrimitiveArrayCritical(passContext, NULL); switch (step) { @@ -1651,7 +1983,7 @@ static void osNetworkSystem_connectStreamWithTimeoutSocketImpl(JNIEnv* env, int result = 0; int handle; - struct sockaddr_in address; + struct sockaddr_storage address; jbyte *context = NULL; int remainingTimeout = timeout; int passedTimeout = 0; @@ -1664,53 +1996,117 @@ static void osNetworkSystem_connectStreamWithTimeoutSocketImpl(JNIEnv* env, finishTime = time_msec_clock() + (int) timeout; } - handle = jniGetFDFromFileDescriptor(env, fileDescriptor); - if (handle == 0 || handle == -1) { - throwSocketException(env, SOCKERR_BADSOCKET); + throwSocketException(env, SOCKERR_BADDESC); return; - } else { - result = inetAddressToSocketAddress(env, inetAddr, remotePort, - (struct sockaddr_in *) &address); + } - if (result < 0) { + result = inetAddressToSocketAddress(env, inetAddr, remotePort, &address); + if (result < 0) // Exception has already been thrown. + return; + + // Check if we're using adb networking and redirect in case it is used. + if (useAdbNetworkingForAddress(&address)) { + int retVal = osNetworkSystem_connectSocketImpl(env, clazz, + fileDescriptor, trafficClass, inetAddr, remotePort); + if (retVal != 0) { throwSocketException(env, SOCKERR_BADSOCKET); - return; } + return; + } - // Check if we're using adb networking and redirect in case it is used. - if (useAdbNetworking && !isLocalhost(&address)) { - int retVal = osNetworkSystem_connectSocketImpl(env, clazz, - fileDescriptor, trafficClass, inetAddr, remotePort); - if (retVal != 0) { - throwSocketException(env, SOCKERR_BADSOCKET); - } - return; + /* + * we will be looping checking for when we are connected so allocate + * the descriptor sets that we will use + */ + context =(jbyte *) malloc(sizeof(struct selectFDSet)); + if (NULL == context) { + throwSocketException(env, SOCKERR_NOBUFFERS); + return; + } + + result = sockConnectWithTimeout(handle, address, 0, SOCKET_STEP_START, context); + if (0 == result) { + /* ok we connected right away so we are done */ + sockConnectWithTimeout(handle, address, 0, SOCKET_STEP_DONE, context); + goto bail; + } else if (result != SOCKERR_NOTCONNECTED) { + sockConnectWithTimeout(handle, address, 0, SOCKET_STEP_DONE, + context); + /* we got an error other than NOTCONNECTED so we cannot continue */ + if (SOCKERR_EACCES == result) { + jniThrowException(env, "java/lang/SecurityException", + netLookupErrorString(result)); + } else { + throwSocketException(env, result); } + goto bail; + } + + while (SOCKERR_NOTCONNECTED == result) { + passedTimeout = remainingTimeout; /* - * we will be looping checking for when we are connected so allocate - * the descriptor sets that we will use + * ok now try and connect. Depending on the platform this may sleep + * for up to passedTimeout milliseconds */ - context =(jbyte *) malloc(sizeof(struct selectFDSet)); + result = sockConnectWithTimeout(handle, address, passedTimeout, + SOCKET_STEP_CHECK, context); - if (NULL == context) { - throwSocketException(env, SOCKERR_NOBUFFERS); - return; + /* + * now check if the socket is still connected. + * Do it here as some platforms seem to think they + * are connected if the socket is closed on them. + */ + handle = jniGetFDFromFileDescriptor(env, fileDescriptor); + if (handle == 0 || handle == -1) { + sockConnectWithTimeout(handle, address, 0, + SOCKET_STEP_DONE, context); + throwSocketException(env, SOCKERR_BADSOCKET); + goto bail; } - result = sockConnectWithTimeout(handle, address, 0, SOCKET_STEP_START, context); + /* + * check if we are now connected, + * if so we can finish the process and return + */ if (0 == result) { - /* ok we connected right away so we are done */ - sockConnectWithTimeout(handle, address, 0, SOCKET_STEP_DONE, context); + sockConnectWithTimeout(handle, address, 0, + SOCKET_STEP_DONE, context); goto bail; - } else if (result != SOCKERR_NOTCONNECTED) { - log_socket_close(handle, result); - sockConnectWithTimeout(handle, address, 0, SOCKET_STEP_DONE, - context); - /* we got an error other than NOTCONNECTED so we cannot continue */ - if (SOCKERR_EACCES == result) { + } + + /* + * if the error is SOCKERR_NOTCONNECTED then we have not yet + * connected and we may not be done yet + */ + if (SOCKERR_NOTCONNECTED == result) { + /* check if the timeout has expired */ + if (hasTimeout) { + remainingTimeout = finishTime - time_msec_clock(); + if (remainingTimeout <= 0) { + sockConnectWithTimeout(handle, address, 0, + SOCKET_STEP_DONE, context); + jniThrowException(env, + "java/net/SocketTimeoutException", + netLookupErrorString(result)); + goto bail; + } + } else { + remainingTimeout = 100; + } + } else { + sockConnectWithTimeout(handle, address, remainingTimeout, + SOCKET_STEP_DONE, context); + if ((SOCKERR_CONNRESET == result) || + (SOCKERR_CONNECTION_REFUSED == result) || + (SOCKERR_ADDRNOTAVAIL == result) || + (SOCKERR_ADDRINUSE == result) || + (SOCKERR_ENETUNREACH == result)) { + jniThrowException(env, "java/net/ConnectException", + netLookupErrorString(result)); + } else if (SOCKERR_EACCES == result) { jniThrowException(env, "java/lang/SecurityException", netLookupErrorString(result)); } else { @@ -1718,81 +2114,6 @@ static void osNetworkSystem_connectStreamWithTimeoutSocketImpl(JNIEnv* env, } goto bail; } - - while (SOCKERR_NOTCONNECTED == result) { - passedTimeout = remainingTimeout; - - /* - * ok now try and connect. Depending on the platform this may sleep - * for up to passedTimeout milliseconds - */ - result = sockConnectWithTimeout(handle, address, passedTimeout, - SOCKET_STEP_CHECK, context); - - /* - * now check if the socket is still connected. - * Do it here as some platforms seem to think they - * are connected if the socket is closed on them. - */ - handle = jniGetFDFromFileDescriptor(env, fileDescriptor); - - if (handle == 0 || handle == -1) { - sockConnectWithTimeout(handle, address, 0, - SOCKET_STEP_DONE, context); - throwSocketException(env, SOCKERR_BADSOCKET); - goto bail; - } - - /* - * check if we are now connected, - * if so we can finish the process and return - */ - if (0 == result) { - sockConnectWithTimeout(handle, address, 0, - SOCKET_STEP_DONE, context); - goto bail; - } - - /* - * if the error is SOCKERR_NOTCONNECTED then we have not yet - * connected and we may not be done yet - */ - if (SOCKERR_NOTCONNECTED == result) { - /* check if the timeout has expired */ - if (hasTimeout) { - remainingTimeout = finishTime - time_msec_clock(); - if (remainingTimeout <= 0) { - log_socket_close(handle, result); - sockConnectWithTimeout(handle, address, 0, - SOCKET_STEP_DONE, context); - jniThrowException(env, - "java/net/SocketTimeoutException", - netLookupErrorString(result)); - goto bail; - } - } else { - remainingTimeout = 100; - } - } else { - log_socket_close(handle, result); - sockConnectWithTimeout(handle, address, remainingTimeout, - SOCKET_STEP_DONE, context); - if ((SOCKERR_CONNRESET == result) || - (SOCKERR_CONNECTION_REFUSED == result) || - (SOCKERR_ADDRNOTAVAIL == result) || - (SOCKERR_ADDRINUSE == result) || - (SOCKERR_ENETUNREACH == result)) { - jniThrowException(env, "java/net/ConnectException", - netLookupErrorString(result)); - } else if (SOCKERR_EACCES == result) { - jniThrowException(env, "java/lang/SecurityException", - netLookupErrorString(result)); - } else { - throwSocketException(env, result); - } - goto bail; - } - } } bail: @@ -1807,37 +2128,25 @@ static jint osNetworkSystem_connectSocketImpl(JNIEnv* env, jclass clazz, jobject fileDescriptor, jint trafficClass, jobject inetAddr, jint port) { //LOGD("ENTER direct-call connectSocketImpl\n"); - struct sockaddr_in address; + struct sockaddr_storage address; int ret; int handle; - jbyteArray java_in_addr; - memset(&address, 0, sizeof(address)); - - address.sin_family = AF_INET; - - ret = inetAddressToSocketAddress(env, inetAddr, port, - (struct sockaddr_in *) &address); - - if (ret < 0) { - throwSocketException(env, SOCKERR_BADSOCKET); + ret = inetAddressToSocketAddress(env, inetAddr, port, &address); + if (ret < 0) return ret; - } handle = jniGetFDFromFileDescriptor(env, fileDescriptor); - if (handle == 0 || handle == -1) { - throwSocketException(env, SOCKERR_BADSOCKET); + throwSocketException(env, SOCKERR_BADDESC); return -1; } - address.sin_port = htons(port); - - if (useAdbNetworking && !isLocalhost(&address)) { + if (useAdbNetworkingForAddress(&address)) { // LOGD("+connect to address 0x%08x port %d (via adb)", // address.sin_addr.s_addr, (int) port); - ret = adb_networking_connect_fd(handle, &address); + ret = adb_networking_connect_fd(handle, (struct sockaddr_in *) &address); // LOGD("-connect ret %d errno %d (via adb)", ret, errno); } else { @@ -1866,27 +2175,21 @@ static void osNetworkSystem_socketBindImpl(JNIEnv* env, jclass clazz, jobject fileDescriptor, jint port, jobject inetAddress) { // LOGD("ENTER socketBindImpl"); - struct sockaddr_in sockaddress; + struct sockaddr_storage sockaddress; int ret; int handle; - ret = inetAddressToSocketAddress(env, inetAddress, port, - (struct sockaddr_in *) &sockaddress); - - if (ret < 0) { - throwSocketException(env, SOCKERR_BADSOCKET); + ret = inetAddressToSocketAddress(env, inetAddress, port, &sockaddress); + if (ret < 0) return; - } handle = jniGetFDFromFileDescriptor(env, fileDescriptor); - if (handle == 0 || handle == -1) { - throwSocketException(env, SOCKERR_BADSOCKET); + throwSocketException(env, SOCKERR_BADDESC); return; } - ret = bind(handle, (const sockaddr*)&sockaddress, sizeof(sockaddress)); - + ret = doBind(handle, &sockaddress); if (ret < 0) { jniThrowException(env, "java/net/BindException", netLookupErrorString(convertError(errno))); @@ -1912,7 +2215,6 @@ static void osNetworkSystem_listenStreamSocketImpl(JNIEnv* env, jclass clazz, if (ret < 0) { int err = convertError(errno); - log_socket_close(handle, err); throwSocketException(env, err); return; } @@ -1928,9 +2230,8 @@ static jint osNetworkSystem_availableStreamImpl(JNIEnv* env, jclass clazz, int result; handle = jniGetFDFromFileDescriptor(env, fileDescriptor); - if (handle == 0 || handle == -1) { - throwSocketException(env, SOCKERR_BADSOCKET); + throwSocketException(env, SOCKERR_BADDESC); return 0; } @@ -1943,7 +2244,6 @@ static jint osNetworkSystem_availableStreamImpl(JNIEnv* env, jclass clazz, } else if (SOCKERR_INTERRUPTED == result) { continue; } else if (0 > result) { - log_socket_close(handle, result); throwSocketException(env, result); return 0; } @@ -1953,11 +2253,9 @@ static jint osNetworkSystem_availableStreamImpl(JNIEnv* env, jclass clazz, if (0 > result) { int err = convertError(errno); - log_socket_close(handle, err); throwSocketException(env, err); return 0; } - add_recv_stats(handle, result); return result; } @@ -1965,10 +2263,7 @@ static void osNetworkSystem_acceptSocketImpl(JNIEnv* env, jclass clazz, jobject fdServer, jobject newSocket, jobject fdnewSocket, jint timeout) { // LOGD("ENTER acceptSocketImpl"); - union { - struct sockaddr address; - struct sockaddr_in in_address; - } sa; + struct sockaddr_storage sa; int ret; int retFD; @@ -1982,55 +2277,47 @@ static void osNetworkSystem_acceptSocketImpl(JNIEnv* env, jclass clazz, } result = pollSelectWait(env, fdServer, timeout, SELECT_READ_TYPE); - if (0 > result) { return; } handle = jniGetFDFromFileDescriptor(env, fdServer); - if (handle == 0 || handle == -1) { - throwSocketException(env, SOCKERR_BADSOCKET); + throwSocketException(env, SOCKERR_BADDESC); return; } do { addrlen = sizeof(sa); - ret = accept(handle, &(sa.address), &addrlen); + ret = accept(handle, (struct sockaddr *) &sa, &addrlen); } while (ret < 0 && errno == EINTR); if (ret < 0) { int err = convertError(errno); - log_socket_close(handle, err); throwSocketException(env, err); return; } retFD = ret; - /* For AF_INET / inetOrLocal == true only: put - * peer address and port in instance variables - * We don't bother for UNIX domain sockets, since most peers are - * anonymous anyway + /* + * For network sockets, put the peer address and port in instance variables. + * We don't bother to do this for UNIX domain sockets, since most peers are + * anonymous anyway. */ - if (sa.address.sa_family == AF_INET) { - // inetOrLocal should also be true - - jobject inetAddress; - - inetAddress = structInToInetAddress(env, &(sa.in_address.sin_addr)); - - if (inetAddress == NULL) { + if (sa.ss_family == AF_INET || sa.ss_family == AF_INET6) { + jobject inetAddress = socketAddressToInetAddress(env, &sa); + if (ret == -1) { close(retFD); - newSocket = NULL; + newSocket = NULL; // Exception has already been thrown. return; } env->SetObjectField(newSocket, gCachedFields.socketimpl_address, inetAddress); - env->SetIntField(newSocket, gCachedFields.socketimpl_port, - ntohs(sa.in_address.sin_port)); + int port = getSocketAddressPort(&sa); + env->SetIntField(newSocket, gCachedFields.socketimpl_port, port); } jniSetFileDescriptorOfFD(env, fdnewSocket, retFD); @@ -2066,7 +2353,6 @@ static void osNetworkSystem_sendUrgentDataImpl(JNIEnv* env, jclass clazz, result = send(handle, (jbyte *) &value, 1, MSG_OOB); if (result < 0) { int err = convertError(errno); - log_socket_close(handle, err); throwSocketException(env, err); } } @@ -2077,20 +2363,16 @@ static void osNetworkSystem_connectDatagramImpl2(JNIEnv* env, jclass clazz, int handle = jniGetFDFromFileDescriptor(env, fd); - struct sockaddr_in sockAddr; + struct sockaddr_storage sockAddr; int ret; ret = inetAddressToSocketAddress(env, inetAddress, port, &sockAddr); + if (ret < 0) // Exception has already been thrown. + return; + ret = doConnect(handle, &sockAddr); if (ret < 0) { - throwSocketException(env, SOCKERR_BADSOCKET); - return; - } - log_socket_connect(handle, ntohl(sockAddr.sin_addr.s_addr), port); - int result = connect(handle, (struct sockaddr *)&sockAddr, sizeof(sockAddr)); - if (result < 0) { int err = convertError(errno); - log_socket_close(handle, err); throwSocketException(env, err); } } @@ -2101,57 +2383,17 @@ static void osNetworkSystem_disconnectDatagramImpl(JNIEnv* env, jclass clazz, int handle = jniGetFDFromFileDescriptor(env, fd); - struct sockaddr_in *sockAddr; - socklen_t sockAddrLen = sizeof(struct sockaddr_in); - sockAddr = (struct sockaddr_in *) malloc(sockAddrLen); - memset(sockAddr, 0, sockAddrLen); - - sockAddr->sin_family = AF_UNSPEC; - int result = connect(handle, (struct sockaddr *)sockAddr, sockAddrLen); - free(sockAddr); + struct sockaddr_storage sockAddr; + memset(&sockAddr, 0, sizeof(sockAddr)); + sockAddr.ss_family = AF_UNSPEC; + int result = doConnect(handle, &sockAddr); if (result < 0) { int err = convertError(errno); - log_socket_close(handle, err); throwSocketException(env, err); } } -static jboolean osNetworkSystem_socketBindImpl2(JNIEnv* env, jclass clazz, - jobject fileDescriptor, jint port, jboolean bindToDevice, - jobject inetAddress) { - // LOGD("ENTER socketBindImpl2"); - - struct sockaddr_in sockaddress; - int ret; - int handle; - - ret = inetAddressToSocketAddress(env, inetAddress, port, - (struct sockaddr_in *) &sockaddress); - - if (ret < 0) { - throwSocketException(env, SOCKERR_BADSOCKET); - return 0; - } - - handle = jniGetFDFromFileDescriptor(env, fileDescriptor); - if (handle == 0 || handle == -1) { - throwSocketException(env, SOCKERR_BADSOCKET); - return 0; - } - - ret = bind(handle, (const sockaddr*)&sockaddress, sizeof(sockaddress)); - - if (ret < 0) { - int err = convertError(errno); - log_socket_close(handle, err); - jniThrowException(env, "java/net/BindException", netLookupErrorString(err)); - return 0; - } - - return 0; -} - static jint osNetworkSystem_peekDatagramImpl(JNIEnv* env, jclass clazz, jobject fd, jobject sender, jint receiveTimeout) { // LOGD("ENTER peekDatagramImpl"); @@ -2164,30 +2406,29 @@ static jint osNetworkSystem_peekDatagramImpl(JNIEnv* env, jclass clazz, } int handle = jniGetFDFromFileDescriptor(env, fd); - if (handle == 0 || handle == -1) { - throwSocketException(env, SOCKERR_BADSOCKET); + throwSocketException(env, SOCKERR_BADDESC); return 0; } - struct sockaddr_in sockAddr; + struct sockaddr_storage sockAddr; socklen_t sockAddrLen = sizeof(sockAddr); - - int length = recvfrom(handle, NULL, 0, MSG_PEEK, - (struct sockaddr *)&sockAddr, &sockAddrLen); - + ssize_t length; + do { + length = recvfrom(handle, NULL, 0, MSG_PEEK, + (struct sockaddr *)&sockAddr, &sockAddrLen); + } while (length < 0 && errno == EINTR); if (length < 0) { int err = convertError(errno); - log_socket_close(handle, err); throwSocketException(env, err); return 0; } - if (socketAddressToInetAddress(env, &sockAddr, sender, &port) < 0) { - throwIOExceptionStr(env, "Address conversion failed"); + sender = socketAddressToInetAddress(env, &sockAddr); + if (sender == NULL) // Exception has already been thrown. return -1; - } - add_recv_stats(handle, length); + + port = getSocketAddressPort(&sockAddr); return port; } @@ -2202,44 +2443,41 @@ static jint osNetworkSystem_receiveDatagramDirectImpl(JNIEnv* env, jclass clazz, } int handle = jniGetFDFromFileDescriptor(env, fd); - if (handle == 0 || handle == -1) { - throwSocketException(env, SOCKERR_BADSOCKET); + throwSocketException(env, SOCKERR_BADDESC); return 0; } - struct sockaddr_in sockAddr; + struct sockaddr_storage sockAddr; socklen_t sockAddrLen = sizeof(sockAddr); int mode = peek ? MSG_PEEK : 0; - int actualLength = recvfrom(handle, (char*)(address + offset), length, mode, - (struct sockaddr *)&sockAddr, &sockAddrLen); - + ssize_t actualLength; + do { + actualLength = recvfrom(handle, (char*)(address + offset), length, mode, + (struct sockaddr *)&sockAddr, &sockAddrLen); + } while (actualLength < 0 && errno == EINTR); if (actualLength < 0) { int err = convertError(errno); - log_socket_close(handle, err); throwSocketException(env, err); return 0; } if (packet != NULL) { - int port = ntohs(sockAddr.sin_port); - jbyteArray addr = env->NewByteArray(sizeof(struct in_addr)); - if ((structInToJavaAddress(env, &sockAddr.sin_addr, addr)) < 0) { - jniThrowException(env, "java/net/SocketException", - "Could not set address of packet."); + jbyteArray addr = socketAddressToByteArray(env, &sockAddr); + if (addr == NULL) // Exception has already been thrown. return 0; - } + int port = getSocketAddressPort(&sockAddr); jobject sender = env->CallStaticObjectMethod( gCachedFields.iaddr_class, gCachedFields.iaddr_getbyaddress, addr); env->SetObjectField(packet, gCachedFields.dpack_address, sender); env->SetIntField(packet, gCachedFields.dpack_port, port); - env->SetIntField(packet, gCachedFields.dpack_length, actualLength); + env->SetIntField(packet, gCachedFields.dpack_length, + (jint) actualLength); } - add_recv_stats(handle, actualLength); - return actualLength; + return (jint) actualLength; } static jint osNetworkSystem_receiveDatagramImpl(JNIEnv* env, jclass clazz, @@ -2256,7 +2494,7 @@ static jint osNetworkSystem_receiveDatagramImpl(JNIEnv* env, jclass clazz, } int actualLength = osNetworkSystem_receiveDatagramDirectImpl(env, clazz, fd, - packet, (jint)bytes, offset, localLength, receiveTimeout, peek); + packet, (jint)bytes, 0, localLength, receiveTimeout, peek); if (actualLength > 0) { env->SetByteArrayRegion(data, offset, actualLength, bytes); @@ -2297,7 +2535,6 @@ static jint osNetworkSystem_recvConnectedDatagramDirectImpl(JNIEnv* env, if ( packet != NULL) { env->SetIntField(packet, gCachedFields.dpack_length, actualLength); } - add_recv_stats(handle, actualLength); return actualLength; } @@ -2315,7 +2552,7 @@ static jint osNetworkSystem_recvConnectedDatagramImpl(JNIEnv* env, jclass clazz, } int actualLength = osNetworkSystem_recvConnectedDatagramDirectImpl(env, - clazz, fd, packet, (jint)bytes, offset, localLength, + clazz, fd, packet, (jint)bytes, 0, localLength, receiveTimeout, peek); if (actualLength > 0) { @@ -2331,8 +2568,6 @@ static jint osNetworkSystem_sendDatagramDirectImpl(JNIEnv* env, jclass clazz, jboolean bindToDevice, jint trafficClass, jobject inetAddress) { // LOGD("ENTER sendDatagramDirectImpl"); - int result = 0; - int handle = jniGetFDFromFileDescriptor(env, fd); if (handle == 0 || handle == -1) { @@ -2340,29 +2575,28 @@ static jint osNetworkSystem_sendDatagramDirectImpl(JNIEnv* env, jclass clazz, return 0; } - struct sockaddr_in receiver; - + struct sockaddr_storage receiver; if (inetAddressToSocketAddress(env, inetAddress, port, &receiver) < 0) { - throwSocketException(env, SOCKERR_BADSOCKET); + // Exception has already been thrown. return 0; } - result = sendto(handle, (char*)(address + offset), length, SOCKET_NOFLAGS, - (struct sockaddr*)&receiver, sizeof(receiver)); - + ssize_t result = 0; + do { + result = sendto(handle, (char*)(address + offset), length, + SOCKET_NOFLAGS, (struct sockaddr*)&receiver, sizeof(receiver)); + } while (result < 0 && errno == EINTR); if (result < 0) { int err = convertError(errno); if ((SOCKERR_CONNRESET == err) || (SOCKERR_CONNECTION_REFUSED == err)) { return 0; } else { - log_socket_close(handle, err); throwSocketException(env, err); return 0; } } - add_send_stats(handle, result); - return result; + return (jint) result; } static jint osNetworkSystem_sendDatagramImpl(JNIEnv* env, jclass clazz, @@ -2398,12 +2632,10 @@ static jint osNetworkSystem_sendConnectedDatagramDirectImpl(JNIEnv* env, if ((SOCKERR_CONNRESET == err) || (SOCKERR_CONNECTION_REFUSED == err)) { return 0; } else { - log_socket_close(handle, err); throwSocketException(env, err); return 0; } } - add_send_stats(handle, length); return result; } @@ -2424,43 +2656,11 @@ static void osNetworkSystem_createServerStreamSocketImpl(JNIEnv* env, jclass clazz, jobject fileDescriptor, jboolean preferIPv4Stack) { // LOGD("ENTER createServerStreamSocketImpl"); - if (fileDescriptor == NULL) { - throwNullPointerException(env); - return; - } - - int handle = socket(PF_INET, SOCK_STREAM, 0); - - if (handle < 0) { - int err = convertError(errno); - throwSocketException(env, err); - return; - } - - jniSetFileDescriptorOfFD(env, fileDescriptor, handle); - - int value = 1; - - setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(int)); -} - -static void osNetworkSystem_createMulticastSocketImpl(JNIEnv* env, - jclass clazz, jobject fileDescriptor, jboolean preferIPv4Stack) { - // LOGD("ENTER createMulticastSocketImpl"); - - int handle = socket(PF_INET, SOCK_DGRAM, 0); - - if (handle < 0) { - int err = convertError(errno); - throwSocketException(env, err); + int handle = createSocketFileDescriptor(env, fileDescriptor, SOCK_STREAM); + if (handle < 0) return; - } - - jniSetFileDescriptorOfFD(env, fileDescriptor, handle); int value = 1; - - // setsockopt(handle, SOL_SOCKET, SO_REUSEPORT, &value, sizeof(jbyte)); setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(int)); } @@ -2505,7 +2705,6 @@ static jint osNetworkSystem_receiveStreamImpl(JNIEnv* env, jclass clazz, * to the Java input stream */ if (0 < result) { - add_recv_stats(handle, result); return result; } else if (0 == result) { return -1; @@ -2516,7 +2715,6 @@ static jint osNetworkSystem_receiveStreamImpl(JNIEnv* env, jclass clazz, netLookupErrorString(SOCKERR_TIMEOUT)); } else { int err = convertError(errno); - log_socket_close(handle, err); throwSocketException(env, err); } return 0; @@ -2560,7 +2758,6 @@ static jint osNetworkSystem_sendStreamImpl(JNIEnv* env, jclass clazz, } env->ReleaseByteArrayElements(data, message, 0); int err = convertError(result); - log_socket_close(handle, err); throwSocketException(env, err); return 0; } @@ -2568,7 +2765,6 @@ static jint osNetworkSystem_sendStreamImpl(JNIEnv* env, jclass clazz, } env->ReleaseByteArrayElements(data, message, 0); - add_send_stats(handle, sent); return sent; } @@ -2590,7 +2786,6 @@ static void osNetworkSystem_shutdownInputImpl(JNIEnv* env, jobject obj, if (ret < 0) { int err = convertError(errno); - log_socket_close(handle, err); throwSocketException(env, err); return; } @@ -2613,7 +2808,6 @@ static void osNetworkSystem_shutdownOutputImpl(JNIEnv* env, jobject obj, if (ret < 0) { int err = convertError(errno); - log_socket_close(handle, err); throwSocketException(env, err); return; } @@ -2625,26 +2819,19 @@ static jint osNetworkSystem_sendDatagramImpl2(JNIEnv* env, jclass clazz, // LOGD("ENTER sendDatagramImpl2"); jbyte *message; - jbyte nhostAddrBytes[4]; unsigned short nPort; - int result = 0, sent = 0; + int ret = 0, sent = 0; int handle = 0; - struct sockaddr_in sockaddrP; + struct sockaddr_storage sockaddrP; if (inetAddress != NULL) { - - result = inetAddressToSocketAddress(env, inetAddress, port, - (struct sockaddr_in *) &sockaddrP); - - if (result < 0) { - throwSocketException(env, SOCKERR_BADSOCKET); + ret = inetAddressToSocketAddress(env, inetAddress, port, &sockaddrP); + if (ret < 0) // Exception has already been thrown. return 0; - } handle = jniGetFDFromFileDescriptor(env, fd); - if (handle == 0 || handle == -1) { - throwSocketException(env, SOCKERR_BADSOCKET); + throwSocketException(env, SOCKERR_BADDESC); return 0; } } @@ -2663,18 +2850,19 @@ static jint osNetworkSystem_sendDatagramImpl2(JNIEnv* env, jclass clazz, if (handle == 0 || handle == -1) { throwSocketException(env, - sent == 0 ? SOCKERR_BADSOCKET : SOCKERR_INTERRUPTED); + sent == 0 ? SOCKERR_BADDESC : SOCKERR_INTERRUPTED); free(message); return 0; } - result = sendto(handle, (char *) (message + sent), - (int) (length - sent), SOCKET_NOFLAGS, - (struct sockaddr *) &sockaddrP, sizeof(sockaddrP)); - + ssize_t result; + do { + result = sendto(handle, (char *) (message + sent), + (int) (length - sent), SOCKET_NOFLAGS, + (struct sockaddr *) &sockaddrP, sizeof(sockaddrP)); + } while (result < 0 && errno == EINTR); if (result < 0) { int err = convertError(errno); - log_socket_close(handle, err); throwSocketException(env, err); free(message); return 0; @@ -2684,7 +2872,6 @@ static jint osNetworkSystem_sendDatagramImpl2(JNIEnv* env, jclass clazz, } free(message); - add_send_stats(handle, sent); return sent; } @@ -2757,10 +2944,10 @@ static jint osNetworkSystem_selectImpl(JNIEnv* env, jclass clazz, } if (0 < result) { - /*output the result to a int array*/ - flagArray = env->GetIntArrayElements(outFlags, &isCopy); + /*output the result to a int array*/ + flagArray = env->GetIntArrayElements(outFlags, &isCopy); - for (val=0; valGetObjectArrayElement(readFDArray,val); handle = jniGetFDFromFileDescriptor(env, gotFD); @@ -2799,33 +2986,30 @@ static jobject osNetworkSystem_getSocketLocalAddressImpl(JNIEnv* env, jclass clazz, jobject fileDescriptor, jboolean preferIPv6Addresses) { // LOGD("ENTER getSocketLocalAddressImpl"); - struct sockaddr_in addr; + struct sockaddr_storage addr; socklen_t addrLen = sizeof(addr); memset(&addr, 0, addrLen); - int handle = jniGetFDFromFileDescriptor(env, fileDescriptor); - - int result; + int handle = jniGetFDFromFileDescriptor(env, fileDescriptor); if (handle == 0 || handle == -1) { throwSocketException(env, SOCKERR_UNKNOWNSOCKET); return NULL; } + int result; result = getsockname(handle, (struct sockaddr *)&addr, &addrLen); // Spec says ignore all errors - - return structInToInetAddress(env, &(addr.sin_addr)); - + return socketAddressToInetAddress(env, &addr); } static jint osNetworkSystem_getSocketLocalPortImpl(JNIEnv* env, jclass clazz, jobject fileDescriptor, jboolean preferIPv6Addresses) { // LOGD("ENTER getSocketLocalPortImpl"); - struct sockaddr_in addr; + struct sockaddr_storage addr; socklen_t addrLen = sizeof(addr); int handle = jniGetFDFromFileDescriptor(env, fileDescriptor); @@ -2842,7 +3026,7 @@ static jint osNetworkSystem_getSocketLocalPortImpl(JNIEnv* env, jclass clazz, // The java spec does not indicate any exceptions on this call return 0; } else { - return ntohs(addr.sin_port); + return getSocketAddressPort(&addr); } } @@ -2856,12 +3040,12 @@ static jobject osNetworkSystem_getSocketOptionImpl(JNIEnv* env, jclass clazz, unsigned char byteValue = 0; socklen_t byteSize = sizeof(unsigned char); int result; - struct sockaddr_in sockVal; + struct sockaddr_storage sockVal; socklen_t sockSize = sizeof(sockVal); handle = jniGetFDFromFileDescriptor(env, fileDescriptor); if (handle == 0 || handle == -1) { - throwSocketException(env, SOCKERR_BADSOCKET); + throwSocketException(env, SOCKERR_BADDESC); return NULL; } @@ -2896,23 +3080,61 @@ static jobject osNetworkSystem_getSocketOptionImpl(JNIEnv* env, jclass clazz, if ((anOption >> 16) & BROKEN_MULTICAST_TTL) { return newJavaLangByte(env, 0); } - result = getsockopt(handle, IPPROTO_IP, IP_MULTICAST_TTL, &byteValue, &byteSize); + // Java uses a byte to store the TTL, but the kernel uses an int. + result = getOrSetSocketOption(SOCKOPT_GET, handle, IP_MULTICAST_TTL, + IPV6_MULTICAST_HOPS, &intValue, + &intSize); if (0 != result) { throwSocketException(env, convertError(errno)); return NULL; } - return newJavaLangByte(env, (jbyte)(byteValue & 0xFF)); + return newJavaLangByte(env, (jbyte)(intValue & 0xFF)); } case JAVASOCKOPT_MCAST_INTERFACE: { if ((anOption >> 16) & BROKEN_MULTICAST_IF) { return NULL; } - result = getsockopt(handle, IPPROTO_IP, IP_MULTICAST_IF, &sockVal, &sockSize); + result = getsockopt(handle, SOL_IP, IP_MULTICAST_IF, &sockVal, &sockSize); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return NULL; + } + // This option is IPv4-only. + sockVal.ss_family = AF_INET; + return socketAddressToInetAddress(env, &sockVal); + } + case JAVASOCKOPT_IP_MULTICAST_IF2: { + if ((anOption >> 16) & BROKEN_MULTICAST_IF) { + return NULL; + } + struct ip_mreqn multicastRequest; + int interfaceIndex; + socklen_t optionLength; + int addressFamily = getSocketAddressFamily(handle); + switch (addressFamily) { + case AF_INET: + optionLength = sizeof(multicastRequest); + result = getsockopt(handle, SOL_IP, IP_MULTICAST_IF, + &multicastRequest, &optionLength); + if (result == 0) + interfaceIndex = multicastRequest.imr_ifindex; + break; + case AF_INET6: + optionLength = sizeof(interfaceIndex); + result = getsockopt(handle, SOL_IPV6, IPV6_MULTICAST_IF, + &interfaceIndex, &optionLength); + break; + default: + throwSocketException(env, SOCKERR_BADAF); + return NULL; + } + if (0 != result) { throwSocketException(env, convertError(errno)); return NULL; } - return structInToInetAddress(env, &(sockVal.sin_addr)); + + return newJavaLangInteger(env, interfaceIndex); } case JAVASOCKOPT_SO_SNDBUF: { result = getsockopt(handle, SOL_SOCKET, SO_SNDBUF, &intValue, &intSize); @@ -2963,7 +3185,10 @@ static jobject osNetworkSystem_getSocketOptionImpl(JNIEnv* env, jclass clazz, return newJavaLangBoolean(env, intValue); } case JAVASOCKOPT_IP_MULTICAST_LOOP: { - result = getsockopt(handle, IPPROTO_IP, IP_MULTICAST_LOOP, &intValue, &intSize); + result = getOrSetSocketOption(SOCKOPT_GET, handle, + IP_MULTICAST_LOOP, + IPV6_MULTICAST_LOOP, &intValue, + &intSize); if (0 != result) { throwSocketException(env, convertError(errno)); return NULL; @@ -2971,7 +3196,8 @@ static jobject osNetworkSystem_getSocketOptionImpl(JNIEnv* env, jclass clazz, return newJavaLangBoolean(env, intValue); } case JAVASOCKOPT_IP_TOS: { - result = getsockopt(handle, IPPROTO_IP, IP_TOS, &intValue, &intSize); + result = getOrSetSocketOption(SOCKOPT_GET, handle, IP_TOS, + IPV6_TCLASS, &intValue, &intSize); if (0 != result) { throwSocketException(env, convertError(errno)); return NULL; @@ -3001,9 +3227,11 @@ static void osNetworkSystem_setSocketOptionImpl(JNIEnv* env, jclass clazz, // LOGD("ENTER setSocketOptionImpl"); int handle, result; - int intVal, intSize = sizeof(int); - unsigned char byteVal, byteSize = sizeof(unsigned char); - struct sockaddr_in sockVal; + int intVal; + socklen_t intSize = sizeof(int); + unsigned char byteVal; + socklen_t byteSize = sizeof(unsigned char); + struct sockaddr_storage sockVal; int sockSize = sizeof(sockVal); if (env->IsInstanceOf(optVal, gCachedFields.integer_class)) { @@ -3014,7 +3242,7 @@ static void osNetworkSystem_setSocketOptionImpl(JNIEnv* env, jclass clazz, byteVal = (int) env->GetByteField(optVal, gCachedFields.byte_class_value); } else if (env->IsInstanceOf(optVal, gCachedFields.iaddr_class)) { if (inetAddressToSocketAddress(env, optVal, 0, &sockVal) < 0) { - throwSocketException(env, SOCKERR_BADSOCKET); + // Exception has already been thrown. return; } } else if (env->IsInstanceOf(optVal, gCachedFields.genericipmreq_class)) { @@ -3026,7 +3254,7 @@ static void osNetworkSystem_setSocketOptionImpl(JNIEnv* env, jclass clazz, handle = jniGetFDFromFileDescriptor(env, fileDescriptor); if (handle == 0 || handle == -1) { - throwSocketException(env, SOCKERR_BADSOCKET); + throwSocketException(env, SOCKERR_BADDESC); return; } @@ -3056,11 +3284,15 @@ static void osNetworkSystem_setSocketOptionImpl(JNIEnv* env, jclass clazz, break; } - case JAVASOCKOPT_MCAST_TTL: { + case JAVASOCKOPT_MCAST_TTL: { if ((anOption >> 16) & BROKEN_MULTICAST_TTL) { return; } - result = setsockopt(handle, IPPROTO_IP, IP_MULTICAST_TTL, &byteVal, byteSize); + // Java uses a byte to store the TTL, but the kernel uses an int. + intVal = byteVal; + result = getOrSetSocketOption(SOCKOPT_SET, handle, IP_MULTICAST_TTL, + IPV6_MULTICAST_HOPS, &intVal, + &intSize); if (0 != result) { throwSocketException(env, convertError(errno)); return; @@ -3071,24 +3303,30 @@ static void osNetworkSystem_setSocketOptionImpl(JNIEnv* env, jclass clazz, case JAVASOCKOPT_MCAST_ADD_MEMBERSHIP: { mcastAddDropMembership(env, handle, optVal, (anOption >> 16) & BROKEN_MULTICAST_IF, IP_ADD_MEMBERSHIP); - return; + break; } case JAVASOCKOPT_MCAST_DROP_MEMBERSHIP: { mcastAddDropMembership(env, handle, optVal, (anOption >> 16) & BROKEN_MULTICAST_IF, IP_DROP_MEMBERSHIP); - return; + break; } case JAVASOCKOPT_MCAST_INTERFACE: { if ((anOption >> 16) & BROKEN_MULTICAST_IF) { return; } + // This call is IPv4 only. The socket may be IPv6, but the address + // that identifies the interface to join must be an IPv4 address. + if (sockVal.ss_family != AF_INET) { + throwSocketException(env, SOCKERR_BADAF); + return; + } struct ip_mreqn mcast_req; memset(&mcast_req, 0, sizeof(mcast_req)); - memcpy(&(mcast_req.imr_address), &(sockVal.sin_addr), - sizeof(struct in_addr)); - result = setsockopt(handle, IPPROTO_IP, IP_MULTICAST_IF, + struct sockaddr_in *sin = (struct sockaddr_in *) &sockVal; + mcast_req.imr_address = sin->sin_addr; + result = setsockopt(handle, SOL_IP, IP_MULTICAST_IF, &mcast_req, sizeof(mcast_req)); if (0 != result) { throwSocketException(env, convertError(errno)); @@ -3097,6 +3335,42 @@ static void osNetworkSystem_setSocketOptionImpl(JNIEnv* env, jclass clazz, break; } + case JAVASOCKOPT_IP_MULTICAST_IF2: { + if ((anOption >> 16) & BROKEN_MULTICAST_IF) { + return; + } + int addressFamily = getSocketAddressFamily(handle); + int interfaceIndex = intVal; + void *optionValue; + socklen_t optionLength; + struct ip_mreqn multicastRequest; + switch (addressFamily) { + case AF_INET: + // IP_MULTICAST_IF expects a pointer to a struct ip_mreqn. + memset(&multicastRequest, 0, sizeof(multicastRequest)); + multicastRequest.imr_ifindex = interfaceIndex; + optionValue = &multicastRequest; + optionLength = sizeof(multicastRequest); + break; + case AF_INET6: + // IPV6_MULTICAST_IF expects a pointer to an integer. + optionValue = &interfaceIndex; + optionLength = sizeof(interfaceIndex); + break; + default: + throwSocketException(env, SOCKERR_BADAF); + return; + } + result = getOrSetSocketOption(SOCKOPT_SET, handle, + IP_MULTICAST_IF, IPV6_MULTICAST_IF, optionValue, + &optionLength); + if (0 != result) { + throwSocketException(env, convertError(errno)); + return; + } + break; + } + case JAVASOCKOPT_SO_SNDBUF: { result = setsockopt(handle, SOL_SOCKET, SO_SNDBUF, &intVal, intSize); if (0 != result) { @@ -3151,7 +3425,10 @@ static void osNetworkSystem_setSocketOptionImpl(JNIEnv* env, jclass clazz, } case JAVASOCKOPT_IP_MULTICAST_LOOP: { - result = setsockopt(handle, IPPROTO_IP, IP_MULTICAST_LOOP, &intVal, intSize); + result = getOrSetSocketOption(SOCKOPT_SET, handle, + IP_MULTICAST_LOOP, + IPV6_MULTICAST_LOOP, &intVal, + &intSize); if (0 != result) { throwSocketException(env, convertError(errno)); return; @@ -3160,7 +3437,8 @@ static void osNetworkSystem_setSocketOptionImpl(JNIEnv* env, jclass clazz, } case JAVASOCKOPT_IP_TOS: { - result = setsockopt(handle, IPPROTO_IP, IP_TOS, &intVal, intSize); + result = getOrSetSocketOption(SOCKOPT_SET, handle, IP_TOS, + IPV6_TCLASS, &intVal, &intSize); if (0 != result) { throwSocketException(env, convertError(errno)); return; @@ -3215,95 +3493,11 @@ static void osNetworkSystem_socketCloseImpl(JNIEnv* env, jclass clazz, return; } - log_socket_close(handle, SOCKET_CLOSE_LOCAL); - jniSetFileDescriptorOfFD(env, fileDescriptor, -1); close(handle); } -static jobject osNetworkSystem_getHostByAddrImpl(JNIEnv* env, jclass clazz, - jbyteArray addrStr) { - // LOGD("ENTER getHostByAddrImpl"); - - if (addrStr == NULL) { - throwNullPointerException(env); - return JNI_FALSE; - } - - jstring address = (jstring)newJavaLangString(env, addrStr); - jstring result; - const char* addr = env->GetStringUTFChars(address, NULL); - - struct hostent* ent = gethostbyaddr(addr, strlen(addr), AF_INET); - - if (ent != NULL && ent->h_name != NULL) { - result = env->NewStringUTF(ent->h_name); - } else { - result = NULL; - } - - env->ReleaseStringUTFChars(address, addr); - - return result; -} - -static jobject osNetworkSystem_getHostByNameImpl(JNIEnv* env, jclass clazz, - jstring nameStr, jboolean preferIPv6Addresses) { - // LOGD("ENTER getHostByNameImpl"); - - if (nameStr == NULL) { - throwNullPointerException(env); - return NULL; - } - - const char* name = env->GetStringUTFChars(nameStr, NULL); - - if (useAdbNetworking) { - - union { - struct in_addr a; - jbyte j[4]; - } outaddr; - - // LOGD("ADB networking: +gethostbyname '%s'", name); - int err; - err = adb_networking_gethostbyname(name, &(outaddr.a)); - - env->ReleaseStringUTFChars(nameStr, name); -#if 0 - LOGD("ADB networking: -gethostbyname err %d addr 0x%08x %u.%u.%u.%u", - err, (unsigned int)outaddr.a.s_addr, - outaddr.j[0],outaddr.j[1], - outaddr.j[2],outaddr.j[3]); -#endif - - if (err < 0) { - return NULL; - } else { - jbyteArray addr = env->NewByteArray(4); - env->SetByteArrayRegion(addr, 0, 4, outaddr.j); - return addr; - } - } else { - - // normal case...no adb networking - struct hostent* ent = gethostbyname(name); - - env->ReleaseStringUTFChars(nameStr, name); - - if (ent != NULL && ent->h_length > 0) { - jbyteArray addr = env->NewByteArray(4); - jbyte v[4]; - memcpy(v, ent->h_addr, 4); - env->SetByteArrayRegion(addr, 0, 4, v); - return addr; - } else { - return NULL; - } - } -} - static void osNetworkSystem_setInetAddressImpl(JNIEnv* env, jobject obj, jobject sender, jbyteArray address) { // LOGD("ENTER setInetAddressImpl"); @@ -3311,6 +3505,7 @@ static void osNetworkSystem_setInetAddressImpl(JNIEnv* env, jobject obj, env->SetObjectField(sender, gCachedFields.iaddr_ipaddress, address); } +// TODO: rewrite this method in Java and make it support IPv6. static jobject osNetworkSystem_inheritedChannelImpl(JNIEnv* env, jobject obj) { // LOGD("ENTER inheritedChannelImpl"); @@ -3402,8 +3597,10 @@ static jobject osNetworkSystem_inheritedChannelImpl(JNIEnv* env, jobject obj) { ntohs(local_addr.sin_port)); // new and set remote addr - addr_object = env->NewObject(gCachedFields.iaddr_class, - gCachedFields.iaddr_class_init); + addr_array = env->NewByteArray((jsize)4); + env->SetByteArrayRegion(addr_array, (jsize)0, (jsize)4, address); + addr_object = env->NewObject(gCachedFields.i4addr_class, + gCachedFields.i4addr_class_init, addr_array); if (NULL == addr_object) { goto clean; } @@ -3415,13 +3612,6 @@ static jobject osNetworkSystem_inheritedChannelImpl(JNIEnv* env, jobject obj) { if (NULL == socketaddr_object) { goto clean; } - addr_field = env->GetFieldID(socketaddr_class, "addr", - "Ljava/net/InetAddress;"); - env->SetObjectField(socketaddr_object, addr_field, addr_object); - addr_array = env->NewByteArray((jsize)4); - env->SetByteArrayRegion(addr_array, (jsize)0, (jsize)4, address); - env->SetObjectField(addr_object, gCachedFields.iaddr_ipaddress, - addr_array); // localAddr socketaddr_class = env->FindClass("java/net/InetSocketAddress"); @@ -3431,9 +3621,11 @@ static jobject osNetworkSystem_inheritedChannelImpl(JNIEnv* env, jobject obj) { socketaddr_field); localAddr_field = env->GetFieldID(channel_class, "localAddress", - "Ljava/net/InetAddress;"); - localAddr_object = env->NewObject(gCachedFields.iaddr_class, - gCachedFields.iaddr_class_init); + "Ljava/net/Inet4Address;"); + addr_array = env->NewByteArray((jsize)4); + env->SetByteArrayRegion(addr_array, (jsize)0, (jsize)4, localAddr); + localAddr_object = env->NewObject(gCachedFields.i4addr_class, + gCachedFields.i4addr_class_init, addr_array); jfieldID socketaddr_field = env->GetFieldID(channel_class, "connectAddress", "Ljava/net/InetSocketAddress;"); jobject socketaddr_object = env->GetObjectField(channel_object, @@ -3443,10 +3635,6 @@ static jobject osNetworkSystem_inheritedChannelImpl(JNIEnv* env, jobject obj) { if (NULL == localAddr_object) { goto clean; } - addr_array = env->NewByteArray((jsize)4); - env->SetByteArrayRegion(addr_array, (jsize)0, (jsize)4, localAddr); - env->SetObjectField(localAddr_object, gCachedFields.iaddr_ipaddress, - addr_array); // set port @@ -3499,10 +3687,13 @@ static jobject osNetworkSystem_inheritedChannelImpl(JNIEnv* env, jobject obj) { goto clean; } + addr_array = env->NewByteArray((jsize)4); localAddr_field = env->GetFieldID(channel_class, "localAddress", "Ljava/net/InetAddress;"); - localAddr_object = env->NewObject(gCachedFields.iaddr_class, - gCachedFields.iaddr_class_init); + memset(address, 0, 4); + env->SetByteArrayRegion(addr_array, (jsize)0, (jsize)4, address); + localAddr_object = env->NewObject(gCachedFields.i4addr_class, + gCachedFields.i4addr_class_init, addr_array); if (NULL == localAddr_object) { goto clean; } @@ -3549,8 +3740,10 @@ static jobject osNetworkSystem_inheritedChannelImpl(JNIEnv* env, jobject obj) { env->SetIntField(channel_object, port_field, ntohs(local_addr.sin_port)); // new and set remote addr + addr_array = env->NewByteArray((jsize)4); + env->SetByteArrayRegion(addr_array, (jsize)0, (jsize)4, address); addr_object = env->NewObject(gCachedFields.iaddr_class, - gCachedFields.iaddr_class_init); + gCachedFields.i4addr_class_init, addr_array); if (NULL == addr_object) { goto clean; } @@ -3561,12 +3754,6 @@ static jobject osNetworkSystem_inheritedChannelImpl(JNIEnv* env, jobject obj) { if (NULL == socketaddr_object) { goto clean; } - addr_field = env->GetFieldID(socketaddr_class, "addr", - "Ljava/net/InetAddress;"); - env->SetObjectField(socketaddr_object, addr_field, addr_object); - addr_array = env->NewByteArray((jsize)4); - env->SetByteArrayRegion(addr_array, (jsize)0, (jsize)4, address); - env->SetObjectField(addr_object, gCachedFields.iaddr_ipaddress, addr_array); // set bound if (0 != local_addr.sin_port) { @@ -3586,7 +3773,7 @@ clean: static JNINativeMethod gMethods[] = { /* name, signature, funcPtr */ { "oneTimeInitializationImpl", "(Z)V", (void*) osNetworkSystem_oneTimeInitializationImpl }, - { "createSocketImpl", "(Ljava/io/FileDescriptor;Z)V", (void*) osNetworkSystem_createSocketImpl }, + { "createStreamSocketImpl", "(Ljava/io/FileDescriptor;Z)V", (void*) osNetworkSystem_createStreamSocketImpl }, { "createDatagramSocketImpl", "(Ljava/io/FileDescriptor;Z)V", (void*) osNetworkSystem_createDatagramSocketImpl }, { "readSocketImpl", "(Ljava/io/FileDescriptor;[BIII)I", (void*) osNetworkSystem_readSocketImpl }, { "readSocketDirectImpl", "(Ljava/io/FileDescriptor;IIII)I", (void*) osNetworkSystem_readSocketDirectImpl }, @@ -3604,7 +3791,6 @@ static JNINativeMethod gMethods[] = { { "sendUrgentDataImpl", "(Ljava/io/FileDescriptor;B)V", (void*) osNetworkSystem_sendUrgentDataImpl }, { "connectDatagramImpl2", "(Ljava/io/FileDescriptor;IILjava/net/InetAddress;)V", (void*) osNetworkSystem_connectDatagramImpl2 }, { "disconnectDatagramImpl", "(Ljava/io/FileDescriptor;)V", (void*) osNetworkSystem_disconnectDatagramImpl }, - { "socketBindImpl2", "(Ljava/io/FileDescriptor;IZLjava/net/InetAddress;)Z", (void*) osNetworkSystem_socketBindImpl2 }, { "peekDatagramImpl", "(Ljava/io/FileDescriptor;Ljava/net/InetAddress;I)I", (void*) osNetworkSystem_peekDatagramImpl }, { "receiveDatagramImpl", "(Ljava/io/FileDescriptor;Ljava/net/DatagramPacket;[BIIIZ)I", (void*) osNetworkSystem_receiveDatagramImpl }, { "receiveDatagramDirectImpl", "(Ljava/io/FileDescriptor;Ljava/net/DatagramPacket;IIIIZ)I", (void*) osNetworkSystem_receiveDatagramDirectImpl }, @@ -3615,7 +3801,6 @@ static JNINativeMethod gMethods[] = { { "sendConnectedDatagramImpl", "(Ljava/io/FileDescriptor;[BIIZ)I", (void*) osNetworkSystem_sendConnectedDatagramImpl }, { "sendConnectedDatagramDirectImpl", "(Ljava/io/FileDescriptor;IIIZ)I", (void*) osNetworkSystem_sendConnectedDatagramDirectImpl }, { "createServerStreamSocketImpl", "(Ljava/io/FileDescriptor;Z)V", (void*) osNetworkSystem_createServerStreamSocketImpl }, - { "createMulticastSocketImpl", "(Ljava/io/FileDescriptor;Z)V", (void*) osNetworkSystem_createMulticastSocketImpl }, { "receiveStreamImpl", "(Ljava/io/FileDescriptor;[BIII)I", (void*) osNetworkSystem_receiveStreamImpl }, { "sendStreamImpl", "(Ljava/io/FileDescriptor;[BII)I", (void*) osNetworkSystem_sendStreamImpl }, { "shutdownInputImpl", "(Ljava/io/FileDescriptor;)V", (void*) osNetworkSystem_shutdownInputImpl }, @@ -3628,10 +3813,10 @@ static JNINativeMethod gMethods[] = { { "setSocketOptionImpl", "(Ljava/io/FileDescriptor;ILjava/lang/Object;)V", (void*) osNetworkSystem_setSocketOptionImpl }, { "getSocketFlagsImpl", "()I", (void*) osNetworkSystem_getSocketFlagsImpl }, { "socketCloseImpl", "(Ljava/io/FileDescriptor;)V", (void*) osNetworkSystem_socketCloseImpl }, - { "getHostByAddrImpl", "([B)Ljava/net/InetAddress;", (void*) osNetworkSystem_getHostByAddrImpl }, - { "getHostByNameImpl", "(Ljava/lang/String;Z)Ljava/net/InetAddress;", (void*) osNetworkSystem_getHostByNameImpl }, { "setInetAddressImpl", "(Ljava/net/InetAddress;[B)V", (void*) osNetworkSystem_setInetAddressImpl }, { "inheritedChannelImpl", "()Ljava/nio/channels/Channel;", (void*) osNetworkSystem_inheritedChannelImpl }, + { "byteArrayToIpString", "([B)Ljava/lang/String;", (void*) osNetworkSystem_byteArrayToIpString }, + { "ipStringToByteArray", "(Ljava/lang/String;)[B", (void*) osNetworkSystem_ipStringToByteArray }, }; int register_org_apache_harmony_luni_platform_OSNetworkSystem(JNIEnv* env) { @@ -3640,3 +3825,4 @@ int register_org_apache_harmony_luni_platform_OSNetworkSystem(JNIEnv* env) { gMethods, NELEM(gMethods)); } +// END android-changed diff --git a/libcore/luni/src/main/native/org_apache_harmony_luni_util_fltparse.c b/libcore/luni/src/main/native/org_apache_harmony_luni_util_fltparse.c index d8a1e6834..e92b776d1 100644 --- a/libcore/luni/src/main/native/org_apache_harmony_luni_util_fltparse.c +++ b/libcore/luni/src/main/native/org_apache_harmony_luni_util_fltparse.c @@ -22,7 +22,7 @@ #include "commonDblParce.h" #include "cbigint.h" -#if defined(LINUX) +#if defined(LINUX) || defined(FREEBSD) || defined(ZOS) #define USE_LL #endif diff --git a/libcore/luni/src/test/java/com/google/coretests/CoreTestResult.java b/libcore/luni/src/test/java/com/google/coretests/CoreTestResult.java index 9a821cd65..802eff845 100644 --- a/libcore/luni/src/test/java/com/google/coretests/CoreTestResult.java +++ b/libcore/luni/src/test/java/com/google/coretests/CoreTestResult.java @@ -131,6 +131,7 @@ public class CoreTestResult extends TestResult { // Ignored } if (thread.isAlive()) { + StackTraceElement[] trace = thread.getStackTrace(); runnable.stop(); thread.stop(); try { @@ -138,8 +139,10 @@ public class CoreTestResult extends TestResult { } catch (InterruptedException ex) { // Ignored } - - addError(test, new CoreTestTimeout("Test timed out")); + + CoreTestTimeout timeout = new CoreTestTimeout("Test timed out"); + timeout.setStackTrace(trace); + addError(test, timeout); } } else { runnable.run(); diff --git a/libcore/luni/src/test/java/com/google/coretests/CoreTestRunnable.java b/libcore/luni/src/test/java/com/google/coretests/CoreTestRunnable.java index ab49e47e7..ed7797e20 100644 --- a/libcore/luni/src/test/java/com/google/coretests/CoreTestRunnable.java +++ b/libcore/luni/src/test/java/com/google/coretests/CoreTestRunnable.java @@ -137,18 +137,18 @@ public class CoreTestRunnable implements Runnable { Throwable throwable = null; File file = File.createTempFile("isolation", ".tmp"); - - fProcess = Runtime.getRuntime().exec( - (IS_DALVIK ? "dalvikvm" : "java") + + + String program = (IS_DALVIK ? "dalvikvm" : "java") + " -classpath " + System.getProperty("java.class.path") + " -Djava.home=" + System.getProperty("java.home") + " -Duser.home=" + System.getProperty("user.home") + - " -Djava.io.tmpdir=" + System.getProperty("user.home") + + " -Djava.io.tmpdir=" + System.getProperty("java.io.tmpdir") + " com.google.coretests.CoreTestIsolator" + " " + fTest.getClass().getName() + " " + fTest.getName() + - " " + file.getAbsolutePath()); - + " " + file.getAbsolutePath(); + fProcess = Runtime.getRuntime().exec(program); + int result = fProcess.waitFor(); if (result != TestRunner.SUCCESS_EXIT) { @@ -158,7 +158,7 @@ public class CoreTestRunnable implements Runnable { throwable = (Throwable)ois.readObject(); ois.close(); } catch (Exception ex) { - throwable = new RuntimeException("Error isolating test", ex); + throwable = new RuntimeException("Error isolating test: " + program, ex); } } diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/platform/OSComponentFactory.java b/libcore/luni/src/test/java/org/apache/harmony/luni/platform/AllTests.java similarity index 56% rename from libcore/luni/src/main/java/org/apache/harmony/luni/platform/OSComponentFactory.java rename to libcore/luni/src/test/java/org/apache/harmony/luni/platform/AllTests.java index a883ee042..be28d419a 100644 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/platform/OSComponentFactory.java +++ b/libcore/luni/src/test/java/org/apache/harmony/luni/platform/AllTests.java @@ -1,4 +1,5 @@ -/* Licensed to the Apache Software Foundation (ASF) under one or more +/* + * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 @@ -16,34 +17,20 @@ package org.apache.harmony.luni.platform; +import junit.framework.Test; +import junit.framework.TestSuite; +import junit.textui.TestRunner; -/** - * OSComponentFactory - * - */ -class OSComponentFactory { - - /** - * @return OSFileSystem - */ - public static IFileSystem getFileSystem() { - // Auto-generated method stub - return new OSFileSystem(); +public class AllTests { + public static void run() { + TestRunner.main(new String[] { AllTests.class.getName() }); } - - /** - * @return OSMemory - */ - public static IMemorySystem getMemorySystem() { - // Auto-generated method stub - return new OSMemory(); + + public static final Test suite() { + TestSuite suite = tests.TestSuiteFactory.createTestSuite("Tests for org.apache.harmony.luni.platform"); + + suite.addTestSuite(OSMemoryTest.class); + + return suite; } - - /** - * @return OSNetwork - */ - public static INetworkSystem getNetworkSystem() { - return OSNetworkSystem.getOSNetworkSystem(); - } - } diff --git a/libcore/luni/src/test/java/org/apache/harmony/luni/platform/OSMemoryTest.java b/libcore/luni/src/test/java/org/apache/harmony/luni/platform/OSMemoryTest.java new file mode 100644 index 000000000..a54628947 --- /dev/null +++ b/libcore/luni/src/test/java/org/apache/harmony/luni/platform/OSMemoryTest.java @@ -0,0 +1,164 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.luni.platform; + +import dalvik.annotation.TestLevel; +import dalvik.annotation.TestTargetNew; +import dalvik.annotation.TestTargetClass; +import dalvik.annotation.AndroidOnly; + +import junit.framework.TestCase; + +/** + * Tests org.apache.harmony.luni.platform.OSMemory (via IMemorySystem). + */ +@TestTargetClass(org.apache.harmony.luni.platform.OSMemory.class) +public class OSMemoryTest extends TestCase { + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "", + method = "memset", + args = {} + ) + public void testMemset() { + IMemorySystem memory = Platform.getMemorySystem(); + + int byteCount = 32; + int ptr = memory.malloc(byteCount); + try { + // Ensure our newly-allocated block isn't zeroed. + memory.setByte(ptr, (byte) 1); + assertEquals((byte) 1, memory.getByte(ptr)); + // Check that we can clear memory. + memory.memset(ptr, (byte) 0, byteCount); + assertBytesEqual((byte) 0, ptr, byteCount); + // Check that we can set an arbitrary value. + memory.memset(ptr, (byte) 1, byteCount); + assertBytesEqual((byte) 1, ptr, byteCount); + } finally { + memory.free(ptr); + } + } + + void assertBytesEqual(byte value, int ptr, int byteCount) { + IMemorySystem memory = Platform.getMemorySystem(); + for (int i = 0; i < byteCount; ++i) { + assertEquals(value, memory.getByte(ptr + i)); + } + } + + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "", + method = "setIntArray", + args = {} + ) + public void testSetIntArray() { + IMemorySystem memory = Platform.getMemorySystem(); + + int[] values = { 3, 7, 31, 127, 8191, 131071, 524287, 2147483647 }; + int[] swappedValues = new int[values.length]; + for (int i = 0; i < values.length; ++i) { + swappedValues[i] = swapInt(values[i]); + } + + int scale = ICommonDataTypes.SIZEOF_JINT; + int ptr = memory.malloc(scale * values.length); + try { + // Regular copy. Memset first so we start from a known state. + memory.memset(ptr, (byte) 0, scale * values.length); + memory.setIntArray(ptr, values, 0, values.length, false); + assertIntsEqual(values, ptr); + + // Swapped copy. + memory.memset(ptr, (byte) 0, scale * values.length); + memory.setIntArray(ptr, values, 0, values.length, true); + assertIntsEqual(swappedValues, ptr); + + // Swapped copies of slices (to ensure we test non-zero offsets). + memory.memset(ptr, (byte) 0, scale * values.length); + for (int i = 0; i < values.length; ++i) { + memory.setIntArray(ptr + i * scale, values, i, 1, true); + } + assertIntsEqual(swappedValues, ptr); + } finally { + memory.free(ptr); + } + } + + private void assertIntsEqual(int[] expectedValues, int ptr) { + IMemorySystem memory = Platform.getMemorySystem(); + for (int i = 0; i < expectedValues.length; ++i) { + assertEquals(expectedValues[i], memory.getInt(ptr + ICommonDataTypes.SIZEOF_JINT * i)); + } + } + + private static int swapInt(int n) { + return (((n >> 0) & 0xff) << 24) | + (((n >> 8) & 0xff) << 16) | + (((n >> 16) & 0xff) << 8) | + (((n >> 24) & 0xff) << 0); + } + + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "", + method = "setShortArray", + args = {} + ) + public void testSetShortArray() { + IMemorySystem memory = Platform.getMemorySystem(); + + short[] values = { 0x0001, 0x0020, 0x0300, 0x4000 }; + short[] swappedValues = { 0x0100, 0x2000, 0x0003, 0x0040 }; + + int scale = ICommonDataTypes.SIZEOF_JSHORT; + int ptr = memory.malloc(scale * values.length); + try { + // Regular copy. Memset first so we start from a known state. + memory.memset(ptr, (byte) 0, scale * values.length); + memory.setShortArray(ptr, values, 0, values.length, false); + assertShortsEqual(values, ptr); + + // Swapped copy. + memory.memset(ptr, (byte) 0, scale * values.length); + memory.setShortArray(ptr, values, 0, values.length, true); + assertShortsEqual(swappedValues, ptr); + + // Swapped copies of slices (to ensure we test non-zero offsets). + memory.memset(ptr, (byte) 0, scale * values.length); + for (int i = 0; i < values.length; ++i) { + memory.setShortArray(ptr + i * scale, values, i, 1, true); + } + assertShortsEqual(swappedValues, ptr); + } finally { + memory.free(ptr); + } + } + + private void assertShortsEqual(short[] expectedValues, int ptr) { + IMemorySystem memory = Platform.getMemorySystem(); + for (int i = 0; i < expectedValues.length; ++i) { + assertEquals(expectedValues[i], memory.getShort(ptr + ICommonDataTypes.SIZEOF_JSHORT * i)); + } + } + + private static short swapShort(short n) { + return (short) ((((n >> 0) & 0xff) << 8) | (((n >> 8) & 0xff) << 0)); + } +} diff --git a/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/io/AllTests.java b/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/io/AllTests.java index 778e52705..056b5214e 100644 --- a/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/io/AllTests.java +++ b/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/io/AllTests.java @@ -34,6 +34,7 @@ public class AllTests TestSuite suite = tests.TestSuiteFactory.createTestSuite("Tests for java.io"); suite.addTestSuite(BufferedReaderTest.class); + suite.addTestSuite(FileCanonPathCacheTest.class); suite.addTestSuite(FilePermissionTest.class); suite.addTestSuite(FileTest.class); suite.addTestSuite(InputStreamReaderTest.class); diff --git a/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/io/FileCanonPathCacheTest.java b/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/io/FileCanonPathCacheTest.java new file mode 100644 index 000000000..f2ac7f3df --- /dev/null +++ b/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/io/FileCanonPathCacheTest.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.luni.tests.java.io; + +import java.io.File; + +import org.apache.harmony.luni.internal.io.FileCanonPathCache; + +import junit.framework.TestCase; + +public class FileCanonPathCacheTest extends TestCase { + + private static int DEFAULT_TIMEOUT = 600000; + + @Override + public void setUp() throws Exception { + FileCanonPathCache.clear(); + FileCanonPathCache.setTimeout(DEFAULT_TIMEOUT); + } + + public void testGetSet() throws Exception { + File file1 = new File("test/hello~1"); + assertNull(FileCanonPathCache.get(file1.getAbsolutePath())); + FileCanonPathCache.put(file1.getAbsolutePath(), file1 + .getCanonicalPath()); + assertEquals(file1.getCanonicalPath(), FileCanonPathCache.get(file1 + .getAbsolutePath())); + + File file2 = new File("test/world~1"); + assertNull(FileCanonPathCache.get(file2.getAbsolutePath())); + FileCanonPathCache.put(file2.getAbsolutePath(), file2 + .getCanonicalPath()); + assertEquals(file2.getCanonicalPath(), FileCanonPathCache.get(file2 + .getAbsolutePath())); + + assertNull(FileCanonPathCache.get("notexist")); + } + + public void testGetTimeout01() throws Exception { + FileCanonPathCache.setTimeout(10); + + File file1 = new File("test/hello~1"); + assertNull(FileCanonPathCache.get(file1.getAbsolutePath())); + FileCanonPathCache.put(file1.getAbsolutePath(), file1 + .getCanonicalPath()); + Thread.sleep(50); + assertNull(FileCanonPathCache.get(file1.getAbsolutePath())); + } + + public void testGetTimeout02() throws Exception { + FileCanonPathCache.setTimeout(10); + + File file1 = new File("test/hello~1"); + assertNull(FileCanonPathCache.get(file1.getAbsolutePath())); + FileCanonPathCache.put(file1.getAbsolutePath(), file1 + .getCanonicalPath()); + File file2 = new File("test/hello~2"); + assertNull(FileCanonPathCache.get(file2.getAbsolutePath())); + FileCanonPathCache.put(file2.getAbsolutePath(), file2 + .getCanonicalPath()); + File file3 = new File("test/hello~3"); + assertNull(FileCanonPathCache.get(file3.getAbsolutePath())); + FileCanonPathCache.put(file3.getAbsolutePath(), file3 + .getCanonicalPath()); + File file4 = new File("test/hello~4"); + assertNull(FileCanonPathCache.get(file4.getAbsolutePath())); + FileCanonPathCache.put(file4.getAbsolutePath(), file4 + .getCanonicalPath()); + File file5 = new File("test/hello~5"); + assertNull(FileCanonPathCache.get(file5.getAbsolutePath())); + FileCanonPathCache.put(file5.getAbsolutePath(), file5 + .getCanonicalPath()); + + Thread.sleep(50); + + assertNull(FileCanonPathCache.get(file1.getAbsolutePath())); + assertNull(FileCanonPathCache.get(file2.getAbsolutePath())); + assertNull(FileCanonPathCache.get(file3.getAbsolutePath())); + assertNull(FileCanonPathCache.get(file4.getAbsolutePath())); + assertNull(FileCanonPathCache.get(file5.getAbsolutePath())); + } + + public void testCacheFull() throws Exception { + int cacheSize = FileCanonPathCache.CACHE_SIZE; + File[] files = new File[cacheSize]; + for (int i = 0; i < cacheSize; ++i) { + files[i] = new File("test/world" + i); + FileCanonPathCache.put(files[i].getAbsolutePath(), files[i] + .getCanonicalPath()); + } + + for (int i = cacheSize; i < files.length; ++i) { + assertEquals(files[i - cacheSize].getCanonicalPath(), + FileCanonPathCache.get(files[i - cacheSize] + .getAbsolutePath())); + files[i] = new File("test/world" + i); + FileCanonPathCache.put(files[i].getAbsolutePath(), files[i] + .getCanonicalPath()); + assertEquals(files[i].getCanonicalPath(), FileCanonPathCache + .get(files[i].getAbsolutePath())); + assertNull(FileCanonPathCache.get(files[i - cacheSize] + .getAbsolutePath())); + } + } +} diff --git a/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/io/FileTest.java b/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/io/FileTest.java index ed8116256..84cddf24d 100644 --- a/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/io/FileTest.java +++ b/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/io/FileTest.java @@ -45,8 +45,9 @@ public class FileTest extends TestCase { File file = new File(root, "/dir/file"); assertEquals("Assert 1: wrong path result ", path.getPath(), file .getPath()); - assertTrue("Assert 1.1: path not absolute ", new File("\\\\\\a\b").isAbsolute()); - + assertFalse("Assert 1.1: path absolute ", new File("\\\\\\a\b").isAbsolute()); + assertTrue("Assert 1.1: path absolute ", new File("///a/b").isAbsolute()); + // Test data used in a few places below String dirName = System.getProperty("java.io.tmpdir"); String fileName = "input.tst"; diff --git a/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/ClassLoaderTest.java b/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/ClassLoaderTest.java index 9b5b17e1b..43a73ed19 100644 --- a/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/ClassLoaderTest.java +++ b/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/ClassLoaderTest.java @@ -45,6 +45,7 @@ import java.util.NoSuchElementException; @TestTargetClass(ClassLoader.class) public class ClassLoaderTest extends TestCase { + private static final String SYSTEM_RESOURCE_PATH = "META-INF/MANIFEST.MF"; public static volatile int flag; @TestTargetNew( @@ -605,7 +606,7 @@ public class ClassLoaderTest extends TestCase { // java.lang.ClassLoader.getSystemClassLoader() ClassLoader cl = ClassLoader.getSystemClassLoader(); - java.io.InputStream is = cl.getResourceAsStream("classes.dex"); + java.io.InputStream is = cl.getResourceAsStream(SYSTEM_RESOURCE_PATH); assertNotNull("Failed to find resource from system classpath", is); try { is.close(); @@ -674,8 +675,8 @@ public class ClassLoaderTest extends TestCase { //assertNotNull("Failed to find resource: " + classResource, // ClassLoader.getSystemResource(classResource)); - URL url = getClass().getClassLoader().getSystemResource("classes.dex"); - assertNotNull("Failed to find resource: classes.dex", url); + URL url = getClass().getClassLoader().getSystemResource(SYSTEM_RESOURCE_PATH); + assertNotNull(String.format("Failed to find resource: %s", SYSTEM_RESOURCE_PATH), url); java.io.InputStream is = url.openStream(); assertTrue("System resource not found", is.available() > 0); @@ -701,8 +702,8 @@ public class ClassLoaderTest extends TestCase { // ClassLoader.getSystemResourceAsStream(classResource)); java.io.InputStream is = getClass().getClassLoader() - .getSystemResourceAsStream("classes.dex"); - assertNotNull("Failed to find resource: classes.dex", is); + .getSystemResourceAsStream(SYSTEM_RESOURCE_PATH); + assertNotNull(String.format("Failed to find resource: %s", SYSTEM_RESOURCE_PATH), is); assertTrue("System resource not found", is.available() > 0); @@ -720,7 +721,7 @@ public class ClassLoaderTest extends TestCase { + "that is sure to exist.") public void test_getSystemResources() { - String textResource = "classes.dex"; + String textResource = SYSTEM_RESOURCE_PATH; try { Enumeration urls = ClassLoader.getSystemResources(textResource); diff --git a/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/ClassTest.java b/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/ClassTest.java index 14ca1ad91..2b492b11c 100644 --- a/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/ClassTest.java +++ b/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/ClassTest.java @@ -838,7 +838,6 @@ public class ClassTest extends junit.framework.TestCase { args = {} ) public void test_getClassLoader() { - assertEquals(ExtendTestClass.class.getClassLoader(), PublicTestClass.class.getClassLoader()); @@ -859,8 +858,8 @@ public class ClassTest extends junit.framework.TestCase { System.setSecurityManager(sm); try { System.class.getClassLoader(); - } catch (SecurityException e) { - fail("SecurityException should not be thrown."); + fail("SecurityException should be thrown."); + } catch (SecurityException expected) { } finally { System.setSecurityManager(oldSm); } diff --git a/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/FloatTest.java b/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/FloatTest.java index 2b03e5a99..b1ad88af6 100644 --- a/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/FloatTest.java +++ b/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/FloatTest.java @@ -1091,8 +1091,6 @@ public class FloatTest extends TestCase { fail("Expected Float.valueOf(null) to throw NPE."); } catch (NullPointerException ex) { // expected - } catch (Exception ex) { - fail("Expected Float.valueOf(null) to throw NPE not " + ex.getClass().getName()); } try { diff --git a/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/IntegerTest.java b/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/IntegerTest.java index 915106860..ea7100929 100644 --- a/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/IntegerTest.java +++ b/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/IntegerTest.java @@ -169,6 +169,56 @@ public class IntegerTest extends TestCase { exception = true; } assertTrue("Failed to throw exception for 9999999999", exception); + + try { + Integer.decode("-"); + fail("Expected exception for -"); + } catch (NumberFormatException e) { + // Expected + } + + try { + Integer.decode("0x"); + fail("Expected exception for 0x"); + } catch (NumberFormatException e) { + // Expected + } + + try { + Integer.decode("#"); + fail("Expected exception for #"); + } catch (NumberFormatException e) { + // Expected + } + + try { + Integer.decode("x123"); + fail("Expected exception for x123"); + } catch (NumberFormatException e) { + // Expected + } + + try { + Integer.decode(null); + fail("Expected exception for null"); + } catch (NullPointerException e) { + // Expected + } + + try { + Integer.decode(""); + fail("Expected exception for empty string"); + } catch (NumberFormatException ex) { + // Expected + } + + try { + Integer.decode(" "); + fail("Expected exception for single space"); + } catch (NumberFormatException ex) { + // Expected + } + } /** @@ -591,6 +641,15 @@ public class IntegerTest extends TestCase { -Integer.MAX_VALUE)); assertEquals("Returned incorrect octal string", "-2147483648", Integer.toString( Integer.MIN_VALUE)); + + // Test for HARMONY-6068 + assertEquals("Returned incorrect octal String", "-1000", Integer.toString(-1000)); + assertEquals("Returned incorrect octal String", "1000", Integer.toString(1000)); + assertEquals("Returned incorrect octal String", "0", Integer.toString(0)); + assertEquals("Returned incorrect octal String", "708", Integer.toString(708)); + assertEquals("Returned incorrect octal String", "-100", Integer.toString(-100)); + assertEquals("Returned incorrect octal String", "-1000000008", Integer.toString(-1000000008)); + assertEquals("Returned incorrect octal String", "2000000008", Integer.toString(2000000008)); } /** diff --git a/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/MathTest.java b/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/MathTest.java index 02bed3c6a..330e0b505 100644 --- a/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/MathTest.java +++ b/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/MathTest.java @@ -17,9 +17,10 @@ package org.apache.harmony.luni.tests.java.lang; +import dalvik.annotation.KnownFailure; import dalvik.annotation.TestLevel; -import dalvik.annotation.TestTargetNew; import dalvik.annotation.TestTargetClass; +import dalvik.annotation.TestTargetNew; @TestTargetClass(Math.class) public class MathTest extends junit.framework.TestCase { @@ -1103,6 +1104,7 @@ public class MathTest extends junit.framework.TestCase { method = "tanh", args = {double.class} ) + @KnownFailure(value = "bug 2139334") public void test_tanh_D() { // Test for special situations assertTrue("Should return NaN", Double.isNaN(Math.tanh(Double.NaN))); diff --git a/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/ObjectTest.java b/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/ObjectTest.java index e2b72be17..758428ef3 100644 --- a/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/ObjectTest.java +++ b/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/ObjectTest.java @@ -23,6 +23,7 @@ import dalvik.annotation.TestLevel; import dalvik.annotation.TestTargetNew; import dalvik.annotation.TestTargetClass; import dalvik.annotation.BrokenTest; +import dalvik.annotation.SideEffect; @TestTargetClass(Object.class) public class ObjectTest extends junit.framework.TestCase { @@ -79,6 +80,7 @@ public class ObjectTest extends junit.framework.TestCase { method = "finalize", args = {} ) + @SideEffect("Causes OutOfMemoryError to test finalization") public void test_finalize() { isCalled = false; class TestObject extends Object { diff --git a/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/ProcessBuilderTest.java b/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/ProcessBuilderTest.java index 93561bf02..d35f50bb1 100644 --- a/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/ProcessBuilderTest.java +++ b/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/ProcessBuilderTest.java @@ -228,6 +228,16 @@ public class ProcessBuilderTest extends TestCase { Process process = pb.start(); InputStream in = process.getInputStream(); InputStream err = process.getErrorStream(); + + while (true) { + try { + process.waitFor(); + break; + } catch (InterruptedException e) { + // Ignored + } + } + byte[] buf = new byte[1024]; if (in.available() > 0) { assertTrue(in.read(buf) > 0); diff --git a/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/SecurityManagerTest.java b/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/SecurityManagerTest.java index be5aa418b..0bd0c1f7b 100644 --- a/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/SecurityManagerTest.java +++ b/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/SecurityManagerTest.java @@ -16,14 +16,11 @@ package org.apache.harmony.luni.tests.java.lang; -import dalvik.annotation.TestTargets; import dalvik.annotation.TestLevel; -import dalvik.annotation.TestTargetNew; import dalvik.annotation.TestTargetClass; - +import dalvik.annotation.TestTargetNew; import junit.framework.TestCase; -import java.io.File; import java.io.FileDescriptor; import java.io.FilePermission; import java.io.IOException; @@ -41,8 +38,6 @@ import java.security.ProtectionDomain; import java.security.Security; import java.security.SecurityPermission; -import tests.support.Support_Exec; - /** * Test case for java.lang.SecurityManager */ @@ -230,14 +225,14 @@ public class SecurityManagerTest extends TestCase { } try { - mutableSM.checkMemberAccess(Object.class, Member.DECLARED); + delegateCallToCheckMemberAccess2(Object.class, Member.DECLARED); fail("SecurityException was not thrown."); } catch(SecurityException se) { //expected } try { - mutableSM.checkMemberAccess(null, Member.PUBLIC); + delegateCallToCheckMemberAccess2(null, Member.PUBLIC); fail("NullPointerException was not thrown."); } catch(NullPointerException npe) { //expected @@ -247,6 +242,23 @@ public class SecurityManagerTest extends TestCase { } } + /** + * Don't call checkMemberAccess directly, since we're checking our caller + * (and not ourselves). This is necessary for unit tests, since JUnit's + * TestCase is usually in the boot classpath for dalvik. This delegating + * method corresponds to Class.getDeclared*(); + */ + private void delegateCallToCheckMemberAccess2(Class cls, int type) { + delegateCallToCheckMemberAccess1(cls, type); + } + + /** + * This delegating method corresponds to Class.checkMemberAccess(). + */ + private void delegateCallToCheckMemberAccess1(Class cls, int type) { + mutableSM.checkMemberAccess(cls, type); + } + /** * @tests java.lang.SecurityManager#checkPermission(java.security.Permission) */ diff --git a/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/StrictMathTest.java b/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/StrictMathTest.java index 137676c18..c6edeb250 100644 --- a/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/StrictMathTest.java +++ b/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/StrictMathTest.java @@ -17,10 +17,11 @@ package org.apache.harmony.luni.tests.java.lang; -import dalvik.annotation.TestTargets; +import dalvik.annotation.KnownFailure; import dalvik.annotation.TestLevel; -import dalvik.annotation.TestTargetNew; import dalvik.annotation.TestTargetClass; +import dalvik.annotation.TestTargetNew; +import dalvik.annotation.TestTargets; @TestTargetClass(StrictMath.class) public class StrictMathTest extends junit.framework.TestCase { @@ -1292,6 +1293,7 @@ public class StrictMathTest extends junit.framework.TestCase { method = "tanh", args = {double.class} ) + @KnownFailure(value = "bug 2139334") public void test_tanh_D() { // Test for special situations assertTrue(Double.isNaN(StrictMath.tanh(Double.NaN))); diff --git a/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/StringBuffer2Test.java b/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/StringBuffer2Test.java index 8b03cede8..6bc1939a8 100644 --- a/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/StringBuffer2Test.java +++ b/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/StringBuffer2Test.java @@ -414,7 +414,6 @@ public class StringBuffer2Test extends junit.framework.TestCase { method = "ensureCapacity", args = {int.class} ) - @KnownFailure("Google TODO 1481226") public void test_ensureCapacityI() { // Test for method void java.lang.StringBuffer.ensureCapacity(int) StringBuffer sb = new StringBuffer(10); diff --git a/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/ThreadLocalTest.java b/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/ThreadLocalTest.java index bcb94c73d..48fddfe89 100644 --- a/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/ThreadLocalTest.java +++ b/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/ThreadLocalTest.java @@ -37,11 +37,7 @@ public class ThreadLocalTest extends TestCase { args = {} ) public void test_Constructor() { - try { - new ThreadLocal(); - } catch (Exception e) { - fail("unexpected exception: " + e.toString()); - } + new ThreadLocal(); } /** diff --git a/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/ThreadTest.java b/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/ThreadTest.java index 47eb16688..a24f457ec 100644 --- a/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/ThreadTest.java +++ b/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/lang/ThreadTest.java @@ -23,9 +23,10 @@ import java.lang.Thread.UncaughtExceptionHandler; import java.security.Permission; import java.util.Map; import java.util.concurrent.Semaphore; +import java.util.concurrent.locks.LockSupport; +import java.util.concurrent.atomic.AtomicReference; import dalvik.annotation.AndroidOnly; -import dalvik.annotation.KnownFailure; import dalvik.annotation.TestLevel; import dalvik.annotation.TestTargetClass; import dalvik.annotation.TestTargetNew; @@ -913,7 +914,7 @@ public class ThreadTest extends junit.framework.TestCase { } catch (InterruptedException e) { fail("Join failed "); } - assertTrue("Joined thread is still alive", !st.isAlive()); + assertFalse("Joined thread is still alive", st.isAlive()); boolean result = true; Thread th = new Thread("test"); try { @@ -938,6 +939,44 @@ public class ThreadTest extends junit.framework.TestCase { st.start(); } + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "Regression test for when join() failed due to spurious wakeups", + method = "join", + args = {long.class, int.class} + ) + public void test_joinWithSpuriousInterruption() throws InterruptedException { + final Thread parker = new Thread() { + @Override + public void run() { + for (int i = 0; i < 10; i++) { + // we used to get spurious wakeups upon unparking + LockSupport.park(); + } + } + }; + Thread unparker = new Thread() { + @Override + public void run() { + for (int i = 0; i < 10; i++) { + try { + Thread.sleep(100); + LockSupport.unpark(parker); + } catch (InterruptedException ignored) { + } + } + } + }; + + long startNanos = System.nanoTime(); + parker.start(); + unparker.start(); + parker.join(500, 500000); + long netWaitTime = System.nanoTime() - startNanos; + assertTrue("Expected to wait at least 500000000ns, but was " + netWaitTime + "ns", + netWaitTime > 500000000); + } + /** * @tests java.lang.Thread#join(long) */ @@ -1919,23 +1958,33 @@ public class ThreadTest extends junit.framework.TestCase { public void run() { while (!sem.hasQueuedThreads()) {} sem.release(); + + // RUNNABLE while (run) {} + try { + // WAITING sem.acquire(); } catch (InterruptedException e) { fail("InterruptedException was thrown."); } + + // BLOCKED synchronized (lock) { lock.equals(new Object()); } synchronized (lock) { try { sem.release(); + + // TIMED_WAITING lock.wait(Long.MAX_VALUE); } catch (InterruptedException e) { // expected } } + + // TERMINATED upon return } }; assertEquals(Thread.State.NEW, th.getState()); @@ -1976,7 +2025,7 @@ public class ThreadTest extends junit.framework.TestCase { } assertEquals(Thread.State.TERMINATED, th.getState()); } - boolean run = true; + volatile boolean run = true; /** * @tests java.lang.Thread#getUncaughtExceptionHandler diff --git a/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/net/ContentHandlerFactoryTest.java b/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/net/ContentHandlerFactoryTest.java index d00968f45..bdbfbd4ac 100644 --- a/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/net/ContentHandlerFactoryTest.java +++ b/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/net/ContentHandlerFactoryTest.java @@ -67,8 +67,7 @@ public class ContentHandlerFactoryTest extends TestCase { assertTrue(isCreateContentHandlerCalled); assertTrue(isGetContentCalled); } catch (Exception e) { - fail("Exception during test : " + e.getMessage()); - + throw new RuntimeException(e); } isGetContentCalled = false; @@ -77,8 +76,7 @@ public class ContentHandlerFactoryTest extends TestCase { con.getContent(new Class[] {}); assertTrue(isGetContentCalled); } catch (Exception e) { - fail("Exception during test : " + e.getMessage()); - + throw new RuntimeException(e); } try { diff --git a/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/net/Inet6AddressTest.java b/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/net/Inet6AddressTest.java index 498c4868e..98ae45e13 100644 --- a/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/net/Inet6AddressTest.java +++ b/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/net/Inet6AddressTest.java @@ -1193,7 +1193,9 @@ public class Inet6AddressTest extends junit.framework.TestCase { 0x25, (byte) 0xFF, (byte) 0xFE, (byte) 0xF8, (byte) 0x7C, (byte) 0xB2 }; aAddr = Inet6Address.getByAddress(bAddr); - assertEquals("fe80:0:0:0:211:25ff:fef8:7cb2", aAddr.getHostAddress()); + String aString = aAddr.getHostAddress(); + assertTrue(aString.equals("fe80:0:0:0:211:25ff:fef8:7cb2") || + aString.equals("fe80::211:25ff:fef8:7cb2")); byte[] cAddr = { (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, @@ -1207,7 +1209,9 @@ public class Inet6AddressTest extends junit.framework.TestCase { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}; aAddr = Inet6Address.getByAddress(dAddr); - assertEquals("0:0:0:0:0:0:0:0", aAddr.getHostAddress()); + aString = aAddr.getHostAddress(); + assertTrue(aString.equals("0:0:0:0:0:0:0:0") || + aString.equals("::")); byte[] eAddr = { (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, (byte) 0x05, (byte) 0x06, (byte) 0x07, diff --git a/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/net/InetAddressTest.java b/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/net/InetAddressTest.java index fa7dd2626..5635ecb76 100644 --- a/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/net/InetAddressTest.java +++ b/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/net/InetAddressTest.java @@ -228,11 +228,14 @@ public class InetAddressTest extends junit.framework.TestCase { System.setSecurityManager(oldman); } - //Regression for HARMONY-56 - InetAddress[] ia = InetAddress.getAllByName(null); - assertEquals("Assert 0: No loopback address", 1, ia.length); - assertTrue("Assert 1: getAllByName(null) not loopback", - ia[0].isLoopbackAddress()); + // Regression for HARMONY-56 + InetAddress[] addresses = InetAddress.getAllByName(null); + assertTrue("getAllByName(null): no results", addresses.length > 0); + for (int i = 0; i < addresses.length; i++) { + InetAddress address = addresses[i]; + assertTrue("Assert 1: getAllByName(null): " + address + + " is not loopback", address.isLoopbackAddress()); + } try { InetAddress.getAllByName("unknown.host"); @@ -478,17 +481,25 @@ public class InetAddressTest extends junit.framework.TestCase { method = "hashCode", args = {} ) - public void test_hashCode() { - // Test for method int java.net.InetAddress.hashCode() + int getHashCode(String literal) { + InetAddress host = null; try { - InetAddress host = InetAddress - .getByName(Support_Configuration.InetTestAddress); - int hashcode = host.hashCode(); - assertTrue("Incorrect hash returned: " + hashcode + " from host: " - + host, hashcode == Support_Configuration.InetTestHashcode); - } catch (java.net.UnknownHostException e) { - fail("Exception during test : " + e.getMessage()); + host = InetAddress.getByName(literal); + } catch(UnknownHostException e) { + fail("Exception during hashCode test : " + e.getMessage()); } + return host.hashCode(); + } + + public void test_hashCode() { + int hashCode = getHashCode(Support_Configuration.InetTestIP); + int ip6HashCode = getHashCode(Support_Configuration.InetTestIP6); + int ip6LOHashCode = getHashCode(Support_Configuration.InetTestIP6LO); + assertFalse("Hash collision", hashCode == ip6HashCode); + assertFalse("Hash collision", ip6HashCode == ip6LOHashCode); + assertFalse("Hash collision", hashCode == ip6LOHashCode); + assertFalse("Hash collision", ip6LOHashCode == 0); + assertFalse("Hash collision", ip6LOHashCode == 1); } /** @@ -503,9 +514,18 @@ public class InetAddressTest extends junit.framework.TestCase { public void test_isMulticastAddress() { // Test for method boolean java.net.InetAddress.isMulticastAddress() try { + InetAddress ia1 = InetAddress.getByName("ff02::1"); + assertTrue("isMulticastAddress returned incorrect result", ia1 + .isMulticastAddress()); InetAddress ia2 = InetAddress.getByName("239.255.255.255"); assertTrue("isMulticastAddress returned incorrect result", ia2 .isMulticastAddress()); + InetAddress ia3 = InetAddress.getByName("fefb::"); + assertFalse("isMulticastAddress returned incorrect result", ia3 + .isMulticastAddress()); + InetAddress ia4 = InetAddress.getByName("10.0.0.1"); + assertFalse("isMulticastAddress returned incorrect result", ia4 + .isMulticastAddress()); } catch (Exception e) { fail("Exception during isMulticastAddress test : " + e.getMessage()); } diff --git a/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/net/InetSocketAddressTest.java b/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/net/InetSocketAddressTest.java index b979628e2..b91ad5fb9 100644 --- a/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/net/InetSocketAddressTest.java +++ b/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/net/InetSocketAddressTest.java @@ -324,7 +324,6 @@ public class InetSocketAddressTest extends TestCase { method = "!SerializationGolden", args = {} ) - @KnownFailure("Problem with deserialization of Localhost vs. localhost") public void testSerializationCompatibility() throws Exception { Object[] testCases = { diff --git a/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/net/URLConnectionTest.java b/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/net/URLConnectionTest.java index c803d3b7f..6077d53aa 100644 --- a/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/net/URLConnectionTest.java +++ b/libcore/luni/src/test/java/org/apache/harmony/luni/tests/java/net/URLConnectionTest.java @@ -22,9 +22,7 @@ import dalvik.annotation.TestLevel; import dalvik.annotation.TestTargetClass; import dalvik.annotation.TestTargetNew; import dalvik.annotation.TestTargets; - import junit.framework.TestCase; - import tests.support.Support_Configuration; import tests.support.Support_PortManager; import tests.support.Support_TestWebData; @@ -59,7 +57,6 @@ import java.net.URLConnection; import java.net.URLStreamHandler; import java.net.UnknownServiceException; import java.security.Permission; -import java.text.DateFormat; import java.text.ParseException; import java.util.Arrays; import java.util.Calendar; @@ -463,6 +460,29 @@ public class URLConnectionTest extends TestCase { } } + @KnownFailure(value="bug 2002061") + public void testHttpPostHeaders() throws IOException { + String path = "/" + Math.random(); + HttpURLConnection connection = (HttpURLConnection) + new URL("http://localhost:" + port + path).openConnection(); + + // post a request + connection.setDoOutput(true); + OutputStreamWriter writer + = new OutputStreamWriter(connection.getOutputStream()); + writer.write("hello"); + writer.flush(); + assertEquals(200, connection.getResponseCode()); + + // validate the request by asking the server what was received + Map headers = server.pathToRequest().get(path).getHeaders(); + assertEquals("*, */*", headers.get("Accept")); + assertEquals("application/x-www-form-urlencoded", headers.get("Content-Type")); + assertEquals("5", headers.get("Content-Length")); + assertEquals("localhost:" + port, headers.get("Host")); + // TODO: test User-Agent? + } + /** * @throws IOException * @tests {@link java.net.URLConnection#getAllowUserInteraction()} @@ -1069,8 +1089,6 @@ public class URLConnectionTest extends TestCase { method = "getHeaderFieldDate", args = {java.lang.String.class, long.class} ) - @KnownFailure("getHeaderFieldDate on Content-Length throws an exception." - + " The RI just returns the default value") public void test_getHeaderFieldDateLjava_lang_StringJ() { Support_TestWebData params = Support_TestWebData.testParams[0]; @@ -1612,7 +1630,6 @@ public class URLConnectionTest extends TestCase { method = "guessContentTypeFromStream", args = {java.io.InputStream.class} ) - @KnownFailure("'"); diff --git a/libcore/luni/src/test/java/tests/AllTests.java b/libcore/luni/src/test/java/tests/AllTests.java index 9ef1aa8b3..893cdf090 100644 --- a/libcore/luni/src/test/java/tests/AllTests.java +++ b/libcore/luni/src/test/java/tests/AllTests.java @@ -49,10 +49,13 @@ public class AllTests suite.addTest(tests.regex.AllTests.suite()); suite.addTest(tests.security.AllTests.suite()); suite.addTest(tests.sql.AllTests.suite()); + suite.addTest(tests.suncompat.AllTests.suite()); suite.addTest(tests.text.AllTests.suite()); suite.addTest(tests.xml.AllTests.suite()); suite.addTest(tests.xnet.AllTests.suite()); + suite.addTest(org.apache.harmony.luni.platform.AllTests.suite()); + return suite; } } diff --git a/libcore/luni/src/test/java/tests/api/java/io/FileTest.java b/libcore/luni/src/test/java/tests/api/java/io/FileTest.java index bde5766a5..81f14e0ee 100644 --- a/libcore/luni/src/test/java/tests/api/java/io/FileTest.java +++ b/libcore/luni/src/test/java/tests/api/java/io/FileTest.java @@ -217,8 +217,6 @@ public class FileTest extends junit.framework.TestCase { method = "File", args = {java.lang.String.class, java.lang.String.class} ) - @AndroidOnly("Test 3 incorrectly fails on the RI; Android is more " + - "better at resolving path names.") public void test_ConstructorLjava_lang_StringLjava_lang_String_112270() { File ref1 = new File("/dir1/file1"); @@ -230,10 +228,10 @@ public class FileTest extends junit.framework.TestCase { ref1.getPath())); File file3 = new File("\\", "\\dir1\\file1"); assertTrue("wrong result 3: " + file3, file3.getPath().equals( - ref1.getPath())); + "\\/\\dir1\\file1")); File file4 = new File("\\", "\\\\dir1\\file1"); assertTrue("wrong result 4: " + file4, file4.getPath().equals( - ref1.getPath())); + "\\/\\\\dir1\\file1")); File ref2 = new File("/lib/content-types.properties"); File file5 = new File("/", "lib/content-types.properties"); @@ -250,8 +248,6 @@ public class FileTest extends junit.framework.TestCase { method = "File", args = {java.io.File.class, java.lang.String.class} ) - @AndroidOnly("Test 3 incorrectly fails on the RI; Android is more " + - "better at resolving path names.") public void test_ConstructorLjava_io_FileLjava_lang_String_112270() { File ref1 = new File("/dir1/file1"); @@ -264,10 +260,10 @@ public class FileTest extends junit.framework.TestCase { ref1.getPath())); File file3 = new File(root, "\\dir1\\file1"); assertTrue("wrong result 3: " + file3, file3.getPath().equals( - ref1.getPath())); + "/\\dir1\\file1")); File file4 = new File(root, "\\\\dir1\\file1"); assertTrue("wrong result 4: " + file4, file4.getPath().equals( - ref1.getPath())); + "/\\\\dir1\\file1")); File ref2 = new File("/lib/content-types.properties"); File file5 = new File(root, "lib/content-types.properties"); @@ -692,8 +688,9 @@ public class FileTest extends junit.framework.TestCase { method = "delete", args = {} ) - @KnownFailure("Non empty directories are deleted on Android.") public void test_delete() { + // this test passes in the emulator, but it fails on the device + // Test for method boolean java.io.File.delete() try { File dir = new File(System.getProperty("java.io.tmpdir"), platformId diff --git a/libcore/luni/src/test/java/tests/api/java/io/InputStreamReaderTest.java b/libcore/luni/src/test/java/tests/api/java/io/InputStreamReaderTest.java index bb3f8f593..748105ac4 100644 --- a/libcore/luni/src/test/java/tests/api/java/io/InputStreamReaderTest.java +++ b/libcore/luni/src/test/java/tests/api/java/io/InputStreamReaderTest.java @@ -726,10 +726,12 @@ public class InputStreamReaderTest extends TestCase { char[] chars = new char[8192]; int at = 0; - outer: for (;;) { int amt = isr.read(chars); - if (amt <= 0) break; + if (amt <= 0) { + break; + } + for (int i = 0; i < amt; i++) { char c = chars[i]; if (at < prefixLength) { diff --git a/libcore/luni/src/test/java/tests/api/java/io/OutputStreamWriterTest.java b/libcore/luni/src/test/java/tests/api/java/io/OutputStreamWriterTest.java index cd17907c9..22e1af780 100644 --- a/libcore/luni/src/test/java/tests/api/java/io/OutputStreamWriterTest.java +++ b/libcore/luni/src/test/java/tests/api/java/io/OutputStreamWriterTest.java @@ -17,7 +17,6 @@ package tests.api.java.io; -import dalvik.annotation.KnownFailure; import dalvik.annotation.TestTargets; import dalvik.annotation.TestLevel; import dalvik.annotation.TestTargetNew; @@ -425,7 +424,6 @@ public class OutputStreamWriterTest extends TestCase { clazz = InputStreamReader.class ) }) - @KnownFailure("Error when reading bytes in UTF-8 expected:<8916> but was:<8907> ") public void test_write$C() throws Exception { int upper; InputStreamReader isr = null; @@ -470,7 +468,7 @@ public class OutputStreamWriterTest extends TestCase { j = 0; } assertEquals("Error when reading bytes in " - + MINIMAL_CHARSETS[i], expected++, largeBuffer[j++]); + + MINIMAL_CHARSETS[i] + " at " + j, expected++, largeBuffer[j++]); } } finally { try { @@ -485,6 +483,99 @@ public class OutputStreamWriterTest extends TestCase { } } + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "", + method = "write", + args = {char[].class} + ) + public void test_write_US_ASCII() throws Exception { + testEncodeCharset("US-ASCII", 128); + } + + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "", + method = "write", + args = {char[].class} + ) + public void test_write_ISO_8859_1() throws Exception { + testEncodeCharset("ISO-8859-1", 256); + } + + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "", + method = "write", + args = {char[].class} + ) + public void test_write_UTF_16BE() throws Exception { + testEncodeCharset("UTF-16BE", 0xd800); + } + + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "", + method = "write", + args = {char[].class} + ) + public void test_write_UTF_16LE() throws Exception { + testEncodeCharset("UTF-16LE", 0xd800); + } + + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "", + method = "write", + args = {char[].class} + ) + public void test_write_UTF_16() throws Exception { + testEncodeCharset("UTF-16", 0xd800); + } + + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "", + method = "write", + args = {char[].class} + ) + public void test_write_UTF_8() throws Exception { + testEncodeCharset("UTF-8", 0xd800); + } + + private void testEncodeCharset(String charset, int maxChar) throws Exception { + char[] chars = new char[maxChar]; + for (int i = 0; i < maxChar; i++) { + chars[i] = (char) i; + } + + // to byte array + ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); + OutputStreamWriter charsOut = new OutputStreamWriter(bytesOut, charset); + charsOut.write(chars); + charsOut.flush(); + + // decode from byte array, one character at a time + ByteArrayInputStream bytesIn = new ByteArrayInputStream(bytesOut.toByteArray()); + InputStreamReader charsIn = new InputStreamReader(bytesIn, charset); + for (int i = 0; i < maxChar; i++) { + assertEquals(i, charsIn.read()); + } + assertEquals(-1, charsIn.read()); + + // decode from byte array, using byte buffers + bytesIn = new ByteArrayInputStream(bytesOut.toByteArray()); + charsIn = new InputStreamReader(bytesIn, charset); + char[] decoded = new char[maxChar]; + for (int r = 0; r < maxChar; ) { + r += charsIn.read(decoded, r, maxChar - r); + } + assertEquals(-1, charsIn.read()); + for (int i = 0; i < maxChar; i++) { + assertEquals(i, decoded[i]); + } + } + /** * @tests java.io.OutputStreamWriter#getEncoding() */ @@ -784,5 +875,4 @@ public class OutputStreamWriterTest extends TestCase { fail("UTF-8 not supported"); } } - } diff --git a/libcore/luni/src/test/java/tests/api/java/io/PipedInputStreamTest.java b/libcore/luni/src/test/java/tests/api/java/io/PipedInputStreamTest.java index 671bfe919..a6b71ed12 100644 --- a/libcore/luni/src/test/java/tests/api/java/io/PipedInputStreamTest.java +++ b/libcore/luni/src/test/java/tests/api/java/io/PipedInputStreamTest.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.io.PipedInputStream; import java.io.PipedOutputStream; +import dalvik.annotation.BrokenTest; import dalvik.annotation.TestLevel; import dalvik.annotation.TestTargetClass; import dalvik.annotation.TestTargetNew; @@ -414,6 +415,7 @@ public class PipedInputStreamTest extends junit.framework.TestCase { method = "receive", args = {int.class} ) + @BrokenTest(value="bug 2002061") public void test_receive() throws IOException { pis = new PipedInputStream(); pos = new PipedOutputStream(); diff --git a/libcore/luni/src/test/java/tests/api/java/io/SerializationStressTest4.java b/libcore/luni/src/test/java/tests/api/java/io/SerializationStressTest4.java index 9b589c582..99ea61958 100644 --- a/libcore/luni/src/test/java/tests/api/java/io/SerializationStressTest4.java +++ b/libcore/luni/src/test/java/tests/api/java/io/SerializationStressTest4.java @@ -44,6 +44,7 @@ import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.TimeZone; +import java.util.Collections; import tests.support.Support_Configuration; import tests.support.Support_Proxy_I1; @@ -2666,50 +2667,28 @@ public class SerializationStressTest4 extends SerializationStressTest { method = "!Serialization", args = {} ) - @KnownFailure("Proxy support broken when user classloader involved") - public void test_writeObject_Proxy() { - // Test for method void - // java.io.ObjectOutputStream.writeObject(java.security.GuardedObject) - - Object objToSave = null; - Object objLoaded = null; - - try { - objToSave = Proxy.getProxyClass(Support_Proxy_I1.class - .getClassLoader(), new Class[] { Support_Proxy_I1.class }); - if (DEBUG) - System.out.println("Obj = " + objToSave); - objLoaded = dumpAndReload(objToSave); + @KnownFailure(value="bug 2188225") + public void test_writeObject_Proxy() + throws ClassNotFoundException, IOException { - assertTrue(MSG_TEST_FAILED + "not a proxy class", Proxy - .isProxyClass((Class) objLoaded)); - Class[] interfaces = ((Class) objLoaded).getInterfaces(); - assertTrue(MSG_TEST_FAILED + "wrong interfaces length", - interfaces.length == 1); - assertTrue(MSG_TEST_FAILED + "wrong interface", - interfaces[0] == Support_Proxy_I1.class); + // serialize class + Class proxyClass = Proxy.getProxyClass(Support_Proxy_I1.class + .getClassLoader(), new Class[]{Support_Proxy_I1.class}); + Class proxyClassOut = (Class) dumpAndReload(proxyClass); - InvocationHandler handler = new MyInvocationHandler(); - objToSave = Proxy.newProxyInstance(Support_Proxy_I1.class - .getClassLoader(), new Class[] { Support_Proxy_I1.class }, - handler); - if (DEBUG) - System.out.println("Obj = " + objToSave); - objLoaded = dumpAndReload(objToSave); + assertTrue(Proxy.isProxyClass(proxyClassOut)); + assertEquals(Collections.singletonList(Support_Proxy_I1.class), + Arrays.asList(proxyClassOut.getInterfaces())); - boolean equals = Proxy.getInvocationHandler(objLoaded).getClass() == MyInvocationHandler.class; - assertTrue(MSG_TEST_FAILED + objToSave, equals); + // serialize instance + InvocationHandler handler = new MyInvocationHandler(); + Object proxyInstance = Proxy.newProxyInstance(Support_Proxy_I1.class + .getClassLoader(), new Class[] { Support_Proxy_I1.class }, + handler); - } catch (IOException e) { - fail("Exception serializing " + objToSave + " : " + e.getMessage()); - } catch (ClassNotFoundException e) { - fail("ClassNotFoundException reading Object type: " - + e.getMessage()); - } catch (Error err) { - System.out.println("Error when obj = " + objToSave); - // err.printStackTrace(); - throw err; - } + Object proxyInstanceOut = dumpAndReload(proxyInstance); + assertEquals(MyInvocationHandler.class, + Proxy.getInvocationHandler(proxyInstanceOut).getClass()); } @TestTargetNew( diff --git a/libcore/luni/src/test/java/tests/api/java/lang/ref/SoftReferenceTest.java b/libcore/luni/src/test/java/tests/api/java/lang/ref/SoftReferenceTest.java index 4c6724134..d890f389d 100644 --- a/libcore/luni/src/test/java/tests/api/java/lang/ref/SoftReferenceTest.java +++ b/libcore/luni/src/test/java/tests/api/java/lang/ref/SoftReferenceTest.java @@ -21,6 +21,7 @@ import dalvik.annotation.TestLevel; import dalvik.annotation.TestTargetNew; import dalvik.annotation.TestTargetClass; import dalvik.annotation.BrokenTest; +import dalvik.annotation.SideEffect; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; @@ -107,6 +108,7 @@ public class SoftReferenceTest extends junit.framework.TestCase { method = "get", args = {} ) + @SideEffect("Causes OutOfMemoryError to test finalization") public void test_get_SoftReference() { class TestObject { diff --git a/libcore/luni/src/test/java/tests/api/java/lang/reflect/ConstructorTest.java b/libcore/luni/src/test/java/tests/api/java/lang/reflect/ConstructorTest.java index 6bdb55ada..e9554dd17 100644 --- a/libcore/luni/src/test/java/tests/api/java/lang/reflect/ConstructorTest.java +++ b/libcore/luni/src/test/java/tests/api/java/lang/reflect/ConstructorTest.java @@ -104,7 +104,11 @@ public class ConstructorTest extends junit.framework.TestCase { public GenericConstructorTestHelper(T t, S s) {} public GenericConstructorTestHelper() throws E{} } - + + static class NoPublicConstructorTestHelper { + // This class has no public constructor. + } + // Used to test synthetic constructor. // // static class Outer { @@ -479,7 +483,6 @@ public class ConstructorTest extends junit.framework.TestCase { } catch (Exception e) { fail("Exception during getGenericExceptionTypes test:" + e.toString()); } - System.out.println(Arrays.toString(types)); assertEquals("Wrong number of exception types returned", 1, types.length); @@ -555,7 +558,35 @@ public class ConstructorTest extends junit.framework.TestCase { .equals( "public tests.api.java.lang.reflect.ConstructorTest$ConstructorTestHelper(java.lang.Object)")); } - + + /** + * @tests java.lang.reflect.Constructor#getConstructor((Class[]) null) + */ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "getConstructor", + args = {} + ) + public void test_getConstructor() throws Exception { + // Passing new Class[0] should be equivalent to (Class[]) null. + Class c2 = ConstructorTestHelper.class; + assertEquals(c2.getConstructor(new Class[0]), c2.getConstructor((Class[]) null)); + assertEquals(c2.getDeclaredConstructor(new Class[0]), + c2.getDeclaredConstructor((Class[]) null)); + + // We can get a non-public constructor via getDeclaredConstructor... + Class c1 = NoPublicConstructorTestHelper.class; + c1.getDeclaredConstructor((Class[]) null); + // ...but not with getConstructor (which only returns public constructors). + try { + c1.getConstructor((Class[]) null); + fail("Should throw NoSuchMethodException"); + } catch (NoSuchMethodException ex) { + // Expected. + } + } + /** * Sets up the fixture, for example, open a network connection. This method * is called before a test is executed. @@ -570,4 +601,3 @@ public class ConstructorTest extends junit.framework.TestCase { protected void tearDown() { } } - diff --git a/libcore/luni/src/test/java/tests/api/java/lang/reflect/GenericReflectionCornerCases.java b/libcore/luni/src/test/java/tests/api/java/lang/reflect/GenericReflectionCornerCases.java index 31dfaa539..8919652e7 100644 --- a/libcore/luni/src/test/java/tests/api/java/lang/reflect/GenericReflectionCornerCases.java +++ b/libcore/luni/src/test/java/tests/api/java/lang/reflect/GenericReflectionCornerCases.java @@ -169,8 +169,6 @@ public class GenericReflectionCornerCases extends GenericReflectionTestsBase { ) }) @SuppressWarnings("unchecked") - @KnownFailure("Class MultipleBoundedWildcardUnEquality can not be found, " - + "maybe the wrong class loader is used to get the raw type?") public void testMultipleBoundedWildcardUnEquality() throws Exception { Class clazz = MultipleBoundedWildcardUnEquality.class; @@ -240,8 +238,6 @@ public class GenericReflectionCornerCases extends GenericReflectionTestsBase { ) }) @SuppressWarnings("unchecked") - @KnownFailure("Class MultipleBoundedWildcardEquality can not be found, " - + "maybe the wrong class loader is used to get the raw type?") public void testMultipleBoundedWildcard() throws Exception { Class clazz = MultipleBoundedWildcardEquality.class; diff --git a/libcore/luni/src/test/java/tests/api/java/lang/reflect/GenericTypesTest.java b/libcore/luni/src/test/java/tests/api/java/lang/reflect/GenericTypesTest.java index c9255b2ae..7db5dbe49 100644 --- a/libcore/luni/src/test/java/tests/api/java/lang/reflect/GenericTypesTest.java +++ b/libcore/luni/src/test/java/tests/api/java/lang/reflect/GenericTypesTest.java @@ -201,8 +201,6 @@ public class GenericTypesTest extends GenericReflectionTestsBase { args = {} ) @SuppressWarnings("unchecked") - @KnownFailure("Class GenericType can not be found, " - + "maybe the wrong class loader is used to get the raw type?") public void testSimpleInheritance() throws Exception { Class clazz = SimpleInheritance.class; TypeVariable subTypeVariable = getTypeParameter(clazz); diff --git a/libcore/luni/src/test/java/tests/api/java/lang/reflect/MethodTest.java b/libcore/luni/src/test/java/tests/api/java/lang/reflect/MethodTest.java index 884bc8c56..506d173cc 100644 --- a/libcore/luni/src/test/java/tests/api/java/lang/reflect/MethodTest.java +++ b/libcore/luni/src/test/java/tests/api/java/lang/reflect/MethodTest.java @@ -226,7 +226,41 @@ public class MethodTest extends junit.framework.TestCase { } assertTrue("Inherited method returned not-equal", m1.equals(m2)); } - + + /** + * @tests java.lang.Class#getMethod(java.lang.String, java.lang.Class[]) + */ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "getMethod", + args = {java.lang.String.class, java.lang.Class[].class}, + clazz = java.lang.Class.class + ) + public void test_getMethod() throws NoSuchMethodException, SecurityException { + // Check that getMethod treats null parameterTypes the same as an empty array. + Method m1 = TestMethod.class.getMethod("invokeInstanceTest", new Class[0]); + Method m2 = TestMethod.class.getMethod("invokeInstanceTest", (Class[]) null); + assertEquals(m1, m2); + } + + /** + * @tests java.lang.Class#getDeclaredMethod(java.lang.String, java.lang.Class[]) + */ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "getDeclaredMethod", + args = {java.lang.String.class, java.lang.Class[].class}, + clazz = java.lang.Class.class + ) + public void test_getDeclaredMethod() throws NoSuchMethodException, SecurityException { + // Check that getDeclaredMethod treats null parameterTypes the same as an empty array. + Method m1 = TestMethod.class.getDeclaredMethod("invokeInstanceTest", new Class[0]); + Method m2 = TestMethod.class.getDeclaredMethod("invokeInstanceTest", (Class[]) null); + assertEquals(m1, m2); + } + /** * @tests java.lang.reflect.Method#getDeclaringClass() */ diff --git a/libcore/luni/src/test/java/tests/api/java/lang/reflect/ParameterizedTypeTest.java b/libcore/luni/src/test/java/tests/api/java/lang/reflect/ParameterizedTypeTest.java index eaff7c8b6..bf3698e98 100644 --- a/libcore/luni/src/test/java/tests/api/java/lang/reflect/ParameterizedTypeTest.java +++ b/libcore/luni/src/test/java/tests/api/java/lang/reflect/ParameterizedTypeTest.java @@ -55,8 +55,6 @@ public class ParameterizedTypeTest extends GenericReflectionTestsBase { args = {} ) }) - @KnownFailure("Class A can not be found, " - + "maybe the wrong class loader is used to get the raw type?") public void testStringParameterizedSuperClass() { Class clazz = B.class; Type genericSuperclass = clazz.getGenericSuperclass(); @@ -93,8 +91,6 @@ public class ParameterizedTypeTest extends GenericReflectionTestsBase { args = {} ) }) - @KnownFailure("Class C can not be found, " - + "maybe the wrong class loader is used to get the raw type?") public void testTypeParameterizedSuperClass() { Class clazz = D.class; Type genericSuperclass = clazz.getGenericSuperclass(); diff --git a/libcore/luni/src/test/java/tests/api/java/net/JarURLConnectionTest.java b/libcore/luni/src/test/java/tests/api/java/net/JarURLConnectionTest.java index 8303db357..7a67bf848 100644 --- a/libcore/luni/src/test/java/tests/api/java/net/JarURLConnectionTest.java +++ b/libcore/luni/src/test/java/tests/api/java/net/JarURLConnectionTest.java @@ -34,6 +34,8 @@ import java.security.cert.Certificate; import java.util.Collection; import java.util.Map; import java.util.Set; +import java.util.Arrays; +import java.util.HashSet; import java.util.jar.Attributes; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -151,8 +153,7 @@ public class JarURLConnectionTest extends junit.framework.TestCase { args = {} ) public void test_getManifest() throws Exception { - - String [] expected = {"plus.bmp", "swt.dll"}; + //URL u = new URL("jar:" // + BASE.toString()+"/lf.jar!/swt.dll"); @@ -161,15 +162,9 @@ public class JarURLConnectionTest extends junit.framework.TestCase { juc = (JarURLConnection) u.openConnection(); Manifest manifest = juc.getManifest(); Map attr = manifest.getEntries(); - assertEquals(expected.length, attr.size()); - Set keys = attr.keySet(); - String [] result = new String[expected.length]; - keys.toArray(result); + assertEquals(new HashSet(Arrays.asList("plus.bmp", "swt.dll")), + attr.keySet()); - for(int i = 0; i < result.length; i++) { - assertEquals(expected[i], result[i]); - } - //URL invURL = new URL("jar:" // + BASE.toString()+"/InvalidJar.jar!/Test.class"); diff --git a/libcore/luni/src/test/java/tests/api/java/net/MulticastSocketTest.java b/libcore/luni/src/test/java/tests/api/java/net/MulticastSocketTest.java index 86e298c17..8c3e7f381 100644 --- a/libcore/luni/src/test/java/tests/api/java/net/MulticastSocketTest.java +++ b/libcore/luni/src/test/java/tests/api/java/net/MulticastSocketTest.java @@ -36,6 +36,9 @@ import java.net.SocketException; import java.net.UnknownHostException; import java.security.Permission; import java.util.Enumeration; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.TimeUnit; import tests.support.Support_NetworkInterface; import tests.support.Support_PortManager; @@ -64,23 +67,24 @@ public class MulticastSocketTest extends SocketTestCase { public MulticastSocket ms; - boolean running = true; + volatile boolean running = true; - volatile public byte[] rbuf = new byte[512]; - - volatile DatagramPacket rdp = null; + private final BlockingQueue queue + = new ArrayBlockingQueue(1); public void run() { try { while (running) { try { + byte[] rbuf = new byte[512]; + rbuf[0] = -1; + DatagramPacket rdp = new DatagramPacket(rbuf, rbuf.length); ms.receive(rdp); + queue.put(rdp); } catch (java.io.InterruptedIOException e) { - Thread.yield(); + } catch (InterruptedException e) { } - ; } - ; } catch (java.io.IOException e) { System.out.println("Multicast server failed: " + e); } finally { @@ -93,15 +97,8 @@ public class MulticastSocketTest extends SocketTestCase { ms.leaveGroup(aGroup); } - public void stopServer() { - running = false; - } - public MulticastServer(InetAddress anAddress, int aPort) throws java.io.IOException { - rbuf = new byte[512]; - rbuf[0] = -1; - rdp = new DatagramPacket(rbuf, rbuf.length); ms = new MulticastSocket(aPort); ms.setSoTimeout(2000); ms.joinGroup(anAddress); @@ -109,13 +106,20 @@ public class MulticastSocketTest extends SocketTestCase { public MulticastServer(SocketAddress anAddress, int aPort, NetworkInterface netInterface) throws java.io.IOException { - rbuf = new byte[512]; - rbuf[0] = -1; - rdp = new DatagramPacket(rbuf, rbuf.length); ms = new MulticastSocket(aPort); ms.setSoTimeout(2000); ms.joinGroup(anAddress, netInterface); } + + public DatagramPacket receive() throws InterruptedException { + return queue.poll(1000, TimeUnit.MILLISECONDS); + } + + public void stopServer() throws InterruptedException { + running = false; + interrupt(); + join(); + } } /** @@ -458,7 +462,7 @@ public class MulticastSocketTest extends SocketTestCase { method = "joinGroup", args = {java.net.InetAddress.class} ) - public void test_joinGroupLjava_net_InetAddress() { + public void test_joinGroupLjava_net_InetAddress() throws InterruptedException { // Test for method void // java.net.MulticastSocket.joinGroup(java.net.InetAddress) String msg = null; @@ -476,8 +480,7 @@ public class MulticastSocketTest extends SocketTestCase { .length(), group, groupPort); mss.joinGroup(group); mss.send(sdp, (byte) 10); - Thread.sleep(1000); - + SecurityManager sm = new SecurityManager() { public void checkPermission(Permission perm) { @@ -514,8 +517,10 @@ public class MulticastSocketTest extends SocketTestCase { } catch (Exception e) { fail("Exception during joinGroup test: " + e.toString()); } - assertTrue("Group member did not recv data: ", new String(server.rdp - .getData(), 0, server.rdp.getLength()).equals(msg)); + DatagramPacket rdb = server.receive(); + + assertEquals("Group member did not recv data: ", msg, + new String(rdb.getData(), 0, rdb.getLength())); } @@ -531,6 +536,7 @@ public class MulticastSocketTest extends SocketTestCase { method = "joinGroup", args = {java.net.SocketAddress.class, java.net.NetworkInterface.class} ) + @KnownFailure(value="bug 2155705") public void test_joinGroupLjava_net_SocketAddressLjava_net_NetworkInterface() throws IOException, InterruptedException { // security manager that allows us to check that we only return the @@ -608,11 +614,11 @@ public class MulticastSocketTest extends SocketTestCase { .length(), group, serverPort); mss.setTimeToLive(2); mss.send(sdp); - Thread.sleep(1000); + DatagramPacket rdp = server.receive(); + // now vaildate that we received the data as expected - assertTrue("Group member did not recv data: ", new String( - server.rdp.getData(), 0, server.rdp.getLength()) - .equals(msg)); + assertEquals("Group member did not recv data: ", msg, + new String(rdp.getData(), 0, rdp.getLength())); server.stopServer(); // now validate that we handled the case were we join a @@ -633,11 +639,9 @@ public class MulticastSocketTest extends SocketTestCase { sdp = new DatagramPacket(msg.getBytes(), msg.length(), group2, serverPort); mss.send(sdp); - Thread.sleep(1000); - assertFalse( - "Group member received data when sent on different group: ", - new String(server.rdp.getData(), 0, server.rdp.getLength()) - .equals(msg)); + rdp = server.receive(); + assertNull("Group member received data when sent on different group", + rdp); server.stopServer(); // if there is more than one network interface then check that @@ -717,20 +721,20 @@ public class MulticastSocketTest extends SocketTestCase { sdp = new DatagramPacket(msg.getBytes(), msg.length(), group, serverPort); mss.send(sdp); - Thread.sleep(1000); + rdp = server.receive(); + if (thisInterface.equals(sendingInterface)) { - assertTrue( + assertEquals( "Group member did not recv data when " + - "bound on specific interface: ", - new String(server.rdp.getData(), 0, - server.rdp.getLength()).equals(msg)); + "bound on specific interface: ", msg, + new String(rdp.getData(), 0, rdp.getLength())); } else { assertFalse( "Group member received data on other " + "interface when only asked for it on one " + "interface: ", - new String(server.rdp.getData(), 0, - server.rdp.getLength()).equals(msg)); + new String(rdp.getData(), 0, + rdp.getLength()).equals(msg)); } server.stopServer(); @@ -829,6 +833,7 @@ public class MulticastSocketTest extends SocketTestCase { method = "leaveGroup", args = {java.net.SocketAddress.class, java.net.NetworkInterface.class} ) + @KnownFailure(value="bug 2155705") public void test_leaveGroupLjava_net_SocketAddressLjava_net_NetworkInterface() { // security manager that allows us to check that we only return the // addresses that we should @@ -942,7 +947,7 @@ public class MulticastSocketTest extends SocketTestCase { method = "send", args = {java.net.DatagramPacket.class, byte.class} ) - public void test_sendLjava_net_DatagramPacketB() { + public void test_sendLjava_net_DatagramPacketB() throws InterruptedException { // Test for method void // java.net.MulticastSocket.send(java.net.DatagramPacket, byte) @@ -1015,11 +1020,10 @@ public class MulticastSocketTest extends SocketTestCase { } catch(IOException ioe) { //expected } - - byte[] data = server.rdp.getData(); - int length = server.rdp.getLength(); - assertTrue("Failed to send data. Received " + length, new String(data, - 0, length).equals(msg)); + + DatagramPacket rdp = server.receive(); + assertEquals("Failed to send data. Received " + rdp.getLength(), msg, + new String(rdp.getData(), 0, rdp.getLength())); } /** @@ -1097,7 +1101,6 @@ public class MulticastSocketTest extends SocketTestCase { method = "setNetworkInterface", args = {java.net.NetworkInterface.class} ) - @KnownFailure("No interfaces if there's no debugger connected") public void test_setNetworkInterfaceLjava_net_NetworkInterface() throws IOException, InterruptedException { String msg = null; @@ -1162,24 +1165,13 @@ public class MulticastSocketTest extends SocketTestCase { DatagramPacket sdp = new DatagramPacket(theBytes, theBytes.length, group, serverPort); mss.send(sdp); - Thread.sleep(1000); - String receivedMessage = new String(server.rdp - .getData(), 0, server.rdp.getLength()); - assertTrue( - "Group member did not recv data when send on " + - "a specific interface: ", - receivedMessage.equals(msg)); - assertTrue( - "Datagram was not received from expected " + - "interface expected[" - + thisInterface - + "] got [" - + NetworkInterface - .getByInetAddress(server.rdp - .getAddress()) + "]", - NetworkInterface.getByInetAddress( - server.rdp.getAddress()).equals( - thisInterface)); + DatagramPacket rdp = server.receive(); + + String receivedMessage = new String(rdp.getData(), 0, rdp.getLength()); + assertEquals("Group member did not recv data when send on " + + "a specific interface: ", msg, receivedMessage); + assertEquals("Datagram was not received as expected.", + thisInterface, NetworkInterface.getByInetAddress(rdp.getAddress())); // stop the server server.stopServer(); @@ -1597,7 +1589,7 @@ public class MulticastSocketTest extends SocketTestCase { * Tears down the fixture, for example, close a network connection. This * method is called after a test is executed. */ - protected void tearDown() { + protected void tearDown() throws InterruptedException { if (t != null) t.interrupt(); diff --git a/libcore/luni/src/test/java/tests/api/java/net/SocketTest.java b/libcore/luni/src/test/java/tests/api/java/net/SocketTest.java index 3d6fbeb00..7cc31a0f4 100644 --- a/libcore/luni/src/test/java/tests/api/java/net/SocketTest.java +++ b/libcore/luni/src/test/java/tests/api/java/net/SocketTest.java @@ -158,7 +158,14 @@ public class SocketTest extends SocketTestCase { Socket socket = null; try { socket = new Socket(InetAddress.getByName(null), sport); - assertEquals(InetAddress.getByName("127.0.0.1"), socket.getLocalAddress()); + InetAddress address = socket.getLocalAddress(); + if (Boolean.getBoolean("java.net.preferIPv6Addresses")) { + assertTrue( + address.equals(InetAddress.getByName("::1")) || + address.equals(InetAddress.getByName("0:0:0:0:0:0:0:1"))); + } else { + assertEquals(address, InetAddress.getByName("127.0.0.1")); + } } finally { try { socket.close(); diff --git a/libcore/luni/src/test/java/tests/api/java/net/UnknownServiceExceptionTest.java b/libcore/luni/src/test/java/tests/api/java/net/UnknownServiceExceptionTest.java index 9fc287cd8..463532ffc 100644 --- a/libcore/luni/src/test/java/tests/api/java/net/UnknownServiceExceptionTest.java +++ b/libcore/luni/src/test/java/tests/api/java/net/UnknownServiceExceptionTest.java @@ -40,7 +40,7 @@ public class UnknownServiceExceptionTest extends junit.framework.TestCase { public void test_Constructor() { // Test for method java.net.UnknownServiceException() try { - new URL("file://moo.txt").openConnection().getOutputStream(); + new URL("file:///moo.txt").openConnection().getOutputStream(); } catch (UnknownServiceException e) { // correct return; diff --git a/libcore/luni/src/test/java/tests/api/java/util/AbstractSequentialListTest.java b/libcore/luni/src/test/java/tests/api/java/util/AbstractSequentialListTest.java index 169afe3af..e2c47b855 100644 --- a/libcore/luni/src/test/java/tests/api/java/util/AbstractSequentialListTest.java +++ b/libcore/luni/src/test/java/tests/api/java/util/AbstractSequentialListTest.java @@ -85,7 +85,7 @@ public class AbstractSequentialListTest extends TestCase { } public boolean hasNext() { - return false; + return true; } public boolean hasPrevious() { diff --git a/libcore/luni/src/test/java/tests/api/java/util/BitSetTest.java b/libcore/luni/src/test/java/tests/api/java/util/BitSetTest.java index c0e6f2a39..34ec49a65 100644 --- a/libcore/luni/src/test/java/tests/api/java/util/BitSetTest.java +++ b/libcore/luni/src/test/java/tests/api/java/util/BitSetTest.java @@ -252,7 +252,7 @@ public class BitSetTest extends junit.framework.TestCase { initialSize = bs.size(); bs.set(0, initialSize); bs.clear(7, 64); - assertEquals("Failed to grow BitSet", 128, bs.size()); + assertEquals("Failed to grow BitSet", 64, bs.size()); for (int i = 0; i < 7; i++) assertTrue("Shouldn't have cleared bit " + i, bs.get(i)); for (int i = 7; i < 64; i++) @@ -624,7 +624,7 @@ public class BitSetTest extends junit.framework.TestCase { // pos1 and pos2 is in the same bitset element, boundary testing bs = new BitSet(16); bs.set(7, 64); - assertEquals("Failed to grow BitSet", 128, bs.size()); + assertEquals("Failed to grow BitSet", 64, bs.size()); for (int i = 0; i < 7; i++) assertTrue("Shouldn't have set bit " + i, !bs.get(i)); for (int i = 7; i < 64; i++) @@ -858,7 +858,7 @@ public class BitSetTest extends junit.framework.TestCase { bs.set(7); bs.set(10); bs.flip(7, 64); - assertEquals("Failed to grow BitSet", 128, bs.size()); + assertEquals("Failed to grow BitSet", 64, bs.size()); for (int i = 0; i < 7; i++) assertTrue("Shouldn't have flipped bit " + i, !bs.get(i)); assertTrue("Failed to flip bit 7", !bs.get(7)); diff --git a/libcore/luni/src/test/java/tests/api/java/util/CollectionsTest.java b/libcore/luni/src/test/java/tests/api/java/util/CollectionsTest.java index b75898b71..2b1b47e96 100644 --- a/libcore/luni/src/test/java/tests/api/java/util/CollectionsTest.java +++ b/libcore/luni/src/test/java/tests/api/java/util/CollectionsTest.java @@ -1435,11 +1435,7 @@ public class CollectionsTest extends junit.framework.TestCase { .indexOfSubList(src, sub2)); } - /** - * @param string2 - * @param string1 - * @param index - */ + private void testwithCharList(int count, String string1, String string2, boolean first) { char[] chars = string1.toCharArray(); @@ -2379,9 +2375,11 @@ public class CollectionsTest extends junit.framework.TestCase { m.put("one", "1"); m.put("two", "2"); Map um = Collections.unmodifiableMap(m); - assertEquals("{one=1, two=2}", um.toString()); + assertTrue("{one=1, two=2}".equals(um.toString()) || + "{two=2, one=1}".equals(um.toString())); } + @TestTargetNew( level = TestLevel.COMPLETE, notes = "", diff --git a/libcore/luni/src/test/java/tests/api/java/util/FormatterTest.java b/libcore/luni/src/test/java/tests/api/java/util/FormatterTest.java index 636f1bdb4..b2030c96f 100644 --- a/libcore/luni/src/test/java/tests/api/java/util/FormatterTest.java +++ b/libcore/luni/src/test/java/tests/api/java/util/FormatterTest.java @@ -4705,7 +4705,7 @@ public class FormatterTest extends TestCase { bw.close(); readOnly = File.createTempFile("readonly", null); - readOnly.setReadOnly(); + assertTrue(readOnly.setReadOnly()); secret = File.createTempFile("secret", null); diff --git a/libcore/luni/src/test/java/tests/api/java/util/HashMapTest.java b/libcore/luni/src/test/java/tests/api/java/util/HashMapTest.java index 1d726ea47..af73c0ae1 100644 --- a/libcore/luni/src/test/java/tests/api/java/util/HashMapTest.java +++ b/libcore/luni/src/test/java/tests/api/java/util/HashMapTest.java @@ -20,17 +20,9 @@ package tests.api.java.util; import dalvik.annotation.TestTargetNew; import dalvik.annotation.TestTargets; import dalvik.annotation.TestLevel; -import dalvik.annotation.TestTargetClass; - -import java.util.AbstractMap; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; +import dalvik.annotation.TestTargetClass; + +import java.util.*; import tests.support.Support_MapTest2; import tests.support.Support_UnmodifiableCollectionTest; @@ -39,7 +31,7 @@ import tests.support.Support_UnmodifiableCollectionTest; public class HashMapTest extends junit.framework.TestCase { class MockMap extends AbstractMap { public Set entrySet() { - return null; + return Collections.EMPTY_SET; } public int size(){ return 0; @@ -151,14 +143,10 @@ public class HashMapTest extends junit.framework.TestCase { for (int counter = 0; counter < hmSize; counter++) assertTrue("Failed to construct correct HashMap", hm .get(objArray2[counter]) == hm2.get(objArray2[counter])); - - try { - Map mockMap = new MockMap(); - hm = new HashMap(mockMap); - fail("Should throw NullPointerException"); - } catch (NullPointerException e) { - //empty - } + + Map mockMap = new MockMap(); + hm = new HashMap(mockMap); + assertEquals(hm, mockMap); } /** @@ -295,6 +283,40 @@ public class HashMapTest extends junit.framework.TestCase { } } + /** + * @tests java.util.HashMap#entrySet() + */ + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "", + method = "entrySet", + args = {} + ) + public void test_entrySetEquals() { + Set s1 = hm.entrySet(); + Set s2 = new HashMap(hm).entrySet(); + assertEquals(s1, s2); + } + + /** + * @tests java.util.HashMap#entrySet() + */ + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "", + method = "entrySet", + args = {} + ) + public void test_removeFromViews() { + hm.put("A", null); + hm.put("B", null); + assertTrue(hm.keySet().remove("A")); + + Map m2 = new HashMap(); + m2.put("B", null); + assertTrue(hm.entrySet().remove(m2.entrySet().iterator().next())); + } + /** * @tests java.util.HashMap#get(java.lang.Object) */ @@ -479,7 +501,39 @@ public class HashMapTest extends junit.framework.TestCase { } catch (NullPointerException e) { // expected. } - } + } + + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "Checks putAll that causes map to resize", + method = "putAll", + args = {java.util.Map.class} + ) + public void test_putAllLjava_util_Map_Resize() { + Random rnd = new Random(666); + + Map m1 = new HashMap(); + int MID = 10000; + for (int i = 0; i < MID; i++) { + Integer j = rnd.nextInt(); + m1.put(j, j); + } + + Map m2 = new HashMap(); + int HI = 30000; + for (int i = MID; i < HI; i++) { + Integer j = rnd.nextInt(); + m2.put(j, j); + } + + m1.putAll(m2); + + rnd = new Random(666); + for (int i = 0; i < HI; i++) { + Integer j = rnd.nextInt(); + assertEquals(j, m1.get(j)); + } + } /** * @tests java.util.HashMap#remove(java.lang.Object) diff --git a/libcore/luni/src/test/java/tests/api/java/util/HashtableTest.java b/libcore/luni/src/test/java/tests/api/java/util/HashtableTest.java index bbdad509f..6c50f1bbe 100644 --- a/libcore/luni/src/test/java/tests/api/java/util/HashtableTest.java +++ b/libcore/luni/src/test/java/tests/api/java/util/HashtableTest.java @@ -35,6 +35,7 @@ import java.util.NoSuchElementException; import java.util.Set; import java.util.TreeMap; import java.util.Vector; +import java.util.Collections; import tests.api.java.util.HashMapTest.ReusableKey; import tests.support.Support_MapTest2; @@ -165,6 +166,24 @@ public class HashtableTest extends junit.framework.TestCase { } } + /** + * @tests java.util.Hashtable#Hashtable(java.util.Map) + */ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "Hashtable", + args = {java.util.Map.class} + ) + public void test_ConversionConstructorNullValue() { + Map map = Collections.singletonMap("Dog", null); + try { + new Hashtable(map); + fail("NullPointerException expected"); + } catch (NullPointerException e) { + //expected + } + } /** * @tests java.util.Hashtable#clear() */ @@ -310,46 +329,49 @@ public class HashtableTest extends junit.framework.TestCase { assertEquals("All keys not retrieved", 10, ht10.size()); } - /** - * @tests java.util.Hashtable#elements() - */ - @TestTargetNew( - level = TestLevel.COMPLETE, - notes = "", - method = "elements", - args = {} - ) - public void test_elements_subtest0() { - // this is the reference implementation behavior - final Hashtable ht = new Hashtable(7); - ht.put("1", "a"); - // these three elements hash to the same bucket in a 7 element Hashtable - ht.put("2", "b"); - ht.put("9", "c"); - ht.put("12", "d"); - // Hashtable looks like: - // 0: "1" - // 1: "12" -> "9" -> "2" - Enumeration en = ht.elements(); - // cache the first entry - en.hasMoreElements(); - ht.remove("12"); - ht.remove("9"); - boolean exception = false; - try { - // cached "12" - Object result = en.nextElement(); - assertNull("unexpected: " + result, result); - // next is removed "9" - result = en.nextElement(); - assertNull("unexpected: " + result, result); - result = en.nextElement(); - assertTrue("unexpected: " + result, "b".equals(result)); - } catch (NoSuchElementException e) { - exception = true; - } - assertTrue("unexpected NoSuchElementException", !exception); - } +// BEGIN android-removed +// implementation dependent +// /** +// * @tests java.util.Hashtable#elements() +// */ +// @TestTargetNew( +// level = TestLevel.COMPLETE, +// notes = "", +// method = "elements", +// args = {} +// ) +// public void test_elements_subtest0() { +// // this is the reference implementation behavior +// final Hashtable ht = new Hashtable(7); +// ht.put("1", "a"); +// // these three elements hash to the same bucket in a 7 element Hashtable +// ht.put("2", "b"); +// ht.put("9", "c"); +// ht.put("12", "d"); +// // Hashtable looks like: +// // 0: "1" +// // 1: "12" -> "9" -> "2" +// Enumeration en = ht.elements(); +// // cache the first entry +// en.hasMoreElements(); +// ht.remove("12"); +// ht.remove("9"); +// boolean exception = false; +// try { +// // cached "12" +// Object result = en.nextElement(); +// assertNull("unexpected: " + result, result); +// // next is removed "9" +// result = en.nextElement(); +// assertNull("unexpected: " + result, result); +// result = en.nextElement(); +// assertTrue("unexpected: " + result, "b".equals(result)); +// } catch (NoSuchElementException e) { +// exception = true; +// } +// assertTrue("unexpected NoSuchElementException", !exception); +// } +// END android-removed /** * @tests java.util.Hashtable#entrySet() @@ -371,9 +393,11 @@ public class HashtableTest extends junit.framework.TestCase { while (e.hasMoreElements()) assertTrue("Returned incorrect entry set", s2.contains(e .nextElement())); - - assertEquals("Not synchronized", - "java.util.Collections$SynchronizedSet", s.getClass().getName()); +// BEGIN android-removed +// implementation dependent +// assertEquals("Not synchronized", +// "java.util.Collections$SynchronizedSet", s.getClass().getName()); +// END android-removed boolean exception = false; try { @@ -417,26 +441,28 @@ public class HashtableTest extends junit.framework.TestCase { Hashtable h = hashtableClone(htfull); assertEquals("Could not retrieve element", "FVal 2", ((String) h.get("FKey 2")) ); - - - // Regression for HARMONY-262 - ReusableKey k = new ReusableKey(); - Hashtable h2 = new Hashtable(); - k.setKey(1); - h2.put(k, "value1"); - - k.setKey(13); - assertNull(h2.get(k)); - - k.setKey(12); - assertNull(h2.get(k)); - try { - h2.get(null); - fail("NullPointerException expected"); - } catch (NullPointerException e) { - //expected - } +// BEGIN android-removed +// implementation dependent +// // Regression for HARMONY-262 +// ReusableKey k = new ReusableKey(); +// Hashtable h2 = new Hashtable(); +// k.setKey(1); +// h2.put(k, "value1"); +// +// k.setKey(13); +// assertNull(h2.get(k)); +// +// k.setKey(12); +// assertNull(h2.get(k)); +// +// try { +// h2.get(null); +// fail("NullPointerException expected"); +// } catch (NullPointerException e) { +// //expected +// } +// END android-removed } /** @@ -569,9 +595,12 @@ public class HashtableTest extends junit.framework.TestCase { assertTrue("Returned incorrect key set", s .contains(e.nextElement())); - assertEquals("Not synchronized", - "java.util.Collections$SynchronizedSet", s.getClass().getName()); - +// BEGIN android-removed +// implementation dependent +// assertEquals("Not synchronized", +// "java.util.Collections$SynchronizedSet", s.getClass().getName()); +// END android-removed + Map map = new Hashtable(101); map.put(new Integer(1), "1"); map.put(new Integer(102), "102"); @@ -651,54 +680,57 @@ public class HashtableTest extends junit.framework.TestCase { } } - /** - * @tests java.util.Hashtable#keySet() - */ - @TestTargetNew( - level = TestLevel.PARTIAL_COMPLETE, - notes = "", - method = "keySet", - args = {} - ) - public void test_keySet_subtest1() { - // this is the reference implementation behavior - final Hashtable ht = new Hashtable(7); - ht.put("1", "a"); - // these three elements hash to the same bucket in a 7 element Hashtable - ht.put("2", "b"); - ht.put("9", "c"); - ht.put("12", "d"); - // Hashtable looks like: - // 0: "1" - // 1: "12" -> "9" -> "2" - Enumeration en = ht.elements(); - // cache the first entry - en.hasMoreElements(); - Iterator it = ht.keySet().iterator(); - // this is mostly a copy of the test in test_elements_subtest0() - // test removing with the iterator does not null the values - while (it.hasNext()) { - String key = (String) it.next(); - if ("12".equals(key) || "9".equals(key)) { - it.remove(); - } - } - it.remove(); - boolean exception = false; - try { - // cached "12" - Object result = en.nextElement(); - assertTrue("unexpected: " + result, "d".equals(result)); - // next is removed "9" - result = en.nextElement(); - assertTrue("unexpected: " + result, "c".equals(result)); - result = en.nextElement(); - assertTrue("unexpected: " + result, "b".equals(result)); - } catch (NoSuchElementException e) { - exception = true; - } - assertTrue("unexpected NoSuchElementException", !exception); - } +// BEGIN android-removed +// implementation dependent +// /** +// * @tests java.util.Hashtable#keySet() +// */ +// @TestTargetNew( +// level = TestLevel.PARTIAL_COMPLETE, +// notes = "", +// method = "keySet", +// args = {} +// ) +// public void test_keySet_subtest1() { +// // this is the reference implementation behavior +// final Hashtable ht = new Hashtable(7); +// ht.put("1", "a"); +// // these three elements hash to the same bucket in a 7 element Hashtable +// ht.put("2", "b"); +// ht.put("9", "c"); +// ht.put("12", "d"); +// // Hashtable looks like: +// // 0: "1" +// // 1: "12" -> "9" -> "2" +// Enumeration en = ht.elements(); +// // cache the first entry +// en.hasMoreElements(); +// Iterator it = ht.keySet().iterator(); +// // this is mostly a copy of the test in test_elements_subtest0() +// // test removing with the iterator does not null the values +// while (it.hasNext()) { +// String key = (String) it.next(); +// if ("12".equals(key) || "9".equals(key)) { +// it.remove(); +// } +// } +// it.remove(); +// boolean exception = false; +// try { +// // cached "12" +// Object result = en.nextElement(); +// assertTrue("unexpected: " + result, "d".equals(result)); +// // next is removed "9" +// result = en.nextElement(); +// assertTrue("unexpected: " + result, "c".equals(result)); +// result = en.nextElement(); +// assertTrue("unexpected: " + result, "b".equals(result)); +// } catch (NoSuchElementException e) { +// exception = true; +// } +// assertTrue("unexpected NoSuchElementException", !exception); +// } +// END android-removed /** * @tests java.util.Hashtable#put(java.lang.Object, java.lang.Object) @@ -871,9 +903,12 @@ public class HashtableTest extends junit.framework.TestCase { while (e.hasMoreElements()) assertTrue("Returned incorrect values", c.contains(e.nextElement())); - assertEquals("Not synchronized", - "java.util.Collections$SynchronizedCollection", c.getClass().getName()); - +// BEGIN android-removed +// implementation dependent +// assertEquals("Not synchronized", +// "java.util.Collections$SynchronizedCollection", c.getClass().getName()); +// END android-removed + Hashtable myHashtable = new Hashtable(); for (int i = 0; i < 100; i++) myHashtable.put(new Integer(i), new Integer(i)); diff --git a/libcore/luni/src/test/java/tests/api/java/util/LinkedHashMapTest.java b/libcore/luni/src/test/java/tests/api/java/util/LinkedHashMapTest.java index bfa474bf5..d5223fee2 100644 --- a/libcore/luni/src/test/java/tests/api/java/util/LinkedHashMapTest.java +++ b/libcore/luni/src/test/java/tests/api/java/util/LinkedHashMapTest.java @@ -20,7 +20,7 @@ package tests.api.java.util; import dalvik.annotation.TestTargetNew; import dalvik.annotation.TestTargets; import dalvik.annotation.TestLevel; -import dalvik.annotation.TestTargetClass; +import dalvik.annotation.TestTargetClass; import java.util.AbstractMap; import java.util.ArrayList; @@ -38,7 +38,7 @@ import tests.support.Support_UnmodifiableCollectionTest; /** * @tests java.util.LinkedHashMap */ -@TestTargetClass(LinkedHashMap.class) +@TestTargetClass(LinkedHashMap.class) public class LinkedHashMapTest extends junit.framework.TestCase { LinkedHashMap hm; @@ -48,13 +48,13 @@ public class LinkedHashMapTest extends junit.framework.TestCase { Object[] objArray; Object[] objArray2; - + static final class CacheMap extends LinkedHashMap { protected boolean removeEldestEntry(Map.Entry e) { return size() > 5; } } - + private static class MockMapNull extends AbstractMap { @Override public Set entrySet() { @@ -131,7 +131,7 @@ public class LinkedHashMapTest extends junit.framework.TestCase { } catch (IllegalArgumentException e) { //expected } - + LinkedHashMap empty = new LinkedHashMap(0, 0.75f); assertNull("Empty hashtable access", empty.get("nothing")); empty.put("something", "here"); @@ -196,7 +196,7 @@ public class LinkedHashMapTest extends junit.framework.TestCase { // Test for method java.lang.Object // java.util.LinkedHashMap.put(java.lang.Object, java.lang.Object) hm.put("KEY", "VALUE"); - assertEquals("Failed to install key/value pair", + assertEquals("Failed to install key/value pair", "VALUE", hm.get("KEY")); LinkedHashMap m = new LinkedHashMap(); @@ -209,6 +209,26 @@ public class LinkedHashMapTest extends junit.framework.TestCase { new Integer(0))); } + + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "test that put on an already present key causes entry to move to tail.", + method = "put", + args = {java.lang.Object.class, java.lang.Object.class} + ) + public void test_putPresent() { + Map m = new LinkedHashMap(8, .75f, true); + m.put("KEY", "VALUE"); + m.put("WOMBAT", "COMBAT"); + m.put("KEY", "VALUE"); + Map.Entry newest = null; + for (Map.Entry e : m.entrySet()) { + newest = e; + } + assertEquals("KEY", newest.getKey()); + assertEquals("VALUE", newest.getValue()); + } + /** * @tests java.util.LinkedHashMap#putAll(java.util.Map) */ @@ -217,7 +237,7 @@ public class LinkedHashMapTest extends junit.framework.TestCase { notes = "", method = "putAll", args = {java.util.Map.class} - ) + ) public void test_putAllLjava_util_Map() { // Test for method void java.util.LinkedHashMap.putAll(java.util.Map) LinkedHashMap hm2 = new LinkedHashMap(); @@ -251,7 +271,7 @@ public class LinkedHashMapTest extends junit.framework.TestCase { } catch (NullPointerException e) { // expected. } - } + } /** * @tests java.util.LinkedHashMap#entrySet() @@ -275,6 +295,26 @@ public class LinkedHashMapTest extends junit.framework.TestCase { } } + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Test that remove removes the entry from the linked list", + method = "entrySet", + args = {} + ) + public void test_entrySetRemove() { + entrySetRemoveHelper("military", "intelligence"); + entrySetRemoveHelper(null, "hypothesis"); + } + private void entrySetRemoveHelper(String key, String value) { + Map m1 = new LinkedHashMap(); + m1.put(key, value); + m1.put("jumbo", "shrimp"); + LinkedHashMap m2 = new LinkedHashMap(m1); + Set> s1 = m1.entrySet(); + s1.remove(m2.entrySet().iterator().next()); + assertEquals("jumbo", s1.iterator().next().getKey()); + } + /** * @tests java.util.LinkedHashMap#keySet() */ @@ -435,23 +475,23 @@ public class LinkedHashMapTest extends junit.framework.TestCase { // get the keySet() and values() on the original Map Set keys = map.keySet(); Collection values = map.values(); - assertEquals("values() does not work", + assertEquals("values() does not work", "value", values.iterator().next()); - assertEquals("keySet() does not work", + assertEquals("keySet() does not work", "key", keys.iterator().next()); AbstractMap map2 = (AbstractMap) map.clone(); map2.put("key", "value2"); Collection values2 = map2.values(); assertTrue("values() is identical", values2 != values); - + // values() and keySet() on the cloned() map should be different - assertEquals("values() was not cloned", + assertEquals("values() was not cloned", "value2", values2.iterator().next()); map2.clear(); map2.put("key2", "value3"); Set key2 = map2.keySet(); assertTrue("keySet() is identical", key2 != keys); - assertEquals("keySet() was not cloned", + assertEquals("keySet() was not cloned", "key2", key2.iterator().next()); } @@ -472,10 +512,10 @@ public class LinkedHashMapTest extends junit.framework.TestCase { hm1.put("c", "c"); LinkedHashMap hm2 = (LinkedHashMap) hm1.clone(); hm1.get("a"); - + Map.Entry[] set = new Map.Entry[3]; Iterator> iterator = hm1.entrySet().iterator(); - + assertEquals("b", iterator.next().getKey()); assertEquals("c", iterator.next().getKey()); assertEquals("a", iterator.next().getKey()); @@ -491,7 +531,7 @@ public class LinkedHashMapTest extends junit.framework.TestCase { notes = "Regression test.", method = "clone", args = {} - ) + ) // regresion test for HARMONY-4603 public void test_clone_Mock() { LinkedHashMap hashMap = new MockMap(); @@ -517,7 +557,30 @@ public class LinkedHashMapTest extends junit.framework.TestCase { protected boolean removeEldestEntry(Map.Entry e) { return num > 1; } - } + } + + /** + * @tests put/get interaction in access-order map where removeEldest + * returns true. + */ + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "Regression for bug Google Bug 2121546", + method = "get", + args = {java.lang.Object.class} + ) + public void test_removeEldestFromSameBucketAsNewEntry() { + LinkedHashMap map + = new LinkedHashMap(6, 0.75F, true) { + @Override + protected boolean removeEldestEntry(Entry e) { + return true; + } + }; + map.put("N", "E"); + map.put("F", "I"); + assertEquals(null, map.get("N")); + } /** * @tests java.util.LinkedHashMap#containsKey(java.lang.Object) diff --git a/libcore/luni/src/test/java/tests/api/java/util/TreeMapTest.java b/libcore/luni/src/test/java/tests/api/java/util/TreeMapTest.java index 7ea4c9e56..0e7f6a187 100644 --- a/libcore/luni/src/test/java/tests/api/java/util/TreeMapTest.java +++ b/libcore/luni/src/test/java/tests/api/java/util/TreeMapTest.java @@ -83,6 +83,9 @@ public class TreeMapTest extends junit.framework.TestCase { if (null == o1) { return -1; } + if (null == o2) { + return 1; + } return o1.compareTo(o2); } } @@ -844,8 +847,6 @@ public class TreeMapTest extends junit.framework.TestCase { method = "equals", args = {java.lang.Object.class} ) - @KnownFailure("equals(Onject o) method throws java.lang.ClassCastException " + - "for TreeMap objects with different key objects.") public void test_equals() throws Exception { // comparing TreeMaps with different object types Map m1 = new TreeMap(); diff --git a/libcore/math/src/main/java/java/math/BigDecimal.java b/libcore/math/src/main/java/java/math/BigDecimal.java index 6fa1f34aa..6c0056047 100644 --- a/libcore/math/src/main/java/java/math/BigDecimal.java +++ b/libcore/math/src/main/java/java/math/BigDecimal.java @@ -25,51 +25,48 @@ import java.io.Serializable; import org.apache.harmony.math.internal.nls.Messages; /** - * This class represents immutable arbitrary precision decimal numbers. Each - * {@code BigDecimal} instance is represented with a unscaled arbitrary - * precision mantissa (the unscaled value) and a scale. The value of the {@code - * BigDecimal} is {@code unscaledValue} 10^(-{@code scale}). - * - * @since Android 1.0 + * This class represents immutable integer numbers of arbitrary length. Large + * numbers are typically used in security applications and therefore BigIntegers + * offer dedicated functionality like the generation of large prime numbers or + * the computation of modular inverse. + *

          + * Since the class was modeled to offer all the functionality as the {@link Integer} + * class does, it provides even methods that operate bitwise on a two's + * complement representation of large integers. Note however that the + * implementations favors an internal representation where magnitude and sign + * are treated separately. Hence such operations are inefficient and should be + * discouraged. In simple words: Do NOT implement any bit fields based on + * BigInteger. */ public class BigDecimal extends Number implements Comparable, Serializable { - /* Static Fields */ /** * The constant zero as a {@code BigDecimal}. - * - * @since Android 1.0 */ public static final BigDecimal ZERO = new BigDecimal(0, 0); /** * The constant one as a {@code BigDecimal}. - * - * @since Android 1.0 */ public static final BigDecimal ONE = new BigDecimal(1, 0); /** * The constant ten as a {@code BigDecimal}. - * - * @since Android 1.0 */ public static final BigDecimal TEN = new BigDecimal(10, 0); /** * Rounding mode where positive values are rounded towards positive infinity * and negative values towards negative infinity. - * + * * @see RoundingMode#UP - * @since Android 1.0 */ public static final int ROUND_UP = 0; /** * Rounding mode where the values are rounded towards zero. - * + * * @see RoundingMode#DOWN - * @since Android 1.0 */ public static final int ROUND_DOWN = 1; @@ -77,9 +74,8 @@ public class BigDecimal extends Number implements Comparable, Serial * Rounding mode to round towards positive infinity. For positive values * this rounding mode behaves as {@link #ROUND_UP}, for negative values as * {@link #ROUND_DOWN}. - * + * * @see RoundingMode#CEILING - * @since Android 1.0 */ public static final int ROUND_CEILING = 2; @@ -87,36 +83,32 @@ public class BigDecimal extends Number implements Comparable, Serial * Rounding mode to round towards negative infinity. For positive values * this rounding mode behaves as {@link #ROUND_DOWN}, for negative values as * {@link #ROUND_UP}. - * + * * @see RoundingMode#FLOOR - * @since Android 1.0 */ public static final int ROUND_FLOOR = 3; /** * Rounding mode where values are rounded towards the nearest neighbor. * Ties are broken by rounding up. - * + * * @see RoundingMode#HALF_UP - * @since Android 1.0 */ public static final int ROUND_HALF_UP = 4; /** * Rounding mode where values are rounded towards the nearest neighbor. * Ties are broken by rounding down. - * + * * @see RoundingMode#HALF_DOWN - * @since Android 1.0 */ public static final int ROUND_HALF_DOWN = 5; /** * Rounding mode where values are rounded towards the nearest neighbor. * Ties are broken by rounding to the even neighbor. - * + * * @see RoundingMode#HALF_EVEN - * @since Android 1.0 */ public static final int ROUND_HALF_EVEN = 6; @@ -124,14 +116,11 @@ public class BigDecimal extends Number implements Comparable, Serial * Rounding mode where the rounding operations throws an {@code * ArithmeticException} for the case that rounding is necessary, i.e. for * the case that the value cannot be represented exactly. - * + * * @see RoundingMode#UNNECESSARY - * @since Android 1.0 */ public static final int ROUND_UNNECESSARY = 7; - /* Private Fields */ - /** This is the serialVersionUID used by the sun implementation. */ private static final long serialVersionUID = 6108874887143696463L; @@ -180,8 +169,8 @@ public class BigDecimal extends Number implements Comparable, Serial 10000000000000000L, 100000000000000000L, 1000000000000000000L, }; - - + + private static final long[] LONG_FIVE_POW = new long[] { 1L, 5L, @@ -211,10 +200,10 @@ public class BigDecimal extends Number implements Comparable, Serial 298023223876953125L, 1490116119384765625L, 7450580596923828125L, }; - + private static final int[] LONG_FIVE_POW_BIT_LENGTH = new int[LONG_FIVE_POW.length]; private static final int[] LONG_TEN_POW_BIT_LENGTH = new int[LONG_TEN_POW.length]; - + private static final int BI_SCALED_BY_ZERO_LENGTH = 11; /** @@ -241,7 +230,7 @@ public class BigDecimal extends Number implements Comparable, Serial ZERO_SCALED_BY[i] = new BigDecimal(0, i); CH_ZEROS[i] = '0'; } - + for (; i < CH_ZEROS.length; i++) { CH_ZEROS[i] = '0'; } @@ -251,7 +240,7 @@ public class BigDecimal extends Number implements Comparable, Serial for(int j=0; j, Serial * representation of {@code BigDecimal}. */ private BigInteger intVal; - + private transient int bitLength; - + private transient long smallValue; - /** - * The 32-bit integer scale in the internal representation of {@code BigDecimal}. + /** + * The 32-bit integer scale in the internal representation of {@code BigDecimal}. */ private int scale; @@ -277,20 +266,18 @@ public class BigDecimal extends Number implements Comparable, Serial * precision is calculated the first time, and used in the following calls * of method precision(). Note that some call to the private * method inplaceRound() could update this field. - * + * * @see #precision() * @see #inplaceRound(MathContext) */ private transient int precision = 0; - /* Constructors */ - private BigDecimal(long smallValue, int scale){ this.smallValue = smallValue; this.scale = scale; this.bitLength = bitLength(smallValue); } - + private BigDecimal(int smallValue, int scale){ this.smallValue = smallValue; this.scale = scale; @@ -300,7 +287,7 @@ public class BigDecimal extends Number implements Comparable, Serial /** * Constructs a new {@code BigDecimal} instance from a string representation * given as a character array. - * + * * @param in * array of characters containing the string representation of * this {@code BigDecimal}. @@ -316,7 +303,6 @@ public class BigDecimal extends Number implements Comparable, Serial * @throws NumberFormatException * if in does not contain a valid string representation of a big * decimal. - * @since Android 1.0 */ public BigDecimal(char[] in, int offset, int len) { int begin = offset; // first index to be copied @@ -347,9 +333,9 @@ public class BigDecimal extends Number implements Comparable, Serial if (in[offset] == '0') { counter++; } else { - wasNonZero = true; - } - }; + wasNonZero = true; + } + } } unscaledBuffer.append(in, begin, offset - begin); @@ -365,9 +351,9 @@ public class BigDecimal extends Number implements Comparable, Serial if (in[offset] == '0') { counter++; } else { - wasNonZero = true; - } - }; + wasNonZero = true; + } + } } scale = offset - begin; bufLength +=scale; @@ -388,7 +374,7 @@ public class BigDecimal extends Number implements Comparable, Serial } // Accumulating all remaining digits scaleString = String.valueOf(in, begin, last + 1 - begin); - // Checking if the scale is defined + // Checking if the scale is defined newScale = (long)scale - Integer.parseInt(scaleString); scale = (int)newScale; if (newScale != scale) { @@ -402,17 +388,17 @@ public class BigDecimal extends Number implements Comparable, Serial bitLength = bitLength(smallValue); } else { setUnscaledValue(new BigInteger(unscaledBuffer.toString())); - } + } precision = unscaledBuffer.length() - counter; if (unscaledBuffer.charAt(0) == '-') { precision --; - } + } } /** * Constructs a new {@code BigDecimal} instance from a string representation * given as a character array. - * + * * @param in * array of characters containing the string representation of * this {@code BigDecimal}. @@ -434,7 +420,6 @@ public class BigDecimal extends Number implements Comparable, Serial * if {@code mc.precision > 0} and {@code mc.roundingMode == * UNNECESSARY} and the new big decimal cannot be represented * within the given precision without rounding. - * @since Android 1.0 */ public BigDecimal(char[] in, int offset, int len, MathContext mc) { this(in, offset, len); @@ -444,7 +429,7 @@ public class BigDecimal extends Number implements Comparable, Serial /** * Constructs a new {@code BigDecimal} instance from a string representation * given as a character array. - * + * * @param in * array of characters containing the string representation of * this {@code BigDecimal}. @@ -453,7 +438,6 @@ public class BigDecimal extends Number implements Comparable, Serial * @throws NumberFormatException * if {@code in} does not contain a valid string representation * of a big decimal. - * @since Android 1.0 */ public BigDecimal(char[] in) { this(in, 0, in.length); @@ -463,7 +447,7 @@ public class BigDecimal extends Number implements Comparable, Serial * Constructs a new {@code BigDecimal} instance from a string representation * given as a character array. The result is rounded according to the * specified math context. - * + * * @param in * array of characters containing the string representation of * this {@code BigDecimal}. @@ -478,7 +462,6 @@ public class BigDecimal extends Number implements Comparable, Serial * if {@code mc.precision > 0} and {@code mc.roundingMode == * UNNECESSARY} and the new big decimal cannot be represented * within the given precision without rounding. - * @since Android 1.0 */ public BigDecimal(char[] in, MathContext mc) { this(in, 0, in.length); @@ -488,14 +471,13 @@ public class BigDecimal extends Number implements Comparable, Serial /** * Constructs a new {@code BigDecimal} instance from a string * representation. - * + * * @param val * string containing the string representation of this {@code * BigDecimal}. * @throws NumberFormatException * if {@code val} does not contain a valid string representation * of a big decimal. - * @since Android 1.0 */ public BigDecimal(String val) { this(val.toCharArray(), 0, val.length()); @@ -505,7 +487,7 @@ public class BigDecimal extends Number implements Comparable, Serial * Constructs a new {@code BigDecimal} instance from a string * representation. The result is rounded according to the specified math * context. - * + * * @param val * string containing the string representation of this {@code * BigDecimal}. @@ -518,7 +500,6 @@ public class BigDecimal extends Number implements Comparable, Serial * if {@code mc.precision > 0} and {@code mc.roundingMode == * UNNECESSARY} and the new big decimal cannot be represented * within the given precision without rounding. - * @since Android 1.0 */ public BigDecimal(String val, MathContext mc) { this(val.toCharArray(), 0, val.length()); @@ -534,12 +515,11 @@ public class BigDecimal extends Number implements Comparable, Serial *

          * To generate a big decimal instance which is equivalent to {@code 0.1} use * the {@code BigDecimal(String)} constructor. - * + * * @param val * double value to be converted to a {@code BigDecimal} instance. * @throws NumberFormatException * if {@code val} is infinity or not a number. - * @since Android 1.0 */ public BigDecimal(double val) { if (Double.isInfinite(val) || Double.isNaN(val)) { @@ -558,7 +538,7 @@ public class BigDecimal extends Number implements Comparable, Serial scale = 0; precision = 1; } - // To simplify all factors '2' in the mantisa + // To simplify all factors '2' in the mantisa if (scale > 0) { trailingZeros = Math.min(scale, Long.numberOfTrailingZeros(mantisa)); mantisa >>>= trailingZeros; @@ -606,7 +586,7 @@ public class BigDecimal extends Number implements Comparable, Serial *

          * To generate a big decimal instance which is equivalent to {@code 0.1} use * the {@code BigDecimal(String)} constructor. - * + * * @param val * double value to be converted to a {@code BigDecimal} instance. * @param mc @@ -617,7 +597,6 @@ public class BigDecimal extends Number implements Comparable, Serial * if {@code mc.precision > 0} and {@code mc.roundingMode == * UNNECESSARY} and the new big decimal cannot be represented * within the given precision without rounding. - * @since Android 1.0 */ public BigDecimal(double val, MathContext mc) { this(val); @@ -627,11 +606,10 @@ public class BigDecimal extends Number implements Comparable, Serial /** * Constructs a new {@code BigDecimal} instance from the given big integer * {@code val}. The scale of the result is {@code 0}. - * + * * @param val * {@code BigInteger} value to be converted to a {@code * BigDecimal} instance. - * @since Android 1.0 */ public BigDecimal(BigInteger val) { this(val, 0); @@ -640,7 +618,7 @@ public class BigDecimal extends Number implements Comparable, Serial /** * Constructs a new {@code BigDecimal} instance from the given big integer * {@code val}. The scale of the result is {@code 0}. - * + * * @param val * {@code BigInteger} value to be converted to a {@code * BigDecimal} instance. @@ -650,7 +628,6 @@ public class BigDecimal extends Number implements Comparable, Serial * if {@code mc.precision > 0} and {@code mc.roundingMode == * UNNECESSARY} and the new big decimal cannot be represented * within the given precision without rounding. - * @since Android 1.0 */ public BigDecimal(BigInteger val, MathContext mc) { this(val); @@ -661,7 +638,7 @@ public class BigDecimal extends Number implements Comparable, Serial * Constructs a new {@code BigDecimal} instance from a given unscaled value * {@code unscaledVal} and a given scale. The value of this instance is * {@code unscaledVal} 10^(-{@code scale}). - * + * * @param unscaledVal * {@code BigInteger} representing the unscaled value of this * {@code BigDecimal} instance. @@ -669,7 +646,6 @@ public class BigDecimal extends Number implements Comparable, Serial * scale of this {@code BigDecimal} instance. * @throws NullPointerException * if {@code unscaledVal == null}. - * @since Android 1.0 */ public BigDecimal(BigInteger unscaledVal, int scale) { if (unscaledVal == null) { @@ -684,7 +660,7 @@ public class BigDecimal extends Number implements Comparable, Serial * {@code unscaledVal} and a given scale. The value of this instance is * {@code unscaledVal} 10^(-{@code scale}). The result is rounded according * to the specified math context. - * + * * @param unscaledVal * {@code BigInteger} representing the unscaled value of this * {@code BigDecimal} instance. @@ -698,7 +674,6 @@ public class BigDecimal extends Number implements Comparable, Serial * within the given precision without rounding. * @throws NullPointerException * if {@code unscaledVal == null}. - * @since Android 1.0 */ public BigDecimal(BigInteger unscaledVal, int scale, MathContext mc) { this(unscaledVal, scale); @@ -708,10 +683,9 @@ public class BigDecimal extends Number implements Comparable, Serial /** * Constructs a new {@code BigDecimal} instance from the given int * {@code val}. The scale of the result is 0. - * + * * @param val * int value to be converted to a {@code BigDecimal} instance. - * @since Android 1.0 */ public BigDecimal(int val) { this(val,0); @@ -721,7 +695,7 @@ public class BigDecimal extends Number implements Comparable, Serial * Constructs a new {@code BigDecimal} instance from the given int {@code * val}. The scale of the result is {@code 0}. The result is rounded * according to the specified math context. - * + * * @param val * int value to be converted to a {@code BigDecimal} instance. * @param mc @@ -730,7 +704,6 @@ public class BigDecimal extends Number implements Comparable, Serial * if {@code mc.precision > 0} and {@code c.roundingMode == * UNNECESSARY} and the new big decimal cannot be represented * within the given precision without rounding. - * @since Android 1.0 */ public BigDecimal(int val, MathContext mc) { this(val,0); @@ -740,10 +713,9 @@ public class BigDecimal extends Number implements Comparable, Serial /** * Constructs a new {@code BigDecimal} instance from the given long {@code * val}. The scale of the result is {@code 0}. - * + * * @param val * long value to be converted to a {@code BigDecimal} instance. - * @since Android 1.0 */ public BigDecimal(long val) { this(val,0); @@ -753,7 +725,7 @@ public class BigDecimal extends Number implements Comparable, Serial * Constructs a new {@code BigDecimal} instance from the given long {@code * val}. The scale of the result is {@code 0}. The result is rounded * according to the specified math context. - * + * * @param val * long value to be converted to a {@code BigDecimal} instance. * @param mc @@ -762,7 +734,6 @@ public class BigDecimal extends Number implements Comparable, Serial * if {@code mc.precision > 0} and {@code mc.roundingMode == * UNNECESSARY} and the new big decimal cannot be represented * within the given precision without rounding. - * @since Android 1.0 */ public BigDecimal(long val, MathContext mc) { this(val); @@ -775,7 +746,7 @@ public class BigDecimal extends Number implements Comparable, Serial * Returns a new {@code BigDecimal} instance whose value is equal to {@code * unscaledVal} 10^(-{@code scale}). The scale of the result is {@code * scale}, and its unscaled value is {@code unscaledVal}. - * + * * @param unscaledVal * unscaled value to be used to construct the new {@code * BigDecimal}. @@ -783,7 +754,6 @@ public class BigDecimal extends Number implements Comparable, Serial * scale to be used to construct the new {@code BigDecimal}. * @return {@code BigDecimal} instance with the value {@code unscaledVal}* * 10^(-{@code unscaledVal}). - * @since Android 1.0 */ public static BigDecimal valueOf(long unscaledVal, int scale) { if (scale == 0) { @@ -800,11 +770,10 @@ public class BigDecimal extends Number implements Comparable, Serial * Returns a new {@code BigDecimal} instance whose value is equal to {@code * unscaledVal}. The scale of the result is {@code 0}, and its unscaled * value is {@code unscaledVal}. - * + * * @param unscaledVal * value to be converted to a {@code BigDecimal}. * @return {@code BigDecimal} instance with the value {@code unscaledVal}. - * @since Android 1.0 */ public static BigDecimal valueOf(long unscaledVal) { if ((unscaledVal >= 0) && (unscaledVal < BI_SCALED_BY_ZERO_LENGTH)) { @@ -815,23 +784,21 @@ public class BigDecimal extends Number implements Comparable, Serial /** * Returns a new {@code BigDecimal} instance whose value is equal to {@code - * unscaledVal}. The new decimal is constructed as if the {@code - * BigDecimal(String)} constructor is called with an argument which is equal - * to {@code Double.toString(val)}. For example, {@code valueOf(0.1)} is - * converted to (unscaled=1, scale=1), although the double {@code 0.1} - * cannot be represented exactly as a double value. In contrast to that, a - * new {@code BigDecimal(0.1)} instance has the value {@code + * val}. The new decimal is constructed as if the {@code BigDecimal(String)} + * constructor is called with an argument which is equal to {@code + * Double.toString(val)}. For example, {@code valueOf("0.1")} is converted to + * (unscaled=1, scale=1), although the double {@code 0.1} cannot be + * represented exactly as a double value. In contrast to that, a new {@code + * BigDecimal(0.1)} instance has the value {@code * 0.1000000000000000055511151231257827021181583404541015625} with an - * unscaled value {@code - * 1000000000000000055511151231257827021181583404541015625} and the scale - * {@code 55}. - * + * unscaled value {@code 1000000000000000055511151231257827021181583404541015625} + * and the scale {@code 55}. + * * @param val * double value to be converted to a {@code BigDecimal}. * @return {@code BigDecimal} instance with the value {@code val}. * @throws NumberFormatException * if {@code val} is infinite or {@code val} is not a number - * @since Android 1.0 */ public static BigDecimal valueOf(double val) { if (Double.isInfinite(val) || Double.isNaN(val)) { @@ -845,13 +812,12 @@ public class BigDecimal extends Number implements Comparable, Serial * Returns a new {@code BigDecimal} whose value is {@code this + augend}. * The scale of the result is the maximum of the scales of the two * arguments. - * + * * @param augend * value to be added to {@code this}. * @return {@code this + augend}. * @throws NullPointerException * if {@code augend == null}. - * @since Android 1.0 */ public BigDecimal add(BigDecimal augend) { int diffScale = this.scale - augend.scale; @@ -895,11 +861,11 @@ public class BigDecimal extends Number implements Comparable, Serial } // END android-changed } - + /** * Returns a new {@code BigDecimal} whose value is {@code this + augend}. * The result is rounded according to the passed context {@code mc}. - * + * * @param augend * value to be added to {@code this}. * @param mc @@ -907,7 +873,6 @@ public class BigDecimal extends Number implements Comparable, Serial * @return {@code this + augend}. * @throws NullPointerException * if {@code augend == null} or {@code mc == null}. - * @since Android 1.0 */ public BigDecimal add(BigDecimal augend, MathContext mc) { BigDecimal larger; // operand with the largest unscaled value @@ -915,7 +880,7 @@ public class BigDecimal extends Number implements Comparable, Serial BigInteger tempBI; long diffScale = (long)this.scale - augend.scale; int largerSignum; - // Some operand is zero or the precision is infinity + // Some operand is zero or the precision is infinity if ((augend.isZero()) || (this.isZero()) || (mc.getPrecision() == 0)) { return add(augend).round(mc); @@ -927,14 +892,14 @@ public class BigDecimal extends Number implements Comparable, Serial } else if (augend.aproxPrecision() < -diffScale - 1) { larger = this; smaller = augend; - } else {// No optimization is done + } else {// No optimization is done return add(augend).round(mc); } if (mc.getPrecision() >= larger.aproxPrecision()) { // No optimization is done return add(augend).round(mc); } - // Cases where it's unnecessary to add two numbers with very different scales + // Cases where it's unnecessary to add two numbers with very different scales largerSignum = larger.signum(); if (largerSignum == smaller.signum()) { tempBI = Multiplication.multiplyByPositiveInt(larger.getUnscaledValue(),10) @@ -945,22 +910,20 @@ public class BigDecimal extends Number implements Comparable, Serial tempBI = Multiplication.multiplyByPositiveInt(tempBI,10) .add(BigInteger.valueOf(largerSignum * 9)); } - // Rounding the improved adding + // Rounding the improved adding larger = new BigDecimal(tempBI, larger.scale + 1); return larger.round(mc); } /** - * Returns a new {@code BigDecimal} whose value is {@code this - subtrahend} - * . The scale of the result is the maximum of the scales of the two - * arguments. - * + * Returns a new {@code BigDecimal} whose value is {@code this - subtrahend}. + * The scale of the result is the maximum of the scales of the two arguments. + * * @param subtrahend * value to be subtracted from {@code this}. * @return {@code this - subtrahend}. * @throws NullPointerException * if {@code subtrahend == null}. - * @since Android 1.0 */ public BigDecimal subtract(BigDecimal subtrahend) { int diffScale = this.scale - subtrahend.scale; @@ -1006,7 +969,7 @@ public class BigDecimal extends Number implements Comparable, Serial /** * Returns a new {@code BigDecimal} whose value is {@code this - subtrahend}. * The result is rounded according to the passed context {@code mc}. - * + * * @param subtrahend * value to be subtracted from {@code this}. * @param mc @@ -1014,14 +977,13 @@ public class BigDecimal extends Number implements Comparable, Serial * @return {@code this - subtrahend}. * @throws NullPointerException * if {@code subtrahend == null} or {@code mc == null}. - * @since Android 1.0 */ public BigDecimal subtract(BigDecimal subtrahend, MathContext mc) { long diffScale = subtrahend.scale - (long)this.scale; int thisSignum; - BigDecimal leftOperand; // it will be only the left operand (this) + BigDecimal leftOperand; // it will be only the left operand (this) BigInteger tempBI; - // Some operand is zero or the precision is infinity + // Some operand is zero or the precision is infinity if ((subtrahend.isZero()) || (this.isZero()) || (mc.getPrecision() == 0)) { return subtract(subtrahend).round(mc); @@ -1052,13 +1014,12 @@ public class BigDecimal extends Number implements Comparable, Serial * Returns a new {@code BigDecimal} whose value is {@code this * * multiplicand}. The scale of the result is the sum of the scales of the * two arguments. - * + * * @param multiplicand * value to be multiplied with {@code this}. * @return {@code this * multiplicand}. * @throws NullPointerException * if {@code multiplicand == null}. - * @since Android 1.0 */ public BigDecimal multiply(BigDecimal multiplicand) { long newScale = (long)this.scale + multiplicand.scale; @@ -1079,7 +1040,7 @@ public class BigDecimal extends Number implements Comparable, Serial * Returns a new {@code BigDecimal} whose value is {@code this * * multiplicand}. The result is rounded according to the passed context * {@code mc}. - * + * * @param multiplicand * value to be multiplied with {@code this}. * @param mc @@ -1087,7 +1048,6 @@ public class BigDecimal extends Number implements Comparable, Serial * @return {@code this * multiplicand}. * @throws NullPointerException * if {@code multiplicand == null} or {@code mc == null}. - * @since Android 1.0 */ public BigDecimal multiply(BigDecimal multiplicand, MathContext mc) { BigDecimal result = multiply(multiplicand); @@ -1101,7 +1061,7 @@ public class BigDecimal extends Number implements Comparable, Serial * As scale of the result the parameter {@code scale} is used. If rounding * is required to meet the specified scale, then the specified rounding mode * {@code roundingMode} is applied. - * + * * @param divisor * value by which {@code this} is divided. * @param scale @@ -1119,7 +1079,6 @@ public class BigDecimal extends Number implements Comparable, Serial * @throws ArithmeticException * if {@code roundingMode == ROUND_UNNECESSARY} and rounding is * necessary according to the given scale. - * @since Android 1.0 */ public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode) { return divide(divisor, scale, RoundingMode.valueOf(roundingMode)); @@ -1130,7 +1089,7 @@ public class BigDecimal extends Number implements Comparable, Serial * As scale of the result the parameter {@code scale} is used. If rounding * is required to meet the specified scale, then the specified rounding mode * {@code roundingMode} is applied. - * + * * @param divisor * value by which {@code this} is divided. * @param scale @@ -1147,7 +1106,6 @@ public class BigDecimal extends Number implements Comparable, Serial * if {@code roundingMode == RoundingMode.UNNECESSAR}Y and * rounding is necessary according to the given scale and given * precision. - * @since Android 1.0 */ public BigDecimal divide(BigDecimal divisor, int scale, RoundingMode roundingMode) { // Let be: this = [u1,s1] and divisor = [u2,s2] @@ -1158,7 +1116,7 @@ public class BigDecimal extends Number implements Comparable, Serial // math.04=Division by zero throw new ArithmeticException(Messages.getString("math.04")); //$NON-NLS-1$ } - + long diffScale = ((long)this.scale - divisor.scale) - scale; if(this.bitLength < 64 && divisor.bitLength < 64 ) { if(diffScale == 0) { @@ -1182,12 +1140,12 @@ public class BigDecimal extends Number implements Comparable, Serial scale, roundingMode); } - + } } BigInteger scaledDividend = this.getUnscaledValue(); BigInteger scaledDivisor = divisor.getUnscaledValue(); // for scaling of 'u2' - + if (diffScale > 0) { // Multiply 'u2' by: 10^((s1 - s2) - scale) scaledDivisor = Multiplication.multiplyByTenPow(scaledDivisor, (int)diffScale); @@ -1197,9 +1155,9 @@ public class BigDecimal extends Number implements Comparable, Serial } return divideBigIntegers(scaledDividend, scaledDivisor, scale, roundingMode); } - + private static BigDecimal divideBigIntegers(BigInteger scaledDividend, BigInteger scaledDivisor, int scale, RoundingMode roundingMode) { - + BigInteger[] quotAndRem = scaledDividend.divideAndRemainder(scaledDivisor); // quotient and remainder // If after division there is a remainder... BigInteger quotient = quotAndRem[0]; @@ -1216,10 +1174,10 @@ public class BigDecimal extends Number implements Comparable, Serial // To look if there is a carry compRem = roundingBehavior(quotient.testBit(0) ? 1 : 0, sign * (5 + compRem), roundingMode); - + } else { - // Checking if: remainder * 2 >= scaledDivisor - compRem = remainder.abs().shiftLeft(1).compareTo(scaledDivisor.abs()); + // Checking if: remainder * 2 >= scaledDivisor + compRem = remainder.abs().shiftLeftOneBit().compareTo(scaledDivisor.abs()); compRem = roundingBehavior(quotient.testBit(0) ? 1 : 0, sign * (5 + compRem), roundingMode); } @@ -1233,7 +1191,7 @@ public class BigDecimal extends Number implements Comparable, Serial // Constructing the result with the appropriate unscaled value return new BigDecimal(quotient, scale); } - + private static BigDecimal dividePrimitiveLongs(long scaledDividend, long scaledDivisor, int scale, RoundingMode roundingMode) { long quotient = scaledDividend / scaledDivisor; long remainder = scaledDividend % scaledDivisor; @@ -1256,7 +1214,7 @@ public class BigDecimal extends Number implements Comparable, Serial * The scale of the result is the scale of {@code this}. If rounding is * required to meet the specified scale, then the specified rounding mode * {@code roundingMode} is applied. - * + * * @param divisor * value by which {@code this} is divided. * @param roundingMode @@ -1272,7 +1230,6 @@ public class BigDecimal extends Number implements Comparable, Serial * @throws ArithmeticException * if {@code roundingMode == ROUND_UNNECESSARY} and rounding is * necessary according to the scale of this. - * @since Android 1.0 */ public BigDecimal divide(BigDecimal divisor, int roundingMode) { return divide(divisor, scale, RoundingMode.valueOf(roundingMode)); @@ -1283,7 +1240,7 @@ public class BigDecimal extends Number implements Comparable, Serial * The scale of the result is the scale of {@code this}. If rounding is * required to meet the specified scale, then the specified rounding mode * {@code roundingMode} is applied. - * + * * @param divisor * value by which {@code this} is divided. * @param roundingMode @@ -1297,7 +1254,6 @@ public class BigDecimal extends Number implements Comparable, Serial * @throws ArithmeticException * if {@code roundingMode == RoundingMode.UNNECESSARY} and * rounding is necessary according to the scale of this. - * @since Android 1.0 */ public BigDecimal divide(BigDecimal divisor, RoundingMode roundingMode) { return divide(divisor, scale, roundingMode); @@ -1309,7 +1265,7 @@ public class BigDecimal extends Number implements Comparable, Serial * and {@code divisor}. If the exact result requires more digits, then the * scale is adjusted accordingly. For example, {@code 1/128 = 0.0078125} * which has a scale of {@code 7} and precision {@code 5}. - * + * * @param divisor * value by which {@code this} is divided. * @return {@code this / divisor}. @@ -1319,7 +1275,6 @@ public class BigDecimal extends Number implements Comparable, Serial * if {@code divisor == 0}. * @throws ArithmeticException * if the result cannot be represented exactly. - * @since Android 1.0 */ public BigDecimal divide(BigDecimal divisor) { BigInteger p = this.getUnscaledValue(); @@ -1376,7 +1331,7 @@ public class BigDecimal extends Number implements Comparable, Serial newScale = toIntScale(diffScale + Math.max(k, l)); // k >= 0 and l >= 0 implies that k - l is in the 32-bit range i = k - l; - + p = (i > 0) ? Multiplication.multiplyByFivePow(p, i) : p.shiftLeft(-i); return new BigDecimal(p, newScale); @@ -1387,7 +1342,7 @@ public class BigDecimal extends Number implements Comparable, Serial * The result is rounded according to the passed context {@code mc}. If the * passed math context specifies precision {@code 0}, then this call is * equivalent to {@code this.divide(divisor)}. - * + * * @param divisor * value by which {@code this} is divided. * @param mc @@ -1400,7 +1355,6 @@ public class BigDecimal extends Number implements Comparable, Serial * @throws ArithmeticException * if {@code mc.getRoundingMode() == UNNECESSARY} and rounding * is necessary according {@code mc.getPrecision()}. - * @since Android 1.0 */ public BigDecimal divide(BigDecimal divisor, MathContext mc) { /* Calculating how many zeros must be append to 'dividend' @@ -1410,7 +1364,7 @@ public class BigDecimal extends Number implements Comparable, Serial long diffScale = (long)scale - divisor.scale; long newScale = diffScale; // scale of the final quotient int compRem; // to compare the remainder - int i = 1; // index + int i = 1; // index int lastPow = TEN_POW.length - 1; // last power of ten BigInteger integerQuot; // for temporal results BigInteger quotAndRem[] = {getUnscaledValue()}; @@ -1429,7 +1383,7 @@ public class BigDecimal extends Number implements Comparable, Serial // Calculating the exact quotient with at least 'mc.precision()' digits if (quotAndRem[1].signum() != 0) { // Checking if: 2 * remainder >= divisor ? - compRem = quotAndRem[1].shiftLeft(1).compareTo( divisor.getUnscaledValue() ); + compRem = quotAndRem[1].shiftLeftOneBit().compareTo( divisor.getUnscaledValue() ); // quot := quot * 10 + r; with 'r' in {-6,-5,-4, 0,+4,+5,+6} integerQuot = integerQuot.multiply(BigInteger.TEN) .add(BigInteger.valueOf(quotAndRem[0].signum() * (5 + compRem))); @@ -1461,7 +1415,7 @@ public class BigDecimal extends Number implements Comparable, Serial * Returns a new {@code BigDecimal} whose value is the integral part of * {@code this / divisor}. The quotient is rounded down towards zero to the * next integer. For example, {@code 0.5/0.2 = 2}. - * + * * @param divisor * value by which {@code this} is divided. * @return integral part of {@code this / divisor}. @@ -1469,7 +1423,6 @@ public class BigDecimal extends Number implements Comparable, Serial * if {@code divisor == null}. * @throws ArithmeticException * if {@code divisor == 0}. - * @since Android 1.0 */ public BigDecimal divideToIntegralValue(BigDecimal divisor) { BigInteger integralValue; // the integer of result @@ -1528,7 +1481,7 @@ public class BigDecimal extends Number implements Comparable, Serial * next integer. The rounding mode passed with the parameter {@code mc} is * not considered. But if the precision of {@code mc > 0} and the integral * part requires more digits, then an {@code ArithmeticException} is thrown. - * + * * @param divisor * value by which {@code this} is divided. * @param mc @@ -1542,7 +1495,6 @@ public class BigDecimal extends Number implements Comparable, Serial * @throws ArithmeticException * if {@code mc.getPrecision() > 0} and the result requires more * digits to be represented. - * @since Android 1.0 */ public BigDecimal divideToIntegralValue(BigDecimal divisor, MathContext mc) { int mcPrecision = mc.getPrecision(); @@ -1560,22 +1512,22 @@ public class BigDecimal extends Number implements Comparable, Serial if (quotPrecision <= 0) { quotAndRem[0] = BigInteger.ZERO; } else if (diffScale == 0) { - // CASE s1 == s2: to calculate u1 / u2 + // CASE s1 == s2: to calculate u1 / u2 quotAndRem[0] = this.getUnscaledValue().divide( divisor.getUnscaledValue() ); } else if (diffScale > 0) { - // CASE s1 >= s2: to calculate u1 / (u2 * 10^(s1-s2) + // CASE s1 >= s2: to calculate u1 / (u2 * 10^(s1-s2) quotAndRem[0] = this.getUnscaledValue().divide( divisor.getUnscaledValue().multiply(Multiplication.powerOf10(diffScale)) ); // To chose 10^newScale to get a quotient with at least 'mc.precision()' digits newScale = Math.min(diffScale, Math.max(mcPrecision - quotPrecision + 1, 0)); // To calculate: (u1 / (u2 * 10^(s1-s2)) * 10^newScale quotAndRem[0] = quotAndRem[0].multiply(Multiplication.powerOf10(newScale)); - } else {// CASE s2 > s1: - /* To calculate the minimum power of ten, such that the quotient + } else {// CASE s2 > s1: + /* To calculate the minimum power of ten, such that the quotient * (u1 * 10^exp) / u2 has at least 'mc.precision()' digits. */ long exp = Math.min(-diffScale, Math.max((long)mcPrecision - diffPrecision, 0)); long compRemDiv; - // Let be: (u1 * 10^exp) / u2 = [q,r] + // Let be: (u1 * 10^exp) / u2 = [q,r] quotAndRem = this.getUnscaledValue().multiply(Multiplication.powerOf10(exp)). divideAndRemainder(divisor.getUnscaledValue()); newScale += exp; // To fix the scale @@ -1640,7 +1592,7 @@ public class BigDecimal extends Number implements Comparable, Serial *

          * The remainder is defined as {@code this - * this.divideToIntegralValue(divisor) * divisor}. - * + * * @param divisor * value by which {@code this} is divided. * @return {@code this % divisor}. @@ -1648,7 +1600,6 @@ public class BigDecimal extends Number implements Comparable, Serial * if {@code divisor == null}. * @throws ArithmeticException * if {@code divisor == 0}. - * @since Android 1.0 */ public BigDecimal remainder(BigDecimal divisor) { return divideAndRemainder(divisor)[1]; @@ -1661,7 +1612,7 @@ public class BigDecimal extends Number implements Comparable, Serial * this.divideToIntegralValue(divisor) * divisor}. *

          * The specified rounding mode {@code mc} is used for the division only. - * + * * @param divisor * value by which {@code this} is divided. * @param mc @@ -1675,7 +1626,6 @@ public class BigDecimal extends Number implements Comparable, Serial * if {@code mc.getPrecision() > 0} and the result of {@code * this.divideToIntegralValue(divisor, mc)} requires more digits * to be represented. - * @since Android 1.0 */ public BigDecimal remainder(BigDecimal divisor, MathContext mc) { return divideAndRemainder(divisor, mc)[1]; @@ -1686,7 +1636,7 @@ public class BigDecimal extends Number implements Comparable, Serial * {@code this / divisor} at index 0 and the remainder {@code this % * divisor} at index 1. The quotient is rounded down towards zero to the * next integer. - * + * * @param divisor * value by which {@code this} is divided. * @return {@code [this.divideToIntegralValue(divisor), @@ -1697,7 +1647,6 @@ public class BigDecimal extends Number implements Comparable, Serial * if {@code divisor == 0}. * @see #divideToIntegralValue * @see #remainder - * @since Android 1.0 */ public BigDecimal[] divideAndRemainder(BigDecimal divisor) { BigDecimal quotAndRem[] = new BigDecimal[2]; @@ -1714,7 +1663,7 @@ public class BigDecimal extends Number implements Comparable, Serial * next integer. The rounding mode passed with the parameter {@code mc} is * not considered. But if the precision of {@code mc > 0} and the integral * part requires more digits, then an {@code ArithmeticException} is thrown. - * + * * @param divisor * value by which {@code this} is divided. * @param mc @@ -1728,7 +1677,6 @@ public class BigDecimal extends Number implements Comparable, Serial * if {@code divisor == 0}. * @see #divideToIntegralValue * @see #remainder - * @since Android 1.0 */ public BigDecimal[] divideAndRemainder(BigDecimal divisor, MathContext mc) { BigDecimal quotAndRem[] = new BigDecimal[2]; @@ -1746,13 +1694,12 @@ public class BigDecimal extends Number implements Comparable, Serial *

          * Implementation Note: The implementation is based on the ANSI standard * X3.274-1996 algorithm. - * + * * @param n * exponent to which {@code this} is raised. * @return {@code this ^ n}. * @throws ArithmeticException * if {@code n < 0} or {@code n > 999999999}. - * @since Android 1.0 */ public BigDecimal pow(int n) { if (n == 0) { @@ -1775,7 +1722,7 @@ public class BigDecimal extends Number implements Comparable, Serial *

          * Implementation Note: The implementation is based on the ANSI standard * X3.274-1996 algorithm. - * + * * @param n * exponent to which {@code this} is raised. * @param mc @@ -1783,7 +1730,6 @@ public class BigDecimal extends Number implements Comparable, Serial * @return {@code this ^ n}. * @throws ArithmeticException * if {@code n < 0} or {@code n > 999999999}. - * @since Android 1.0 */ public BigDecimal pow(int n, MathContext mc) { // The ANSI standard X3.274-1996 algorithm @@ -1807,7 +1753,7 @@ public class BigDecimal extends Number implements Comparable, Serial newPrecision = new MathContext( mcPrecision + elength + 1, mc.getRoundingMode()); } - // The result is calculated as if 'n' were positive + // The result is calculated as if 'n' were positive accum = round(newPrecision); oneBitMask = Integer.highestOneBit(m) >> 1; @@ -1830,9 +1776,8 @@ public class BigDecimal extends Number implements Comparable, Serial /** * Returns a new {@code BigDecimal} whose value is the absolute value of * {@code this}. The scale of the result is the same as the scale of this. - * + * * @return {@code abs(this)} - * @since Android 1.0 */ public BigDecimal abs() { return ((signum() < 0) ? negate() : this); @@ -1842,11 +1787,10 @@ public class BigDecimal extends Number implements Comparable, Serial * Returns a new {@code BigDecimal} whose value is the absolute value of * {@code this}. The result is rounded according to the passed context * {@code mc}. - * + * * @param mc * rounding mode and precision for the result of this operation. * @return {@code abs(this)} - * @since Android 1.0 */ public BigDecimal abs(MathContext mc) { // BEGIN android-changed @@ -1859,9 +1803,8 @@ public class BigDecimal extends Number implements Comparable, Serial /** * Returns a new {@code BigDecimal} whose value is the {@code -this}. The * scale of the result is the same as the scale of this. - * + * * @return {@code -this} - * @since Android 1.0 */ public BigDecimal negate() { if(bitLength < 63 || (bitLength == 63 && smallValue!=Long.MIN_VALUE)) { @@ -1873,11 +1816,10 @@ public class BigDecimal extends Number implements Comparable, Serial /** * Returns a new {@code BigDecimal} whose value is the {@code -this}. The * result is rounded according to the passed context {@code mc}. - * + * * @param mc * rounding mode and precision for the result of this operation. * @return {@code -this} - * @since Android 1.0 */ public BigDecimal negate(MathContext mc) { // BEGIN android-changed @@ -1890,9 +1832,8 @@ public class BigDecimal extends Number implements Comparable, Serial /** * Returns a new {@code BigDecimal} whose value is {@code +this}. The scale * of the result is the same as the scale of this. - * + * * @return {@code this} - * @since Android 1.0 */ public BigDecimal plus() { return this; @@ -1901,11 +1842,10 @@ public class BigDecimal extends Number implements Comparable, Serial /** * Returns a new {@code BigDecimal} whose value is {@code +this}. The result * is rounded according to the passed context {@code mc}. - * + * * @param mc * rounding mode and precision for the result of this operation. - * @return {@code this} - * @since Android 1.0 + * @return {@code this}, rounded */ public BigDecimal plus(MathContext mc) { return round(mc); @@ -1913,19 +1853,17 @@ public class BigDecimal extends Number implements Comparable, Serial /** * Returns the sign of this {@code BigDecimal}. - * - * @return {@code -1} if {@code this < 0}, + * + * @return {@code -1} if {@code this < 0}, * {@code 0} if {@code this == 0}, - * {@code 1} if {@code this > 0}. - * @since Android 1.0 - */ + * {@code 1} if {@code this > 0}. */ public int signum() { if( bitLength < 64) { return Long.signum( this.smallValue ); } return getUnscaledValue().signum(); } - + private boolean isZero() { //Watch out: -1 has a bitLength=0 return bitLength == 0 && this.smallValue != -1; @@ -1936,9 +1874,8 @@ public class BigDecimal extends Number implements Comparable, Serial * digits behind the decimal point. The value of this {@code BigDecimal} is * the unsignedValue * 10^(-scale). If the scale is negative, then this * {@code BigDecimal} represents a big integer. - * + * * @return the scale of this {@code BigDecimal}. - * @since Android 1.0 */ public int scale() { return scale; @@ -1949,9 +1886,8 @@ public class BigDecimal extends Number implements Comparable, Serial * number of decimal digits used to represent this decimal. It is equivalent * to the number of digits of the unscaled value. The precision of {@code 0} * is {@code 1} (independent of the scale). - * + * * @return the precision of this {@code BigDecimal}. - * @since Android 1.0 */ public int precision() { // Checking if the precision already was calculated @@ -1987,9 +1923,8 @@ public class BigDecimal extends Number implements Comparable, Serial * Returns the unscaled value (mantissa) of this {@code BigDecimal} instance * as a {@code BigInteger}. The unscaled value can be computed as {@code * this} 10^(scale). - * + * * @return unscaled value (this * 10^(scale)). - * @since Android 1.0 */ public BigInteger unscaledValue() { return getUnscaledValue(); @@ -2004,7 +1939,7 @@ public class BigDecimal extends Number implements Comparable, Serial * If {@code mc.precision > 0} and {@code mc.roundingMode == UNNECESSARY}, * then an {@code ArithmeticException} is thrown if the result cannot be * represented exactly within the given precision. - * + * * @param mc * rounding mode and precision for the result of this operation. * @return {@code this} rounded according to the passed context. @@ -2012,7 +1947,6 @@ public class BigDecimal extends Number implements Comparable, Serial * if {@code mc.precision > 0} and {@code mc.roundingMode == * UNNECESSARY} and this cannot be represented within the given * precision. - * @since Android 1.0 */ public BigDecimal round(MathContext mc) { BigDecimal thisBD = new BigDecimal(getUnscaledValue(), scale); @@ -2031,7 +1965,7 @@ public class BigDecimal extends Number implements Comparable, Serial * removed. If these trailing digits are not zero, then the remaining * unscaled value has to be rounded. For this rounding operation the * specified rounding mode is used. - * + * * @param newScale * scale of the result returned. * @param roundingMode @@ -2042,14 +1976,13 @@ public class BigDecimal extends Number implements Comparable, Serial * @throws ArithmeticException * if {@code roundingMode == ROUND_UNNECESSARY} and rounding is * necessary according to the given scale. - * @since Android 1.0 */ public BigDecimal setScale(int newScale, RoundingMode roundingMode) { if (roundingMode == null) { throw new NullPointerException(); } long diffScale = newScale - (long)scale; - // Let be: 'this' = [u,s] + // Let be: 'this' = [u,s] if(diffScale == 0) { return this; } @@ -2079,7 +2012,7 @@ public class BigDecimal extends Number implements Comparable, Serial * removed. If these trailing digits are not zero, then the remaining * unscaled value has to be rounded. For this rounding operation the * specified rounding mode is used. - * + * * @param newScale * scale of the result returned. * @param roundingMode @@ -2090,7 +2023,6 @@ public class BigDecimal extends Number implements Comparable, Serial * @throws ArithmeticException * if {@code roundingMode == ROUND_UNNECESSARY} and rounding is * necessary according to the given scale. - * @since Android 1.0 */ public BigDecimal setScale(int newScale, int roundingMode) { return setScale(newScale, RoundingMode.valueOf(roundingMode)); @@ -2105,13 +2037,12 @@ public class BigDecimal extends Number implements Comparable, Serial *

          * If no exception is thrown, then the following equation holds: {@code * x.setScale(s).compareTo(x) == 0}. - * + * * @param newScale * scale of the result returned. * @return a new {@code BigDecimal} instance with the specified scale. * @throws ArithmeticException * if rounding would be necessary. - * @since Android 1.0 */ public BigDecimal setScale(int newScale) { return setScale(newScale, RoundingMode.UNNECESSARY); @@ -2128,11 +2059,10 @@ public class BigDecimal extends Number implements Comparable, Serial *

          * Note, that {@code movePointLeft(0)} returns a result which is * mathematically equivalent, but which has {@code scale >= 0}. - * + * * @param n * number of placed the decimal point has to be moved. - * @return {@code this} 10^({@code -n}). - * @since Android 1.0 + * @return {@code this * 10^(-n}). */ public BigDecimal movePointLeft(int n) { return movePoint(scale + (long)n); @@ -2170,11 +2100,10 @@ public class BigDecimal extends Number implements Comparable, Serial *

          * Note, that {@code movePointRight(0)} returns a result which is * mathematically equivalent, but which has scale >= 0. - * + * * @param n * number of placed the decimal point has to be moved. - * @return {@code this} 10^{@code n}. - * @since Android 1.0 + * @return {@code this * 10^n}. */ public BigDecimal movePointRight(int n) { return movePoint(scale - (long)n); @@ -2182,16 +2111,15 @@ public class BigDecimal extends Number implements Comparable, Serial /** * Returns a new {@code BigDecimal} whose value is {@code this} 10^{@code n}. - * The scale of the result is {@code this.scale()} - {@code n}. + * The scale of the result is {@code this.scale()} - {@code n}. * The precision of the result is the precision of {@code this}. *

          * This method has the same effect as {@link #movePointRight}, except that * the precision is not changed. - * + * * @param n * number of places the decimal point has to be moved. - * @return {@code this} 10^{@code n} - * @since Android 1.0 + * @return {@code this * 10^n} */ public BigDecimal scaleByPowerOfTen(int n) { long newScale = scale - (long)n; @@ -2210,10 +2138,9 @@ public class BigDecimal extends Number implements Comparable, Serial * this} but with a unscaled value where the trailing zeros have been * removed. If the unscaled value of {@code this} has n trailing zeros, then * the scale and the precision of the result has been reduced by n. - * + * * @return a new {@code BigDecimal} instance equivalent to this where the * trailing zeros of the unscaled value have been removed. - * @since Android 1.0 */ public BigDecimal stripTrailingZeros() { int i = 1; // 1 <= i <= 18 @@ -2221,13 +2148,11 @@ public class BigDecimal extends Number implements Comparable, Serial long newScale = scale; if (isZero()) { - // BEGIN android-changed return new BigDecimal("0"); - // END android-changed } BigInteger strippedBI = getUnscaledValue(); BigInteger[] quotAndRem; - + // while the number is even... while (!strippedBI.testBit(0)) { // To divide by 10^i @@ -2261,14 +2186,13 @@ public class BigDecimal extends Number implements Comparable, Serial * difference is 0 then 0 is returned. This means, that if two decimal * instances are compared which are equal in value but differ in scale, then * these two instances are considered as equal. - * + * * @param val * value to be compared with {@code this}. * @return {@code 1} if {@code this > val}, {@code -1} if {@code this < val}, * {@code 0} if {@code this == val}. * @throws NullPointerException * if {@code val == null}. - * @since Android 1.0 */ public int compareTo(BigDecimal val) { int thisSign = signum(); @@ -2308,37 +2232,35 @@ public class BigDecimal extends Number implements Comparable, Serial * their unscaled value and their scale is equal. For example, 1.0 * (10*10^(-1)) is not equal to 1.00 (100*10^(-2)). Similarly, zero * instances are not equal if their scale differs. - * + * * @param x * object to be compared with {@code this}. * @return true if {@code x} is a {@code BigDecimal} and {@code this == x}. - * @since Android 1.0 */ @Override - public boolean equals(Object x) { + public boolean equals(Object x) { if (this == x) { return true; } if (x instanceof BigDecimal) { - BigDecimal x1 = (BigDecimal) x; - return x1.scale == scale + BigDecimal x1 = (BigDecimal) x; + return x1.scale == scale && (bitLength < 64 ? (x1.smallValue == smallValue) : intVal.equals(x1.intVal)); - - - } - return false; - } + + + } + return false; + } /** * Returns the minimum of this {@code BigDecimal} and {@code val}. - * + * * @param val * value to be used to compute the minimum with this. * @return {@code min(this, val}. * @throws NullPointerException * if {@code val == null}. - * @since Android 1.0 */ public BigDecimal min(BigDecimal val) { return ((compareTo(val) <= 0) ? this : val); @@ -2346,13 +2268,12 @@ public class BigDecimal extends Number implements Comparable, Serial /** * Returns the maximum of this {@code BigDecimal} and {@code val}. - * + * * @param val * value to be used to compute the maximum with this. * @return {@code max(this, val}. * @throws NullPointerException * if {@code val == null}. - * @since Android 1.0 */ public BigDecimal max(BigDecimal val) { return ((compareTo(val) >= 0) ? this : val); @@ -2360,23 +2281,22 @@ public class BigDecimal extends Number implements Comparable, Serial /** * Returns a hash code for this {@code BigDecimal}. - * + * * @return hash code for {@code this}. - * @since Android 1.0 */ @Override - public int hashCode() { + public int hashCode() { if (hashCode != 0) { return hashCode; } if (bitLength < 64) { hashCode = (int)(smallValue & 0xffffffff); hashCode = 33 * hashCode + (int)((smallValue >> 32) & 0xffffffff); - hashCode = 17 * hashCode + scale; + hashCode = 17 * hashCode + scale; return hashCode; } - hashCode = 17 * intVal.hashCode() + scale; - return hashCode; + hashCode = 17 * intVal.hashCode() + scale; + return hashCode; } /** @@ -2386,10 +2306,9 @@ public class BigDecimal extends Number implements Comparable, Serial *

          * If the scale is negative or if {@code scale - precision >= 6} then * scientific notation is used. - * + * * @return a string representation of {@code this} in scientific notation if * necessary. - * @since Android 1.0 */ @Override public String toString() { @@ -2440,10 +2359,9 @@ public class BigDecimal extends Number implements Comparable, Serial * engineering notation is used. Engineering notation is similar to the * scientific notation except that the exponent is made to be a multiple of * 3 such that the integer part is >= 1 and < 1000. - * + * * @return a string representation of {@code this} in engineering notation * if necessary. - * @since Android 1.0 */ public String toEngineeringString() { String intString = getUnscaledValue().toString(); @@ -2511,9 +2429,8 @@ public class BigDecimal extends Number implements Comparable, Serial * {@code false}. *

          * {@code x.compareTo(new BigDecimal(x.toPlainString())} returns {@code 0}. - * + * * @return a string representation of {@code this} without exponent part. - * @since Android 1.0 */ public String toPlainString() { String intStr = getUnscaledValue().toString(); @@ -2526,7 +2443,7 @@ public class BigDecimal extends Number implements Comparable, Serial StringBuffer result = new StringBuffer(intStr.length() + 1 + Math.abs(scale)); if (begin == 1) { - // If the number is negative, we insert a '-' character at front + // If the number is negative, we insert a '-' character at front result.append('-'); } if (scale > 0) { @@ -2559,9 +2476,8 @@ public class BigDecimal extends Number implements Comparable, Serial /** * Returns this {@code BigDecimal} as a big integer instance. A fractional * part is discarded. - * + * * @return this {@code BigDecimal} as a big integer instance. - * @since Android 1.0 */ public BigInteger toBigInteger() { if ((scale == 0) || (isZero())) { @@ -2577,11 +2493,10 @@ public class BigDecimal extends Number implements Comparable, Serial * Returns this {@code BigDecimal} as a big integer instance if it has no * fractional part. If this {@code BigDecimal} has a fractional part, i.e. * if rounding would be necessary, an {@code ArithmeticException} is thrown. - * + * * @return this {@code BigDecimal} as a big integer value. * @throws ArithmeticException * if rounding is necessary. - * @since Android 1.0 */ public BigInteger toBigIntegerExact() { if ((scale == 0) || (isZero())) { @@ -2597,7 +2512,7 @@ public class BigDecimal extends Number implements Comparable, Serial } integerAndFraction = getUnscaledValue().divideAndRemainder(Multiplication.powerOf10(scale)); if (integerAndFraction[1].signum() != 0) { - // It exists a non-zero fractional part + // It exists a non-zero fractional part // math.08=Rounding necessary throw new ArithmeticException(Messages.getString("math.08")); //$NON-NLS-1$ } @@ -2609,9 +2524,8 @@ public class BigDecimal extends Number implements Comparable, Serial * Returns this {@code BigDecimal} as an long value. Any fractional part is * discarded. If the integral part of {@code this} is too big to be * represented as an long, then {@code this} % 2^64 is returned. - * + * * @return this {@code BigDecimal} as a long value. - * @since Android 1.0 */ @Override public long longValue() { @@ -2628,11 +2542,10 @@ public class BigDecimal extends Number implements Comparable, Serial * Returns this {@code BigDecimal} as a long value if it has no fractional * part and if its value fits to the int range ([-2^{63}..2^{63}-1]). If * these conditions are not met, an {@code ArithmeticException} is thrown. - * + * * @return this {@code BigDecimal} as a long value. * @throws ArithmeticException * if rounding is necessary or the number doesn't fit in a long. - * @since Android 1.0 */ public long longValueExact() { return valueExact(64); @@ -2642,9 +2555,8 @@ public class BigDecimal extends Number implements Comparable, Serial * Returns this {@code BigDecimal} as an int value. Any fractional part is * discarded. If the integral part of {@code this} is too big to be * represented as an int, then {@code this} % 2^32 is returned. - * + * * @return this {@code BigDecimal} as a int value. - * @since Android 1.0 */ @Override public int intValue() { @@ -2662,11 +2574,10 @@ public class BigDecimal extends Number implements Comparable, Serial * Returns this {@code BigDecimal} as a int value if it has no fractional * part and if its value fits to the int range ([-2^{31}..2^{31}-1]). If * these conditions are not met, an {@code ArithmeticException} is thrown. - * + * * @return this {@code BigDecimal} as a int value. * @throws ArithmeticException * if rounding is necessary or the number doesn't fit in a int. - * @since Android 1.0 */ public int intValueExact() { return (int)valueExact(32); @@ -2676,12 +2587,11 @@ public class BigDecimal extends Number implements Comparable, Serial * Returns this {@code BigDecimal} as a short value if it has no fractional * part and if its value fits to the short range ([-2^{15}..2^{15}-1]). If * these conditions are not met, an {@code ArithmeticException} is thrown. - * + * * @return this {@code BigDecimal} as a short value. * @throws ArithmeticException * if rounding is necessary of the number doesn't fit in a * short. - * @since Android 1.0 */ public short shortValueExact() { return (short)valueExact(16); @@ -2691,11 +2601,10 @@ public class BigDecimal extends Number implements Comparable, Serial * Returns this {@code BigDecimal} as a byte value if it has no fractional * part and if its value fits to the byte range ([-128..127]). If these * conditions are not met, an {@code ArithmeticException} is thrown. - * + * * @return this {@code BigDecimal} as a byte value. * @throws ArithmeticException * if rounding is necessary or the number doesn't fit in a byte. - * @since Android 1.0 */ public byte byteValueExact() { return (byte)valueExact(8); @@ -2716,9 +2625,8 @@ public class BigDecimal extends Number implements Comparable, Serial *

          * Similarly, if the instance {@code new BigDecimal(16777217)} is converted * to a float, the result is {@code 1.6777216E}7. - * + * * @return this {@code BigDecimal} as a float value. - * @since Android 1.0 */ @Override public float floatValue() { @@ -2754,9 +2662,8 @@ public class BigDecimal extends Number implements Comparable, Serial * Similarly, if the instance {@code new BigDecimal(9007199254740993L)} is * converted to a double, the result is {@code 9.007199254740992E15}. *

          - * + * * @return this {@code BigDecimal} as a double value. - * @since Android 1.0 */ @Override public double doubleValue() { @@ -2766,14 +2673,14 @@ public class BigDecimal extends Number implements Comparable, Serial int discardedSize; long powerOfTwo = this.bitLength - (long)(scale / LOG10_2); long bits; // IEEE-754 Standard - long tempBits; // for temporal calculations + long tempBits; // for temporal calculations BigInteger mantisa; if ((powerOfTwo < -1074) || (sign == 0)) { - // Cases which 'this' is very small + // Cases which 'this' is very small return (sign * 0.0d); } else if (powerOfTwo > 1025) { - // Cases which 'this' is very large + // Cases which 'this' is very large return (sign * Double.POSITIVE_INFINITY); } mantisa = getUnscaledValue().abs(); @@ -2796,7 +2703,7 @@ public class BigDecimal extends Number implements Comparable, Serial // Computing (mantisa * 2^k) / 10^s quotAndRem = mantisa.divideAndRemainder(powerOfTen); // To check if the fractional part >= 0.5 - compRem = quotAndRem[1].shiftLeft(1).compareTo(powerOfTen); + compRem = quotAndRem[1].shiftLeftOneBit().compareTo(powerOfTen); // To add two rounded bits at end of mantisa mantisa = quotAndRem[0].shiftLeft(2).add( BigInteger.valueOf((compRem * (compRem + 3)) / 2 + 1)); @@ -2808,13 +2715,13 @@ public class BigDecimal extends Number implements Comparable, Serial // mantisa = (abs(u) * 10^s) >> (n - 54) bits = mantisa.shiftRight(discardedSize).longValue(); tempBits = bits; - // #bits = 54, to check if the discarded fraction produces a carry + // #bits = 54, to check if the discarded fraction produces a carry if ((((bits & 1) == 1) && (lowestSetBit < discardedSize)) || ((bits & 3) == 3)) { bits += 2; } } else {// (n <= 54) - // mantisa = (abs(u) * 10^s) << (54 - n) + // mantisa = (abs(u) * 10^s) << (54 - n) bits = mantisa.longValue() << -discardedSize; tempBits = bits; // #bits = 54, to check if the discarded fraction produces a carry: @@ -2832,7 +2739,7 @@ public class BigDecimal extends Number implements Comparable, Serial bits >>= 2; exponent += discardedSize + 1; } - // To test if the 53-bits number fits in 'double' + // To test if the 53-bits number fits in 'double' if (exponent > 2046) {// (exponent - bias > 1023) return (sign * Double.POSITIVE_INFINITY); } else if (exponent <= 0) {// (exponent - bias <= -1023) @@ -2840,7 +2747,7 @@ public class BigDecimal extends Number implements Comparable, Serial if (exponent < -53) {// exponent - bias < -1076 return (sign * 0.0d); } - // -1076 <= exponent - bias <= -1023 + // -1076 <= exponent - bias <= -1023 // To discard '- exponent + 1' bits bits = tempBits >> 1; tempBits = bits & (-1L >>> (63 + exponent)); @@ -2871,9 +2778,8 @@ public class BigDecimal extends Number implements Comparable, Serial * For class {@code BigDecimal}, the ULP of a number is simply 10^(-scale). *

          * For example, {@code new BigDecimal(0.1).ulp()} returns {@code 1E-55}. - * + * * @return unit in the last place (ULP) of this {@code BigDecimal} instance. - * @since Android 1.0 */ public BigDecimal ulp() { return valueOf(1, scale); @@ -2885,7 +2791,7 @@ public class BigDecimal extends Number implements Comparable, Serial * It does all rounding work of the public method * {@code round(MathContext)}, performing an inplace rounding * without creating a new object. - * + * * @param mc * the {@code MathContext} for perform the rounding. * @see #round(MathContext) @@ -2916,7 +2822,7 @@ public class BigDecimal extends Number implements Comparable, Serial // If the discarded fraction is non-zero, perform rounding if (integerAndFraction[1].signum() != 0) { // To check if the discarded fraction >= 0.5 - compRem = (integerAndFraction[1].abs().shiftLeft(1).compareTo(sizeOfFraction)); + compRem = (integerAndFraction[1].abs().shiftLeftOneBit().compareTo(sizeOfFraction)); // To look if there is a carry compRem = roundingBehavior( integerAndFraction[0].testBit(0) ? 1 : 0, integerAndFraction[1].signum() * (5 + compRem), @@ -2943,7 +2849,7 @@ public class BigDecimal extends Number implements Comparable, Serial /** * This method implements an efficient rounding for numbers which unscaled * value fits in the type {@code long}. - * + * * @param mc * the context to use * @param discardedPrecision @@ -2983,7 +2889,7 @@ public class BigDecimal extends Number implements Comparable, Serial /** * Return an increment that can be -1,0 or 1, depending of * {@code roundingMode}. - * + * * @param parityBit * can be 0 or 1, it's only used in the case * {@code HALF_EVEN} @@ -3039,7 +2945,7 @@ public class BigDecimal extends Number implements Comparable, Serial * the range of the primitive type. If the number fits in the primitive type * returns this number as {@code long}, otherwise throws an * exception. - * + * * @param bitLengthOfType * number of bits of the type whose value will be calculated * exactly @@ -3064,7 +2970,7 @@ public class BigDecimal extends Number implements Comparable, Serial * it calculates a very good approximation efficiently . Note that this * value will be {@code precision()} or {@code precision()-1} * in the worst case. - * + * * @return an approximation of {@code precision()} value */ private int aproxPrecision() { @@ -3079,7 +2985,7 @@ public class BigDecimal extends Number implements Comparable, Serial * It tests if a scale of type {@code long} fits in 32 bits. It * returns the same scale being casted to {@code int} type when is * possible, otherwise throws an exception. - * + * * @param longScale * a 64 bit scale * @return a 32 bit scale when is possible @@ -3106,7 +3012,7 @@ public class BigDecimal extends Number implements Comparable, Serial * {@code longScale < Integer.MIN_VALUE} the scale will be * {@code Integer.MIN_VALUE}; otherwise {@code longScale} is * casted to the type {@code int}. - * + * * @param longScale * the scale to which the value 0 will be scaled. * @return the value 0 scaled by the closer scale of type {@code int}. @@ -3152,7 +3058,7 @@ public class BigDecimal extends Number implements Comparable, Serial } return intVal; } - + private void setUnscaledValue(BigInteger unscaledValue) { this.intVal = unscaledValue; this.bitLength = unscaledValue.bitLength(); @@ -3160,19 +3066,19 @@ public class BigDecimal extends Number implements Comparable, Serial this.smallValue = unscaledValue.longValue(); } } - + private static int bitLength(long smallValue) { if(smallValue < 0) { smallValue = ~smallValue; } return 64 - Long.numberOfLeadingZeros(smallValue); } - + private static int bitLength(int smallValue) { if(smallValue < 0) { smallValue = ~smallValue; } return 32 - Integer.numberOfLeadingZeros(smallValue); } - + } diff --git a/libcore/math/src/main/java/java/math/BigInteger.java b/libcore/math/src/main/java/java/math/BigInteger.java index aece0787c..c463b4852 100644 --- a/libcore/math/src/main/java/java/math/BigInteger.java +++ b/libcore/math/src/main/java/java/math/BigInteger.java @@ -14,21 +14,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ // BEGIN android-note // Since the original Harmony Code of the BigInteger class was strongly modified, @@ -63,8 +48,6 @@ import org.apache.harmony.math.internal.nls.Messages; * Implementation Note:
          * The native OpenSSL library with its BIGNUM operations covers all the * meaningful functionality (everything but bit level operations). - * - * @since Android 1.0 */ public class BigInteger extends Number implements Comparable, Serializable { @@ -139,34 +122,24 @@ public class BigInteger extends Number implements Comparable, /** The magnitude of this in the little-endian representation. */ transient int digits[]; - /** - * The length of this in measured in ints. Can be less than digits.length(). - */ + /** The length of this in measured in ints. Can be less than digits.length(). */ transient int numberLength; /** The sign of this. */ transient int sign; - /* Static Fields */ - /** * The {@code BigInteger} constant 0. - * - * @since Android 1.0 */ public static final BigInteger ZERO = new BigInteger(0, 0); /** * The {@code BigInteger} constant 1. - * - * @since Android 1.0 */ public static final BigInteger ONE = new BigInteger(1, 1); /** * The {@code BigInteger} constant 10. - * - * @since Android 1.0 */ public static final BigInteger TEN = new BigInteger(1, 10); @@ -191,8 +164,6 @@ public class BigInteger extends Number implements Comparable, /**/ private transient int firstNonzeroDigit = -2; - /* Serialized Fields */ - /** sign field, used for serialization. */ private int signum; @@ -202,9 +173,6 @@ public class BigInteger extends Number implements Comparable, /** Cache for the hash code. */ private transient int hashCode = 0; - - /* Package Constructors */ - BigInteger(BigInt a) { bigInt = a; bigIntIsValid = true; @@ -239,9 +207,6 @@ public class BigInteger extends Number implements Comparable, withNewRepresentation("BigInteger(int sign, int numberLength, int[] digits)"); } - - /* Public Constructors */ - /** * Constructs a random non-negative {@code BigInteger} instance in the range * [0, 2^(numBits)-1]. @@ -252,8 +217,6 @@ public class BigInteger extends Number implements Comparable, * is an optional random generator to be used. * @throws IllegalArgumentException * if {@code numBits} < 0. - * - * @since Android 1.0 */ public BigInteger(int numBits, Random rnd) { if (numBits < 0) { @@ -299,8 +262,6 @@ public class BigInteger extends Number implements Comparable, * is an optional random generator to be used. * @throws ArithmeticException * if {@code bitLength} < 2. - * - * @since Android 1.0 */ public BigInteger(int bitLength, int certainty, Random rnd) { if (bitLength < 2) { @@ -324,8 +285,6 @@ public class BigInteger extends Number implements Comparable, * @throws NumberFormatException * if {@code val} is not a valid representation of a {@code * BigInteger}. - * - * @since Android 1.0 */ public BigInteger(String val) { bigInt = new BigInt(); @@ -351,8 +310,6 @@ public class BigInteger extends Number implements Comparable, * if {@code val} is not a valid representation of a {@code * BigInteger} or if {@code radix < Character.MIN_RADIX} or * {@code radix > Character.MAX_RADIX}. - * - * @since Android 1.0 */ public BigInteger(String val, int radix) { if (val == null) { @@ -399,8 +356,6 @@ public class BigInteger extends Number implements Comparable, * @throws NumberFormatException * if the sign is not one of -1, 0, 1 or if the sign is zero and * the magnitude contains non-zero entries. - * - * @since Android 1.0 */ public BigInteger(int signum, byte[] magnitude) { if (magnitude == null) { @@ -435,8 +390,6 @@ public class BigInteger extends Number implements Comparable, * if {@code val == null}. * @throws NumberFormatException * if the length of {@code val} is zero. - * - * @since Android 1.0 */ public BigInteger(byte[] val) { if (val.length == 0) { @@ -449,8 +402,6 @@ public class BigInteger extends Number implements Comparable, } - /* Public Methods */ - /** * Creates a new {@code BigInteger} whose value is equal to the specified * {@code long} argument. @@ -458,8 +409,6 @@ public class BigInteger extends Number implements Comparable, * @param val * the value of the new {@code BigInteger}. * @return {@code BigInteger} instance with the value {@code val}. - * - * @since Android 1.0 */ public static BigInteger valueOf(long val) { if (val < 0) { @@ -479,8 +428,6 @@ public class BigInteger extends Number implements Comparable, * array. * * @return two's complement representation of {@code this}. - * - * @since Android 1.0 */ public byte[] toByteArray() { return twosComplement(); @@ -491,8 +438,6 @@ public class BigInteger extends Number implements Comparable, * {@code this}. * * @return {@code abs(this)}. - * - * @since Android 1.0 */ public BigInteger abs() { validate1("abs()", this); @@ -508,8 +453,6 @@ public class BigInteger extends Number implements Comparable, * Returns a new {@code BigInteger} whose value is the {@code -this}. * * @return {@code -this}. - * - * @since Android 1.0 */ public BigInteger negate() { validate1("negate()", this); @@ -530,8 +473,6 @@ public class BigInteger extends Number implements Comparable, * @return {@code this + val}. * @throws NullPointerException * if {@code val == null}. - * - * @since Android 1.0 */ public BigInteger add(BigInteger val) { validate2("add", this, val); @@ -548,8 +489,6 @@ public class BigInteger extends Number implements Comparable, * @return {@code this - val}. * @throws NullPointerException * if {@code val == null}. - * - * @since Android 1.0 */ public BigInteger subtract(BigInteger val) { validate2("subtract", this, val); @@ -563,8 +502,6 @@ public class BigInteger extends Number implements Comparable, * @return {@code -1} if {@code this < 0}, * {@code 0} if {@code this == 0}, * {@code 1} if {@code this > 0}. - * - * @since Android 1.0 */ public int signum() { // Optimization to avoid unnecessary duplicate representation: @@ -586,8 +523,6 @@ public class BigInteger extends Number implements Comparable, * shift distance * @return {@code this >> n} if {@code n >= 0}; {@code this << (-n)} * otherwise - * - * @since Android 1.0 */ public BigInteger shiftRight(int n) { return shiftLeft(-n); @@ -606,8 +541,6 @@ public class BigInteger extends Number implements Comparable, * shift distance. * @return {@code this << n} if {@code n >= 0}; {@code this >> (-n)}. * otherwise - * - * @since Android 1.0 */ public BigInteger shiftLeft(int n) { if (n == 0) return this; @@ -626,6 +559,10 @@ public class BigInteger extends Number implements Comparable, } } + BigInteger shiftLeftOneBit() { + return (signum() == 0) ? this : BitLevel.shiftLeftOneBit(this); + } + /** * Returns the length of the value's two's complement representation without * leading zeros for positive numbers / without leading ones for negative @@ -639,8 +576,6 @@ public class BigInteger extends Number implements Comparable, * * @return the length of the minimal two's complement representation for * {@code this} without the sign bit. - * - * @since Android 1.0 */ public int bitLength() { // Optimization to avoid unnecessary duplicate representation: @@ -662,8 +597,6 @@ public class BigInteger extends Number implements Comparable, * @return {@code this & (2^n) != 0}. * @throws ArithmeticException * if {@code n < 0}. - * - * @since Android 1.0 */ public boolean testBit(int n) { if (n < 0) { @@ -717,8 +650,6 @@ public class BigInteger extends Number implements Comparable, * @return {@code this | 2^n}. * @throws ArithmeticException * if {@code n < 0}. - * - * @since Android 1.0 */ public BigInteger setBit(int n) { establishOldRepresentation("setBit"); @@ -742,8 +673,6 @@ public class BigInteger extends Number implements Comparable, * @return {@code this & ~(2^n)}. * @throws ArithmeticException * if {@code n < 0}. - * - * @since Android 1.0 */ public BigInteger clearBit(int n) { establishOldRepresentation("clearBit"); @@ -767,8 +696,6 @@ public class BigInteger extends Number implements Comparable, * @return {@code this ^ 2^n}. * @throws ArithmeticException * if {@code n < 0}. - * - * @since Android 1.0 */ public BigInteger flipBit(int n) { establishOldRepresentation("flipBit"); @@ -788,8 +715,6 @@ public class BigInteger extends Number implements Comparable, * the current implementation is not efficient. * * @return position of lowest bit if {@code this != 0}, {@code -1} otherwise - * - * @since Android 1.0 */ public int getLowestSetBit() { establishOldRepresentation("getLowestSetBit"); @@ -816,8 +741,6 @@ public class BigInteger extends Number implements Comparable, * * @return number of bits in the binary representation of {@code this} which * differ from the sign bit - * - * @since Android 1.0 */ public int bitCount() { establishOldRepresentation("bitCount"); @@ -832,8 +755,6 @@ public class BigInteger extends Number implements Comparable, * the current implementation is not efficient. * * @return {@code ~this}. - * - * @since Android 1.0 */ public BigInteger not() { this.establishOldRepresentation("not"); @@ -851,8 +772,6 @@ public class BigInteger extends Number implements Comparable, * @return {@code this & val}. * @throws NullPointerException * if {@code val == null}. - * - * @since Android 1.0 */ public BigInteger and(BigInteger val) { this.establishOldRepresentation("and1"); @@ -871,8 +790,6 @@ public class BigInteger extends Number implements Comparable, * @return {@code this | val}. * @throws NullPointerException * if {@code val == null}. - * - * @since Android 1.0 */ public BigInteger or(BigInteger val) { this.establishOldRepresentation("or1"); @@ -891,8 +808,6 @@ public class BigInteger extends Number implements Comparable, * @return {@code this ^ val} * @throws NullPointerException * if {@code val == null} - * - * @since Android 1.0 */ public BigInteger xor(BigInteger val) { this.establishOldRepresentation("xor1"); @@ -913,8 +828,6 @@ public class BigInteger extends Number implements Comparable, * @return {@code this & ~val}. * @throws NullPointerException * if {@code val == null}. - * - * @since Android 1.0 */ public BigInteger andNot(BigInteger val) { this.establishOldRepresentation("andNot1"); @@ -927,8 +840,6 @@ public class BigInteger extends Number implements Comparable, * big to be represented as an int, then {@code this} % 2^32 is returned. * * @return this {@code BigInteger} as an int value. - * - * @since Android 1.0 */ @Override public int intValue() { @@ -946,8 +857,6 @@ public class BigInteger extends Number implements Comparable, * big to be represented as an long, then {@code this} % 2^64 is returned. * * @return this {@code BigInteger} as a long value. - * - * @since Android 1.0 */ @Override public long longValue() { @@ -972,8 +881,6 @@ public class BigInteger extends Number implements Comparable, * 24. For example, 2^24+1 = 16777217 is returned as float 16777216.0. * * @return this {@code BigInteger} as a float value. - * - * @since Android 1.0 */ @Override public float floatValue() { @@ -991,8 +898,6 @@ public class BigInteger extends Number implements Comparable, * 9007199254740993 is returned as double 9007199254740992.0. * * @return this {@code BigInteger} as a double value - * - * @since Android 1.0 */ @Override public double doubleValue() { @@ -1010,8 +915,6 @@ public class BigInteger extends Number implements Comparable, * , {@code 0} if {@code this == val}. * @throws NullPointerException * if {@code val == null}. - * - * @since Android 1.0 */ public int compareTo(BigInteger val) { validate2("compareTo", this, val); @@ -1026,8 +929,6 @@ public class BigInteger extends Number implements Comparable, * @return {@code min(this, val)}. * @throws NullPointerException * if {@code val == null}. - * - * @since Android 1.0 */ public BigInteger min(BigInteger val) { return ((this.compareTo(val) == -1) ? this : val); @@ -1041,8 +942,6 @@ public class BigInteger extends Number implements Comparable, * @return {@code max(this, val)} * @throws NullPointerException * if {@code val == null} - * - * @since Android 1.0 */ public BigInteger max(BigInteger val) { return ((this.compareTo(val) == 1) ? this : val); @@ -1052,8 +951,6 @@ public class BigInteger extends Number implements Comparable, * Returns a hash code for this {@code BigInteger}. * * @return hash code for {@code this}. - * - * @since Android 1.0 */ @Override public int hashCode() { @@ -1077,8 +974,6 @@ public class BigInteger extends Number implements Comparable, * object to be compared with {@code this}. * @return true if {@code x} is a BigInteger and {@code this == x}, * {@code false} otherwise. - * - * @since Android 1.0 */ @Override public boolean equals(Object x) { @@ -1096,8 +991,6 @@ public class BigInteger extends Number implements Comparable, * form. * * @return a string representation of {@code this} in decimal form. - * - * @since Android 1.0 */ @Override public String toString() { @@ -1115,8 +1008,6 @@ public class BigInteger extends Number implements Comparable, * @param radix * base to be used for the string representation. * @return a string representation of this with radix 10. - * - * @since Android 1.0 */ public String toString(int radix) { validate1("toString(int radix)", this); @@ -1140,8 +1031,6 @@ public class BigInteger extends Number implements Comparable, * @return {@code gcd(this, val)}. * @throws NullPointerException * if {@code val == null}. - * - * @since Android 1.0 */ public BigInteger gcd(BigInteger val) { validate2("gcd", this, val); @@ -1156,8 +1045,6 @@ public class BigInteger extends Number implements Comparable, * @return {@code this * val}. * @throws NullPointerException * if {@code val == null}. - * - * @since Android 1.0 */ public BigInteger multiply(BigInteger val) { validate2("multiply", this, val); @@ -1172,8 +1059,6 @@ public class BigInteger extends Number implements Comparable, * @return {@code this ^ exp}. * @throws ArithmeticException * if {@code exp < 0}. - * - * @since Android 1.0 */ public BigInteger pow(int exp) { if (exp < 0) { @@ -1197,8 +1082,6 @@ public class BigInteger extends Number implements Comparable, * if {@code divisor == 0}. * @see #divide * @see #remainder - * -@since Android 1.0 */ public BigInteger[] divideAndRemainder(BigInteger divisor) { validate2("divideAndRemainder", this, divisor); @@ -1223,8 +1106,6 @@ public class BigInteger extends Number implements Comparable, * if {@code divisor == null}. * @throws ArithmeticException * if {@code divisor == 0}. - * - * @since Android 1.0 */ public BigInteger divide(BigInteger divisor) { validate2("divide", this, divisor); @@ -1245,8 +1126,6 @@ public class BigInteger extends Number implements Comparable, * if {@code divisor == null}. * @throws ArithmeticException * if {@code divisor == 0}. - * - * @since Android 1.0 */ public BigInteger remainder(BigInteger divisor) { validate2("remainder", this, divisor); @@ -1269,8 +1148,6 @@ public class BigInteger extends Number implements Comparable, * @throws ArithmeticException * if {@code m < 0 or} if {@code this} is not relatively prime * to {@code m} - * - * @since Android 1.0 */ public BigInteger modInverse(BigInteger m) { if (m.signum() <= 0) { @@ -1299,8 +1176,6 @@ public class BigInteger extends Number implements Comparable, * @throws ArithmeticException * if {@code m < 0} or if {@code exponent<0} and this is not * relatively prime to {@code m}. - * - * @since Android 1.0 */ public BigInteger modPow(BigInteger exponent, BigInteger m) { if (m.signum() <= 0) { @@ -1332,8 +1207,6 @@ public class BigInteger extends Number implements Comparable, * if {@code m == null}. * @throws ArithmeticException * if {@code m < 0}. - * - * @since Android 1.0 */ public BigInteger mod(BigInteger m) { if (m.signum() <= 0) { @@ -1355,8 +1228,6 @@ public class BigInteger extends Number implements Comparable, * tolerated primality uncertainty. * @return {@code true}, if {@code this} is probably prime, {@code false} * otherwise. - * - * @since Android 1.0 */ public boolean isProbablePrime(int certainty) { validate1("isProbablePrime", this); @@ -1371,8 +1242,6 @@ public class BigInteger extends Number implements Comparable, * @return smallest integer > {@code this} which is robably prime. * @throws ArithmeticException * if {@code this < 0}. - * - * @since Android 1.0 */ public BigInteger nextProbablePrime() { if (sign < 0) { @@ -1396,14 +1265,11 @@ public class BigInteger extends Number implements Comparable, * @return probably prime random {@code BigInteger} instance. * @throws IllegalArgumentException * if {@code bitLength < 2}. - * - * @since Android 1.0 */ public static BigInteger probablePrime(int bitLength, Random rnd) { return new BigInteger(bitLength, 100, rnd); } - /* Private Methods */ /** diff --git a/libcore/math/src/main/java/java/math/BitLevel.java b/libcore/math/src/main/java/java/math/BitLevel.java index ab4f9cc60..22a486cbf 100644 --- a/libcore/math/src/main/java/java/math/BitLevel.java +++ b/libcore/math/src/main/java/java/math/BitLevel.java @@ -31,16 +31,12 @@ package java.math; * * All operations are provided in immutable way, and some in both mutable and * immutable. - * - * @author Intel Middleware Product Division - * @author Instituto Tecnologico de Cordoba */ class BitLevel { /** Just to denote that this class can't be instantiated. */ private BitLevel() {} - /** @see BigInteger#bitLength() */ static int bitLength(BigInteger val) { // BEGIN android-added @@ -180,6 +176,31 @@ class BitLevel { // } // END android-removed + static void shiftLeftOneBit(int result[], int source[], int srcLen) { + int carry = 0; + for(int i = 0; i < srcLen; i++) { + int val = source[i]; + result[i] = (val << 1) | carry; + carry = val >>> 31; + } + if(carry != 0) { + result[srcLen] = carry; + } + } + + static BigInteger shiftLeftOneBit(BigInteger source) { + // BEGIN android-added + source.establishOldRepresentation("BitLevel.shiftLeftOneBit"); + // END android-added + int srcLen = source.numberLength; + int resLen = srcLen + 1; + int resDigits[] = new int[resLen]; + shiftLeftOneBit(resDigits, source.digits, srcLen); + BigInteger result = new BigInteger(source.sign, resLen, resDigits); + result.cutOffLeadingZeroes(); + return result; + } + /** @see BigInteger#shiftRight(int) */ static BigInteger shiftRight(BigInteger source, int count) { // BEGIN android-added diff --git a/libcore/math/src/main/java/java/math/Conversion.java b/libcore/math/src/main/java/java/math/Conversion.java index 65fc89624..a02edb528 100644 --- a/libcore/math/src/main/java/java/math/Conversion.java +++ b/libcore/math/src/main/java/java/math/Conversion.java @@ -20,9 +20,6 @@ package java.math; /** * Static library that provides {@link BigInteger} base conversion from/to any * integer represented in an {@link java.lang.String} Object. - * - * @author Intel Middleware Product Division - * @author Instituto Tecnologico de Cordoba */ class Conversion { diff --git a/libcore/math/src/main/java/java/math/Division.java b/libcore/math/src/main/java/java/math/Division.java index 35e365029..ce2519d02 100644 --- a/libcore/math/src/main/java/java/math/Division.java +++ b/libcore/math/src/main/java/java/math/Division.java @@ -25,7 +25,7 @@ package java.math; * Static library that provides all operations related with division and modular * arithmetic to {@link BigInteger}. Some methods are provided in both mutable * and immutable way. There are several variants provided listed below: - * + * *

            *
          • Division *
              @@ -41,9 +41,6 @@ package java.math; *
            *
          • *
          - * - * @author Intel Middleware Product Division - * @author Instituto Tecnologico de Cordoba */ class Division { @@ -55,7 +52,7 @@ class Division { /** * Divides an array by an integer value. Implements the Knuth's division * algorithm. See D. Knuth, The Art of Computer Programming, vol. 2. - * + * * @param dest the quotient * @param src the dividend * @param srcLength the length of the dividend @@ -108,7 +105,7 @@ class Division { /** * Divides an array by an integer value. Implements the Knuth's division * algorithm. See D. Knuth, The Art of Computer Programming, vol. 2. - * + * * @param src the dividend * @param srcLength the length of the dividend * @param divisor the divisor @@ -130,7 +127,7 @@ class Division { /** * Divides a BigInteger by a signed int and * returns the remainder. - * + * * @param dividend the BigInteger to be divided. Must be non-negative. * @param divisor a signed int * @return divide % divisor @@ -146,7 +143,7 @@ class Division { /** * Divides an unsigned long a by an unsigned int b. It is supposed that the * most significant bit of b is set to 1, i.e. b < 0 - * + * * @param a the dividend * @param b the divisor * @return the long value containing the unsigned integer remainder in the @@ -191,7 +188,7 @@ class Division { /** * Computes the quotient and the remainder after a division by an {@code int} * number. - * + * * @return an array of the form {@code [quotient, remainder]}. */ static BigInteger[] divideAndRemainderByInteger(BigInteger val, diff --git a/libcore/math/src/main/java/java/math/Logical.java b/libcore/math/src/main/java/java/math/Logical.java index a4c61e5f1..677f7f089 100644 --- a/libcore/math/src/main/java/java/math/Logical.java +++ b/libcore/math/src/main/java/java/math/Logical.java @@ -27,13 +27,11 @@ package java.math; *
        • or
        • *
        • xor
        • * - * @author Intel Middleware Product Division - * @author Instituto Tecnologico de Cordoba */ class Logical { /** Just to denote that this class can't be instantiated. */ - + private Logical() {} @@ -91,7 +89,7 @@ class Logical { if (val.equals(BigInteger.MINUS_ONE)) { return that; } - + if (val.sign > 0) { if (that.sign > 0) { return andPositive(val, that); @@ -108,22 +106,22 @@ class Logical { } } } - + /** @return sign = 1, magnitude = val.magnitude & that.magnitude*/ static BigInteger andPositive(BigInteger val, BigInteger that) { // PRE: both arguments are positive int resLength = Math.min(val.numberLength, that.numberLength); int i = Math.max(val.getFirstNonzeroDigit(), that.getFirstNonzeroDigit()); - + if (i >= resLength) { return BigInteger.ZERO; } - + int resDigits[] = new int[resLength]; for ( ; i < resLength; i++) { resDigits[i] = val.digits[i] & that.digits[i]; } - + BigInteger result = new BigInteger(1, resLength, resDigits); result.cutOffLeadingZeroes(); return result; @@ -134,7 +132,7 @@ class Logical { // PRE: positive is positive and negative is negative int iPos = positive.getFirstNonzeroDigit(); int iNeg = negative.getFirstNonzeroDigit(); - + // Look if the trailing zeros of the negative will "blank" all // the positive digits if (iNeg >= positive.numberLength) { @@ -142,7 +140,7 @@ class Logical { } int resLength = positive.numberLength; int resDigits[] = new int[resLength]; - + // Must start from max(iPos, iNeg) int i = Math.max(iPos, iNeg); if (i == iNeg) { @@ -160,24 +158,24 @@ class Logical { resDigits[i] = positive.digits[i]; } } // else positive ended and must "copy" virtual 0's, do nothing then - + BigInteger result = new BigInteger(1, resLength, resDigits); result.cutOffLeadingZeroes(); return result; } - + /** @return sign = -1, magnitude = -(-longer.magnitude & -shorter.magnitude)*/ static BigInteger andNegative(BigInteger longer, BigInteger shorter) { // PRE: longer and shorter are negative // PRE: longer has at least as many digits as shorter int iLonger = longer.getFirstNonzeroDigit(); int iShorter = shorter.getFirstNonzeroDigit(); - + // Does shorter matter? if (iLonger >= shorter.numberLength) { return longer; } - + int resLength; int resDigits[]; int i = Math.max(iShorter, iLonger); @@ -217,15 +215,13 @@ class Logical { for( ; i < longer.numberLength; i++){ resDigits[i] = longer.digits[i]; } - + BigInteger result = new BigInteger(-1, resLength, resDigits); return result; } - + /** @see BigInteger#andNot(BigInteger) */ static BigInteger andNot(BigInteger val, BigInteger that) { - // BEGIN android-changed - // copied from newer version of harmony if (that.sign == 0 ) { return val; } @@ -238,10 +234,10 @@ class Logical { if (that.equals(BigInteger.MINUS_ONE)){ return BigInteger.ZERO; } - + //if val == that, return 0 - - if (val.sign > 0) { + + if (val.sign > 0) { if (that.sign > 0) { return andNotPositive(val, that); } else { @@ -254,7 +250,6 @@ class Logical { return andNotNegative(val, that); } } - // END android-changed } /** @return sign = 1, magnitude = val.magnitude & ~that.magnitude*/ diff --git a/libcore/math/src/main/java/java/math/MathContext.java b/libcore/math/src/main/java/java/math/MathContext.java index 9e1314f36..cf1d4eb7b 100644 --- a/libcore/math/src/main/java/java/math/MathContext.java +++ b/libcore/math/src/main/java/java/math/MathContext.java @@ -27,23 +27,13 @@ import org.apache.harmony.math.internal.nls.Messages; /** * Immutable objects describing settings such as rounding mode and digit * precision for the numerical operations provided by class {@link BigDecimal}. - * - * @since Android 1.0 */ public final class MathContext implements Serializable { - // BEGIN android-note - // copied from newer version of harmony - // added final modifier - // END android-note - - /* Fields */ /** * A {@code MathContext} which corresponds to the IEEE 754r quadruple * decimal precision format: 34 digit precision and * {@link RoundingMode#HALF_EVEN} rounding. - * - * @since Android 1.0 */ public static final MathContext DECIMAL128 = new MathContext(34, RoundingMode.HALF_EVEN); @@ -52,8 +42,6 @@ public final class MathContext implements Serializable { * A {@code MathContext} which corresponds to the IEEE 754r single decimal * precision format: 7 digit precision and {@link RoundingMode#HALF_EVEN} * rounding. - * - * @since Android 1.0 */ public static final MathContext DECIMAL32 = new MathContext(7, RoundingMode.HALF_EVEN); @@ -62,8 +50,6 @@ public final class MathContext implements Serializable { * A {@code MathContext} which corresponds to the IEEE 754r double decimal * precision format: 16 digit precision and {@link RoundingMode#HALF_EVEN} * rounding. - * - * @since Android 1.0 */ public static final MathContext DECIMAL64 = new MathContext(16, RoundingMode.HALF_EVEN); @@ -71,8 +57,6 @@ public final class MathContext implements Serializable { /** * A {@code MathContext} for unlimited precision with * {@link RoundingMode#HALF_UP} rounding. - * - * @since Android 1.0 */ public static final MathContext UNLIMITED = new MathContext(0, RoundingMode.HALF_UP); @@ -96,7 +80,7 @@ public final class MathContext implements Serializable { * An array of {@code char} containing: {@code * 'p','r','e','c','i','s','i','o','n','='}. It's used to improve the * methods related to {@code String} conversion. - * + * * @see #MathContext(String) * @see #toString() */ @@ -107,32 +91,25 @@ public final class MathContext implements Serializable { * An array of {@code char} containing: {@code * 'r','o','u','n','d','i','n','g','M','o','d','e','='}. It's used to * improve the methods related to {@code String} conversion. - * + * * @see #MathContext(String) * @see #toString() */ private final static char[] chRoundingMode = { 'r', 'o', 'u', 'n', 'd', 'i', 'n', 'g', 'M', 'o', 'd', 'e', '=' }; - /* Constructors */ - /** * Constructs a new {@code MathContext} with the specified precision and * with the rounding mode {@link RoundingMode#HALF_UP HALF_UP}. If the * precision passed is zero, then this implies that the computations have to * be performed exact, the rounding mode in this case is irrelevant. - * + * * @param precision * the precision for the new {@code MathContext}. * @throws IllegalArgumentException * if {@code precision < 0}. - * - * @since Android 1.0 */ public MathContext(int precision) { - // BEGIN android-note - // parameter names changed. - // END android-note this(precision, RoundingMode.HALF_UP); } @@ -141,7 +118,7 @@ public final class MathContext implements Serializable { * with the specified rounding mode. If the precision passed is zero, then * this implies that the computations have to be performed exact, the * rounding mode in this case is irrelevant. - * + * * @param precision * the precision for the new {@code MathContext}. * @param roundingMode @@ -150,13 +127,8 @@ public final class MathContext implements Serializable { * if {@code precision < 0}. * @throws NullPointerException * if {@code roundingMode} is {@code null}. - * - * @since Android 1.0 */ public MathContext(int precision, RoundingMode roundingMode) { - // BEGIN android-note - // parameter names changed. - // END android-note if (precision < 0) { // math.0C=Digits < 0 throw new IllegalArgumentException(Messages.getString("math.0C")); //$NON-NLS-1$ @@ -175,15 +147,13 @@ public final class MathContext implements Serializable { * the following syntax: "precision=<precision> roundingMode=<roundingMode>" * This is the same form as the one returned by the {@link #toString} * method. - * + * * @param val * a string describing the precision and rounding mode for the * new {@code MathContext}. * @throws IllegalArgumentException * if the string is not in the correct format or if the * precision specified is < 0. - * - * @since Android 1.0 */ public MathContext(String val) { char[] charVal = val.toCharArray(); @@ -258,10 +228,8 @@ public final class MathContext implements Serializable { * guaranteed to be non negative. If the precision is zero, then the * computations have to be performed exact, results are not rounded in this * case. - * + * * @return the precision. - * - * @since Android 1.0 */ public int getPrecision() { return precision; @@ -270,20 +238,18 @@ public final class MathContext implements Serializable { /** * Returns the rounding mode. The rounding mode is the strategy to be used * to round results. - *

          - * The rounding mode is one of + *

          + * The rounding mode is one of * {@link RoundingMode#UP}, - * {@link RoundingMode#DOWN}, - * {@link RoundingMode#CEILING}, + * {@link RoundingMode#DOWN}, + * {@link RoundingMode#CEILING}, * {@link RoundingMode#FLOOR}, - * {@link RoundingMode#HALF_UP}, - * {@link RoundingMode#HALF_DOWN}, + * {@link RoundingMode#HALF_UP}, + * {@link RoundingMode#HALF_DOWN}, * {@link RoundingMode#HALF_EVEN}, or * {@link RoundingMode#UNNECESSARY}. - * + * * @return the rounding mode. - * - * @since Android 1.0 */ public RoundingMode getRoundingMode() { return roundingMode; @@ -292,13 +258,11 @@ public final class MathContext implements Serializable { /** * Returns true if x is a {@code MathContext} with the same precision * setting and the same rounding mode as this {@code MathContext} instance. - * + * * @param x * object to be compared. * @return {@code true} if this {@code MathContext} instance is equal to the * {@code x} argument; {@code false} otherwise. - * - * @since Android 1.0 */ @Override public boolean equals(Object x) { @@ -309,10 +273,8 @@ public final class MathContext implements Serializable { /** * Returns the hash code for this {@code MathContext} instance. - * + * * @return the hash code for this {@code MathContext}. - * - * @since Android 1.0 */ @Override public int hashCode() { @@ -322,15 +284,14 @@ public final class MathContext implements Serializable { /** * Returns the string representation for this {@code MathContext} instance. - * The string has the form + * The string has the form * {@code * "precision=<precision> roundingMode=<roundingMode>" - * } where {@code <precision>} is an integer describing the number + * } where {@code <precision>} is an integer describing the number * of digits used for operations and {@code <roundingMode>} is the * string representation of the rounding mode. - * + * * @return a string representation for this {@code MathContext} instance - * @since Android 1.0 */ @Override public String toString() { @@ -347,7 +308,7 @@ public final class MathContext implements Serializable { /** * Makes checks upon deserialization of a {@code MathContext} instance. * Checks whether {@code precision >= 0} and {@code roundingMode != null} - * + * * @throws StreamCorruptedException * if {@code precision < 0} * @throws StreamCorruptedException diff --git a/libcore/math/src/main/java/java/math/Multiplication.java b/libcore/math/src/main/java/java/math/Multiplication.java index ec222073f..05c460509 100644 --- a/libcore/math/src/main/java/java/math/Multiplication.java +++ b/libcore/math/src/main/java/java/math/Multiplication.java @@ -21,9 +21,6 @@ import org.apache.harmony.math.internal.nls.Messages; /** * Static library that provides all multiplication of {@link BigInteger} methods. - * - * @author Intel Middleware Product Division - * @author Instituto Tecnologico de Cordoba */ class Multiplication { @@ -45,7 +42,7 @@ class Multiplication { static final int tenPows[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; - + /** * An array with powers of five that fit in the type {@code int}. * ({@code 5^0,5^1,...,5^13}) @@ -66,13 +63,13 @@ class Multiplication { * ({@code 5^0,5^1,...,5^31}) */ static final BigInteger bigFivePows[] = new BigInteger[32]; - - + + static { int i; long fivePow = 1L; - + for (i = 0; i <= 18; i++) { bigFivePows[i] = BigInteger.valueOf(fivePow); bigTenPows[i] = BigInteger.valueOf(fivePow << i); @@ -115,7 +112,7 @@ class Multiplication { ? multiplyByPositiveInt(val, tenPows[(int)exp]) : val.multiply(powerOf10(exp))); } - + /** * It calculates a power of ten, which exponent could be out of 32-bit range. * Note that internally this method will be used in the worst case with @@ -143,7 +140,7 @@ class Multiplication { * estimated size, measured in bytes: 1 + [exp / log10(2)] */ long byteArraySize = 1 + (long)(exp / 2.4082399653118496); - + if (byteArraySize > Runtime.getRuntime().freeMemory()) { // math.01=power of ten too big throw new OutOfMemoryError(Messages.getString("math.01")); //$NON-NLS-1$ @@ -154,7 +151,7 @@ class Multiplication { } /* * "HUGE POWERS" - * + * * This branch probably won't be executed since the power of ten is too * big. */ @@ -162,7 +159,7 @@ class Multiplication { BigInteger powerOfFive = bigFivePows[1].pow(Integer.MAX_VALUE); BigInteger res = powerOfFive; long longExp = exp - Integer.MAX_VALUE; - + intExp = (int)(exp % Integer.MAX_VALUE); while (longExp > Integer.MAX_VALUE) { res = res.multiply(powerOfFive); @@ -179,7 +176,7 @@ class Multiplication { res = res.shiftLeft(intExp); return res; } - + /** * Multiplies a number by a power of five. * This method is used in {@code BigDecimal} class. diff --git a/libcore/math/src/main/java/java/math/Primality.java b/libcore/math/src/main/java/java/math/Primality.java index fd06b3b0d..48557a63c 100644 --- a/libcore/math/src/main/java/java/math/Primality.java +++ b/libcore/math/src/main/java/java/math/Primality.java @@ -14,21 +14,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ // BEGIN android-note // Since the original Harmony Code of the BigInteger class was strongly modified, @@ -40,13 +25,14 @@ package java.math; import java.util.Arrays; +/** + * Provides primality probabilistic methods. + */ class Primality { /** Just to denote that this class can't be instantiated. */ private Primality() {} - /* Private Fields */ - /** All prime numbers with bit length lesser than 10 bits. */ private static final int primes[] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, @@ -94,14 +80,12 @@ class Primality { } } - /* Package Methods */ - /** * It uses the sieve of Eratosthenes to discard several composite numbers in * some appropriate range (at the moment {@code [this, this + 1024]}). After * this process it applies the Miller-Rabin test to the numbers that were * not discarded in the sieve. - * + * * @see BigInteger#nextProbablePrime() * @see #millerRabin(BigInteger, int) */ diff --git a/libcore/math/src/main/java/java/math/RoundingMode.java b/libcore/math/src/main/java/java/math/RoundingMode.java index 84dcc52d3..b51564f3c 100644 --- a/libcore/math/src/main/java/java/math/RoundingMode.java +++ b/libcore/math/src/main/java/java/math/RoundingMode.java @@ -22,8 +22,6 @@ import org.apache.harmony.math.internal.nls.Messages; /** * Specifies the rounding behavior for operations whose results cannot be * represented exactly. - * - * @since Android 1.0 */ public enum RoundingMode { @@ -32,8 +30,6 @@ public enum RoundingMode { * and negative values towards negative infinity. *
          * Rule: {@code x.round().abs() >= x.abs()} - * - * @since Android 1.0 */ UP(BigDecimal.ROUND_UP), @@ -41,8 +37,6 @@ public enum RoundingMode { * Rounding mode where the values are rounded towards zero. *
          * Rule: {@code x.round().abs() <= x.abs()} - * - * @since Android 1.0 */ DOWN(BigDecimal.ROUND_DOWN), @@ -52,8 +46,6 @@ public enum RoundingMode { * {@link #DOWN}. *
          * Rule: {@code x.round() >= x} - * - * @since Android 1.0 */ CEILING(BigDecimal.ROUND_CEILING), @@ -63,32 +55,24 @@ public enum RoundingMode { * {@link #UP}. *
          * Rule: {@code x.round() <= x} - * - * @since Android 1.0 */ FLOOR(BigDecimal.ROUND_FLOOR), /** * Rounding mode where values are rounded towards the nearest neighbor. Ties * are broken by rounding up. - * - * @since Android 1.0 */ HALF_UP(BigDecimal.ROUND_HALF_UP), /** * Rounding mode where values are rounded towards the nearest neighbor. Ties * are broken by rounding down. - * - * @since Android 1.0 */ HALF_DOWN(BigDecimal.ROUND_HALF_DOWN), /** * Rounding mode where values are rounded towards the nearest neighbor. Ties * are broken by rounding to the even neighbor. - * - * @since Android 1.0 */ HALF_EVEN(BigDecimal.ROUND_HALF_EVEN), @@ -96,8 +80,6 @@ public enum RoundingMode { * Rounding mode where the rounding operations throws an ArithmeticException * for the case that rounding is necessary, i.e. for the case that the value * cannot be represented exactly. - * - * @since Android 1.0 */ UNNECESSARY(BigDecimal.ROUND_UNNECESSARY); @@ -112,17 +94,12 @@ public enum RoundingMode { /** * Converts rounding mode constants from class {@code BigDecimal} into * {@code RoundingMode} values. - * + * * @param mode * rounding mode constant as defined in class {@code BigDecimal} * @return corresponding rounding mode object - * - * @since Android 1.0 */ public static RoundingMode valueOf(int mode) { - // BEGIN android-note - // parameter name changed. - // END android-note switch (mode) { case BigDecimal.ROUND_CEILING: return CEILING; diff --git a/libcore/math/src/test/java/tests/api/java/math/BigDecimalTest.java b/libcore/math/src/test/java/tests/api/java/math/BigDecimalTest.java index 47e5b31d9..572f2c1dd 100644 --- a/libcore/math/src/test/java/tests/api/java/math/BigDecimalTest.java +++ b/libcore/math/src/test/java/tests/api/java/math/BigDecimalTest.java @@ -30,6 +30,7 @@ import java.io.ObjectOutputStream; import java.math.BigDecimal; import java.math.BigInteger; import java.math.RoundingMode; +import java.math.MathContext; @TestTargetClass(BigDecimal.class) public class BigDecimalTest extends junit.framework.TestCase { @@ -1310,6 +1311,41 @@ public class BigDecimalTest extends junit.framework.TestCase { BigDecimal zerotest = new BigDecimal("0.0000"); assertEquals("stripTrailingZero failed for 0.0000", 0, (zerotest.stripTrailingZeros()).scale() ); - } + } + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "", + method = "abs", + args = {MathContext.class} + ) + public void testMathContextConstruction() { + String a = "-12380945E+61"; + BigDecimal aNumber = new BigDecimal(a); + int precision = 6; + RoundingMode rm = RoundingMode.HALF_DOWN; + MathContext mcIntRm = new MathContext(precision, rm); + MathContext mcStr = new MathContext("precision=6 roundingMode=HALF_DOWN"); + MathContext mcInt = new MathContext(precision); + BigDecimal res = aNumber.abs(mcInt); + assertEquals("MathContext Constructor with int precision failed", + res, + new BigDecimal("1.23809E+68")); + + assertEquals("Equal MathContexts are not Equal ", + mcIntRm, + mcStr); + + assertEquals("Different MathContext are reported as Equal ", + mcInt.equals(mcStr), + false); + + assertEquals("Equal MathContexts have different hashcodes ", + mcIntRm.hashCode(), + mcStr.hashCode()); + + assertEquals("MathContext.toString() returning incorrect value", + mcIntRm.toString(), + "precision=6 roundingMode=HALF_DOWN"); + } } diff --git a/libcore/math/src/test/java/tests/api/java/math/BigIntegerTest.java b/libcore/math/src/test/java/tests/api/java/math/BigIntegerTest.java index c64063853..d04f7429e 100644 --- a/libcore/math/src/test/java/tests/api/java/math/BigIntegerTest.java +++ b/libcore/math/src/test/java/tests/api/java/math/BigIntegerTest.java @@ -1266,6 +1266,16 @@ public class BigIntegerTest extends junit.framework.TestCase { (i1.testBit(i) && !i2.testBit(i)) == res.testBit(i)); } } + + //regression for HARMONY-4653 + try{ + BigInteger.ZERO.andNot(null); + fail("should throw NPE"); + }catch(Exception e){ + //expected + } + BigInteger bi = new BigInteger(0, new byte[]{}); + assertEquals(BigInteger.ZERO, bi.andNot(BigInteger.ZERO)); } diff --git a/libcore/nio/src/main/java/org/apache/harmony/nio/internal/DatagramChannelImpl.java b/libcore/nio/src/main/java/org/apache/harmony/nio/internal/DatagramChannelImpl.java index ddd56e54b..ac8f77bef 100644 --- a/libcore/nio/src/main/java/org/apache/harmony/nio/internal/DatagramChannelImpl.java +++ b/libcore/nio/src/main/java/org/apache/harmony/nio/internal/DatagramChannelImpl.java @@ -15,14 +15,13 @@ * limitations under the License. */ -/* - * Android Notice - * In this class the address length was changed from long to int. - * This is due to performance optimizations for the device. - */ - package org.apache.harmony.nio.internal; +// BEGIN android-note +// Copied from a newer version of Harmony. +// In this class the address length was changed from long to int. +// END android-note + import java.io.FileDescriptor; import java.io.IOException; import java.io.InterruptedIOException; @@ -43,7 +42,7 @@ import java.nio.channels.NotYetConnectedException; import java.nio.channels.spi.SelectorProvider; import org.apache.harmony.luni.net.NetUtil; -import org.apache.harmony.luni.net.SocketImplProvider; +import org.apache.harmony.luni.net.PlainDatagramSocketImpl; import org.apache.harmony.luni.platform.FileDescriptorHandler; import org.apache.harmony.luni.platform.INetworkSystem; import org.apache.harmony.luni.platform.Platform; @@ -52,15 +51,10 @@ import org.apache.harmony.nio.AddressUtil; /* * The default implementation class of java.nio.channels.DatagramChannel. - * */ class DatagramChannelImpl extends DatagramChannel implements FileDescriptorHandler { - // ------------------------------------------------------------------- - // Class variables - // ------------------------------------------------------------------- - // The singleton to do the native network operation. private static final INetworkSystem networkSystem = Platform .getNetworkSystem(); @@ -72,10 +66,6 @@ class DatagramChannelImpl extends DatagramChannel implements private static final byte[] stubArray = new byte[0]; - // ------------------------------------------------------------------- - // Instance variables - // ------------------------------------------------------------------- - // The fd to interact with native code private FileDescriptor fd; @@ -94,19 +84,17 @@ class DatagramChannelImpl extends DatagramChannel implements // whether the socket is bound boolean isBound = false; - private final Object readLock = new Object(); + private static class ReadLock {} + private final Object readLock = new ReadLock(); - private final Object writeLock = new Object(); + private static class WriteLock {} + private final Object writeLock = new WriteLock(); // used to store the trafficClass value which is simply returned // as the value that was set. We also need it to pass it to methods // that specify an address packets are going to be sent to private int trafficClass = 0; - // ------------------------------------------------------------------- - // Constructor - // ------------------------------------------------------------------- - /* * Constructor */ @@ -120,16 +108,13 @@ class DatagramChannelImpl extends DatagramChannel implements /* * for native call */ - private DatagramChannelImpl(){ + @SuppressWarnings("unused") + private DatagramChannelImpl() { super(SelectorProvider.provider()); fd = new FileDescriptor(); connectAddress = new InetSocketAddress(0); } - // ------------------------------------------------------------------- - // Methods for getting internal DatagramSocket. - // ------------------------------------------------------------------- - /* * Getting the internal DatagramSocket If we have not the socket, we create * a new one. @@ -137,8 +122,8 @@ class DatagramChannelImpl extends DatagramChannel implements @Override synchronized public DatagramSocket socket() { if (null == socket) { - socket = new DatagramSocketAdapter(SocketImplProvider - .getDatagramSocketImpl(fd, localPort), this); + socket = new DatagramSocketAdapter( + new PlainDatagramSocketImpl(fd, localPort), this); } return socket; } @@ -155,12 +140,7 @@ class DatagramChannelImpl extends DatagramChannel implements .preferIPv6Addresses()); } - // ------------------------------------------------------------------- - // Methods for connect and disconnect - // ------------------------------------------------------------------- - - /* - * + /** * @see java.nio.channels.DatagramChannel#isConnected() */ @Override @@ -168,8 +148,7 @@ class DatagramChannelImpl extends DatagramChannel implements return connected; } - /* - * + /** * @see java.nio.channels.DatagramChannel#connect(java.net.SocketAddress) */ @Override @@ -214,8 +193,7 @@ class DatagramChannelImpl extends DatagramChannel implements return this; } - /* - * + /** * @see java.nio.channels.DatagramChannel#disconnect() */ @Override @@ -232,12 +210,7 @@ class DatagramChannelImpl extends DatagramChannel implements return this; } - // ------------------------------------------------------------------- - // Methods for send and receive - // ------------------------------------------------------------------- - - /* - * + /** * @see java.nio.channels.DatagramChannel#receive(java.nio.ByteBuffer) */ @Override @@ -284,14 +257,15 @@ class DatagramChannelImpl extends DatagramChannel implements .position() + target.arrayOffset(), target.remaining()); } else { - receivePacket = new DatagramPacket(new byte[target.remaining()], target.remaining()); + receivePacket = new DatagramPacket(new byte[target.remaining()], + target.remaining()); } do { if (isConnected()) { - received = networkSystem.recvConnectedDatagram(fd, receivePacket, - receivePacket.getData(), receivePacket.getOffset(), - receivePacket.getLength(), isBlocking() ? 0 - : DEFAULT_TIMEOUT, false); + received = networkSystem.recvConnectedDatagram(fd, + receivePacket, receivePacket.getData(), receivePacket + .getOffset(), receivePacket.getLength(), + isBlocking() ? 0 : DEFAULT_TIMEOUT, false); } else { received = networkSystem.receiveDatagram(fd, receivePacket, receivePacket.getData(), receivePacket.getOffset(), @@ -327,24 +301,23 @@ class DatagramChannelImpl extends DatagramChannel implements return retAddr; } - private SocketAddress receiveDirectImpl(ByteBuffer target, boolean loop) throws IOException - { - SocketAddress retAddr = null; - DatagramPacket receivePacket = new DatagramPacket( - stubArray, 0); + private SocketAddress receiveDirectImpl(ByteBuffer target, boolean loop) + throws IOException { + SocketAddress retAddr = null; + DatagramPacket receivePacket = new DatagramPacket(stubArray, 0); int oldposition = target.position(); int received = 0; do { int address = AddressUtil.getDirectBufferAddress(target); if (isConnected()) { - received = networkSystem.recvConnectedDatagramDirect(fd, receivePacket, - address, target.position(), - target.remaining(), isBlocking() ? 0 + received = networkSystem.recvConnectedDatagramDirect(fd, + receivePacket, address, target.position(), target + .remaining(), isBlocking() ? 0 : DEFAULT_TIMEOUT, false); } else { - received = networkSystem.receiveDatagramDirect(fd, receivePacket, - address, target.position(), - target.remaining(), isBlocking() ? 0 + received = networkSystem.receiveDatagramDirect(fd, + receivePacket, address, target.position(), target + .remaining(), isBlocking() ? 0 : DEFAULT_TIMEOUT, false); } @@ -352,15 +325,14 @@ class DatagramChannelImpl extends DatagramChannel implements SecurityManager sm = System.getSecurityManager(); if (!isConnected() && null != sm) { try { - sm.checkAccept(receivePacket.getAddress() - .getHostAddress(), receivePacket.getPort()); + sm.checkAccept(receivePacket.getAddress().getHostAddress(), + receivePacket.getPort()); } catch (SecurityException e) { // do discard the datagram packet receivePacket = null; } } - if (null != receivePacket - && null != receivePacket.getAddress()) { + if (null != receivePacket && null != receivePacket.getAddress()) { // copy the data of received packet if (received > 0) { target.position(oldposition + received); @@ -372,7 +344,7 @@ class DatagramChannelImpl extends DatagramChannel implements return retAddr; } - /* + /** * @see java.nio.channels.DatagramChannel#send(java.nio.ByteBuffer, * java.net.SocketAddress) */ @@ -445,12 +417,7 @@ class DatagramChannelImpl extends DatagramChannel implements } } - // ------------------------------------------------------------------- - // Methods for read and write. - // ------------------------------------------------------------------- - - /* - * + /** * @see java.nio.channels.DatagramChannel#read(java.nio.ByteBuffer) */ @Override @@ -467,10 +434,10 @@ class DatagramChannelImpl extends DatagramChannel implements return 0; } - int readCount = 0; + int readCount = 0; if (target.isDirect() || target.hasArray()) { readCount = readImpl(target); - if(readCount > 0){ + if (readCount > 0) { target.position(target.position() + readCount); } @@ -478,15 +445,14 @@ class DatagramChannelImpl extends DatagramChannel implements byte[] readArray = new byte[target.remaining()]; ByteBuffer readBuffer = ByteBuffer.wrap(readArray); readCount = readImpl(readBuffer); - if(readCount > 0){ + if (readCount > 0) { target.put(readArray, 0, readCount); } } return readCount; } - /* - * + /** * @see java.nio.channels.DatagramChannel#read(java.nio.ByteBuffer[], int, * int) */ @@ -530,7 +496,7 @@ class DatagramChannelImpl extends DatagramChannel implements * read from channel, and store the result in the target. */ private int readImpl(ByteBuffer readBuffer) throws IOException { - synchronized(readLock){ + synchronized (readLock) { int readCount = 0; try { begin(); @@ -542,8 +508,9 @@ class DatagramChannelImpl extends DatagramChannel implements if (readBuffer.isDirect()) { int address = AddressUtil.getDirectBufferAddress(readBuffer); if (isConnected()) { - readCount = networkSystem.recvConnectedDatagramDirect(fd, - null, address, start, length, timeout, false); + readCount = networkSystem.recvConnectedDatagramDirect( + fd, null, address, start, length, timeout, + false); } else { readCount = networkSystem.receiveDatagramDirect(fd, null, address, start, length, timeout, false); @@ -553,11 +520,11 @@ class DatagramChannelImpl extends DatagramChannel implements byte[] target = readBuffer.array(); start += readBuffer.arrayOffset(); if (isConnected()) { - readCount = networkSystem.recvConnectedDatagram(fd, null, - target, start, length, timeout, false); + readCount = networkSystem.recvConnectedDatagram(fd, + null, target, start, length, timeout, false); } else { - readCount = networkSystem.receiveDatagram(fd, null, target, - start, length, timeout, false); + readCount = networkSystem.receiveDatagram(fd, null, + target, start, length, timeout, false); } } return readCount; @@ -570,7 +537,7 @@ class DatagramChannelImpl extends DatagramChannel implements } } - /* + /** * @see java.nio.channels.DatagramChannel#write(java.nio.ByteBuffer) */ @Override @@ -602,7 +569,7 @@ class DatagramChannelImpl extends DatagramChannel implements return result; } - /* + /** * @see java.nio.channels.DatagramChannel#write(java.nio.ByteBuffer[], int, * int) */ @@ -621,7 +588,7 @@ class DatagramChannelImpl extends DatagramChannel implements return 0; } ByteBuffer writeBuf = ByteBuffer.allocate(count); - for (int val = offset; val < length+offset; val++) { + for (int val = offset; val < length + offset; val++) { ByteBuffer source = sources[val]; int oldPosition = source.position(); writeBuf.put(source); @@ -642,10 +609,10 @@ class DatagramChannelImpl extends DatagramChannel implements } /* - * write the source. return the count of bytes written. + * Write the source. Return the count of bytes written. */ private int writeImpl(ByteBuffer buf) throws IOException { - synchronized(writeLock){ + synchronized (writeLock) { int result = 0; try { begin(); @@ -654,13 +621,13 @@ class DatagramChannelImpl extends DatagramChannel implements if (buf.isDirect()) { int address = AddressUtil.getDirectBufferAddress(buf); - result = networkSystem.sendConnectedDatagramDirect(fd, address, - start, length, isBound); + result = networkSystem.sendConnectedDatagramDirect(fd, + address, start, length, isBound); } else { // buf is assured to have array. start += buf.arrayOffset(); - result = networkSystem.sendConnectedDatagram(fd, buf.array(), - start, length, isBound); + result = networkSystem.sendConnectedDatagram(fd, buf + .array(), start, length, isBound); } return result; } catch (SocketException e) { @@ -677,12 +644,8 @@ class DatagramChannelImpl extends DatagramChannel implements } } - // ------------------------------------------------------------------- - // Protected Inherited methods - // ------------------------------------------------------------------- - /* - * do really closing action here + * Do really closing action here. */ @Override synchronized protected void implCloseSelectableChannel() throws IOException { @@ -694,8 +657,7 @@ class DatagramChannelImpl extends DatagramChannel implements } } - /* - * + /** * @see java.nio.channels.spi.AbstractSelectableChannel#implConfigureBlocking(boolean) */ @Override @@ -707,12 +669,8 @@ class DatagramChannelImpl extends DatagramChannel implements // decided by isBlocking() method. } - // ------------------------------------------------------------------- - // Share methods for checking. - // ------------------------------------------------------------------- - /* - * status check, must be open. + * Status check, must be open. */ private void checkOpen() throws IOException { if (!isOpen()) { @@ -721,7 +679,7 @@ class DatagramChannelImpl extends DatagramChannel implements } /* - * status check, must be open and connected, for read and write. + * Status check, must be open and connected, for read and write. */ private void checkOpenConnected() throws IOException { checkOpen(); @@ -731,7 +689,7 @@ class DatagramChannelImpl extends DatagramChannel implements } /* - * buffer check, must not null + * Buffer check, must not null */ private void checkNotNull(ByteBuffer source) { if (null == source) { @@ -740,7 +698,7 @@ class DatagramChannelImpl extends DatagramChannel implements } /* - * buffer check, must not null and not read only buffer, for read and + * Buffer check, must not null and not read only buffer, for read and * receive. */ private void checkWritable(ByteBuffer target) { @@ -750,12 +708,8 @@ class DatagramChannelImpl extends DatagramChannel implements } } - // ------------------------------------------------------------------- - // Adapter classes for internal socket. - // ------------------------------------------------------------------- - /* - * get the fd for internal use. + * Get the fd for internal use. */ public FileDescriptor getFD() { return fd; @@ -781,7 +735,7 @@ class DatagramChannelImpl extends DatagramChannel implements private DatagramChannelImpl channelImpl; /* - * init the datagramSocketImpl and datagramChannelImpl + * Constructor initialize the datagramSocketImpl and datagramChannelImpl */ DatagramSocketAdapter(DatagramSocketImpl socketimpl, DatagramChannelImpl channelImpl) { @@ -790,14 +744,14 @@ class DatagramChannelImpl extends DatagramChannel implements } /* - * get the internal datagramChannelImpl + * Get the internal datagramChannelImpl */ @Override public DatagramChannel getChannel() { return channelImpl; } - /* + /** * @see java.net.DatagramSocket#isBound() */ @Override @@ -805,7 +759,7 @@ class DatagramChannelImpl extends DatagramChannel implements return channelImpl.isBound; } - /* + /** * @see java.net.DatagramSocket#isConnected() */ @Override @@ -813,7 +767,7 @@ class DatagramChannelImpl extends DatagramChannel implements return channelImpl.isConnected(); } - /* + /** * @see java.net.DatagramSocket#getInetAddress() */ @Override @@ -824,7 +778,7 @@ class DatagramChannelImpl extends DatagramChannel implements return channelImpl.connectAddress.getAddress(); } - /* + /** * @see java.net.DatagramSocket#getLocalAddress() */ @Override @@ -832,7 +786,7 @@ class DatagramChannelImpl extends DatagramChannel implements return channelImpl.getLocalAddress(); } - /* + /** * @see java.net.DatagramSocket#getPort() */ @Override @@ -843,7 +797,7 @@ class DatagramChannelImpl extends DatagramChannel implements return channelImpl.connectAddress.getPort(); } - /* + /** * @see java.net.DatagramSocket#bind(java.net.SocketAddress) */ @Override @@ -855,7 +809,7 @@ class DatagramChannelImpl extends DatagramChannel implements channelImpl.isBound = true; } - /* + /** * @see java.net.DatagramSocket#receive(java.net.DatagramPacket) */ @Override @@ -866,7 +820,7 @@ class DatagramChannelImpl extends DatagramChannel implements super.receive(packet); } - /* + /** * @see java.net.DatagramSocket#send(java.net.DatagramPacket) */ @Override @@ -877,7 +831,7 @@ class DatagramChannelImpl extends DatagramChannel implements super.send(packet); } - /* + /** * @see java.net.DatagramSocket#close() */ @Override @@ -894,7 +848,7 @@ class DatagramChannelImpl extends DatagramChannel implements } } - /* + /** * @see java.net.DatagramSocket#disconnect() */ @Override diff --git a/libcore/nio/src/main/java/org/apache/harmony/nio/internal/ServerSocketChannelImpl.java b/libcore/nio/src/main/java/org/apache/harmony/nio/internal/ServerSocketChannelImpl.java index 3bc368fe5..dbef6564f 100644 --- a/libcore/nio/src/main/java/org/apache/harmony/nio/internal/ServerSocketChannelImpl.java +++ b/libcore/nio/src/main/java/org/apache/harmony/nio/internal/ServerSocketChannelImpl.java @@ -17,6 +17,10 @@ package org.apache.harmony.nio.internal; +// BEGIN android-note +// Copied from a newer version of Harmony. +// END android-note + import java.io.FileDescriptor; import java.io.IOException; import java.net.ServerSocket; @@ -27,13 +31,12 @@ import java.net.SocketTimeoutException; import java.nio.channels.ClosedChannelException; import java.nio.channels.IllegalBlockingModeException; import java.nio.channels.NotYetBoundException; -import java.nio.channels.SelectableChannel; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.nio.channels.spi.SelectorProvider; import org.apache.harmony.luni.net.NetUtil; -import org.apache.harmony.luni.net.SocketImplProvider; +import org.apache.harmony.luni.net.PlainServerSocketImpl; import org.apache.harmony.luni.platform.FileDescriptorHandler; import org.apache.harmony.luni.platform.Platform; @@ -43,10 +46,6 @@ import org.apache.harmony.luni.platform.Platform; public class ServerSocketChannelImpl extends ServerSocketChannel implements FileDescriptorHandler { - // ---------------------------------------------------- - // Class variables - // ---------------------------------------------------- - // status un-init, not initialized. private static final int SERVER_STATUS_UNINIT = -1; @@ -56,10 +55,6 @@ public class ServerSocketChannelImpl extends ServerSocketChannel implements // status closed. private static final int SERVER_STATUS_CLOSED = 1; - // ------------------------------------------------------------------- - // Instance variables - // ------------------------------------------------------------------- - // The fd to interact with native code private final FileDescriptor fd; @@ -74,13 +69,9 @@ public class ServerSocketChannelImpl extends ServerSocketChannel implements boolean isBound = false; // lock for accept - private class AcceptLock {} + private static class AcceptLock {} private final Object acceptLock = new AcceptLock(); - // ---------------------------------------------------- - // Constructor - // ---------------------------------------------------- - /* * Constructor */ @@ -88,26 +79,23 @@ public class ServerSocketChannelImpl extends ServerSocketChannel implements super(sp); status = SERVER_STATUS_OPEN; fd = new FileDescriptor(); - Platform.getNetworkSystem().createServerStreamSocket(fd, + Platform.getNetworkSystem().createStreamSocket(fd, NetUtil.preferIPv4Stack()); - impl = SocketImplProvider.getServerSocketImpl(fd); + impl = new PlainServerSocketImpl(fd); socket = new ServerSocketAdapter(impl, this); } // for native call + @SuppressWarnings("unused") private ServerSocketChannelImpl() throws IOException { super(SelectorProvider.provider()); status = SERVER_STATUS_OPEN; fd = new FileDescriptor(); - impl = SocketImplProvider.getServerSocketImpl(fd); + impl = new PlainServerSocketImpl(fd); socket = new ServerSocketAdapter(impl, this); isBound = false; } - // ---------------------------------------------------- - // Methods - // ---------------------------------------------------- - /* * Getting the internal Socket If we have not the socket, we create a new * one. @@ -128,7 +116,7 @@ public class ServerSocketChannelImpl extends ServerSocketChannel implements throw new NotYetBoundException(); } - SocketChannel sockChannel = SocketChannel.open(); + SocketChannel sockChannel = new SocketChannelImpl(SelectorProvider.provider(), false); Socket socketGot = sockChannel.socket(); try { @@ -168,10 +156,6 @@ public class ServerSocketChannelImpl extends ServerSocketChannel implements return sockChannel; } - // ------------------------------------------------------------------- - // Protected inherited methods - // ------------------------------------------------------------------- - /* * @see java.nio.channels.spi.AbstractSelectableChannel#implConfigureBlocking * @@ -202,10 +186,6 @@ public class ServerSocketChannelImpl extends ServerSocketChannel implements return fd; } - // ---------------------------------------------------- - // Adapter classes. - // ---------------------------------------------------- - /* * The adapter class of ServerSocket. */ @@ -264,10 +244,7 @@ public class ServerSocketChannelImpl extends ServerSocketChannel implements synchronized (this) { super.implAccept(aSocket); sockChannel.setConnected(); - // BEGIN android-added - // copied from a newer version of harmony sockChannel.setBound(true); - // END android-added } SecurityManager sm = System.getSecurityManager(); if (sm != null) { diff --git a/libcore/nio/src/main/java/org/apache/harmony/nio/internal/SocketChannelImpl.java b/libcore/nio/src/main/java/org/apache/harmony/nio/internal/SocketChannelImpl.java index 8e6c52ff9..1affb219e 100644 --- a/libcore/nio/src/main/java/org/apache/harmony/nio/internal/SocketChannelImpl.java +++ b/libcore/nio/src/main/java/org/apache/harmony/nio/internal/SocketChannelImpl.java @@ -17,6 +17,11 @@ package org.apache.harmony.nio.internal; +// BEGIN android-note +// Copied from a newer version of Harmony. +// In this class the address length was changed from long to int. +// END android-note + import java.io.FileDescriptor; import java.io.IOException; import java.io.InputStream; @@ -42,25 +47,20 @@ import java.nio.channels.UnresolvedAddressException; import java.nio.channels.UnsupportedAddressTypeException; import java.nio.channels.spi.SelectorProvider; -import org.apache.harmony.luni.net.SocketImplProvider; +import org.apache.harmony.luni.net.PlainSocketImpl; import org.apache.harmony.luni.platform.FileDescriptorHandler; import org.apache.harmony.luni.platform.INetworkSystem; import org.apache.harmony.luni.platform.Platform; import org.apache.harmony.luni.util.ErrorCodeException; +import org.apache.harmony.luni.util.Msg; import org.apache.harmony.nio.AddressUtil; import org.apache.harmony.nio.internal.nls.Messages; /* - * * The default implementation class of java.nio.channels.SocketChannel. - * */ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { - // ------------------------------------------------------------------- - // Class variables - // ------------------------------------------------------------------- - private static final int EOF = -1; private static final int ERRCODE_SOCKET_NONBLOCKING_WOULD_BLOCK = -211; @@ -68,41 +68,37 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { // The singleton to do the native network operation. static final INetworkSystem networkSystem = Platform.getNetworkSystem(); - // status un-init, not initialized. + // Status un-init, not initialized. static final int SOCKET_STATUS_UNINIT = EOF; - // status before connect. + // Status before connect. static final int SOCKET_STATUS_UNCONNECTED = 0; - // status connection pending + // Status connection pending. static final int SOCKET_STATUS_PENDING = 1; - // status after connection success + // Status after connection success. static final int SOCKET_STATUS_CONNECTED = 2; - // status closed. + // Status closed. static final int SOCKET_STATUS_CLOSED = 3; - // timeout used for non-block mode. + // Timeout used for non-block mode. private static final int TIMEOUT_NONBLOCK = 0; - // timeout used for block mode. + // Timeout used for block mode. private static final int TIMEOUT_BLOCK = EOF; - // step used for connect + // Step used for connect. private static final int HY_SOCK_STEP_START = 0; - // step used for finishConnect + // Step used for finishConnect. private static final int HY_PORT_SOCKET_STEP_CHECK = 1; - // connect success + // Connect success. private static final int CONNECT_SUCCESS = 0; - // ------------------------------------------------------------------- - // Instance Variables - // ------------------------------------------------------------------- - - // The fd to interact with native code + // The descriptor to interact with native code. FileDescriptor fd; // Our internal Socket. @@ -111,24 +107,23 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { // The address to be connected. InetSocketAddress connectAddress = null; - // Local address of the this socket (package private for adapter) + // Local address of the this socket (package private for adapter). InetAddress localAddress = null; - // local port + // Local port number. int localPort; // At first, uninitialized. int status = SOCKET_STATUS_UNINIT; - // BEGIN android-changed - // copied from a newer version of harmony - // whether the socket is bound + // Whether the socket is bound. volatile boolean isBound = false; - // END adroid-changed - private final Object readLock = new Object(); + private static class ReadLock {} + private final Object readLock = new ReadLock(); - private final Object writeLock = new Object(); + private static class WriteLock {} + private final Object writeLock = new WriteLock(); // BEGIN android-changed // this content is a struct used in connect_withtimeout(). @@ -136,30 +131,37 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { private byte[] connectContext = new byte[392]; // END android-changed - // used to store the trafficClass value which is simply returned + // Used to store the trafficClass value which is simply returned // as the value that was set. We also need it to pass it to methods - // that specify an address packets are going to be sent to + // that specify an address packets are going to be sent to. private int trafficClass = 0; - // ------------------------------------------------------------------- - // Constructor - // ------------------------------------------------------------------- - /* - * Constructor + * Constructor for creating a connected socket channel. */ public SocketChannelImpl(SelectorProvider selectorProvider) throws IOException { + this(selectorProvider, true); + } + + /* + * Constructor for creating an optionally connected socket channel. + */ + public SocketChannelImpl(SelectorProvider selectorProvider, boolean connect) + throws IOException { super(selectorProvider); fd = new FileDescriptor(); status = SOCKET_STATUS_UNCONNECTED; - networkSystem.createSocket(fd, true); + if (connect) { + networkSystem.createStreamSocket(fd, true); + } } /* - * for native call + * For native call. */ - private SocketChannelImpl(){ + @SuppressWarnings("unused") + private SocketChannelImpl() { super(SelectorProvider.provider()); fd = new FileDescriptor(); connectAddress = new InetSocketAddress(0); @@ -186,10 +188,6 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { status = SOCKET_STATUS_UNCONNECTED; } - // ------------------------------------------------------------------- - // Methods for getting internal Socket. - // ------------------------------------------------------------------- - /* * Getting the internal Socket If we have not the socket, we create a new * one. @@ -204,8 +202,8 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { addr = connectAddress.getAddress(); port = connectAddress.getPort(); } - socket = new SocketAdapter(SocketImplProvider.getSocketImpl(fd, - localPort, addr, port), this); + socket = new SocketAdapter( + new PlainSocketImpl(fd, localPort, addr, port), this); } catch (SocketException e) { return null; } @@ -213,11 +211,7 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { return socket; } - // ------------------------------------------------------------------- - // Methods for connect and finishConnect - // ------------------------------------------------------------------- - - /* + /** * @see java.nio.channels.SocketChannel#isConnected() */ @Override @@ -226,20 +220,17 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { } /* - * status setting used by other class. + * Status setting used by other class. */ synchronized void setConnected() { status = SOCKET_STATUS_CONNECTED; } - // BEGIN android-added - // copied from a newer version of harmony void setBound(boolean flag) { isBound = flag; } - // END android-added - /* + /** * @see java.nio.channels.SocketChannel#isConnectionPending() */ @Override @@ -247,7 +238,7 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { return status == SOCKET_STATUS_PENDING; } - /* + /** * @see java.nio.channels.SocketChannel#connect(java.net.SocketAddress) */ @Override @@ -257,9 +248,15 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { // check the address InetSocketAddress inetSocketAddress = validateAddress(socketAddress); + InetAddress normalAddr = inetSocketAddress.getAddress(); + + // When connecting, map ANY address to Localhost + if (normalAddr.isAnyLocalAddress()) { + normalAddr = InetAddress.getLocalHost(); + } int port = inetSocketAddress.getPort(); - String hostName = inetSocketAddress.getAddress().getHostName(); + String hostName = normalAddr.getHostName(); // security check SecurityManager sm = System.getSecurityManager(); if (sm != null) { @@ -271,23 +268,12 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { boolean finished = false; try { - if (!isBound) { - // bind - networkSystem.bind2(fd, 0, true, InetAddress - .getByAddress(new byte[] { 0, 0, 0, 0 })); - isBound = true; - } - if (isBlocking()) { begin(); - result = networkSystem.connect(fd, trafficClass, - inetSocketAddress.getAddress(), inetSocketAddress - .getPort()); - + result = networkSystem.connect(fd, trafficClass, normalAddr, port); } else { result = networkSystem.connectWithTimeout(fd, 0, trafficClass, - inetSocketAddress.getAddress(), inetSocketAddress - .getPort(), HY_SOCK_STEP_START, connectContext); + normalAddr, port, HY_SOCK_STEP_START, connectContext); // set back to nonblocking to work around with a bug in portlib if (!this.isBlocking()) { networkSystem.setNonBlocking(fd, true); @@ -328,7 +314,7 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { return finished; } - /* + /** * @see java.nio.channels.SocketChannel#finishConnect() */ @Override @@ -372,14 +358,13 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { synchronized (this) { status = (finished ? SOCKET_STATUS_CONNECTED : status); isBound = finished; + // TPE: Workaround for bug that turns socket back to blocking + if (!isBlocking()) implConfigureBlocking(false); } return finished; } - // ------------------------------------------------------------------- - // Methods for read and write - // ------------------------------------------------------------------- - /* + /** * @see java.nio.channels.SocketChannel#read(java.nio.ByteBuffer) */ @Override @@ -411,7 +396,7 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { return readCount; } - /* + /** * @see java.nio.channels.SocketChannel#read(java.nio.ByteBuffer[], int, * int) */ @@ -449,16 +434,17 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { private boolean isIndexValid(ByteBuffer[] targets, int offset, int length) { return (length >= 0) && (offset >= 0) - && ((long)length + (long)offset <= targets.length); + && ((long) length + (long) offset <= targets.length); } - /* - * read from channel, and store the result in the target. + /** + * Read from channel, and store the result in the target. * - * @param target output parameter + * @param target + * output parameter */ private int readImpl(ByteBuffer target) throws IOException { - synchronized(readLock){ + synchronized (readLock) { int readCount = 0; try { if (isBlocking()) { @@ -467,10 +453,13 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { int offset = target.position(); int length = target.remaining(); if (target.isDirect()) { + // BEGIN android-changed + // changed address from long to int; split address and offset paramters int address = AddressUtil.getDirectBufferAddress(target); readCount = networkSystem.readDirect(fd, address, offset, length, (isBlocking() ? TIMEOUT_BLOCK : TIMEOUT_NONBLOCK)); + // END android-changed } else { // target is assured to have array. byte[] array = target.array(); @@ -487,8 +476,7 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { } } - /* - * + /** * @see java.nio.channels.SocketChannel#write(java.nio.ByteBuffer) */ @Override @@ -503,7 +491,7 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { return writeImpl(source); } - /* + /** * @see java.nio.channels.SocketChannel#write(java.nio.ByteBuffer[], int, * int) */ @@ -520,7 +508,7 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { return 0; } ByteBuffer writeBuf = ByteBuffer.allocate(count); - for (int val = offset; val < length+offset; val++) { + for (int val = offset; val < length + offset; val++) { ByteBuffer source = sources[val]; int oldPosition = source.position(); writeBuf.put(source); @@ -550,10 +538,10 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { } /* - * write the source. return the count of bytes written. + * Write the source. return the count of bytes written. */ private int writeImpl(ByteBuffer source) throws IOException { - synchronized(writeLock){ + synchronized (writeLock) { if (!source.hasRemaining()) { return 0; } @@ -565,9 +553,12 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { begin(); } if (source.isDirect()) { + // BEGIN android-changed + // changed address from long to int; split address and pos parameters int address = AddressUtil.getDirectBufferAddress(source); - writeCount = networkSystem - .writeDirect(fd, address, pos, length); + writeCount = networkSystem.writeDirect(fd, address, pos, + length); + // END android-changed } else if (source.hasArray()) { pos += source.arrayOffset(); writeCount = networkSystem.write(fd, source.array(), pos, @@ -595,12 +586,8 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { } } - // ------------------------------------------------------------------- - // Shared methods - // ------------------------------------------------------------------- - /* - * status check, open and "connected", when read and write. + * Status check, open and "connected", when read and write. */ synchronized private void checkOpenConnected() throws ClosedChannelException { @@ -613,7 +600,7 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { } /* - * status check, open and "unconnected", before connection. + * Status check, open and "unconnected", before connection. */ synchronized private void checkUnconnected() throws IOException { if (!isOpen()) { @@ -628,7 +615,7 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { } /* - * shared by this class and DatagramChannelImpl, to do the address transfer + * Shared by this class and DatagramChannelImpl, to do the address transfer * and check. */ static InetSocketAddress validateAddress(SocketAddress socketAddress) { @@ -646,7 +633,7 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { } /* - * get local address + * Get local address. */ public InetAddress getLocalAddress() throws UnknownHostException { byte[] any_bytes = { 0, 0, 0, 0 }; @@ -656,11 +643,8 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { return localAddress; } - // ------------------------------------------------------------------- - // Protected inherited methods - // ------------------------------------------------------------------- /* - * do really closing action here + * Do really closing action here. */ @Override synchronized protected void implCloseSelectableChannel() throws IOException { @@ -674,7 +658,7 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { } } - /* + /** * @see java.nio.channels.spi.AbstractSelectableChannel#implConfigureBlocking(boolean) */ @Override @@ -685,30 +669,21 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { } /* - * get the fd + * Get the fd. */ public FileDescriptor getFD() { return fd; } - // ------------------------------------------------------------------- - // Adapter classes for internal socket. - // ------------------------------------------------------------------- - + /* + * Adapter classes for internal socket. + */ private static class SocketAdapter extends Socket { - // ---------------------------------------------------- - // Class Variables - // ---------------------------------------------------- - SocketChannelImpl channel; SocketImpl socketImpl; - // ---------------------------------------------------- - // Methods - // ---------------------------------------------------- - SocketAdapter(SocketImpl socketimpl, SocketChannelImpl channel) throws SocketException { super(socketimpl); @@ -716,8 +691,7 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { this.channel = channel; } - /* - * + /** * @see java.net.Socket#getChannel() */ @Override @@ -725,8 +699,7 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { return channel; } - /* - * + /** * @see java.net.Socket#isBound() */ @Override @@ -734,8 +707,7 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { return channel.isBound; } - /* - * + /** * @see java.net.Socket#isConnected() */ @Override @@ -743,8 +715,7 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { return channel.isConnected(); } - /* - * + /** * @see java.net.Socket#getLocalAddress() */ @Override @@ -756,8 +727,7 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { } } - /* - * + /** * @see java.net.Socket#connect(java.net.SocketAddress, int) */ @Override @@ -778,8 +748,7 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { } } - /* - * + /** * @see java.net.Socket#bind(java.net.SocketAddress) */ @Override @@ -798,8 +767,7 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { } - /* - * + /** * @see java.net.Socket#close() */ @Override @@ -849,8 +817,7 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { .intValue(); } - /* - * + /** * @see java.net.Socket#getKeepAlive() */ @Override @@ -860,8 +827,7 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { .booleanValue(); } - /* - * + /** * @see java.net.Socket#getOOBInline() */ @Override @@ -871,8 +837,7 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { .booleanValue(); } - /* - * + /** * @see java.net.Socket#getSoLinger() */ @Override @@ -882,7 +847,7 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { .intValue(); } - /* + /** * @see java.net.Socket#getTcpNoDelay() */ @Override @@ -892,7 +857,82 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { .booleanValue(); } - /* + @Override + public void setKeepAlive(boolean value) throws SocketException { + checkOpen(); + socketImpl.setOption(SocketOptions.SO_KEEPALIVE, value ? Boolean.TRUE + : Boolean.FALSE); + } + + @Override + public void setOOBInline(boolean oobinline) throws SocketException { + checkOpen(); + socketImpl.setOption(SocketOptions.SO_OOBINLINE, oobinline ? Boolean.TRUE + : Boolean.FALSE); + } + + @Override + public synchronized void setReceiveBufferSize(int size) + throws SocketException { + checkOpen(); + if (size < 1) { + throw new IllegalArgumentException(Msg.getString("K0035")); //$NON-NLS-1$ + } + socketImpl + .setOption(SocketOptions.SO_RCVBUF, Integer.valueOf(size)); + } + + @Override + public void setReuseAddress(boolean reuse) throws SocketException { + checkOpen(); + socketImpl.setOption(SocketOptions.SO_REUSEADDR, reuse ? Boolean.TRUE + : Boolean.FALSE); + } + + @Override + public synchronized void setSendBufferSize(int size) throws SocketException { + checkOpen(); + if (size < 1) { + throw new IllegalArgumentException(Msg.getString("K0035")); //$NON-NLS-1$ + } + socketImpl.setOption(SocketOptions.SO_SNDBUF, Integer.valueOf(size)); + } + + @Override + public void setSoLinger(boolean on, int timeout) throws SocketException { + checkOpen(); + if (on && timeout < 0) { + throw new IllegalArgumentException(Msg.getString("K0045")); //$NON-NLS-1$ + } + int val = on ? (65535 < timeout ? 65535 : timeout) : -1; + socketImpl.setOption(SocketOptions.SO_LINGER, Integer.valueOf(val)); + } + + @Override + public synchronized void setSoTimeout(int timeout) throws SocketException { + checkOpen(); + if (timeout < 0) { + throw new IllegalArgumentException(Msg.getString("K0036")); //$NON-NLS-1$ + } + socketImpl.setOption(SocketOptions.SO_TIMEOUT, Integer.valueOf(timeout)); + } + + @Override + public void setTcpNoDelay(boolean on) throws SocketException { + checkOpen(); + socketImpl.setOption(SocketOptions.TCP_NODELAY, Boolean.valueOf(on)); + } + + @Override + public void setTrafficClass(int value) throws SocketException { + checkOpen(); + if (value < 0 || value > 255) { + throw new IllegalArgumentException(); + } + socketImpl.setOption(SocketOptions.IP_TOS, Integer.valueOf(value)); + } + + /** * @see java.net.Socket#getOutputStream() */ @Override @@ -912,8 +952,7 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { return new SocketChannelOutputStream(channel); } - /* - * + /** * @see java.net.Socket#getInputStream() */ @Override @@ -934,7 +973,7 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { } /* - * Checks whether the channel is open + * Checks whether the channel is open. */ private void checkOpen() throws SocketException { if (isClosed()) { @@ -944,7 +983,7 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { } /* - * used for net and nio exchange + * Used for net and nio exchange. */ public SocketImpl getImpl() { return socketImpl; @@ -964,7 +1003,7 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { } /* - * Closes this stream and channel + * Closes this stream and channel. * * @exception IOException thrown if an error occurs during the close */ @@ -973,7 +1012,7 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { channel.close(); } - /* + /** * @see java.io.OutputStream#write(byte[], int, int) */ @Override @@ -989,7 +1028,7 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { channel.write(buf); } - /* + /** * @see java.io.OutputStream#write(int) */ @Override @@ -1023,7 +1062,7 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { channel.close(); } - /* + /** * @see java.io.InputStream#read() */ @Override @@ -1038,7 +1077,7 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { // END android-changed } - /* + /** * @see java.io.InputStream#read(byte[], int, int) */ @Override diff --git a/libcore/nio/src/test/java/org/apache/harmony/nio/tests/java/nio/channels/DatagramChannelTest.java b/libcore/nio/src/test/java/org/apache/harmony/nio/tests/java/nio/channels/DatagramChannelTest.java index 36e008271..8a77b9d62 100644 --- a/libcore/nio/src/test/java/org/apache/harmony/nio/tests/java/nio/channels/DatagramChannelTest.java +++ b/libcore/nio/src/test/java/org/apache/harmony/nio/tests/java/nio/channels/DatagramChannelTest.java @@ -17,6 +17,7 @@ package org.apache.harmony.nio.tests.java.nio.channels; +import dalvik.annotation.KnownFailure; import dalvik.annotation.TestTargetNew; import dalvik.annotation.TestTargets; import dalvik.annotation.TestLevel; @@ -666,6 +667,7 @@ public class DatagramChannelTest extends TestCase { method = "socket", args = {} ) + @KnownFailure(value="bug 2155708") public void testSocket_BasicStatusBeforeConnect() throws SocketException { assertFalse(this.channel1.isConnected());// not connected DatagramSocket s1 = this.channel1.socket(); diff --git a/libcore/nio/src/test/java/org/apache/harmony/nio/tests/java/nio/channels/SocketChannelTest.java b/libcore/nio/src/test/java/org/apache/harmony/nio/tests/java/nio/channels/SocketChannelTest.java index b7a1012c4..ce34dba5a 100755 --- a/libcore/nio/src/test/java/org/apache/harmony/nio/tests/java/nio/channels/SocketChannelTest.java +++ b/libcore/nio/src/test/java/org/apache/harmony/nio/tests/java/nio/channels/SocketChannelTest.java @@ -29,6 +29,8 @@ import java.io.InputStream; import java.io.OutputStream; import java.net.BindException; import java.net.ConnectException; +import java.net.Inet4Address; +import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; @@ -4114,6 +4116,95 @@ public class SocketChannelTest extends TestCase { } } + /** + * @throws IOException + * @tests java.nio.channels.SocketChannel#read(ByteBuffer) + */ + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "", + method = "read", + args = {java.nio.ByteBuffer[].class} + ) + public void test_socketChannel_read_DirectByteBuffer() throws InterruptedException, IOException { + + ServerThread server = new ServerThread(); + server.start(); + Thread.currentThread().sleep(1000); + + InetSocketAddress address = new InetSocketAddress(InetAddress + .getByName("localhost"), port); + + // First test with array based byte buffer + SocketChannel sc = SocketChannel.open(); + sc.connect(address); + + ByteBuffer buf = ByteBuffer.allocate(data.length); + buf.limit(data.length / 2); + sc.read(buf); + + buf.limit(buf.capacity()); + sc.read(buf); + sc.close(); + + // Make sure the buffer is filled correctly + buf.rewind(); + assertSameContent(data, buf); + + // Now test with direct byte buffer + sc = SocketChannel.open(); + sc.connect(address); + + buf = ByteBuffer.allocateDirect(data.length); + buf.limit(data.length / 2); + sc.read(buf); + + buf.limit(buf.capacity()); + sc.read(buf); + sc.close(); + + // Make sure the buffer is filled correctly + buf.rewind(); + assertSameContent(data, buf); + } + + private void assertSameContent(byte[] data, ByteBuffer buf) { + for (byte b : data) { + if (b != buf.get()) { + int pos = buf.position() - 1; + fail("Content not equal. Buffer position: " + + (pos) + " expected: " + b + " was: " + buf.get(pos)); + } + } + } + + public static boolean done = false; + public static int port = Support_PortManager.getNextPort(); + public static byte[] data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + + static class ServerThread extends Thread { + @Override + public void run() { + try { + ServerSocketChannel ssc = ServerSocketChannel.open(); + InetSocketAddress addr = new InetSocketAddress(InetAddress + .getByAddress(new byte[] {0, 0, 0, 0}), port); + ssc.socket().bind(addr, 0); + + ByteBuffer buf = ByteBuffer.allocate(10); + buf.put(data); + + while (!done) { + SocketChannel sc = ssc.accept(); + buf.rewind(); + sc.write(buf); + } + } catch (Exception e) { + // ignore + } + } + } + class MockSocketChannel extends SocketChannel { private boolean isWriteCalled = false; diff --git a/libcore/nio_char/src/test/java/tests/api/java/nio/charset/CharsetProviderTest.java b/libcore/nio_char/src/test/java/tests/api/java/nio/charset/CharsetProviderTest.java index 6bcdaef77..903c68947 100644 --- a/libcore/nio_char/src/test/java/tests/api/java/nio/charset/CharsetProviderTest.java +++ b/libcore/nio_char/src/test/java/tests/api/java/nio/charset/CharsetProviderTest.java @@ -266,8 +266,6 @@ public class CharsetProviderTest extends TestCase { method = "charsetForName", args = {String.class} ) - @KnownFailure("Android throws Error in case of insufficient privileges, " + - "RI throws SecurityException") public void testIsSupported_InsufficientPrivilege() throws Exception { SecurityManager oldMan = System.getSecurityManager(); System.setSecurityManager(new MockSecurityManager()); @@ -301,8 +299,6 @@ public class CharsetProviderTest extends TestCase { method = "charsetForName", args = {String.class} ) - @KnownFailure("Android throws Error in case of insufficient privileges, " + - "RI throws SecurityException") public void testForName_InsufficientPrivilege() throws Exception { SecurityManager oldMan = System.getSecurityManager(); System.setSecurityManager(new MockSecurityManager()); diff --git a/libcore/prefs/src/main/java/java/util/prefs/AbstractPreferences.java b/libcore/prefs/src/main/java/java/util/prefs/AbstractPreferences.java index 711cc0118..326456935 100644 --- a/libcore/prefs/src/main/java/java/util/prefs/AbstractPreferences.java +++ b/libcore/prefs/src/main/java/java/util/prefs/AbstractPreferences.java @@ -20,6 +20,7 @@ package java.util.prefs; import java.io.IOException; import java.io.OutputStream; import java.io.UnsupportedEncodingException; +import java.util.Collection; import java.util.EventListener; import java.util.EventObject; import java.util.HashMap; @@ -27,7 +28,6 @@ import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.StringTokenizer; import java.util.TreeSet; import org.apache.harmony.luni.util.Base64; @@ -38,8 +38,9 @@ import org.apache.harmony.prefs.internal.nls.Messages; * Preferences, which can be used to simplify {@code Preferences} provider's * implementation. This class defines nine abstract SPI methods, which must be * implemented by a preference provider. - * - * @since Android 1.0 + * + * @since 1.4 + * @see Preferences */ public abstract class AbstractPreferences extends Preferences { /* @@ -47,14 +48,9 @@ public abstract class AbstractPreferences extends Preferences { * Class fields * ----------------------------------------------------------- */ - /** - * The unhandled events collection. - */ + /** the unhandled events collection */ private static final List events = new LinkedList(); - - /** - * The event dispatcher thread. - */ + /** the event dispatcher thread */ private static final EventDispatcher dispatcher = new EventDispatcher("Preference Event Dispatcher"); //$NON-NLS-1$ /* @@ -72,11 +68,13 @@ public abstract class AbstractPreferences extends Preferences { Preferences sroot = Preferences.systemRoot(); try { uroot.flush(); - } catch (BackingStoreException e) {//ignore + } catch (BackingStoreException e) { + // ignore } try { sroot.flush(); - } catch (BackingStoreException e) {//ignore + } catch (BackingStoreException e) { + // ignore } } }); @@ -87,9 +85,7 @@ public abstract class AbstractPreferences extends Preferences { * Instance fields (package-private) * ----------------------------------------------------------- */ - /** - * True, if this node is in user preference hierarchy. - */ + /** true if this node is in user preference hierarchy */ boolean userNode; /* @@ -97,16 +93,11 @@ public abstract class AbstractPreferences extends Preferences { * Instance fields (private) * ----------------------------------------------------------- */ - /** - * Marker class for 'lock' field. - */ - private static class Lock { - } + /** Marker class for 'lock' field. */ + private static class Lock {} /** * The object used to lock this node. - * - * @since Android 1.0 */ protected final Object lock; @@ -115,14 +106,10 @@ public abstract class AbstractPreferences extends Preferences { * backing store. This field's default value is false, and it is checked * when the node creation is completed, and if it is true, the node change * event will be fired for this node's parent. - * - * @since Android 1.0 */ protected boolean newNode; - /** - * Cached child nodes - */ + /** cached child nodes */ private Map cachedNode; //the collections of listeners @@ -147,9 +134,9 @@ public abstract class AbstractPreferences extends Preferences { * ----------------------------------------------------------- */ /** - * Constructs a new {@code AbstractPreferences} instance using the given parent node - * and node name. - * + * Constructs a new {@code AbstractPreferences} instance using the given + * parent node and node name. + * * @param parent * the parent node of the new node or {@code null} to indicate * that the new node is a root node. @@ -159,7 +146,6 @@ public abstract class AbstractPreferences extends Preferences { * @throws IllegalArgumentException * if the name contains a slash character or is empty if {@code * parent} is not {@code null}. - * @since Android 1.0 */ protected AbstractPreferences(AbstractPreferences parent, String name) { if ((null == parent ^ name.length() == 0) || name.indexOf("/") >= 0) { //$NON-NLS-1$ @@ -185,7 +171,6 @@ public abstract class AbstractPreferences extends Preferences { * Returns an array of all cached child nodes. * * @return the array of cached child nodes. - * @since Android 1.0 */ protected final AbstractPreferences[] cachedChildren() { return cachedNode.values().toArray(new AbstractPreferences[cachedNode.size()]); @@ -193,9 +178,10 @@ public abstract class AbstractPreferences extends Preferences { /** * Returns the child node with the specified name or {@code null} if it - * doesn't exist. Implementers can assume that the name supplied to this method - * will be a valid node name string (conforming to the node naming format) and - * will not correspond to a node that has been cached or removed. + * doesn't exist. Implementers can assume that the name supplied to this + * method will be a valid node name string (conforming to the node naming + * format) and will not correspond to a node that has been cached or + * removed. * * @param name * the name of the desired child node. @@ -204,7 +190,6 @@ public abstract class AbstractPreferences extends Preferences { * @throws BackingStoreException * if the backing store is unavailable or causes an operation * failure. - * @since Android 1.0 */ protected AbstractPreferences getChild(String name) throws BackingStoreException { @@ -226,10 +211,9 @@ public abstract class AbstractPreferences extends Preferences { /** * Returns whether this node has been removed by invoking the method {@code * removeNode()}. - * + * * @return {@code true}, if this node has been removed, {@code false} * otherwise. - * @since Android 1.0 */ protected boolean isRemoved() { synchronized (lock) { @@ -240,33 +224,31 @@ public abstract class AbstractPreferences extends Preferences { /** * Flushes changes of this node to the backing store. This method should * only flush this node and should not include the descendant nodes. Any - * implementation that wants to provide functionality to flush all nodes + * implementation that wants to provide functionality to flush all nodes * at once should override the method {@link #flush() flush()}. - * + * * @throws BackingStoreException * if the backing store is unavailable or causes an operation * failure. - * @since Android 1.0 */ protected abstract void flushSpi() throws BackingStoreException; /** - * Returns the names of all of the child nodes of this node or an empty array if - * this node has no children. The names of cached children are not required to be - * returned. - * + * Returns the names of all of the child nodes of this node or an empty + * array if this node has no children. The names of cached children are not + * required to be returned. + * * @return the names of this node's children. * @throws BackingStoreException * if the backing store is unavailable or causes an operation * failure. - * @since Android 1.0 */ protected abstract String[] childrenNamesSpi() throws BackingStoreException; /** * Returns the child preference node with the given name, creating it * if it does not exist. The caller of this method should ensure that the - * given name is valid and that this node has not been removed or cached. + * given name is valid and that this node has not been removed or cached. * If the named node has just been removed, the implementation * of this method must create a new one instead of reactivating the removed * one. @@ -278,7 +260,6 @@ public abstract class AbstractPreferences extends Preferences { * @param name * the name of the child preference to be returned. * @return the child preference node. - * @since Android 1.0 */ protected abstract AbstractPreferences childSpi(String name); @@ -287,39 +268,37 @@ public abstract class AbstractPreferences extends Preferences { * Puts the given key-value pair into this node. Caller of this method * should ensure that both of the given values are valid and that this * node has not been removed. - * + * * @param name * the given preference key. * @param value * the given preference value. - * @since Android 1.0 */ protected abstract void putSpi(String name, String value); /** - * Gets the preference value mapped to the given key. The caller of this method - * should ensure that the given key is valid and that this node has not been - * removed. This method should not throw any exceptions but if it does, the - * caller will ignore the exception, regarding it as a {@code null} return value. - * + * Gets the preference value mapped to the given key. The caller of this + * method should ensure that the given key is valid and that this node has + * not been removed. This method should not throw any exceptions but if it + * does, the caller will ignore the exception, regarding it as a {@code + * null} return value. + * * @param key * the given key to be searched for. * @return the preference value mapped to the given key. - * @since Android 1.0 */ protected abstract String getSpi(String key); /** * Returns an array of all preference keys of this node or an empty array if - * no preferences have been found. The caller of this method should ensure that - * this node has not been removed. - * + * no preferences have been found. The caller of this method should ensure + * that this node has not been removed. + * * @return the array of all preference keys. * @throws BackingStoreException * if the backing store is unavailable or causes an operation * failure. - * @since Android 1.0 */ protected abstract String[] keysSpi() throws BackingStoreException; @@ -329,11 +308,10 @@ public abstract class AbstractPreferences extends Preferences { * method {@link Preferences#removeNode() Preferences.removeNode()} should * invoke this method multiple-times in bottom-up pattern. The removal is * not required to be persisted until after it is flushed. - * + * * @throws BackingStoreException * if the backing store is unavailable or causes an operation * failure. - * @since Android 1.0 */ protected abstract void removeNodeSpi() throws BackingStoreException; @@ -344,20 +322,18 @@ public abstract class AbstractPreferences extends Preferences { * * @param key * the key of the preference that is to be removed. - * @since Android 1.0 */ protected abstract void removeSpi(String key); /** * Synchronizes this node with the backing store. This method should only * synchronize this node and should not include the descendant nodes. An - * implementation that wants to provide functionality to synchronize all nodes at once should - * override the method {@link #sync() sync()}. + * implementation that wants to provide functionality to synchronize all + * nodes at once should override the method {@link #sync() sync()}. * * @throws BackingStoreException * if the backing store is unavailable or causes an operation * failure. - * @since Android 1.0 */ protected abstract void syncSpi() throws BackingStoreException; @@ -385,7 +361,7 @@ public abstract class AbstractPreferences extends Preferences { for (int i = 0; i < names.length; i++) { result.add(names[i]); } - return result.toArray(new String[0]); + return result.toArray(new String[result.size()]); } } @@ -439,13 +415,13 @@ public abstract class AbstractPreferences extends Preferences { if (key == null) { throw new NullPointerException(); } - String result; + String result = null; synchronized (lock) { checkState(); try { result = getSpi(key); } catch (Exception e) { - result = null; + // ignored } } return (result == null ? deflt : result); @@ -456,9 +432,10 @@ public abstract class AbstractPreferences extends Preferences { String result = get(key, null); if (result == null) { return deflt; - } else if (result.equalsIgnoreCase("true")) { //$NON-NLS-1$ + } + if ("true".equalsIgnoreCase(result)) { //$NON-NLS-1$ return true; - } else if (result.equalsIgnoreCase("false")) { //$NON-NLS-1$ + } else if ("false".equalsIgnoreCase(result)) { //$NON-NLS-1$ return false; } else { return deflt; @@ -474,17 +451,15 @@ public abstract class AbstractPreferences extends Preferences { if (svalue.length() == 0) { return new byte[0]; } - byte[] dres; try { byte[] bavalue = svalue.getBytes("US-ASCII"); //$NON-NLS-1$ if (bavalue.length % 4 != 0) { return deflt; } - dres = Base64.decode(bavalue); + return Base64.decode(bavalue); } catch (Exception e) { - dres = deflt; + return deflt; } - return dres; } @Override @@ -493,13 +468,11 @@ public abstract class AbstractPreferences extends Preferences { if (result == null) { return deflt; } - double dres; try { - dres = Double.parseDouble(result); + return Double.parseDouble(result); } catch (NumberFormatException e) { - dres = deflt; + return deflt; } - return dres; } @Override @@ -508,13 +481,11 @@ public abstract class AbstractPreferences extends Preferences { if (result == null) { return deflt; } - float fres; try { - fres = Float.parseFloat(result); + return Float.parseFloat(result); } catch (NumberFormatException e) { - fres = deflt; + return deflt; } - return fres; } @Override @@ -523,13 +494,11 @@ public abstract class AbstractPreferences extends Preferences { if (result == null) { return deflt; } - int ires; try { - ires = Integer.parseInt(result); + return Integer.parseInt(result); } catch (NumberFormatException e) { - ires = deflt; + return deflt; } - return ires; } @Override @@ -538,13 +507,11 @@ public abstract class AbstractPreferences extends Preferences { if (result == null) { return deflt; } - long lres; try { - lres = Long.parseLong(result); + return Long.parseLong(result); } catch (NumberFormatException e) { - lres = deflt; + return deflt; } - return lres; } @Override @@ -583,42 +550,44 @@ public abstract class AbstractPreferences extends Preferences { startNode = this; } } - Preferences result = null; try { - result = startNode.nodeImpl(name, true); + return startNode.nodeImpl(name, true); } catch (BackingStoreException e) { - //should not happen + // should not happen + return null; } - return result; } private void validateName(String name) { if (name.endsWith("/") && name.length() > 1) { //$NON-NLS-1$ // prefs.6=Name cannot end with '/'\! - throw new IllegalArgumentException(Messages.getString("prefs.6")); //$NON-NLS-1$ + throw new IllegalArgumentException(Messages.getString("prefs.6")); //$NON-NLS-1$ } if (name.indexOf("//") >= 0) { //$NON-NLS-1$ // prefs.7=Name cannot contains consecutive '/'\! - throw new IllegalArgumentException( - Messages.getString("prefs.7")); //$NON-NLS-1$ + throw new IllegalArgumentException(Messages.getString("prefs.7")); //$NON-NLS-1$ } } private AbstractPreferences nodeImpl(String path, boolean createNew) throws BackingStoreException { - StringTokenizer st = new StringTokenizer(path, "/"); //$NON-NLS-1$ + String[] names = path.split("/");//$NON-NLS-1$ AbstractPreferences currentNode = this; AbstractPreferences temp = null; - while (st.hasMoreTokens() && null != currentNode) { - String name = st.nextToken(); - synchronized (currentNode.lock) { - temp = currentNode.cachedNode.get(name); - if (temp == null) { - temp = getNodeFromBackend(createNew, currentNode, name); + if (null != currentNode) { + for (int i = 0; i < names.length; i++) { + String name = names[i]; + synchronized (currentNode.lock) { + temp = currentNode.cachedNode.get(name); + if (temp == null) { + temp = getNodeFromBackend(createNew, currentNode, name); + } + } + currentNode = temp; + if (null == currentNode) { + break; } } - - currentNode = temp; } return currentNode; } @@ -626,12 +595,12 @@ public abstract class AbstractPreferences extends Preferences { private AbstractPreferences getNodeFromBackend(boolean createNew, AbstractPreferences currentNode, String name) throws BackingStoreException { - AbstractPreferences temp; if (name.length() > MAX_NAME_LENGTH) { // prefs.8=Name length is too long: {0} - throw new IllegalArgumentException(Messages.getString("prefs.8", //$NON-NLS-1$ + throw new IllegalArgumentException(Messages.getString("prefs.8", //$NON-NLS-1$ name)); } + AbstractPreferences temp; if (createNew) { temp = currentNode.childSpi(name); currentNode.cachedNode.put(name, temp); @@ -646,6 +615,9 @@ public abstract class AbstractPreferences extends Preferences { @Override public boolean nodeExists(String name) throws BackingStoreException { + if (null == name) { + throw new NullPointerException(); + } AbstractPreferences startNode = null; synchronized (lock) { if (isRemoved()) { @@ -771,10 +743,11 @@ public abstract class AbstractPreferences extends Preferences { cachedNode.put(childrenNames[i], child); } } - AbstractPreferences[] children = cachedNode - .values().toArray(new AbstractPreferences[0]); - for (int i = 0; i < children.length; i++) { - children[i].removeNodeImpl(); + + final Collection values = cachedNode.values(); + final AbstractPreferences[] children = values.toArray(new AbstractPreferences[values.size()]); + for (AbstractPreferences child : children) { + child.removeNodeImpl(); } removeNodeSpi(); isRemoved = true; diff --git a/libcore/prefs/src/main/java/java/util/prefs/BackingStoreException.java b/libcore/prefs/src/main/java/java/util/prefs/BackingStoreException.java index e8a805c2d..553d7ab4b 100644 --- a/libcore/prefs/src/main/java/java/util/prefs/BackingStoreException.java +++ b/libcore/prefs/src/main/java/java/util/prefs/BackingStoreException.java @@ -17,40 +17,35 @@ package java.util.prefs; - /** * An exception to indicate that an error was encountered while accessing the * backing store. - * - * @since Android 1.0 + * + * @since 1.4 */ public class BackingStoreException extends Exception { - + private static final long serialVersionUID = 859796500401108469L; - + /** - * Constructs a new {@code BackingStoreException} instance with a detailed exception - * message. + * Constructs a new {@code BackingStoreException} instance with a detailed + * exception message. * * @param s * the detailed exception message. - * @since Android 1.0 */ public BackingStoreException (String s) { super(s); } /** - * Constructs a new {@code BackingStoreException} instance with a nested {@code Throwable}. - * + * Constructs a new {@code BackingStoreException} instance with a nested + * {@code Throwable}. + * * @param t * the nested {@code Throwable}. - * @since Android 1.0 */ public BackingStoreException (Throwable t) { super(t); } } - - - diff --git a/libcore/prefs/src/main/java/java/util/prefs/FilePreferencesFactoryImpl.java b/libcore/prefs/src/main/java/java/util/prefs/FilePreferencesFactoryImpl.java index cc68e62e9..69eaa0151 100644 --- a/libcore/prefs/src/main/java/java/util/prefs/FilePreferencesFactoryImpl.java +++ b/libcore/prefs/src/main/java/java/util/prefs/FilePreferencesFactoryImpl.java @@ -17,10 +17,10 @@ package java.util.prefs; /** - * The default implementation of PreferencesFactory for the Linux + * The default implementation of PreferencesFactory for the Linux * platform, using the file system as its back end. - * - * @since Android 1.0 + * + * @since 1.4 */ class FilePreferencesFactoryImpl implements PreferencesFactory { // user root preferences diff --git a/libcore/prefs/src/main/java/java/util/prefs/FilePreferencesImpl.java b/libcore/prefs/src/main/java/java/util/prefs/FilePreferencesImpl.java index cf85fa081..f6e5e8f73 100644 --- a/libcore/prefs/src/main/java/java/util/prefs/FilePreferencesImpl.java +++ b/libcore/prefs/src/main/java/java/util/prefs/FilePreferencesImpl.java @@ -28,12 +28,12 @@ import java.util.Set; import org.apache.harmony.prefs.internal.nls.Messages; /** - * The default implementation of AbstractPreferences for the Linux platform, - * using the file system as its back end. - * + * The default implementation of AbstractPreferences for the Linux + * platform, using the file system as its back end. + * * TODO some sync mechanism with backend, Performance - check file edit date - * - * @since Android 1.0 + * + * @since 1.4 */ class FilePreferencesImpl extends AbstractPreferences { @@ -64,7 +64,6 @@ class FilePreferencesImpl extends AbstractPreferences { SYSTEM_HOME = System.getProperty("java.home") + "/.systemPrefs";//$NON-NLS-1$//$NON-NLS-2$ return null; } - }); } @@ -97,9 +96,9 @@ class FilePreferencesImpl extends AbstractPreferences { * Constructors * -------------------------------------------------------------- */ - + /** - * Construct root FilePreferencesImpl instance, construct + * Construct root FilePreferencesImpl instance, construct * user root if userNode is true, system root otherwise */ FilePreferencesImpl(boolean userNode) { @@ -108,9 +107,9 @@ class FilePreferencesImpl extends AbstractPreferences { path = userNode ? USER_HOME : SYSTEM_HOME; initPrefs(); } - + /** - * Construct a prefs using given parent and given name + * Construct a prefs using given parent and given name */ private FilePreferencesImpl(AbstractPreferences parent, String name) { super(parent, name); @@ -132,16 +131,16 @@ class FilePreferencesImpl extends AbstractPreferences { @Override protected String[] childrenNamesSpi() throws BackingStoreException { String[] names = AccessController - .doPrivileged(new PrivilegedAction() { - public String[] run() { - return dir.list(new FilenameFilter() { - public boolean accept(File parent, String name) { - return new File(path + File.separator + name).isDirectory(); - } - }); - + .doPrivileged(new PrivilegedAction() { + public String[] run() { + return dir.list(new FilenameFilter() { + public boolean accept(File parent, String name) { + return new File(path + File.separator + name).isDirectory(); } }); + + } + }); if (null == names) {// file is not a directory, exception case // prefs.3=Cannot get children names for {0}! throw new BackingStoreException( @@ -192,14 +191,16 @@ class FilePreferencesImpl extends AbstractPreferences { prefs = XMLParser.loadFilePrefs(prefsFile); } return prefs.getProperty(key); - } catch (Exception e) {// if Exception happened, return null + } catch (Exception e) { + // if Exception happened, return null return null; } } @Override protected String[] keysSpi() throws BackingStoreException { - return prefs.keySet().toArray(new String[0]); + final Set ks = prefs.keySet(); + return ks.toArray(new String[ks.size()]); } @Override diff --git a/libcore/prefs/src/main/java/java/util/prefs/InvalidPreferencesFormatException.java b/libcore/prefs/src/main/java/java/util/prefs/InvalidPreferencesFormatException.java index b31b3a15f..ba8940bbd 100644 --- a/libcore/prefs/src/main/java/java/util/prefs/InvalidPreferencesFormatException.java +++ b/libcore/prefs/src/main/java/java/util/prefs/InvalidPreferencesFormatException.java @@ -21,51 +21,43 @@ package java.util.prefs; * An exception to indicate that the input XML file is not well-formed or could * not be validated against the appropriate document type (specified by * in the {@code Preferences}). - * - * @since Android 1.0 */ public class InvalidPreferencesFormatException extends Exception { - + private static final long serialVersionUID = -791715184232119669L; - + /** - * Constructs a new {@code InvalidPreferencesFormatException} instance with a - * detailed exception message. + * Constructs a new {@code InvalidPreferencesFormatException} instance with + * a detailed exception message. * * @param s * the detailed exception message. - * @since Android 1.0 */ public InvalidPreferencesFormatException (String s) { super(s); } /** - * Constructs a new {@code InvalidPreferencesFormatException} instance with a - * detailed exception message and a nested {@code Throwable}. + * Constructs a new {@code InvalidPreferencesFormatException} instance with + * a detailed exception message and a nested {@code Throwable}. * * @param s * the detailed exception message. * @param t * the nested {@code Throwable}. - * @since Android 1.0 */ public InvalidPreferencesFormatException (String s, Throwable t) { super(s,t); } /** - * Constructs a new {@code InvalidPreferencesFormatException} instance with a nested - * {@code Throwable}. - * + * Constructs a new {@code InvalidPreferencesFormatException} instance with + * a nested {@code Throwable}. + * * @param t * the nested {@code Throwable}. - * @since Android 1.0 */ public InvalidPreferencesFormatException (Throwable t) { super(t); } } - - - diff --git a/libcore/prefs/src/main/java/java/util/prefs/NodeChangeEvent.java b/libcore/prefs/src/main/java/java/util/prefs/NodeChangeEvent.java index e9824bce8..3e23f5a18 100644 --- a/libcore/prefs/src/main/java/java/util/prefs/NodeChangeEvent.java +++ b/libcore/prefs/src/main/java/java/util/prefs/NodeChangeEvent.java @@ -14,7 +14,6 @@ * limitations under the License. */ - package java.util.prefs; import java.io.Serializable; @@ -28,19 +27,22 @@ import java.io.IOException; * This is the event class to indicate that one child of the preference node has * been added or deleted. *

          - * Please note that the serialization functionality has not yet been implemented, so - * the serialization methods do nothing but throw a {@code NotSerializableException}. - *

          + * Please note that although the class is marked as {@code Serializable} by + * inheritance from {@code EventObject}, this type is not intended to be serialized + * so the serialization methods do nothing but throw a {@code NotSerializableException}. + * + * @see java.util.prefs.Preferences + * @see java.util.prefs.NodeChangeListener * - * @since Android 1.0 + * @since 1.4 */ public class NodeChangeEvent extends EventObject implements Serializable { - + private static final long serialVersionUID = 8068949086596572957L; - + private final Preferences parent; private final Preferences child; - + /** * Constructs a new {@code NodeChangeEvent} instance. * @@ -49,43 +51,40 @@ public class NodeChangeEvent extends EventObject implements Serializable { * considered as the event source. * @param c * the child {@code Preferences} instance that was added or deleted. - * @since Android 1.0 */ public NodeChangeEvent (Preferences p, Preferences c) { super(p); parent = p; child = c; } - + /** * Gets the {@code Preferences} instance that fired this event. * * @return the {@code Preferences} instance that fired this event. - * @since Android 1.0 */ public Preferences getParent() { return parent; } - + /** * Gets the child {@code Preferences} node that was added or removed. * * @return the added or removed child {@code Preferences} node. - * @since Android 1.0 */ public Preferences getChild() { return child; } - - /* + + /** * This method always throws a NotSerializableException, * because this object cannot be serialized, */ private void writeObject (ObjectOutputStream out) throws IOException { throw new NotSerializableException(); } - - /* + + /** * This method always throws a NotSerializableException, * because this object cannot be serialized, */ @@ -93,7 +92,3 @@ public class NodeChangeEvent extends EventObject implements Serializable { throw new NotSerializableException(); } } - - - - diff --git a/libcore/prefs/src/main/java/java/util/prefs/NodeChangeListener.java b/libcore/prefs/src/main/java/java/util/prefs/NodeChangeListener.java index f16b2061b..41da23e5a 100644 --- a/libcore/prefs/src/main/java/java/util/prefs/NodeChangeListener.java +++ b/libcore/prefs/src/main/java/java/util/prefs/NodeChangeListener.java @@ -14,41 +14,36 @@ * limitations under the License. */ - package java.util.prefs; import java.util.EventListener; import java.util.prefs.NodeChangeEvent; /** - * This interface is used to handle preference node change events. - * The implementation of this interface can be installed by the {@code Preferences} instance. + * This interface is used to handle preference node change events. The + * implementation of this interface can be installed by the {@code Preferences} + * instance. * + * @see Preferences * @see NodeChangeEvent * - * @since Android 1.0 + * @since 1.4 */ public interface NodeChangeListener extends EventListener { - /** * This method gets called whenever a child node is added to another node. * * @param e * the node change event. - * @since Android 1.0 */ public void childAdded (NodeChangeEvent e); - + /** * This method gets called whenever a child node is removed from another * node. * * @param e * the node change event. - * @since Android 1.0 */ public void childRemoved (NodeChangeEvent e); } - - - diff --git a/libcore/prefs/src/main/java/java/util/prefs/PreferenceChangeEvent.java b/libcore/prefs/src/main/java/java/util/prefs/PreferenceChangeEvent.java index f0f0787a3..d355f4e03 100644 --- a/libcore/prefs/src/main/java/java/util/prefs/PreferenceChangeEvent.java +++ b/libcore/prefs/src/main/java/java/util/prefs/PreferenceChangeEvent.java @@ -14,7 +14,6 @@ * limitations under the License. */ - package java.util.prefs; import java.io.IOException; @@ -28,16 +27,19 @@ import java.util.EventObject; * This is the event class to indicate that a preference has been added, deleted * or updated. *

          - * Please note that the serialization functionality has not yet been implemented, so - * the serialization methods do nothing but throw a {@code NotSerializableException}. - *

          + * Please note that although the class is marked as {@code Serializable} by + * inheritance from {@code EventObject}, this type is not intended to be serialized + * so the serialization methods do nothing but throw a {@code NotSerializableException}. + * + * @see java.util.prefs.Preferences + * @see java.util.prefs.PreferenceChangeListener * - * @since Android 1.0 + * @since 1.4 */ public class PreferenceChangeEvent extends EventObject implements Serializable { private static final long serialVersionUID = 793724513368024975L; - + private final Preferences node; private final String key; @@ -55,7 +57,6 @@ public class PreferenceChangeEvent extends EventObject implements Serializable { * @param v * the new value of the changed preference, this value can be * {@code null}, which means the preference has been removed. - * @since Android 1.0 */ public PreferenceChangeEvent(Preferences p, String k, String v) { super(p); @@ -68,7 +69,6 @@ public class PreferenceChangeEvent extends EventObject implements Serializable { * Gets the key of the changed preference. * * @return the changed preference's key. - * @since Android 1.0 */ public String getKey() { return key; @@ -78,9 +78,8 @@ public class PreferenceChangeEvent extends EventObject implements Serializable { * Gets the new value of the changed preference or {@code null} if the * preference has been removed. * - * @return the new value of the changed preference or null if the preference - * has been removed. - * @since Android 1.0 + * @return the new value of the changed preference or {@code null} if the + * preference has been removed. */ public String getNewValue() { return value; @@ -90,13 +89,12 @@ public class PreferenceChangeEvent extends EventObject implements Serializable { * Gets the {@code Preferences} instance that fired this event. * * @return the {@code Preferences} instance that fired this event. - * @since Android 1.0 */ public Preferences getNode() { return node; } - /* + /** * This method always throws a NotSerializableException, * because this object cannot be serialized, */ @@ -104,7 +102,7 @@ public class PreferenceChangeEvent extends EventObject implements Serializable { throw new NotSerializableException(); } - /* + /** * This method always throws a NotSerializableException, * because this object cannot be serialized, */ @@ -112,5 +110,3 @@ public class PreferenceChangeEvent extends EventObject implements Serializable { throw new NotSerializableException(); } } - - diff --git a/libcore/prefs/src/main/java/java/util/prefs/PreferenceChangeListener.java b/libcore/prefs/src/main/java/java/util/prefs/PreferenceChangeListener.java index 28bb763e5..97aecedba 100644 --- a/libcore/prefs/src/main/java/java/util/prefs/PreferenceChangeListener.java +++ b/libcore/prefs/src/main/java/java/util/prefs/PreferenceChangeListener.java @@ -14,21 +14,23 @@ * limitations under the License. */ - package java.util.prefs; import java.util.EventListener; /** - * This interface is used to handle preferences change events. The implementation of - * this interface can be installed by the {@code Preferences} instance. + * This interface is used to handle preferences change events. The + * implementation of this interface can be installed by the {@code Preferences} + * instance. * + * @see Preferences * @see PreferenceChangeEvent + * * - * @since Android 1.0 + * @since 1.4 */ public interface PreferenceChangeListener extends EventListener { - + /** * This method gets invoked whenever a preference is added, deleted or * updated. @@ -36,10 +38,6 @@ public interface PreferenceChangeListener extends EventListener { * @param pce * the event instance which describes the changed {@code Preferences} * instance and the preference value. - * @since Android 1.0 */ void preferenceChange (PreferenceChangeEvent pce); } - - - diff --git a/libcore/prefs/src/main/java/java/util/prefs/Preferences.java b/libcore/prefs/src/main/java/java/util/prefs/Preferences.java index b7a0c70d3..8b961e4e2 100644 --- a/libcore/prefs/src/main/java/java/util/prefs/Preferences.java +++ b/libcore/prefs/src/main/java/java/util/prefs/Preferences.java @@ -30,89 +30,83 @@ import java.io.OutputStream; import java.net.MalformedURLException; import java.security.AccessController; import java.security.PrivilegedAction; +import java.util.Locale; import org.apache.harmony.prefs.internal.nls.Messages; /** - * An instance of the class {@code Preferences} represents one node in a preference tree, - * which provides a mechanism to store and access configuration data in a - * hierarchical way. Two hierarchy trees are maintained, one for system - * preferences shared by all users and the other for user preferences + * An instance of the class {@code Preferences} represents one node in a + * preference tree, which provides a mechanism to store and access configuration + * data in a hierarchical way. Two hierarchy trees are maintained, one for + * system preferences shared by all users and the other for user preferences * specific to the user. {@code Preferences} hierarchy trees and data are stored * in an implementation-dependent back-end. *

          - * Every node has one name and one unique absolute path following the same - * notational conventions as directories in a file system. The root node's - * name is "", and other node name strings cannot contain the slash character - * and cannot be empty. The root node's absolute path is "/", and all other - * nodes' absolute paths are constructed in the standard way: <parent's absolute - * path> + "/" + <node's name>. Since the set of nodes forms a tree with - * the root node at its base, all absolute paths start with the slash character. - * Every node has one relative path to each of its ancestors. The relative path - * doesn't start with slash: it equals the node's absolute path with leading - * substring removed corresponding to the ancestor's absolute path and a slash. - *

          + * Every node has one name and one unique absolute path following the same + * notational conventions as directories in a file system. The root node's + * name is "", and other node name strings cannot contain the slash character + * and cannot be empty. The root node's absolute path is "/", and all other + * nodes' absolute paths are constructed in the standard way: <parent's + * absolute path> + "/" + <node's name>. Since the set of nodes forms a + * tree with the root node at its base, all absolute paths start with the slash + * character. Every node has one relative path to each of its ancestors. The + * relative path doesn't start with slash: it equals the node's absolute path + * with leading substring removed corresponding to the ancestor's absolute path + * and a slash. *

          - * Modification to preferences data may be asynchronous, which means that - * preference update method calls may return immediately instead of blocking. - * The {@code flush()} and {@code sync()} methods force the back-end to - * synchronously perform all pending updates, but the implementation is - * permitted to perform the modifications on the underlying back-end data - * at any time between the moment the request is made and the moment the - * {@code flush()} or {@code sync()} method returns. - * Please note that if JVM exit normally, the implementation must assure all - * modifications are persisted implicitly. - *

          + * Modification to preferences data may be asynchronous, which means that + * preference update method calls may return immediately instead of blocking. + * The {@code flush()} and {@code sync()} methods force the back-end to + * synchronously perform all pending updates, but the implementation is + * permitted to perform the modifications on the underlying back-end data + * at any time between the moment the request is made and the moment the + * {@code flush()} or {@code sync()} method returns. Please note that if the JVM + * exits normally, the implementation must assure all modifications are + * persisted implicitly. *

          - * When invoking a method that retrieves preferences, the user must provide - * a default value. The default value is returned when the preferences cannot - * be found or the back-end is unavailable. Some other methods will throw + * When invoking a method that retrieves preferences, the user must provide + * a default value. The default value is returned when the preferences cannot + * be found or the back-end is unavailable. Some other methods will throw * {@code BackingStoreException} when the back-end is unavailable. *

          *

          - * Preferences can be exported to and imported from an XML files. + * Preferences can be exported to and imported from an XML files. These + * documents must have an XML DOCTYPE declaration: + *

          {@code
          + * 
          + * }
          + * This system URI is not really accessed by network, it is only a + * identification string. Visit the DTD location to see the actual format + * permitted. *

          * There must be a concrete {@code PreferencesFactory} type for every concrete - * {@code Preferences} type developed. Every J2SE implementation must provide a default - * implementation for every supported platform, and must also provide a means of - * replacing the default implementation. This implementation uses the system property - * {@code java.util.prefs.PreferencesFactory} to detemine which preferences - * implementation to use. - *

          + * {@code Preferences} type developed. Every J2SE implementation must provide a + * default implementation for every supported platform, and must also provide a + * means of replacing the default implementation. This implementation uses the + * system property {@code java.util.prefs.PreferencesFactory} to detemine which + * preferences implementation to use. *

          - * The methods of this class are thread-safe. If multiple JVMs are using the same - * back-end concurrently, the back-end won't be corrupted, but no other + * The methods of this class are thread-safe. If multiple JVMs are using the + * same back-end concurrently, the back-end won't be corrupted, but no other * behavior guarantees are made. - *

          + * + * @see PreferencesFactory * - * @since Android 1.0 + * @since 1.4 */ public abstract class Preferences { - - /* - * --------------------------------------------------------- - * Class fields - * --------------------------------------------------------- - */ - /** * Maximum size in characters allowed for a preferences key. - * - * @since Android 1.0 */ public static final int MAX_KEY_LENGTH = 80; - + /** * Maximum size in characters allowed for a preferences name. - * - * @since Android 1.0 */ public static final int MAX_NAME_LENGTH = 80; - + /** * Maximum size in characters allowed for a preferences value. - * - * @since Android 1.0 */ public static final int MAX_VALUE_LENGTH = 8192; @@ -137,36 +131,53 @@ public abstract class Preferences { //permission private static final RuntimePermission PREFS_PERM = new RuntimePermission("preferences"); //$NON-NLS-1$ - + //factory used to get user/system prefs root private static final PreferencesFactory factory; - - /** - * --------------------------------------------------------- - * Class initializer - * --------------------------------------------------------- - */ - static{ + + // BEGIN android-removed + // // default provider factory name for Windows + // private static final String DEFAULT_FACTORY_NAME_WIN = "java.util.prefs.RegistryPreferencesFactoryImpl"; //$NON-NLS-1$ + // + // // default provider factory name for Unix + // private static final String DEFAULT_FACTORY_NAME_UNIX = "java.util.prefs.FilePreferencesFactoryImpl"; //$NON-NLS-1$ + // END android-removed + + static { String factoryClassName = AccessController.doPrivileged(new PrivilegedAction() { public String run() { return System.getProperty("java.util.prefs.PreferencesFactory"); //$NON-NLS-1$ } }); // BEGIN android-removed - // if(factoryClassName != null) { - // try { - // ClassLoader loader = Thread.currentThread().getContextClassLoader(); - // if(loader == null){ - // loader = ClassLoader.getSystemClassLoader(); + // // set default provider + // if (factoryClassName == null) { + // String osName = AccessController.doPrivileged(new PrivilegedAction() { + // public String run() { + // return System.getProperty("os.name"); //$NON-NLS-1$ + // } + // }); + // + // // only comparing ASCII, so assume english locale + // osName = (osName == null ? null : osName.toLowerCase(Locale.ENGLISH)); + // + // if (osName != null && osName.startsWith("windows")) { + // factoryClassName = DEFAULT_FACTORY_NAME_WIN; + // } else { + // factoryClassName = DEFAULT_FACTORY_NAME_UNIX; + // } // } - // Class factoryClass = loader.loadClass(factoryClassName); - // factory = (PreferencesFactory) factoryClass.newInstance(); + // try { + // ClassLoader loader = Thread.currentThread().getContextClassLoader(); + // if(loader == null){ + // loader = ClassLoader.getSystemClassLoader(); + // } + // Class factoryClass = loader.loadClass(factoryClassName); + // factory = (PreferencesFactory) factoryClass.newInstance(); // } catch (Exception e) { - // // prefs.10=Cannot initiate PreferencesFactory: {0}. Caused by {1} - // throw new InternalError(Messages.getString("prefs.10", factoryClassName, e)); //$NON-NLS-1$ - // } + // // prefs.10=Cannot initiate PreferencesFactory: {0}. Caused by {1} + // throw new InternalError(Messages.getString("prefs.10", factoryClassName, e)); //$NON-NLS-1$ // } - // END android-removed // BEGIN android-added ClassLoader loader = Thread.currentThread().getContextClassLoader(); if (loader == null) { @@ -182,10 +193,8 @@ public abstract class Preferences { try { InputStream is = en.nextElement().openStream(); // Read each line for charset provider class names - // BEGIN android-modified reader = new BufferedReader(new InputStreamReader(is, CONFIGURATION_FILE_ENCODING), 8192); - // END android-modified factoryClassName = reader.readLine(); commentIndex = factoryClassName.indexOf(CONFIGURATION_FILE_COMMENT); if (commentIndex > 0) { @@ -215,42 +224,27 @@ public abstract class Preferences { factory = (PreferencesFactory)c.newInstance(); } catch (Exception e) { // prefs.10=Cannot initiate PreferencesFactory: {0}. Caused by {1} - throw new InternalError(Messages.getString("prefs.10", factoryClassName, e)); //$NON-NLS-1$ + throw new InternalError(Messages.getString("prefs.10", factoryClassName, e)); //$NON-NLS-1$ } // END android-added } - - /* - * --------------------------------------------------------- - * Constructors - * --------------------------------------------------------- - */ - + /** * Default constructor, for use by subclasses only. - * - * @since Android 1.0 */ protected Preferences() { super(); } - - /* - * --------------------------------------------------------- - * Methods - * --------------------------------------------------------- - */ - + /** * Gets the absolute path string of this preference node. * * @return the preference node's absolute path string. - * @since Android 1.0 */ public abstract String absolutePath(); - + /** - * Returns the names of all children of this node or an empty string if this + * Returns the names of all children of this node or an empty array if this * node has no children. * * @return the names of all children of this node. @@ -259,10 +253,9 @@ public abstract class Preferences { * failure. * @throws IllegalStateException * if this node has been removed. - * @since Android 1.0 */ public abstract String[] childrenNames() throws BackingStoreException; - + /** * Removes all preferences of this node. * @@ -271,13 +264,12 @@ public abstract class Preferences { * failure. * @throws IllegalStateException * if this node has been removed. - * @since Android 1.0 */ public abstract void clear() throws BackingStoreException; - + /** - * Exports all of the preferences of this node to a XML document using the given - * output stream. + * Exports all of the preferences of this node to a XML document using the + * given output stream. *

          * This XML document uses the UTF-8 encoding and is written according to the * DTD in its DOCTYPE declaration, which is the following: @@ -286,7 +278,8 @@ public abstract class Preferences { * <!DOCTYPE preferences SYSTEM "http://java.sun.com/dtd/preferences.dtd"> * * - * Please note that (unlike the methods of this class that don't concern serialization), this call is not thread-safe. + * Please note that (unlike the methods of this class that don't concern + * serialization), this call is not thread-safe. *

          * * @param ostream @@ -298,13 +291,12 @@ public abstract class Preferences { * failure. * @throws IllegalStateException * if this node has been removed. - * @since Android 1.0 */ - public abstract void exportNode (OutputStream ostream) throws IOException, BackingStoreException; - + public abstract void exportNode(OutputStream ostream) throws IOException, BackingStoreException; + /** - * Exports all of the preferences of this node and all its descendants to a XML - * document using the given output stream. + * Exports all of the preferences of this node and all its descendants to a + * XML document using the given output stream. *

          * This XML document uses the UTF-8 encoding and is written according to the * DTD in its DOCTYPE declaration, which is the following: @@ -313,7 +305,8 @@ public abstract class Preferences { * <!DOCTYPE preferences SYSTEM "http://java.sun.com/dtd/preferences.dtd"> * * - * Please note that (unlike the methods of this class that don't concern serialization), this call is not thread-safe. + * Please note that (unlike the methods of this class that don't concern + * serialization), this call is not thread-safe. *

          * * @param ostream @@ -325,12 +318,12 @@ public abstract class Preferences { * failure. * @throws IllegalStateException * if this node has been removed. - * @since Android 1.0 */ - public abstract void exportSubtree (OutputStream ostream) throws IOException, BackingStoreException; - + public abstract void exportSubtree(OutputStream ostream) throws IOException, + BackingStoreException; + /** - * Forces all pending updates to this node and its descendants to be + * Forces all pending updates to this node and its descendants to be * persisted in the backing store. *

          * If this node has been removed, the invocation of this method only flushes @@ -340,13 +333,12 @@ public abstract class Preferences { * @throws BackingStoreException * if the backing store is unavailable or causes an operation * failure. - * @since Android 1.0 */ public abstract void flush() throws BackingStoreException; - + /** - * Gets the {@code String} value mapped to the given key or its default value if no - * value is mapped or no backing store is available. + * Gets the {@code String} value mapped to the given key or its default + * value if no value is mapped or no backing store is available. *

          * Some implementations may store default values in backing stores. In this * case, if there is no value mapped to the given key, the stored default @@ -363,16 +355,16 @@ public abstract class Preferences { * if this node has been removed. * @throws NullPointerException * if the parameter {@code key} is {@code null}. - * @since Android 1.0 */ - public abstract String get (String key, String deflt); - + public abstract String get(String key, String deflt); + /** - * Gets the {@code boolean} value mapped to the given key or its default value if no - * value is mapped, if the backing store is unavailable, or if the value is invalid. + * Gets the {@code boolean} value mapped to the given key or its default + * value if no value is mapped, if the backing store is unavailable, or if + * the value is invalid. *

          - * The only valid values are the {@code String} "true", which represents {@code true} and - * "false", which represents {@code false}, ignoring case. + * The only valid values are the {@code String} "true", which represents + * {@code true} and "false", which represents {@code false}, ignoring case. *

          *

          * Some implementations may store default values in backing stores. In this @@ -384,25 +376,24 @@ public abstract class Preferences { * the preference key. * @param deflt * the default value, which will be returned if no value is - * mapped to the given key, if the backing store is unavailable, or if the - * value is invalid. + * mapped to the given key, if the backing store is unavailable, + * or if the value is invalid. * @return the boolean value mapped to the given key. * @throws IllegalStateException * if this node has been removed. * @throws NullPointerException * if the parameter {@code key} is {@code null}. - * @since Android 1.0 */ - public abstract boolean getBoolean (String key, boolean deflt); - + public abstract boolean getBoolean(String key, boolean deflt); + /** - * Gets the {@code byte} array value mapped to the given key or its default value if - * no value is mapped, if the backing store is unavailable, or if the value is an - * invalid string. + * Gets the {@code byte} array value mapped to the given key or its default + * value if no value is mapped, if the backing store is unavailable, or if + * the value is an invalid string. *

          - * To be valid, the value string must be Base64-encoded binary data. The Base64 encoding - * is as defined in RFC - * 2045, section 6.8. + * To be valid, the value string must be Base64-encoded binary data. The + * Base64 encoding is as defined in RFC 2045, section 6.8. *

          *

          * Some implementations may store default values in backing stores. In this @@ -414,25 +405,24 @@ public abstract class Preferences { * the preference key. * @param deflt * the default value, which will be returned if no value is - * mapped to the given key, if the backing store is unavailable, or if the - * value is invalid. + * mapped to the given key, if the backing store is unavailable, + * or if the value is invalid. * @return the byte array value mapped to the given key. * @throws IllegalStateException * if this node has been removed. * @throws NullPointerException * if the parameter {@code key} is {@code null}. - * @since Android 1.0 */ - public abstract byte[] getByteArray (String key, byte[] deflt); - + public abstract byte[] getByteArray(String key, byte[] deflt); + /** - * Gets the {@code double} value mapped to the given key or its default value if no - * value is mapped, if the backing store is unavailable, or if the value is an invalid - * string. + * Gets the {@code double} value mapped to the given key or its default + * value if no value is mapped, if the backing store is unavailable, or if + * the value is an invalid string. *

          - * To be valid, the value string must be a string that can be converted to a {@code double} by - * {@link Double#parseDouble(String) Double.parseDouble(String)}. - *

          + * To be valid, the value string must be a string that can be converted to a + * {@code double} by {@link Double#parseDouble(String) + * Double.parseDouble(String)}. *

          * Some implementations may store default values in backing stores. In this * case, if there is no value mapped to the given key, the stored default @@ -450,17 +440,17 @@ public abstract class Preferences { * if this node has been removed. * @throws NullPointerException * if the parameter {@code key} is {@code null}. - * @since Android 1.0 */ - public abstract double getDouble (String key, double deflt); - + public abstract double getDouble(String key, double deflt); + /** - * Gets the {@code float} value mapped to the given key or its default value if no - * value is mapped, if the backing store is unavailable, or if the value is an invalid - * string. + * Gets the {@code float} value mapped to the given key or its default value + * if no value is mapped, if the backing store is unavailable, or if the + * value is an invalid string. *

          - * To be valid, the value string must be a string that can be converted to a {@code float} by - * {@link Float#parseFloat(String) Float.parseFloat(String)}. + * To be valid, the value string must be a string that can be converted to a + * {@code float} by {@link Float#parseFloat(String) + * Float.parseFloat(String)}. *

          *

          * Some implementations may store default values in backing stores. In this @@ -479,17 +469,17 @@ public abstract class Preferences { * if this node has been removed. * @throws NullPointerException * if the parameter {@code key} is {@code null}. - * @since Android 1.0 */ - public abstract float getFloat (String key, float deflt); - + public abstract float getFloat(String key, float deflt); + /** - * Gets the {@code int} value mapped to the given key or its default value if no - * value is mapped, if the backing store is unavailable, or if the value is an invalid - * string. + * Gets the {@code int} value mapped to the given key or its default value + * if no value is mapped, if the backing store is unavailable, or if the + * value is an invalid string. *

          - * To be valid, the value string must be a string that can be converted to an {@code int} by - * {@link Integer#parseInt(String) Integer.parseInt(String)}. + * To be valid, the value string must be a string that can be converted to + * an {@code int} by {@link Integer#parseInt(String) + * Integer.parseInt(String)}. *

          *

          * Some implementations may store default values in backing stores. In this @@ -501,24 +491,23 @@ public abstract class Preferences { * the preference key. * @param deflt * the default value, which will be returned if no value is - * mapped to the given key, if the backing store is unavailable, or if the - * value is invalid. + * mapped to the given key, if the backing store is unavailable, + * or if the value is invalid. * @return the integer value mapped to the given key. * @throws IllegalStateException * if this node has been removed. * @throws NullPointerException * if the parameter {@code key} is {@code null}. - * @since Android 1.0 */ - public abstract int getInt (String key, int deflt); - + public abstract int getInt(String key, int deflt); + /** - * Gets the {@code long} value mapped to the given key or its default value if no - * value is mapped, if the backing store is unavailable, or if the value is an invalid - * string. + * Gets the {@code long} value mapped to the given key or its default value + * if no value is mapped, if the backing store is unavailable, or if the + * value is an invalid string. *

          - * To be valid, the value string must be a string that can be converted to a {@code long} by - * {@link Long#parseLong(String) Long.parseLong(String)}. + * To be valid, the value string must be a string that can be converted to a + * {@code long} by {@link Long#parseLong(String) Long.parseLong(String)}. *

          *

          * Some implementations may store default values in backing stores. In this @@ -530,29 +519,29 @@ public abstract class Preferences { * the preference key. * @param deflt * the default value, which will be returned if no value is - * mapped to the given key, if the backing store is unavailable, or if the - * value is invalid. + * mapped to the given key, if the backing store is unavailable, + * or if the value is invalid. * @return the long value mapped to the given key. * @throws IllegalStateException * if this node has been removed. * @throws NullPointerException * if the parameter {@code key} is {@code null}. - * @since Android 1.0 */ - public abstract long getLong (String key, long deflt); - + public abstract long getLong(String key, long deflt); + /** * Imports all the preferences from an XML document using the given input * stream. *

          - * This XML document uses the UTF-8 encoding and must be written according to the - * DTD in its DOCTYPE declaration, which must be the following: + * This XML document uses the UTF-8 encoding and must be written according + * to the DTD in its DOCTYPE declaration, which must be the following: * *

                * <!DOCTYPE preferences SYSTEM "http://java.sun.com/dtd/preferences.dtd">
                * 
          * - * Please note that (unlike the methods of this class that don't concern serialization), this call is not thread-safe. + * Please note that (unlike the methods of this class that don't concern + * serialization), this call is not thread-safe. *

          * * @param istream @@ -565,7 +554,6 @@ public abstract class Preferences { * @throws SecurityException * if {@code RuntimePermission("preferences")} is denied by a * SecurityManager. - * @since Android 1.0 */ public static void importPreferences (InputStream istream) throws InvalidPreferencesFormatException, IOException { checkSecurity(); @@ -575,16 +563,15 @@ public abstract class Preferences { } XMLParser.importPrefs(istream); } - + /** * Returns whether this is a user preference node. * * @return {@code true}, if this is a user preference node, {@code false} if * this is a system preference node. - * @since Android 1.0 */ public abstract boolean isUserNode(); - + /** * Returns all preference keys stored in this node or an empty array if no * key was found. @@ -595,18 +582,16 @@ public abstract class Preferences { * failure. * @throws IllegalStateException * if this node has been removed. - * @since Android 1.0 */ public abstract String[] keys() throws BackingStoreException; - + /** * Returns the name of this node. * * @return the name of this node. - * @since Android 1.0 */ public abstract String name(); - + /** * Returns the preference node with the given path name. The path name can * be relative or absolute. The requested node and its ancestors will @@ -625,10 +610,9 @@ public abstract class Preferences { * if the path name is invalid. * @throws NullPointerException * if the given path is {@code null}. - * @since Android 1.0 */ - public abstract Preferences node (String path); - + public abstract Preferences node(String path); + /** * Returns whether the preference node with the given path name exists. The * path is treated as relative to this node if it doesn't start with a slash, @@ -653,10 +637,9 @@ public abstract class Preferences { * @throws BackingStoreException * if the backing store is unavailable or causes an operation * failure. - * @since Android 1.0 */ - public abstract boolean nodeExists (String path) throws BackingStoreException; - + public abstract boolean nodeExists(String path) throws BackingStoreException; + /** * Returns the parent preference node of this node or {@code null} if this * node is the root node. @@ -664,10 +647,9 @@ public abstract class Preferences { * @return the parent preference node of this node. * @throws IllegalStateException * if this node has been removed. - * @since Android 1.0 */ public abstract Preferences parent(); - + /** * Adds a new preference to this node using the given key and value or * updates the value if a preference with the given key already exists. @@ -684,14 +666,13 @@ public abstract class Preferences { * MAX_VALUE_LENGTH}. * @throws IllegalStateException * if this node has been removed. - * @since Android 1.0 */ - public abstract void put (String key, String value); - + public abstract void put(String key, String value); + /** - * Adds a new preference with a {@code boolean} value to this node using the given - * key and value or updates the value if a preference with the given key - * already exists. + * Adds a new preference with a {@code boolean} value to this node using the + * given key and value or updates the value if a preference with the given + * key already exists. * * @param key * the preference key to be added or updated. @@ -704,10 +685,9 @@ public abstract class Preferences { * MAX_KEY_LENGTH}. * @throws IllegalStateException * if this node has been removed. - * @since Android 1.0 */ - public abstract void putBoolean (String key, boolean value); - + public abstract void putBoolean(String key, boolean value); + /** * Adds a new preference to this node using the given key and the string * form of the given value or updates the value if a preference with the @@ -730,10 +710,9 @@ public abstract class Preferences { * quarters of {@code MAX_KEY_LENGTH}. * @throws IllegalStateException * if this node has been removed. - * @since Android 1.0 */ - public abstract void putByteArray (String key, byte[] value); - + public abstract void putByteArray(String key, byte[] value); + /** * Adds a new preference to this node using the given key and {@code double} * value or updates the value if a preference with the @@ -754,12 +733,11 @@ public abstract class Preferences { * MAX_KEY_LENGTH}. * @throws IllegalStateException * if this node has been removed. - * @since Android 1.0 */ - public abstract void putDouble (String key, double value); - + public abstract void putDouble(String key, double value); + /** - * Adds a new preference to this node using the given key and {@code float} + * Adds a new preference to this node using the given key and {@code float} * value or updates the value if a preference with the * given key already exists. *

          @@ -778,12 +756,11 @@ public abstract class Preferences { * MAX_KEY_LENGTH}. * @throws IllegalStateException * if this node has been removed. - * @since Android 1.0 */ - public abstract void putFloat (String key, float value); - + public abstract void putFloat(String key, float value); + /** - * Adds a new preference to this node using the given key and {@code int} + * Adds a new preference to this node using the given key and {@code int} * value or updates the value if a preference with the * given key already exists. *

          @@ -802,12 +779,11 @@ public abstract class Preferences { * MAX_KEY_LENGTH}. * @throws IllegalStateException * if this node has been removed. - * @since Android 1.0 */ - public abstract void putInt (String key, int value); - + public abstract void putInt(String key, int value); + /** - * Adds a new preference to this node using the given key and {@code long} + * Adds a new preference to this node using the given key and {@code long} * value or updates the value if a preference with the * given key already exists. *

          @@ -826,9 +802,8 @@ public abstract class Preferences { * MAX_KEY_LENGTH}. * @throws IllegalStateException * if this node has been removed. - * @since Android 1.0 */ - public abstract void putLong (String key, long value); + public abstract void putLong(String key, long value); /** * Removes the preference mapped to the given key from this node. @@ -839,13 +814,12 @@ public abstract class Preferences { * if the given key is {@code null}. * @throws IllegalStateException * if this node has been removed. - * @since Android 1.0 */ - public abstract void remove (String key); - + public abstract void remove(String key); + /** - * Removes this preference node with all its descendants. The removal - * won't necessarily be persisted until the method {@code flush()} is invoked. + * Removes this preference node with all its descendants. The removal won't + * necessarily be persisted until the method {@code flush()} is invoked. * * @throws BackingStoreException * if the backing store is unavailable or causes an operation @@ -854,14 +828,13 @@ public abstract class Preferences { * if this node has been removed. * @throws UnsupportedOperationException * if this is a root node. - * @since Android 1.0 */ public abstract void removeNode() throws BackingStoreException; - + /** - * Registers a {@code NodeChangeListener} instance for this node, which will handle - * {@code NodeChangeEvent}s. {@code NodeChangeEvent}s will be fired when a child node has - * been added to or removed from this node. + * Registers a {@code NodeChangeListener} instance for this node, which will + * handle {@code NodeChangeEvent}s. {@code NodeChangeEvent}s will be fired + * when a child node has been added to or removed from this node. * * @param ncl * the listener to be registered. @@ -869,25 +842,24 @@ public abstract class Preferences { * if the given listener is {@code null}. * @throws IllegalStateException * if this node has been removed. - * @since Android 1.0 */ - public abstract void addNodeChangeListener (NodeChangeListener ncl); - + public abstract void addNodeChangeListener(NodeChangeListener ncl); + /** - * Registers a {@code PreferenceChangeListener} instance for this node, which will - * handle {@code PreferenceChangeEvent}s. {@code PreferenceChangeEvent}s will be fired when - * a preference has been added to, removed from, or updated for this node. - * + * Registers a {@code PreferenceChangeListener} instance for this node, + * which will handle {@code PreferenceChangeEvent}s. {@code + * PreferenceChangeEvent}s will be fired when a preference has been added + * to, removed from, or updated for this node. + * * @param pcl * the listener to be registered. * @throws NullPointerException * if the given listener is {@code null}. * @throws IllegalStateException * if this node has been removed. - * @since Android 1.0 */ public abstract void addPreferenceChangeListener (PreferenceChangeListener pcl); - + /** * Removes the given {@code NodeChangeListener} instance from this node. * @@ -897,12 +869,12 @@ public abstract class Preferences { * if the given listener is {@code null}. * @throws IllegalStateException * if this node has been removed. - * @since Android 1.0 */ public abstract void removeNodeChangeListener (NodeChangeListener ncl); - + /** - * Removes the given {@code PreferenceChangeListener} instance from this node. + * Removes the given {@code PreferenceChangeListener} instance from this + * node. * * @param pcl * the listener to be removed. @@ -910,36 +882,34 @@ public abstract class Preferences { * if the given listener is {@code null}. * @throws IllegalStateException * if this node has been removed. - * @since Android 1.0 */ public abstract void removePreferenceChangeListener (PreferenceChangeListener pcl); - + /** * Synchronizes the data of this preference node and its descendants with - * the back-end preference store. Any changes found in the back-end data should be reflected - * in this node and its descendants, and at the same time any local changes to this node and - * descendants should be persisted. + * the back-end preference store. Any changes found in the back-end data + * should be reflected in this node and its descendants, and at the same + * time any local changes to this node and descendants should be persisted. * * @throws BackingStoreException * if the backing store is unavailable or causes an operation * failure. * @throws IllegalStateException * if this node has been removed. - * @since Android 1.0 */ public abstract void sync() throws BackingStoreException; - + /** * Returns the system preference node for the package of the given class. * The absolute path of the returned node is one slash followed by the given * class's full package name, replacing each period character ('.') with - * a slash. For example, the absolute path of the preference associated with + * a slash. For example, the absolute path of the preference associated with * the class Object would be "/java/lang". As a special case, the unnamed * package is associated with a preference node "/<unnamed>". This * method will create the node and its ancestors as needed. Any nodes created - * by this method won't necessarily be persisted until the method {@code flush()} is - * invoked. - * + * by this method won't necessarily be persisted until the method {@code + * flush()} is invoked. + * * @param c * the given class. * @return the system preference node for the package of the given class. @@ -948,13 +918,12 @@ public abstract class Preferences { * @throws SecurityException * if the {@code RuntimePermission("preferences")} is denied by * a SecurityManager. - * @since Android 1.0 */ public static Preferences systemNodeForPackage (Class c) { checkSecurity(); return factory.systemRoot().node(getNodeName(c)); } - + /** * Returns the root node of the system preference hierarchy. * @@ -962,33 +931,32 @@ public abstract class Preferences { * @throws SecurityException * if the {@code RuntimePermission("preferences")} is denied by * a SecurityManager. - * @since Android 1.0 */ public static Preferences systemRoot() { checkSecurity(); return factory.systemRoot(); } - + //check the RuntimePermission("preferences") private static void checkSecurity() { SecurityManager manager = System.getSecurityManager(); if(null != manager){ manager.checkPermission(PREFS_PERM); } - + } /** * Returns the user preference node for the package of the given class. * The absolute path of the returned node is one slash followed by the given * class's full package name, replacing each period character ('.') with - * a slash. For example, the absolute path of the preference associated with + * a slash. For example, the absolute path of the preference associated with * the class Object would be "/java/lang". As a special case, the unnamed * package is associated with a preference node "/<unnamed>". This * method will create the node and its ancestors as needed. Any nodes created - * by this method won't necessarily be persisted until the method {@code flush()} is - * invoked. - * + * by this method won't necessarily be persisted until the method {@code + * flush()} is invoked. + * * @param c * the given class. * @return the user preference node for the package of the given class. @@ -997,24 +965,19 @@ public abstract class Preferences { * @throws SecurityException * if the {@code RuntimePermission("preferences")} is denied by * a SecurityManager. - * @since Android 1.0 */ public static Preferences userNodeForPackage (Class c) { checkSecurity(); return factory.userRoot().node(getNodeName(c)); } - + //parse node's absolute path from class instance private static String getNodeName(Class c){ - // ??? PREFS TODO change back to harmony code once getPackage - // delivers the correct results - // Package p = c.getPackage(); - // if(null == p){ - // return "/"; //$NON-NLS-1$ - // } - // return "/"+p.getName().replace('.', '/'); //$NON-NLS-1$ - int dotIndex = c.getName().lastIndexOf("."); - return "/" + c.getName().substring(0, dotIndex).replace(".", "/"); + Package p = c.getPackage(); + if(null == p){ + return "/"; //$NON-NLS-1$ + } + return "/"+p.getName().replace('.', '/'); //$NON-NLS-1$ } /** @@ -1024,7 +987,6 @@ public abstract class Preferences { * @throws SecurityException * if the {@code RuntimePermission("preferences")} is denied by * a SecurityManager. - * @since Android 1.0 */ public static Preferences userRoot() { checkSecurity(); @@ -1036,7 +998,6 @@ public abstract class Preferences { * Preference Node: " followed by this node's absolute path. * * @return the string representation of this node. - * @since Android 1.0 */ @Override public abstract String toString(); diff --git a/libcore/prefs/src/main/java/java/util/prefs/PreferencesFactory.java b/libcore/prefs/src/main/java/java/util/prefs/PreferencesFactory.java index e56dd95d2..3ac13e415 100644 --- a/libcore/prefs/src/main/java/java/util/prefs/PreferencesFactory.java +++ b/libcore/prefs/src/main/java/java/util/prefs/PreferencesFactory.java @@ -14,36 +14,30 @@ * limitations under the License. */ - package java.util.prefs; /** * This interface is used by the {@link Preferences} class as factory class to - * create {@code Preferences} instances. This interface can be implemented and installed - * to replace the default preferences implementation. + * create {@code Preferences} instances. This interface can be implemented and + * installed to replace the default preferences implementation. + * + * @see java.util.prefs.Preferences * - * @since Android 1.0 + * @since 1.4 */ public interface PreferencesFactory { - /** * Returns the root node of the preferences hierarchy for the calling user * context. * * @return the user preferences hierarchy root node. - * @since Android 1.0 */ Preferences userRoot(); - + /** * Returns the root node of the system preferences hierarchy. * * @return the system preferences hierarchy root node. - * @since Android 1.0 */ Preferences systemRoot(); } - - - - diff --git a/libcore/prefs/src/main/java/java/util/prefs/XMLParser.java b/libcore/prefs/src/main/java/java/util/prefs/XMLParser.java index 2edfc7126..c5a234c51 100644 --- a/libcore/prefs/src/main/java/java/util/prefs/XMLParser.java +++ b/libcore/prefs/src/main/java/java/util/prefs/XMLParser.java @@ -14,7 +14,6 @@ * limitations under the License. */ - package java.util.prefs; import java.io.BufferedInputStream; @@ -27,6 +26,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.StringReader; +import java.io.Writer; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; import java.security.AccessController; @@ -64,9 +64,7 @@ import org.w3c.dom.Node; // END android-added /** - * Utility class for importing and exporting {@code Preferences} data from an XML file. - * - * @since Android 1.0 + * Utility class for the Preferences import/export from XML file. */ class XMLParser { @@ -79,15 +77,15 @@ class XMLParser { * Constant - the DTD string */ static final String PREFS_DTD = "" //$NON-NLS-1$ - + " " //$NON-NLS-1$ - + " " //$NON-NLS-1$ - + " " //$NON-NLS-1$ - + " " //$NON-NLS-1$ - + " " //$NON-NLS-1$ - + " " //$NON-NLS-1$ - + " " //$NON-NLS-1$ - + " " //$NON-NLS-1$ - + " "; //$NON-NLS-1$ + + " " //$NON-NLS-1$ + + " " //$NON-NLS-1$ + + " " //$NON-NLS-1$ + + " " //$NON-NLS-1$ + + " " //$NON-NLS-1$ + + " " //$NON-NLS-1$ + + " " //$NON-NLS-1$ + + " " //$NON-NLS-1$ + + " "; //$NON-NLS-1$ /* * Constant - the specified header @@ -103,24 +101,24 @@ class XMLParser { * empty string array constant */ private static final String[] EMPTY_SARRAY = new String[0]; - + /* - * Constant - used by FilePreferencesImpl, which is default implementation of Linux platform + * Constant - used by FilePreferencesImpl, which is default implementation of Linux platform */ private static final String FILE_PREFS = ""; //$NON-NLS-1$ /* * Constant - specify the DTD version */ - private static final float XML_VERSION = 1.0f; - + private static final float XML_VERSION = 1.0f; + /* * DOM builder */ private static final DocumentBuilder builder; /* - * specify the indent level + * specify the indent level */ private static int indent = -1; @@ -139,7 +137,7 @@ class XMLParser { } builder.setEntityResolver(new EntityResolver() { public InputSource resolveEntity(String publicId, String systemId) - throws SAXException, IOException { + throws SAXException, IOException { if (systemId.equals(PREFS_DTD_NAME)) { InputSource result = new InputSource(new StringReader( PREFS_DTD)); @@ -196,7 +194,7 @@ class XMLParser { flushEmptyElement("map", out); //$NON-NLS-1$ StringTokenizer ancestors = new StringTokenizer(prefs.absolutePath(), - "/"); //$NON-NLS-1$ + "/"); //$NON-NLS-1$ exportNode(ancestors, prefs, withSubTree, out); flushEndTag("root", out); //$NON-NLS-1$ @@ -207,7 +205,7 @@ class XMLParser { private static void exportNode(StringTokenizer ancestors, Preferences prefs, boolean withSubTree, BufferedWriter out) - throws IOException, BackingStoreException { + throws IOException, BackingStoreException { if (ancestors.hasMoreTokens()) { String name = ancestors.nextToken(); flushStartTag( @@ -226,7 +224,7 @@ class XMLParser { } private static void exportSubTree(Preferences prefs, BufferedWriter out) - throws BackingStoreException, IOException { + throws BackingStoreException, IOException { String[] names = prefs.childrenNames(); if (names.length > 0) { for (int i = 0; i < names.length; i++) { @@ -241,7 +239,7 @@ class XMLParser { } private static void exportEntries(Preferences prefs, BufferedWriter out) - throws BackingStoreException, IOException { + throws BackingStoreException, IOException { String[] keys = prefs.keys(); String[] values = new String[keys.length]; for (int i = 0; i < keys.length; i++) { @@ -267,7 +265,7 @@ class XMLParser { } private static void flushEndTag(String tagName, BufferedWriter out) - throws IOException { + throws IOException { flushIndent(indent--, out); out.write(" input = new ArrayList(); - + String[] path = string.split("/"); - + NodeList childNodes = documentElement.getChildNodes(); - + if(path[0].equals("entry") || path[0].equals("node")) { for(int i = 0; i < childNodes.getLength(); i++) { Object next = childNodes.item(i); @@ -483,21 +481,21 @@ class XMLParser { } } } - + result = new NodeSet(input.iterator()); - + return result; } // END android-added - + /*************************************************************************** * utilities for FilePreferencesImpl, which is default implementation of Linux platform **************************************************************************/ /** * load preferences from file, if cannot load, create a new one FIXME: need * lock or not? - * - * @param file the XML file to be read + * + * @param file the XML file to be read * @return Properties instance which indicates the preferences key-value pairs */ static Properties loadFilePrefs(final File file) { @@ -506,14 +504,6 @@ class XMLParser { return loadFilePrefsImpl(file); } }); - - // try { - // //FIXME: lines below can be deleted, because it is not required to - // persistent at the very beginning - // flushFilePrefs(file, result); - // } catch (IOException e) { - // e.printStackTrace(); - // } } static Properties loadFilePrefsImpl(final File file) { @@ -524,7 +514,6 @@ class XMLParser { InputStream in = null; FileLock lock = null; try { - FileInputStream istream = new FileInputStream(file); // BEGIN android-modified in = new BufferedInputStream(istream, 8192); @@ -544,17 +533,16 @@ class XMLParser { result.setProperty(key, value); } return result; - } catch (Exception e) { - e.printStackTrace(); + } catch (IOException e) { + } catch (SAXException e) { + // BEGIN android-removed + // } catch (TransformerException e) { + // // transform shouldn't fail for xpath call + // throw new AssertionError(e); + // END android-removed } finally { - try { - lock.release(); - } catch (Exception e) {//ignore - } - try { - in.close(); - } catch (Exception e) {//ignore - } + releaseQuietly(lock); + closeQuietly(in); } } else { file.delete(); @@ -563,7 +551,7 @@ class XMLParser { } /** - * + * * @param file * @param prefs * @throws PrivilegedActionException @@ -576,7 +564,7 @@ class XMLParser { } }); } - + static void flushFilePrefsImpl(File file, Properties prefs) throws IOException { BufferedWriter out = null; FileLock lock = null; @@ -604,18 +592,35 @@ class XMLParser { } out.flush(); } finally { - try { - lock.release(); - } catch (Exception e) {//ignore - } - try { - if (null != out) { - out.close(); - } - } catch (Exception e) {//ignore - } + releaseQuietly(lock); + closeQuietly(out); } } -} + private static void releaseQuietly(FileLock lock) { + if (lock == null) { + return; + } + try { + lock.release(); + } catch (IOException e) {} + } + private static void closeQuietly(Writer out) { + if (out == null) { + return; + } + try { + out.close(); + } catch (IOException e) {} + } + + private static void closeQuietly(InputStream in) { + if (in == null) { + return; + } + try { + in.close(); + } catch (IOException e) {} + } +} diff --git a/libcore/prefs/src/main/java/org/apache/harmony/prefs/internal/nls/Messages.java b/libcore/prefs/src/main/java/org/apache/harmony/prefs/internal/nls/Messages.java index cfc7236c0..aaf5d8da5 100644 --- a/libcore/prefs/src/main/java/org/apache/harmony/prefs/internal/nls/Messages.java +++ b/libcore/prefs/src/main/java/org/apache/harmony/prefs/internal/nls/Messages.java @@ -43,7 +43,7 @@ import org.apache.harmony.luni.util.MsgHelp; * is looked up, or resource bundle support is not available, the key itself * will be returned as the associated message. This means that the KEY * should a reasonable human-readable (english) string. - * + * */ public class Messages { @@ -54,7 +54,7 @@ public class Messages { /** * Retrieves a message which has no arguments. - * + * * @param msg * String the key to look up. * @return String the message for that key in the system message bundle. @@ -67,7 +67,7 @@ public class Messages { /** * Retrieves a message which takes 1 argument. - * + * * @param msg * String the key to look up. * @param arg @@ -80,7 +80,7 @@ public class Messages { /** * Retrieves a message which takes 1 integer argument. - * + * * @param msg * String the key to look up. * @param arg @@ -93,7 +93,7 @@ public class Messages { /** * Retrieves a message which takes 1 character argument. - * + * * @param msg * String the key to look up. * @param arg @@ -106,7 +106,7 @@ public class Messages { /** * Retrieves a message which takes 2 arguments. - * + * * @param msg * String the key to look up. * @param arg1 @@ -121,7 +121,7 @@ public class Messages { /** * Retrieves a message which takes several arguments. - * + * * @param msg * String the key to look up. * @param args diff --git a/libcore/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/AbstractPreferencesTest.java b/libcore/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/AbstractPreferencesTest.java index 418e52db9..76ef4e7f4 100644 --- a/libcore/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/AbstractPreferencesTest.java +++ b/libcore/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/AbstractPreferencesTest.java @@ -35,9 +35,13 @@ import java.util.prefs.PreferenceChangeEvent; import java.util.prefs.PreferenceChangeListener; import java.util.prefs.Preferences; +import tests.util.PrefsTester; + @TestTargetClass(AbstractPreferences.class) public class AbstractPreferencesTest extends TestCase { + private final PrefsTester prefsTester = new PrefsTester(); + AbstractPreferences pref; static AbstractPreferences root; @@ -51,13 +55,8 @@ public class AbstractPreferencesTest extends TestCase { protected void setUp() throws Exception { super.setUp(); + prefsTester.setUp(); - System.setProperty("user.home", System.getProperty("java.io.tmpdir")); - System.setProperty("java.home", System.getProperty("java.io.tmpdir")); - - Preferences.systemRoot().clear(); - Preferences.userRoot().clear(); - root = (AbstractPreferences) Preferences.userRoot(); parent = (AbstractPreferences) Preferences.userNodeForPackage(this.getClass()); @@ -65,11 +64,7 @@ public class AbstractPreferencesTest extends TestCase { } protected void tearDown() throws Exception { - parent.removeNode(); - Preferences.systemRoot().clear(); - Preferences.userRoot().clear(); - System.setProperty("user.home", oldUserHome); - System.setProperty("java.home", oldJavaHome); + prefsTester.tearDown(); super.tearDown(); } @@ -854,6 +849,31 @@ public class AbstractPreferencesTest extends TestCase { } } + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "", + method = "nodeExists", + args = {String.class} + ) + public void test_nodeExists() throws BackingStoreException { + AbstractPreferences test = (AbstractPreferences) Preferences.userRoot() + .node("test"); + try { + test.nodeExists(null); + fail("should throw NullPointerException"); + } catch (NullPointerException e) { + // Expected + } + + test.removeNode(); + try { + test.nodeExists(null); + fail("should throw NullPointerException"); + } catch (NullPointerException e) { + // Expected + } + } + @TestTargetNew( level = TestLevel.COMPLETE, notes = "", diff --git a/libcore/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/FilePreferencesImplTest.java b/libcore/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/FilePreferencesImplTest.java index dc2ec2e9b..0cb197503 100644 --- a/libcore/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/FilePreferencesImplTest.java +++ b/libcore/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/FilePreferencesImplTest.java @@ -28,19 +28,23 @@ import java.util.prefs.BackingStoreException; import java.util.prefs.Preferences; import junit.framework.TestCase; +import tests.util.PrefsTester; @TestTargetClass(java.util.prefs.Preferences.class) public class FilePreferencesImplTest extends TestCase { - public FilePreferencesImplTest() { - super(); - } - + private final PrefsTester prefsTester = new PrefsTester(); + @Override - protected void setUp(){ + protected void setUp() throws Exception { + super.setUp(); + prefsTester.setUp(); } + @Override - protected void tearDown(){ + protected void tearDown() throws Exception { + prefsTester.tearDown(); + super.tearDown(); } @TestTargets({ @@ -303,6 +307,7 @@ public class FilePreferencesImplTest extends TestCase { System.setSecurityManager(dflt); } + @Override public void checkPermission(Permission perm) { if (perm instanceof FilePermission) { throw new SecurityException(); @@ -311,6 +316,7 @@ public class FilePreferencesImplTest extends TestCase { } } + @Override public void checkPermission(Permission perm, Object ctx) { if (perm instanceof FilePermission) { System.out.println(perm.getActions()); diff --git a/libcore/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/InvalidPreferencesFormatExceptionTest.java b/libcore/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/InvalidPreferencesFormatExceptionTest.java index 28c953ef5..c7ff946f4 100644 --- a/libcore/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/InvalidPreferencesFormatExceptionTest.java +++ b/libcore/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/InvalidPreferencesFormatExceptionTest.java @@ -44,7 +44,7 @@ public class InvalidPreferencesFormatExceptionTest extends TestCase { ) public void testInvalidPreferencesFormatExceptionString() { InvalidPreferencesFormatException e = new InvalidPreferencesFormatException( - "msg"); + "msg"); assertNull(e.getCause()); assertEquals("msg", e.getMessage()); } @@ -99,7 +99,7 @@ public class InvalidPreferencesFormatExceptionTest extends TestCase { public void testSerializationSelf() throws Exception { SerializationTest.verifySelf(new InvalidPreferencesFormatException( - "msg")); + "msg")); } /** diff --git a/libcore/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/MockAbstractPreferences.java b/libcore/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/MockAbstractPreferences.java index 18209542c..b14922586 100644 --- a/libcore/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/MockAbstractPreferences.java +++ b/libcore/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/MockAbstractPreferences.java @@ -84,6 +84,7 @@ public class MockAbstractPreferences extends AbstractPreferences { return lock; } + @Override public String[] childrenNamesSpi() throws BackingStoreException { checkException(); if (result == returnNull) @@ -108,6 +109,7 @@ public class MockAbstractPreferences extends AbstractPreferences { return childSpi(name); } + @Override public AbstractPreferences childSpi(String name) { try { checkException(); @@ -123,11 +125,13 @@ public class MockAbstractPreferences extends AbstractPreferences { return r; } + @Override public void flushSpi() throws BackingStoreException { checkException(); flushedTimes++; } + @Override public String getSpi(String key) { try { checkException(); @@ -139,6 +143,7 @@ public class MockAbstractPreferences extends AbstractPreferences { return result == returnNull ? null : attr.getProperty(key); } + @Override public String[] keysSpi() throws BackingStoreException { checkException(); Set keys = attr.keySet(); @@ -147,6 +152,7 @@ public class MockAbstractPreferences extends AbstractPreferences { return result == returnNull ? null : results; } + @Override public void putSpi(String name, String value) { try { checkException(); @@ -158,11 +164,13 @@ public class MockAbstractPreferences extends AbstractPreferences { attr.put(name, value); } + @Override protected void removeNodeSpi() throws BackingStoreException { checkException(); ((MockAbstractPreferences) parent()).childs.remove(name()); } + @Override public void removeSpi(String key) { try { checkException(); @@ -174,6 +182,7 @@ public class MockAbstractPreferences extends AbstractPreferences { attr.remove(key); } + @Override public void syncSpi() throws BackingStoreException { checkException(); syncTimes++; diff --git a/libcore/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/MockSecurityManager.java b/libcore/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/MockSecurityManager.java index e5a0bfdcd..d6047fbb3 100644 --- a/libcore/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/MockSecurityManager.java +++ b/libcore/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/MockSecurityManager.java @@ -39,6 +39,7 @@ class MockSecurityManager extends SecurityManager { System.setSecurityManager(dflt); } + @Override public void checkPermission(Permission perm) { if (perm instanceof RuntimePermission && perm.getName().equals("preferences")) { @@ -48,6 +49,7 @@ class MockSecurityManager extends SecurityManager { } } + @Override public void checkPermission(Permission perm, Object ctx) { if (perm instanceof RuntimePermission && perm.getName().equals("preferences")) { diff --git a/libcore/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/NodeChangeEventTest.java b/libcore/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/NodeChangeEventTest.java index 3260a04de..1afd7557b 100644 --- a/libcore/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/NodeChangeEventTest.java +++ b/libcore/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/NodeChangeEventTest.java @@ -30,6 +30,7 @@ import java.util.prefs.Preferences; import junit.framework.TestCase; import org.apache.harmony.testframework.serialization.SerializationTest; +import tests.util.PrefsTester; /** * @@ -37,8 +38,20 @@ import org.apache.harmony.testframework.serialization.SerializationTest; @TestTargetClass(NodeChangeEvent.class) public class NodeChangeEventTest extends TestCase { + private final PrefsTester prefsTester = new PrefsTester(); + NodeChangeEvent event; + protected void setUp() throws Exception { + super.setUp(); + prefsTester.setUp(); + } + + protected void tearDown() throws Exception { + prefsTester.tearDown(); + super.tearDown(); + } + @TestTargetNew( level = TestLevel.COMPLETE, notes = "", diff --git a/libcore/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/NodeChangeListenerTest.java b/libcore/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/NodeChangeListenerTest.java index c5e3252da..3cdb4d974 100644 --- a/libcore/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/NodeChangeListenerTest.java +++ b/libcore/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/NodeChangeListenerTest.java @@ -26,6 +26,7 @@ import java.util.prefs.NodeChangeListener; import java.util.prefs.Preferences; import junit.framework.TestCase; +import tests.util.PrefsTester; /** * @@ -33,20 +34,26 @@ import junit.framework.TestCase; @TestTargetClass(NodeChangeListener.class) public class NodeChangeListenerTest extends TestCase { + private final PrefsTester prefsTester = new PrefsTester(); + NodeChangeListener l; /* * @see TestCase#setUp() */ + @Override protected void setUp() throws Exception { super.setUp(); + prefsTester.setUp(); l = new NodeChangeListenerImpl(); } /* * @see TestCase#tearDown() */ + @Override protected void tearDown() throws Exception { + prefsTester.tearDown(); super.tearDown(); } diff --git a/libcore/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/PreferenceChangeEventTest.java b/libcore/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/PreferenceChangeEventTest.java index 4030b8918..dda1f6ccd 100644 --- a/libcore/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/PreferenceChangeEventTest.java +++ b/libcore/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/PreferenceChangeEventTest.java @@ -92,7 +92,7 @@ public class PreferenceChangeEventTest extends TestCase { ) public void testConstructor() { event = new PreferenceChangeEvent(Preferences.userRoot(), "key", - "value"); + "value"); assertEquals("key", event.getKey()); assertEquals("value", event.getNewValue()); assertSame(Preferences.userRoot(), event.getNode()); @@ -107,7 +107,7 @@ public class PreferenceChangeEventTest extends TestCase { ) public void testSerialization() throws Exception { event = new PreferenceChangeEvent(Preferences.userRoot(), "key", - "value"); + "value"); try { SerializationTest.copySerializable(event); fail("No expected NotSerializableException"); diff --git a/libcore/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/PreferenceChangeListenerTest.java b/libcore/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/PreferenceChangeListenerTest.java index e4df9c4ff..7080aa503 100644 --- a/libcore/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/PreferenceChangeListenerTest.java +++ b/libcore/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/PreferenceChangeListenerTest.java @@ -38,6 +38,7 @@ public class PreferenceChangeListenerTest extends TestCase { /* * @see TestCase#setUp() */ + @Override protected void setUp() throws Exception { super.setUp(); l = new PreferenceChangeListenerImpl(); diff --git a/libcore/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/PreferencesFactoryTest.java b/libcore/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/PreferencesFactoryTest.java index 818d5adb8..729bc05b1 100644 --- a/libcore/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/PreferencesFactoryTest.java +++ b/libcore/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/PreferencesFactoryTest.java @@ -37,6 +37,7 @@ public class PreferencesFactoryTest extends TestCase { /* * @see TestCase#setUp() */ + @Override protected void setUp() throws Exception { super.setUp(); f = new PreferencesFactoryImpl(); @@ -49,7 +50,7 @@ public class PreferencesFactoryTest extends TestCase { args = {} ) public void testUserRoot() { - f.userRoot(); + assertNull(f.userRoot()); } @TestTargetNew( @@ -59,7 +60,7 @@ public class PreferencesFactoryTest extends TestCase { args = {} ) public void testSystemRoot() { - f.systemRoot(); + assertNull(f.systemRoot()); } public static class PreferencesFactoryImpl implements PreferencesFactory { diff --git a/libcore/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/PreferencesTest.java b/libcore/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/PreferencesTest.java index 9d526d66e..c9c74fd35 100644 --- a/libcore/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/PreferencesTest.java +++ b/libcore/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/PreferencesTest.java @@ -39,14 +39,15 @@ import java.util.prefs.NodeChangeListener; import java.util.prefs.PreferenceChangeListener; import java.util.prefs.Preferences; +import tests.util.PrefsTester; + /** * */ @TestTargetClass(Preferences.class) public class PreferencesTest extends TestCase { - private String oldJavaHome; - private String oldUserHome; + private final PrefsTester prefsTester = new PrefsTester(); MockSecurityManager manager = new MockSecurityManager(); @@ -74,39 +75,24 @@ public class PreferencesTest extends TestCase { /* * @see TestCase#setUp() */ + @Override protected void setUp() throws Exception { super.setUp(); in = new ByteArrayInputStream( "" .getBytes("UTF-8")); stream = new MockInputStream(in); - - Preferences.systemRoot().clear(); - Preferences.userRoot().clear(); - - Preferences p = Preferences.userNodeForPackage(Preferences.class); - p.clear(); - try { - p.removeNode(); - } catch (BackingStoreException e) { - } + prefsTester.setUp(); } /* * @see TestCase#tearDown() */ + @Override protected void tearDown() throws Exception { - super.tearDown(); stream.close(); - Preferences p = Preferences.userNodeForPackage(Preferences.class); - p.clear(); - try { - p.removeNode(); - } catch (BackingStoreException e) { - } - - Preferences.systemRoot().clear(); - Preferences.userRoot().clear(); + prefsTester.tearDown(); + super.tearDown(); } @TestTargetNew( @@ -1826,6 +1812,7 @@ public class PreferencesTest extends TestCase { wrapper = in; } + @Override public int read() throws IOException { checkException(); return wrapper.read(); @@ -1839,136 +1826,170 @@ public class PreferencesTest extends TestCase { super(); } + @Override public String absolutePath() { return null; } + @Override public String[] childrenNames() throws BackingStoreException { return null; } + @Override public void clear() throws BackingStoreException { } + @Override public void exportNode(OutputStream ostream) throws IOException, BackingStoreException { } + @Override public void exportSubtree(OutputStream ostream) throws IOException, BackingStoreException { } + @Override public void flush() throws BackingStoreException { } + @Override public String get(String key, String deflt) { return null; } + @Override public boolean getBoolean(String key, boolean deflt) { return false; } + @Override public byte[] getByteArray(String key, byte[] deflt) { return null; } + @Override public double getDouble(String key, double deflt) { return 0; } + @Override public float getFloat(String key, float deflt) { return 0; } + @Override public int getInt(String key, int deflt) { return 0; } + @Override public long getLong(String key, long deflt) { return 0; } + @Override public boolean isUserNode() { return false; } + @Override public String[] keys() throws BackingStoreException { return null; } + @Override public String name() { return null; } + @Override public Preferences node(String name) { return null; } + @Override public boolean nodeExists(String name) throws BackingStoreException { return false; } + @Override public Preferences parent() { return null; } + @Override public void put(String key, String value) { } + @Override public void putBoolean(String key, boolean value) { } + @Override public void putByteArray(String key, byte[] value) { } + @Override public void putDouble(String key, double value) { } + @Override public void putFloat(String key, float value) { } + @Override public void putInt(String key, int value) { } + @Override public void putLong(String key, long value) { } + @Override public void remove(String key) { } + @Override public void removeNode() throws BackingStoreException { } + @Override public void addNodeChangeListener(NodeChangeListener ncl) { } + @Override public void addPreferenceChangeListener(PreferenceChangeListener pcl) { } + @Override public void removeNodeChangeListener(NodeChangeListener ncl) { } + @Override public void removePreferenceChangeListener(PreferenceChangeListener pcl) { } + @Override public void sync() throws BackingStoreException { } + @Override public String toString() { return null; } diff --git a/libcore/prefs/src/test/java/tests/prefs/AllTests.java b/libcore/prefs/src/test/java/tests/prefs/AllTests.java index 843d733b2..b3f2ed6ed 100644 --- a/libcore/prefs/src/test/java/tests/prefs/AllTests.java +++ b/libcore/prefs/src/test/java/tests/prefs/AllTests.java @@ -20,7 +20,8 @@ import junit.framework.Test; import junit.framework.TestSuite; /** - * Test suite that includes all tests for the Math project. + * Test suite that includes all tests for the Prefs project. + * */ public class AllTests { diff --git a/libcore/prefs/src/test/resources/prefs/java/util/prefs/preferences.dtd b/libcore/prefs/src/test/resources/prefs/java/util/prefs/preferences.dtd deleted file mode 100644 index a11601528..000000000 --- a/libcore/prefs/src/test/resources/prefs/java/util/prefs/preferences.dtd +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/libcore/regex/src/main/java/java/util/regex/Pattern.java b/libcore/regex/src/main/java/java/util/regex/Pattern.java index c058db8fb..2853bbe39 100644 --- a/libcore/regex/src/main/java/java/util/regex/Pattern.java +++ b/libcore/regex/src/main/java/java/util/regex/Pattern.java @@ -356,28 +356,33 @@ public final class Pattern implements Serializable { } /** - * Splits the given input sequence around occurrences of the {@code Pattern}. - * The function first determines all occurrences of the {@code Pattern} - * inside the input sequence. It then builds an array of the - * "remaining" strings before, in-between, and after these - * occurrences. An additional parameter determines the maximal number of - * entries in the resulting array and the handling of trailing empty - * strings. + * Splits the given input sequence at occurrences of this {@code Pattern}. + * + * If this {@code Pattern} does not occur in the input, the result is an + * array containing the input (converted from a {@code CharSequence} to + * a {@code String}). + * + * Otherwise, the {@code limit} parameter controls the contents of the + * returned array as described below. * * @param inputSeq * the input sequence. * @param limit - * Determines the maximal number of entries in the resulting - * array. + * Determines the maximum number of entries in the resulting + * array, and the treatment of trailing empty strings. *
            - *
          • For n > 0, it is guaranteed that the resulting array - * contains at most n entries. + *
          • For n > 0, the resulting array contains at most n + * entries. If this is fewer than the number of matches, the + * final entry will contain all remaining input. *
          • For n < 0, the length of the resulting array is - * exactly the number of occurrences of the {@code Pattern} +1. + * exactly the number of occurrences of the {@code Pattern} + * plus one for the text after the final separator. * All entries are included. - *
          • For n == 0, the length of the resulting array is at most - * the number of occurrences of the {@code Pattern} +1. Empty - * strings at the end of the array are not included. + *
          • For n == 0, the result is as for n < 0, except + * trailing empty strings will not be returned. (Note that + * the case where the input is itself an empty string is + * special, as described above, and the limit parameter does + * not apply there.) *
          * * @return the resulting array. @@ -385,6 +390,13 @@ public final class Pattern implements Serializable { * @since Android 1.0 */ public String[] split(CharSequence inputSeq, int limit) { + if (inputSeq.length() == 0) { + // Unlike Perl, which considers the result of splitting the empty + // string to be the empty array, Java returns an array containing + // the empty string. + return new String[] { "" }; + } + int maxLength = limit <= 0 ? Integer.MAX_VALUE : limit; String input = inputSeq.toString(); @@ -393,14 +405,10 @@ public final class Pattern implements Serializable { Matcher matcher = new Matcher(this, inputSeq); int savedPos = 0; - // Add text preceding each occurrence, if enough space. Only do this for - // non-empty input sequences, because otherwise we'd add the "trailing - // empty string" twice. - if (inputSeq.length() != 0) { - while(matcher.find() && list.size() + 1 < maxLength) { - list.add(input.substring(savedPos, matcher.start())); - savedPos = matcher.end(); - } + // Add text preceding each occurrence, if enough space. + while(matcher.find() && list.size() + 1 < maxLength) { + list.add(input.substring(savedPos, matcher.start())); + savedPos = matcher.end(); } // Add trailing text if enough space. @@ -412,11 +420,10 @@ public final class Pattern implements Serializable { } } - // Remove trailing spaces, if limit == 0 is requested. + // Remove trailing empty matches in the limit == 0 case. if (limit == 0) { int i = list.size() - 1; - // Don't remove 1st element, since array must not be empty. - while(i > 0 && "".equals(list.get(i))) { + while (i >= 0 && "".equals(list.get(i))) { list.remove(i); i--; } diff --git a/libcore/regex/src/test/java/org/apache/harmony/regex/tests/java/util/regex/SplitTest.java b/libcore/regex/src/test/java/org/apache/harmony/regex/tests/java/util/regex/SplitTest.java index ea615c05c..894dfff56 100644 --- a/libcore/regex/src/test/java/org/apache/harmony/regex/tests/java/util/regex/SplitTest.java +++ b/libcore/regex/src/test/java/org/apache/harmony/regex/tests/java/util/regex/SplitTest.java @@ -32,12 +32,62 @@ public class SplitTest extends TestCase { Pattern p = Pattern.compile("/"); String[] results = p.split("have/you/done/it/right"); String[] expected = new String[] { "have", "you", "done", "it", "right" }; - assertEquals(expected.length, results.length); + assertArraysEqual(expected, results); + } + + @TestTargets({ + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "Verifies the basic functionality of split with empty matches.", + method = "split", + args = {java.lang.CharSequence.class} + ) + }) + public void testEmptySplits() { + // Trailing empty matches are removed. + assertArraysEqual(new String[0], "hello".split(".")); + assertArraysEqual(new String[] { "1", "2" }, "1:2:".split(":")); + // ...including when that results in an empty result. + assertArraysEqual(new String[0], ":".split(":")); + // ...but not when limit < 0. + assertArraysEqual(new String[] { "1", "2", "" }, "1:2:".split(":", -1)); + + // Leading empty matches are retained. + assertArraysEqual(new String[] { "", "", "o" }, "hello".split("..")); + + // A separator that doesn't occur in the input gets you the input. + assertArraysEqual(new String[] { "hello" }, "hello".split("not-present-in-test")); + // ...including when the input is the empty string. + // (Perl returns an empty list instead.) + assertArraysEqual(new String[] { "" }, "".split("not-present-in-test")); + assertArraysEqual(new String[] { "" }, "".split("A?")); + + // The limit argument controls the size of the result. + // If l == 0, the result is as long as needed, except trailing empty matches are dropped. + // If l < 0, the result is as long as needed, and trailing empty matches are retained. + // If l > 0, the result contains the first l matches, plus one string containing the remaining input. + // Examples without a trailing separator (and hence without a trailing empty match): + assertArraysEqual(new String[] { "a", "b", "c" }, "a,b,c".split(",", 0)); + assertArraysEqual(new String[] { "a,b,c" }, "a,b,c".split(",", 1)); + assertArraysEqual(new String[] { "a", "b,c" }, "a,b,c".split(",", 2)); + assertArraysEqual(new String[] { "a", "b", "c" }, "a,b,c".split(",", 3)); + assertArraysEqual(new String[] { "a", "b", "c" }, "a,b,c".split(",", Integer.MAX_VALUE)); + // Examples with a trailing separator (and hence possibly with a trailing empty match): + assertArraysEqual(new String[] { "a", "b", "c" }, "a,b,c,".split(",", 0)); + assertArraysEqual(new String[] { "a,b,c," }, "a,b,c,".split(",", 1)); + assertArraysEqual(new String[] { "a", "b,c," }, "a,b,c,".split(",", 2)); + assertArraysEqual(new String[] { "a", "b", "c," }, "a,b,c,".split(",", 3)); + assertArraysEqual(new String[] { "a", "b", "c", "" }, "a,b,c,".split(",", Integer.MAX_VALUE)); + assertArraysEqual(new String[] { "a", "b", "c", "" }, "a,b,c,".split(",", -1)); + } + + private void assertArraysEqual(String[] expected, String[] actual) { + assertEquals(expected.length, actual.length); for (int i = 0; i < expected.length; i++) { - assertEquals(results[i], expected[i]); + assertEquals(Integer.toString(i), expected[i], actual[i]); } } - + @TestTargetNew( level = TestLevel.PARTIAL_COMPLETE, notes = "Verifies the functionality of split(java.lang.CharSequence). Test uses not empty pattern.", diff --git a/libcore/run-core-tests b/libcore/run-core-tests index 62b3c5186..70ab320e4 100755 --- a/libcore/run-core-tests +++ b/libcore/run-core-tests @@ -26,5 +26,5 @@ mkdir $tmp chmod 777 $tmp exec dalvikvm -Duser.language=en -Duser.region=US -Djava.io.tmpdir=$tmp \ - -Xbootclasspath:$BOOTCLASSPATH:/system/framework/core-tests.jar \ + -classpath /system/framework/core-tests.jar \ -Xmx64M com.google.coretests.Main "$@" diff --git a/libcore/security/src/main/files/cacerts.bks b/libcore/security/src/main/files/cacerts.bks index bbcc080e1f92cc715efba3d20fb82335df72f56c..36f4919c550300f560afec996d6cb1737dc99cd0 100644 GIT binary patch delta 4054 zcmdlzm3i@IW?cpb21W)329Z3mJ({256n1~!8T@Gr<1bI`khpgY4D9C_8JLVL85kIq z8$CD8-t5k_g^kk!!d>66nT7KXGp9L(yJq6#1KV0R=kZoDahgGdRvy{>f&UyUrzwQH zY~|+TqQOj@CJ^r8@0*n+mNRi0L%0i_H(!>LWac!2aOZVz{wk})%w-Cd{++n_he{_4 zHczFXEsNNoB;_y zwS2q_c5-xl1rsOKkCon=yEV(0S(O_-%W^lf>1wfnnA7+-AGUhN3iE35hs}%4q`@+i zK5TYzOJL<v4n|o(fvT!j%gE4u^X3?6f%$CZHo{gCdAix@-XKG-{z`&AV(8LmB(8TPrfSHMr ziHXG`d1smdFB_*;n@8JsUPeY%RtAH{EtBKR#g!Zk?AVw?S-3^a%gf94Qu9iRN{dVM z@={Cmodbg7jSNgICRbLtiy9jkf|M}}Yq=tnOy1}tp{1Y!Qlx1hC(dhWX=r3*VrXt^ zU}_j8&T9zaS{Q-2)UuBqYTx9C(-rI2`g*SvYTNW`u>!lOSc;am_r?9(UcP;A8_JfZ zZ?AATQ0#Of2FhWe5z3+HlZZtmdz+Fm|4^z4(vb=R{}R=0|@O^u#z6p(LwVpkf^<3D?Mcpfb4 ztoM}sdtk-h&c*5a$L8-TluX#6`%>rRgtD~iDUa4yTuTj(wYfHD(|JwipZZGzHeK;J z$7&SX^>58sm+?4`Hs@9*RYO=vaH{8{a>B(v?Ysl8lVY*m%$-uDcBqDmp+3=`BQ z@8R>zn7IFk`{lV)oeuK(-p^M%e^^2>Db2ay+SLQx><^ik85tNCmlzaHPCOuDFVDst zDyz&QVIbBZ68fa^qF}?m8=X5j790LtsKP0>v%o+Wq=1h_j0LOoLio zsn+ecA}h6znF(Ide$C#!^wzu>$+=-dUqducu)f_Sq}BU{S^EF;NdoN~thOGW%W5vP z`1lvmomZDwip>tTQl9v6Mbo0Jj*Nr#l}t)|RxP`>?*0am_#N#R?$*>-|6eb2{@HEz zwQ;vw=U&j}HNMSq@tLQCuy)yRyO(o1`9;r3Y-p`e^w0L^`(>7Xmi^Ozjth_O-Q2IW zen)$oq7BQb?Nc)9&)dA5aoyton*%okKInNztaHo1pm#XoR#9ltULV(aiM#fwDqU7s(RBXR=B?}Pn6;q=`&m$) zW?+El>iGsuEVB)oSXiLB+NoXFXR<^2O@9M(149E-LrWtAV@puJgK|wlT!Y39hTI06 zpv=oA%oG}IC}to6;&2J`LUXc?f@fZ`o}s0IIY^va*kE!)xu{>ULS|laeo;YwQDRAI zib7JQLQ!g3YEf!la;l*`I32NY3yWzaYbqES7?>%bnx$)?4Kj;aSOqFRxnQ&GWHB4X z`fXFUIXnG2maCQTaes=BWjO1*!8mLY|w zs;cTI+^v;uxY@WnQtgo7zh{#leQ$V@`rE{uPqU%GcfCcM-n{zo_4CBny_Pwxp%}kS z=En0yEJ{Bw-{QY<8j@q17}X7$7?mfR)+yB+vvFv%F|x9kv6!}tb_ZQ>av zB?VUc`uWMl1<0kdfiWm2%d_YhXc=fMP+OqVrUX@)T$F>XP`@~}s4TU}P%pVC$G{q- zNrA=0z{tRWjU`l8fx$q-fL9G>9}}aP)Z`6S5{d!A;24$#mlPQjo}@2X7Tj>WEp5ri z@7~V8TlVHo=B-wmoERWnZ?0#^%*4=$R=F@YHZt&RXsOW^{x{2ZzXa=4TLq8BVvX&h z0isJfdgmuxvC4m~n`hOwWaXuUb33azWR~b|cJJd)@ybtQZnpO~nPRp|Gq<~!NlCls zZ28iHscjQ4ZrdNU&p0c4=Fj|xLi<=w8}I)rB<1I#u`624*I?)8EhXpjK4pYO+`X~z zZwTub<%z8)e=;&K8AIDdrvx|u*|Zy81t0sk`M|b+@QU_`!sO#SQh z6z@#_yEBi;$YOKut~rd8o$KT#i|rAe%%~t-f35g=jepsN-u-WOE!t;tRKsX%&7Yh( zI@)_V=LPT2j$0+@vF$0x;?1XQ6MZkcac;Idyx?(isPv~@@7G#Bblz#P_?do}n)Uky z0bxInas9P8dxX0naLV)|MR}r*_rJ2ZPlOq zdous(33;T{&w4QR!?O39{frw$xAMp?{g?SaP-w!-F4n0AO{^0Qnpi5KWtTIr=DsxCZ$KE4cfIxd!=p z`nfBF2D>Ua#{0P_1iAWnI{G=g8oC>}LbTdB2LvlP`}>7Bdin(`c=`tT2ZcELg(&!V z`g(>qg3Jq6@OM)PcMJ-0^b7HH4ORfz<>Tq-lx^P9K|fG3M-yB z`be-T=qmUaI)U3&cND*7{H!|H{;A^r4QpSv+G8qDR-T>98202_q4=u$QYDEeCArde zVOKXUle-(K7JQ2B*)HyU^W7_s%@MU`lWylZB;|X#aoz2J)TLG_{stzqv{%ai)xKy^ zD4Fr8?{)P_2OEzgk{h=RsJ?eu{;Yg#4i9Zjkco@v; zrB=M-xM%ugW&W^D{dZ4BvOY}~-O^s?;P!GWLpAdj-Y3)2b?!+=a!bkYNOoCw@VZO; zk%^IW7@TvD-RIh{k>%{aeFkw>szrAf*}ZkMI=&<(=85VjVfGj6Zq1gQz367b+;bD$ zvl6QvUWgp}r{5BlCVpW0WlxKH$^1<7VWqU1K@+1A`K7cGsKAwH(KgUDP+y?BK)Fp3 zM=1?TvB`~*UXz9x3UfKYRAazcOzJG2C! zq##nyt!fkTtMAStC+o7pyqqik+B<_|Hbv~*cqd=#$>SpdpB$TZeW^Wu@aM0d;Qc$3 zwtGI7<@zIk?MPYH7ZMKI_yXCiGI$(Xh0G`J zY$`X2)Q!2{l&7<;>JrO@^bGHUKe{{qPe{qHoA~{rd%f}J=p7MyiZ@CVq=nD?*tFtT z)=I4?f7e!rEq6^=w-=mZeE+cVxnRI;yGPdr^s+?jw_jPDw5c;QG5lD4-@Gd-Ri76+ z$1kYryc2o0_Il~^J;7JqO^+EGD9oR|Yiskf&QM0*iPqOTP1|jnv#T_lu6SR0CRVb6 zQDAi@n}~V!_cQ~3D^SzV7}{;!UAXz@VRL3qXwz*+|7OeMxy-zvA`jYn+q!R~E-#ZP agV57R-}#G6V{+`*U;X**m^K41-xmO4;LHyI delta 1177 zcmZ3ynR)kAW?cpb21W)31`!8N{TI3i?NuWdUthMoUsR=bx40n#1M3z>1|}m*1_nmO zrweqPHaA}9W#R=1SwMs|-8SlOVdFH1@YG~Czhb+?%xMPUD*fJkp6eeIrzwOh|8?^< z-gB&+CJ?U7+s$i*gPAyuAzaCmn>odnGjSS0xMGhs@05^a<}`$Gh2L+!Ev>}NWn=*1 z3(9Vut$dc5%M{Ab`?8r`zLSNE5#rSO?@vws6C=Ob&@`NxRq^TkH(xgQT4=GbDn4Dn zC$+gqJ%yEv5#r|guQ)cZc9I55af@!=WAls^>iqf71vZ=d^s<69v4?Fw?UumG&13>G ze?fM|=7&0-Ok9i*zs-N*zWH&a2*|Dl%V`ygoaQ?@@7;0GfZ5J5O2?an6o)Q zRRwGxBirUbF~!WV&`UW#`8-$a#^Nh?=7N`Rk zB+lN<9(tJt8f5ctPur|eR|GP7{;$`YGfTEH!$LlG<>rcncP!AroPT5L=HoqA!6yDt z*gUwiQTdz2@}@I!GnqhRCA@dDM#XzpPDn7%zsj(=Xr=}Wmmwrt=6}uEEIZYk zg`3F)8f_uon+01nnYj$18a{2?{C2@Hcr*sOZFcFX1gVF}FYwddykxFDGnX+mT)qBm z-m}D!h07QkdhVY#v#-3s%w-ITtpzTZHa}dW3yY!!jw?2cZ?c2?({AEs$z8jdVZO0W z0|%(X{^ZR^_lYstL;Ym2i~$5#BlJuSEEyP>D-D{MOAVTsVizzoF)}f6Gl66ou2pQG z!fL?F#;Mij(e|B}k&$Jx)WJL^LzB&22j?&{F|bXxJghJ|;F`?j4ec!TO^l2T>sHPG zv;ER(`FKlB4T*UsCv*Jsr9PxAD)90bZkwH;G4DmA=Zspe-A^~nwKcol&l>oj(@;zP zZfE&Ce!ZNjEN|ORZ(*=+Ve)Or($cNqUYD=EsM)1i{%k;#kIMSJ-cI)h2 zxyq@=^x%#9tIG|o{F0q5*K=%0H0ilKW2LjnH~w}_>$!)*B4(5y{w}@d+uR>^tCokj ze)#(Hs$uHC?1(vqo8_(ty)qL253BP4 diff --git a/libcore/security/src/main/files/cacerts/3e7271e8.0 b/libcore/security/src/main/files/cacerts/3e7271e8.0 new file mode 100644 index 000000000..62b5b2271 --- /dev/null +++ b/libcore/security/src/main/files/cacerts/3e7271e8.0 @@ -0,0 +1,85 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 946059622 (0x3863b966) + Signature Algorithm: sha1WithRSAEncryption + Issuer: O=Entrust.net, OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.), OU=(c) 1999 Entrust.net Limited, CN=Entrust.net Certification Authority (2048) + Validity + Not Before: Dec 24 17:50:51 1999 GMT + Not After : Dec 24 18:20:51 2019 GMT + Subject: O=Entrust.net, OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.), OU=(c) 1999 Entrust.net Limited, CN=Entrust.net Certification Authority (2048) + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public Key: (2048 bit) + Modulus (2048 bit): + 00:ad:4d:4b:a9:12:86:b2:ea:a3:20:07:15:16:64: + 2a:2b:4b:d1:bf:0b:4a:4d:8e:ed:80:76:a5:67:b7: + 78:40:c0:73:42:c8:68:c0:db:53:2b:dd:5e:b8:76: + 98:35:93:8b:1a:9d:7c:13:3a:0e:1f:5b:b7:1e:cf: + e5:24:14:1e:b1:81:a9:8d:7d:b8:cc:6b:4b:03:f1: + 02:0c:dc:ab:a5:40:24:00:7f:74:94:a1:9d:08:29: + b3:88:0b:f5:87:77:9d:55:cd:e4:c3:7e:d7:6a:64: + ab:85:14:86:95:5b:97:32:50:6f:3d:c8:ba:66:0c: + e3:fc:bd:b8:49:c1:76:89:49:19:fd:c0:a8:bd:89: + a3:67:2f:c6:9f:bc:71:19:60:b8:2d:e9:2c:c9:90: + 76:66:7b:94:e2:af:78:d6:65:53:5d:3c:d6:9c:b2: + cf:29:03:f9:2f:a4:50:b2:d4:48:ce:05:32:55:8a: + fd:b2:64:4c:0e:e4:98:07:75:db:7f:df:b9:08:55: + 60:85:30:29:f9:7b:48:a4:69:86:e3:35:3f:1e:86: + 5d:7a:7a:15:bd:ef:00:8e:15:22:54:17:00:90:26: + 93:bc:0e:49:68:91:bf:f8:47:d3:9d:95:42:c1:0e: + 4d:df:6f:26:cf:c3:18:21:62:66:43:70:d6:d5:c0: + 07:e1 + Exponent: 65537 (0x10001) + X509v3 extensions: + Netscape Cert Type: + SSL CA, S/MIME CA, Object Signing CA + X509v3 Authority Key Identifier: + keyid:55:E4:81:D1:11:80:BE:D8:89:B9:08:A3:31:F9:A1:24:09:16:B9:70 + + X509v3 Subject Key Identifier: + 55:E4:81:D1:11:80:BE:D8:89:B9:08:A3:31:F9:A1:24:09:16:B9:70 + 1.2.840.113533.7.65.0: + 0...V5.0:4.0.... + Signature Algorithm: sha1WithRSAEncryption + 59:47:ac:21:84:8a:17:c9:9c:89:53:1e:ba:80:85:1a:c6:3c: + 4e:3e:b1:9c:b6:7c:c6:92:5d:18:64:02:e3:d3:06:08:11:61: + 7c:63:e3:2b:9d:31:03:70:76:d2:a3:28:a0:f4:bb:9a:63:73: + ed:6d:e5:2a:db:ed:14:a9:2b:c6:36:11:d0:2b:eb:07:8b:a5: + da:9e:5c:19:9d:56:12:f5:54:29:c8:05:ed:b2:12:2a:8d:f4: + 03:1b:ff:e7:92:10:87:b0:3a:b5:c3:9d:05:37:12:a3:c7:f4: + 15:b9:d5:a4:39:16:9b:53:3a:23:91:f1:a8:82:a2:6a:88:68: + c1:79:02:22:bc:aa:a6:d6:ae:df:b0:14:5f:b8:87:d0:dd:7c: + 7f:7b:ff:af:1c:cf:e6:db:07:ad:5e:db:85:9d:d0:2b:0d:33: + db:04:d1:e6:49:40:13:2b:76:fb:3e:e9:9c:89:0f:15:ce:18: + b0:85:78:21:4f:6b:4f:0e:fa:36:67:cd:07:f2:ff:08:d0:e2: + de:d9:bf:2a:af:b8:87:86:21:3c:04:ca:b7:94:68:7f:cf:3c: + e9:98:d7:38:ff:ec:c0:d9:50:f0:2e:4b:58:ae:46:6f:d0:2e: + c3:60:da:72:55:72:bd:4c:45:9e:61:ba:bf:84:81:92:03:d1: + d2:69:7c:c5 +-----BEGIN CERTIFICATE----- +MIIEXDCCA0SgAwIBAgIEOGO5ZjANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChML +RW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBp +bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5 +IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQxNzUwNTFaFw0xOTEy +MjQxODIwNTFaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3d3d3 +LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxp +YWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEG +A1UEAxMqRW50cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArU1LqRKGsuqjIAcVFmQq +K0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOLGp18EzoOH1u3Hs/lJBQe +sYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSrhRSGlVuX +MlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVT +XTzWnLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/ +HoZdenoVve8AjhUiVBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH +4QIDAQABo3QwcjARBglghkgBhvhCAQEEBAMCAAcwHwYDVR0jBBgwFoAUVeSB0RGA +vtiJuQijMfmhJAkWuXAwHQYDVR0OBBYEFFXkgdERgL7YibkIozH5oSQJFrlwMB0G +CSqGSIb2fQdBAAQQMA4bCFY1LjA6NC4wAwIEkDANBgkqhkiG9w0BAQUFAAOCAQEA +WUesIYSKF8mciVMeuoCFGsY8Tj6xnLZ8xpJdGGQC49MGCBFhfGPjK50xA3B20qMo +oPS7mmNz7W3lKtvtFKkrxjYR0CvrB4ul2p5cGZ1WEvVUKcgF7bISKo30Axv/55IQ +h7A6tcOdBTcSo8f0FbnVpDkWm1M6I5HxqIKiaohowXkCIryqptau37AUX7iH0N18 +f3v/rxzP5tsHrV7bhZ3QKw0z2wTR5klAEyt2+z7pnIkPFc4YsIV4IU9rTw76NmfN +B/L/CNDi3tm/Kq+4h4YhPATKt5Rof8886ZjXOP/swNlQ8C5LWK5Gb9Auw2DaclVy +vUxFnmG6v4SBkgPR0ml8xQ== +-----END CERTIFICATE----- diff --git a/libcore/security/src/main/files/cacerts/ab86d4de.0 b/libcore/security/src/main/files/cacerts/ab86d4de.0 new file mode 100644 index 000000000..c6f241fea --- /dev/null +++ b/libcore/security/src/main/files/cacerts/ab86d4de.0 @@ -0,0 +1,104 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 946062766 (0x3863c5ae) + Signature Algorithm: sha1WithRSAEncryption + Issuer: O=Entrust.net, OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.), OU=(c) 1999 Entrust.net Limited, CN=Entrust.net Certification Authority (2048) + Validity + Not Before: Aug 25 18:14:26 2008 GMT + Not After : Aug 25 18:44:26 2018 GMT + Subject: C=US, O=Entrust, Inc., OU=AND ADDITIONAL TERMS GOVERNING USE AND RELIANCE, OU=CPS CONTAINS IMPORTANT LIMITATIONS OF WARRANTIES AND LIABILITY, OU=www.entrust.net/CPS is incorporated by reference, OU=(c) 2008 Entrust, Inc., CN=Entrust Certification Authority - L1B + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public Key: (2048 bit) + Modulus (2048 bit): + 00:dc:21:f5:68:f9:7a:ce:87:f2:78:df:d8:3b:4d: + 06:7d:c6:24:e4:a9:cd:9d:01:56:e4:f6:71:17:aa: + 7f:75:22:18:e4:74:6d:1b:3e:56:d5:b1:a6:1e:dd: + 59:26:53:ca:06:e6:ba:0b:6f:37:bb:a8:c6:9c:15: + 3b:06:1b:87:0c:c2:1a:4d:d3:81:ae:db:50:65:a5: + 3a:64:4f:30:34:9a:2b:a9:1f:fd:2b:d1:38:71:19: + 68:f2:8e:eb:7b:c9:40:3c:48:c4:19:b1:b7:10:25: + ef:44:a7:e6:77:9b:7d:22:9a:de:d8:5e:d9:c3:ce: + c9:71:22:bb:ae:ef:05:d6:f2:17:e7:56:78:e1:53: + 05:4a:26:73:b8:c7:49:67:93:23:0f:56:b2:8f:dd: + c9:59:05:e5:63:15:b4:87:7e:40:46:e9:b5:00:7b: + 03:b4:0d:e4:96:67:2c:de:1b:59:0b:1a:1f:b8:63: + 44:ae:c1:d7:44:87:c4:91:59:9c:00:43:6d:c6:df: + 0a:b0:b1:04:cd:fe:be:30:5e:3a:25:72:dd:a2:3e: + ed:46:3a:c7:a4:5c:5c:e4:25:f2:13:07:e8:ae:da: + 9b:19:9b:a2:d9:60:9d:ce:90:47:6a:61:7b:40:e8: + 14:c2:fe:2f:84:5a:66:17:c0:97:d3:49:38:de:63: + 02:9f + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Basic Constraints: critical + CA:TRUE + Authority Information Access: + OCSP - URI:http://ocsp.entrust.net + + X509v3 CRL Distribution Points: + URI:http://crl.entrust.net/2048ca.crl + + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: http://www.entrust.net/CPS + + X509v3 Subject Key Identifier: + F5:F2:96:88:7D:0D:F3:2A:F9:4E:E7:34:A0:BD:46:7E:13:D6:16:C8 + X509v3 Authority Key Identifier: + keyid:55:E4:81:D1:11:80:BE:D8:89:B9:08:A3:31:F9:A1:24:09:16:B9:70 + + 1.2.840.113533.7.65.0: + 0 +..V7.1.... + Signature Algorithm: sha1WithRSAEncryption + 0b:25:3c:58:fa:8e:dc:a2:42:3b:76:71:6e:6c:d4:4f:2b:b9: + 53:5c:b2:58:b9:b1:dc:6f:1a:e4:e3:c4:50:f2:41:82:ba:f4: + 7d:c7:c1:f9:fa:8c:53:bf:b9:62:b7:49:e3:1d:0a:fc:1f:d6: + c4:76:6a:93:cb:77:1e:2c:7f:d0:3f:16:63:4c:72:4c:67:60: + 0f:f8:80:d6:a7:9a:ca:a2:33:91:0f:44:b2:66:3d:8e:68:0c: + 40:85:12:37:91:b9:82:77:34:59:2d:5c:df:82:6e:2c:b6:7a: + d2:04:90:67:68:4b:70:fc:2d:b8:ff:90:64:6f:7e:91:f7:d1: + 47:33:f3:5b:b8:58:2e:21:d8:75:60:1b:13:cc:f8:b2:a8:fa: + 6a:a9:2a:5a:4f:45:85:40:b4:dd:34:05:b7:70:ca:01:ef:e1: + 81:e7:11:50:db:3e:e2:d7:10:2e:6a:15:7f:b7:d4:a3:62:b2: + 89:69:61:57:c6:7f:8e:9e:d4:24:7a:f3:a1:43:5f:a0:7a:89: + dc:59:cd:7d:d7:75:a7:bc:53:d5:47:35:c6:31:30:20:9f:9b: + ba:b5:83:e6:89:55:01:4d:91:3b:d6:89:35:87:3c:83:6b:7a: + 29:82:d4:4b:d4:e6:16:74:b0:01:10:ab:69:06:14:37:7b:f7: + 66:30:3a:c5 +-----BEGIN CERTIFICATE----- +MIIFkTCCBHmgAwIBAgIEOGPFrjANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChML +RW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBp +bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5 +IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw0wODA4MjUxODE0MjZaFw0xODA4 +MjUxODQ0MjZaMIIBNDELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVudHJ1c3QsIElu +Yy4xODA2BgNVBAsTL0FORCBBRERJVElPTkFMIFRFUk1TIEdPVkVSTklORyBVU0Ug +QU5EIFJFTElBTkNFMUcwRQYDVQQLEz5DUFMgQ09OVEFJTlMgSU1QT1JUQU5UIExJ +TUlUQVRJT05TIE9GIFdBUlJBTlRJRVMgQU5EIExJQUJJTElUWTE5MDcGA1UECxMw +d3d3LmVudHJ1c3QubmV0L0NQUyBpcyBpbmNvcnBvcmF0ZWQgYnkgcmVmZXJlbmNl +MR8wHQYDVQQLExYoYykgMjAwOCBFbnRydXN0LCBJbmMuMS4wLAYDVQQDEyVFbnRy +dXN0IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gTDFCMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEA3CH1aPl6zofyeN/YO00GfcYk5KnNnQFW5PZxF6p/ +dSIY5HRtGz5W1bGmHt1ZJlPKBua6C283u6jGnBU7BhuHDMIaTdOBrttQZaU6ZE8w +NJorqR/9K9E4cRlo8o7re8lAPEjEGbG3ECXvRKfmd5t9Ipre2F7Zw87JcSK7ru8F +1vIX51Z44VMFSiZzuMdJZ5MjD1ayj93JWQXlYxW0h35ARum1AHsDtA3klmcs3htZ +CxofuGNErsHXRIfEkVmcAENtxt8KsLEEzf6+MF46JXLdoj7tRjrHpFxc5CXyEwfo +rtqbGZui2WCdzpBHamF7QOgUwv4vhFpmF8CX00k43mMCnwIDAQABo4IBJjCCASIw +DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wMwYIKwYBBQUHAQEEJzAl +MCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5lbnRydXN0Lm5ldDAyBgNVHR8EKzAp +MCegJaAjhiFodHRwOi8vY3JsLmVudHJ1c3QubmV0LzIwNDhjYS5jcmwwOwYDVR0g +BDQwMjAwBgRVHSAAMCgwJgYIKwYBBQUHAgEWGmh0dHA6Ly93d3cuZW50cnVzdC5u +ZXQvQ1BTMB0GA1UdDgQWBBT18paIfQ3zKvlO5zSgvUZ+E9YWyDAfBgNVHSMEGDAW +gBRV5IHREYC+2Im5CKMx+aEkCRa5cDAZBgkqhkiG9n0HQQAEDDAKGwRWNy4xAwIA +gTANBgkqhkiG9w0BAQUFAAOCAQEACyU8WPqO3KJCO3ZxbmzUTyu5U1yyWLmx3G8a +5OPEUPJBgrr0fcfB+fqMU7+5YrdJ4x0K/B/WxHZqk8t3Hix/0D8WY0xyTGdgD/iA +1qeayqIzkQ9EsmY9jmgMQIUSN5G5gnc0WS1c34JuLLZ60gSQZ2hLcPwtuP+QZG9+ +kffRRzPzW7hYLiHYdWAbE8z4sqj6aqkqWk9FhUC03TQFt3DKAe/hgecRUNs+4tcQ +LmoVf7fUo2KyiWlhV8Z/jp7UJHrzoUNfoHqJ3FnNfdd1p7xT1Uc1xjEwIJ+burWD +5olVAU2RO9aJNYc8g2t6KYLUS9TmFnSwARCraQYUN3v3ZjA6xQ== +-----END CERTIFICATE----- diff --git a/libcore/security/src/main/files/cacerts/b0f3e76e.0 b/libcore/security/src/main/files/cacerts/b0f3e76e.0 index 05ce1ec27..386b70a6e 100644 --- a/libcore/security/src/main/files/cacerts/b0f3e76e.0 +++ b/libcore/security/src/main/files/cacerts/b0f3e76e.0 @@ -2,12 +2,12 @@ Certificate: Data: Version: 3 (0x2) Serial Number: - 02:00:00:00:00:00:d6:78:b7:94:05 - Signature Algorithm: md5WithRSAEncryption + 04:00:00:00:00:01:15:4b:5a:c3:94 + Signature Algorithm: sha1WithRSAEncryption Issuer: C=BE, O=GlobalSign nv-sa, OU=Root CA, CN=GlobalSign Root CA Validity Not Before: Sep 1 12:00:00 1998 GMT - Not After : Jan 28 12:00:00 2014 GMT + Not After : Jan 28 12:00:00 2028 GMT Subject: C=BE, O=GlobalSign nv-sa, OU=Root CA, CN=GlobalSign Root CA Subject Public Key Info: Public Key Algorithm: rsaEncryption @@ -35,32 +35,31 @@ Certificate: X509v3 extensions: X509v3 Key Usage: critical Certificate Sign, CRL Sign - X509v3 Subject Key Identifier: - 60:7B:66:1A:45:0D:97:CA:89:50:2F:7D:04:CD:34:A8:FF:FC:FD:4B X509v3 Basic Constraints: critical CA:TRUE - Signature Algorithm: md5WithRSAEncryption - ae:aa:9f:fc:b7:d2:cb:1f:5f:39:29:28:18:9e:34:c9:6c:4f: - 6f:1a:f0:64:a2:70:4a:4f:13:86:9b:60:28:9e:e8:81:49:98: - 7d:0a:bb:e5:b0:9d:3d:36:db:8f:05:51:ff:09:31:2a:1f:dd: - 89:77:9e:0f:2e:6c:95:04:ed:86:cb:b4:00:3f:84:02:4d:80: - 6a:2a:2d:78:0b:ae:6f:2b:a2:83:44:83:1f:cd:50:82:4c:24: - af:bd:f7:a5:b4:c8:5a:0f:f4:e7:47:5e:49:8e:37:96:fe:9a: - 88:05:3a:d9:c0:db:29:87:e6:19:96:47:a7:3a:a6:8c:8b:3c: - 77:fe:46:63:a7:53:da:21:d1:ac:7e:49:a2:4b:e6:c3:67:59: - 2f:b3:8a:0e:bb:2c:bd:a9:aa:42:7c:35:c1:d8:7f:d5:a7:31: - 3a:4e:63:43:39:af:08:b0:61:34:8c:d3:98:a9:43:34:f6:0f: - 87:29:3b:9d:c2:56:58:98:77:c3:f7:1b:ac:f6:9d:f8:3e:aa: - a7:54:45:f0:f5:f9:d5:31:65:fe:6b:58:9c:71:b3:1e:d7:52: - ea:32:17:fc:40:60:1d:c9:79:24:b2:f6:6c:fd:a8:66:0e:82: - dd:98:cb:da:c2:44:4f:2e:a0:7b:f2:f7:6b:2c:76:11:84:46: - 8a:78:a3:e3 -SHA1 Fingerprint=2F:17:3F:7D:E9:96:67:AF:A5:7A:F8:0A:A2:D1:B1:2F:AC:83:03:38 + X509v3 Subject Key Identifier: + 60:7B:66:1A:45:0D:97:CA:89:50:2F:7D:04:CD:34:A8:FF:FC:FD:4B + Signature Algorithm: sha1WithRSAEncryption + d6:73:e7:7c:4f:76:d0:8d:bf:ec:ba:a2:be:34:c5:28:32:b5: + 7c:fc:6c:9c:2c:2b:bd:09:9e:53:bf:6b:5e:aa:11:48:b6:e5: + 08:a3:b3:ca:3d:61:4d:d3:46:09:b3:3e:c3:a0:e3:63:55:1b: + f2:ba:ef:ad:39:e1:43:b9:38:a3:e6:2f:8a:26:3b:ef:a0:50: + 56:f9:c6:0a:fd:38:cd:c4:0b:70:51:94:97:98:04:df:c3:5f: + 94:d5:15:c9:14:41:9c:c4:5d:75:64:15:0d:ff:55:30:ec:86: + 8f:ff:0d:ef:2c:b9:63:46:f6:aa:fc:df:bc:69:fd:2e:12:48: + 64:9a:e0:95:f0:a6:ef:29:8f:01:b1:15:b5:0c:1d:a5:fe:69: + 2c:69:24:78:1e:b3:a7:1c:71:62:ee:ca:c8:97:ac:17:5d:8a: + c2:f8:47:86:6e:2a:c4:56:31:95:d0:67:89:85:2b:f9:6c:a6: + 5d:46:9d:0c:aa:82:e4:99:51:dd:70:b7:db:56:3d:61:e4:6a: + e1:5c:d6:f6:fe:3d:de:41:cc:07:ae:63:52:bf:53:53:f4:2b: + e9:c7:fd:b6:f7:82:5f:85:d2:41:18:db:81:b3:04:1c:c5:1f: + a4:80:6f:15:20:c9:de:0c:88:0a:1d:d6:66:55:e2:fc:48:c9: + 29:26:69:e0 -----BEGIN CERTIFICATE----- -MIIDdTCCAl2gAwIBAgILAgAAAAAA1ni3lAUwDQYJKoZIhvcNAQEEBQAwVzELMAkG +MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw -MDBaFw0xNDAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i +MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp @@ -68,12 +67,12 @@ xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp 1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8 -9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIABjAdBgNVHQ4EFgQU -YHtmGkUNl8qJUC99BM00qP/8/UswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0B -AQQFAAOCAQEArqqf/LfSyx9fOSkoGJ40yWxPbxrwZKJwSk8ThptgKJ7ogUmYfQq7 -5bCdPTbbjwVR/wkxKh/diXeeDy5slQTthsu0AD+EAk2AaioteAuubyuig0SDH81Q -gkwkr733pbTIWg/050deSY43lv6aiAU62cDbKYfmGZZHpzqmjIs8d/5GY6dT2iHR -rH5Jokvmw2dZL7OKDrssvamqQnw1wdh/1acxOk5jQzmvCLBhNIzTmKlDNPYPhyk7 -ncJWWJh3w/cbrPad+D6qp1RF8PX51TFl/mtYnHGzHtdS6jIX/EBgHcl5JLL2bP2o -Zg6C3ZjL2sJETy6ge/L3ayx2EYRGinij4w== +9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E +BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B +AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz +yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE +38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP +AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad +DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME +HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== -----END CERTIFICATE----- diff --git a/libcore/security/src/main/files/cacerts/bf64f35b.0 b/libcore/security/src/main/files/cacerts/bf64f35b.0 new file mode 100644 index 000000000..389623cc3 --- /dev/null +++ b/libcore/security/src/main/files/cacerts/bf64f35b.0 @@ -0,0 +1,92 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 1116155212 (0x42872d4c) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=US, O=Entrust.net, OU=www.entrust.net/CPS incorp. by ref. (limits liab.), OU=(c) 1999 Entrust.net Limited, CN=Entrust.net Secure Server Certification Authority + Validity + Not Before: Jan 5 19:20:39 2007 GMT + Not After : Jan 5 19:50:39 2017 GMT + Subject: C=US, O=Entrust, Inc., OU=www.entrust.net/CPS is incorporated by reference, OU=(c) 2006 Entrust, Inc., CN=Entrust Root Certification Authority + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public Key: (2048 bit) + Modulus (2048 bit): + 00:b6:95:b6:43:42:fa:c6:6d:2a:6f:48:df:94:4c: + 39:57:05:ee:c3:79:11:41:68:36:ed:ec:fe:9a:01: + 8f:a1:38:28:fc:f7:10:46:66:2e:4d:1e:1a:b1:1a: + 4e:c6:d1:c0:95:88:b0:c9:ff:31:8b:33:03:db:b7: + 83:7b:3e:20:84:5e:ed:b2:56:28:a7:f8:e0:b9:40: + 71:37:c5:cb:47:0e:97:2a:68:c0:22:95:62:15:db: + 47:d9:f5:d0:2b:ff:82:4b:c9:ad:3e:de:4c:db:90: + 80:50:3f:09:8a:84:00:ec:30:0a:3d:18:cd:fb:fd: + 2a:59:9a:23:95:17:2c:45:9e:1f:6e:43:79:6d:0c: + 5c:98:fe:48:a7:c5:23:47:5c:5e:fd:6e:e7:1e:b4: + f6:68:45:d1:86:83:5b:a2:8a:8d:b1:e3:29:80:fe: + 25:71:88:ad:be:bc:8f:ac:52:96:4b:aa:51:8d:e4: + 13:31:19:e8:4e:4d:9f:db:ac:b3:6a:d5:bc:39:54: + 71:ca:7a:7a:7f:90:dd:7d:1d:80:d9:81:bb:59:26: + c2:11:fe:e6:93:e2:f7:80:e4:65:fb:34:37:0e:29: + 80:70:4d:af:38:86:2e:9e:7f:57:af:9e:17:ae:eb: + 1c:cb:28:21:5f:b6:1c:d8:e7:a2:04:22:f9:d3:da: + d8:cb + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Key Usage: critical + Certificate Sign, CRL Sign + X509v3 Basic Constraints: critical + CA:TRUE + Authority Information Access: + OCSP - URI:http://ocsp.entrust.net + + X509v3 CRL Distribution Points: + URI:http://crl.entrust.net/server1.crl + + X509v3 Certificate Policies: + Policy: X509v3 Any Policy + CPS: http://www.entrust.net/CPS + + X509v3 Subject Key Identifier: + 68:90:E4:67:A4:A6:53:80:C7:86:66:A4:F1:F7:4B:43:FB:84:BD:6D + X509v3 Authority Key Identifier: + keyid:F0:17:62:13:55:3D:B3:FF:0A:00:6B:FB:50:84:97:F3:ED:62:D0:1A + + 1.2.840.113533.7.65.0: + 0 +..V7.1.... + Signature Algorithm: sha1WithRSAEncryption + 0c:b0:84:7c:2d:13:fe:9a:3d:bf:18:05:95:3d:20:48:a3:16: + 81:87:15:50:15:a4:88:8d:9f:60:d4:3a:6f:eb:2d:6e:3a:86: + a4:a9:d2:c1:9d:89:7a:08:1c:a4:2d:b3:47:8e:0f:64:4a:6f: + 66:03:83:3f:4f:34:94:36:aa:29:6d:8b:8d:02:22:2b:8c:cd: + 77:a5:70:95:86:91:d1:b6:bf:52:be:33:6a:6b:99:f9:6f:e1: + 12:be:04:cb:33:bf:f5:12:1a:4e:44:ba:5b:16:4d:30:b9:f3: + b4:74:ce:6e:f2:68:56:58:dd:d8:a1:fd:54:05:f4:23:91:85: + c9:f9 +-----BEGIN CERTIFICATE----- +MIIEmzCCBASgAwIBAgIEQoctTDANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMC +VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5u +ZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc +KGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVzdC5u +ZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNzAx +MDUxOTIwMzlaFw0xNzAxMDUxOTUwMzlaMIGwMQswCQYDVQQGEwJVUzEWMBQGA1UE +ChMNRW50cnVzdCwgSW5jLjE5MDcGA1UECxMwd3d3LmVudHJ1c3QubmV0L0NQUyBp +cyBpbmNvcnBvcmF0ZWQgYnkgcmVmZXJlbmNlMR8wHQYDVQQLExYoYykgMjAwNiBF +bnRydXN0LCBJbmMuMS0wKwYDVQQDEyRFbnRydXN0IFJvb3QgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC2lbZD +QvrGbSpvSN+UTDlXBe7DeRFBaDbt7P6aAY+hOCj89xBGZi5NHhqxGk7G0cCViLDJ +/zGLMwPbt4N7PiCEXu2yViin+OC5QHE3xctHDpcqaMAilWIV20fZ9dAr/4JLya0+ +3kzbkIBQPwmKhADsMAo9GM37/SpZmiOVFyxFnh9uQ3ltDFyY/kinxSNHXF79buce +tPZoRdGGg1uiio2x4ymA/iVxiK2+vI+sUpZLqlGN5BMxGehOTZ/brLNq1bw5VHHK +enp/kN19HYDZgbtZJsIR/uaT4veA5GX7NDcOKYBwTa84hi6ef1evnheu6xzLKCFf +thzY56IEIvnT2tjLAgMBAAGjggEnMIIBIzAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T +AQH/BAUwAwEB/zAzBggrBgEFBQcBAQQnMCUwIwYIKwYBBQUHMAGGF2h0dHA6Ly9v +Y3NwLmVudHJ1c3QubmV0MDMGA1UdHwQsMCowKKAmoCSGImh0dHA6Ly9jcmwuZW50 +cnVzdC5uZXQvc2VydmVyMS5jcmwwOwYDVR0gBDQwMjAwBgRVHSAAMCgwJgYIKwYB +BQUHAgEWGmh0dHA6Ly93d3cuZW50cnVzdC5uZXQvQ1BTMB0GA1UdDgQWBBRokORn +pKZTgMeGZqTx90tD+4S9bTAfBgNVHSMEGDAWgBTwF2ITVT2z/woAa/tQhJfz7WLQ +GjAZBgkqhkiG9n0HQQAEDDAKGwRWNy4xAwIAgTANBgkqhkiG9w0BAQUFAAOBgQAM +sIR8LRP+mj2/GAWVPSBIoxaBhxVQFaSIjZ9g1Dpv6y1uOoakqdLBnYl6CBykLbNH +jg9kSm9mA4M/TzSUNqopbYuNAiIrjM13pXCVhpHRtr9SvjNqa5n5b+ESvgTLM7/1 +EhpORLpbFk0wufO0dM5u8mhWWN3Yof1UBfQjkYXJ+Q== +-----END CERTIFICATE----- diff --git a/libcore/security/src/main/files/certimport.sh b/libcore/security/src/main/files/certimport.sh index 7c0fab068..c021a1083 100755 --- a/libcore/security/src/main/files/certimport.sh +++ b/libcore/security/src/main/files/certimport.sh @@ -1,4 +1,5 @@ #!/bin/bash +# java version >= 1.6 is required for this script. # This script was tested to work with bouncycastle 1.32. set -x @@ -6,6 +7,14 @@ set -e CERTSTORE=cacerts.bks +# Check java version. +JAVA_VERSION=`java -version 2>&1 | head -1` +JAVA_VERSION_MINOR=`expr match "$JAVA_VERSION" "java version \"[1-9]\.\([0-9]\).*\""` +if [ $JAVA_VERSION_MINOR -lt 6 ]; then + echo "java version 1.6 or greater required for keytool usage" + exit 255 +fi + if [ -a $CERTSTORE ]; then rm $CERTSTORE || exit 1 fi diff --git a/libcore/security/src/main/java/org/apache/harmony/security/x501/AttributeTypeAndValue.java b/libcore/security/src/main/java/org/apache/harmony/security/x501/AttributeTypeAndValue.java index c8f949472..34337ebe6 100644 --- a/libcore/security/src/main/java/org/apache/harmony/security/x501/AttributeTypeAndValue.java +++ b/libcore/security/src/main/java/org/apache/harmony/security/x501/AttributeTypeAndValue.java @@ -327,11 +327,15 @@ public class AttributeTypeAndValue { if (attrFormat == X500Principal.CANONICAL) { // only PrintableString and UTF8String in string format // all others are output in hex format + // BEGIN android-changed + // no hex for teletex; see bug 2102191 int tag = value.getTag(); if (!ASN1StringType.UTF8STRING.checkTag(tag) - && !ASN1StringType.PRINTABLESTRING.checkTag(tag)) { + && !ASN1StringType.PRINTABLESTRING.checkTag(tag) + && !ASN1StringType.TELETEXSTRING.checkTag(tag)) { hexFormat = true; } + // END android-changed } } else { diff --git a/libcore/security/src/main/java/org/bouncycastle/jce/provider/IndexedPKIXParameters.java b/libcore/security/src/main/java/org/bouncycastle/jce/provider/IndexedPKIXParameters.java index e8e834afc..03679e20d 100644 --- a/libcore/security/src/main/java/org/bouncycastle/jce/provider/IndexedPKIXParameters.java +++ b/libcore/security/src/main/java/org/bouncycastle/jce/provider/IndexedPKIXParameters.java @@ -109,7 +109,7 @@ public class IndexedPKIXParameters extends PKIXParameters { if (anchor != null) { return anchor; } - } catch (Exception e) { + } catch (Exception e) { Logger.getLogger(IndexedPKIXParameters.class.getName()).log( Level.WARNING, "Error encoding cert.", e); } @@ -124,6 +124,21 @@ public class IndexedPKIXParameters extends PKIXParameters { return null; } + /** + * Returns true if the given certificate is found in the trusted key + * store. + */ + public boolean isDirectlyTrusted(X509Certificate cert) { + try { + Bytes encoded = new Bytes(cert.getEncoded()); + return encodings.containsKey(encoded); + } catch (Exception e) { + Logger.getLogger(IndexedPKIXParameters.class.getName()).log( + Level.WARNING, "Error encoding cert.", e); + return false; + } + } + /** * Wraps a byte[] and adds equals() and hashCode() support. */ diff --git a/libcore/security/src/test/java/org/bouncycastle/jce/provider/AllTests.java b/libcore/security/src/test/java/org/bouncycastle/jce/provider/AllTests.java new file mode 100644 index 000000000..e9fbdb0e3 --- /dev/null +++ b/libcore/security/src/test/java/org/bouncycastle/jce/provider/AllTests.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.bouncycastle.jce.provider; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class AllTests { + + public static void main(String[] args) { + junit.textui.TestRunner.run(AllTests.suite()); + } + + public static Test suite() { + TestSuite suite = tests.TestSuiteFactory.createTestSuite( + "All tests for " + AllTests.class.getPackage()); + suite.addTestSuite(PKIXCertPathValidatorSpiTest.class); + return suite; + } +} diff --git a/libcore/security/src/test/java/org/bouncycastle/jce/provider/PKIXCertPathValidatorSpiTest.java b/libcore/security/src/test/java/org/bouncycastle/jce/provider/PKIXCertPathValidatorSpiTest.java new file mode 100644 index 000000000..734e11d8f --- /dev/null +++ b/libcore/security/src/test/java/org/bouncycastle/jce/provider/PKIXCertPathValidatorSpiTest.java @@ -0,0 +1,65 @@ +// Copyright 2009 Google Inc. All Rights Reserved. + +package org.bouncycastle.jce.provider; + +import junit.framework.TestCase; +import org.apache.harmony.security.provider.cert.X509CertImpl; +import org.apache.harmony.security.provider.cert.X509CertPathImpl; +import org.bouncycastle.asn1.x509.X509CertificateStructure; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.ASN1InputStream; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Set; +import java.util.HashSet; +import java.security.cert.CertificateException; +import java.security.cert.TrustAnchor; +import java.security.cert.CertPathValidatorException; +import java.security.KeyStoreException; +import java.security.InvalidAlgorithmParameterException; + +/** + * Verify the behavior of PKIXCertPathValidatorSpi. + */ +public class PKIXCertPathValidatorSpiTest extends TestCase { + + /** + * A chain of 3 ASN1-encoded certificates for https://service.sprint.com. + * The certificate subjects are "service.sprint.com", "Entrust Certification + * Authority - L1B", and "Entrust.net Certification Authority (2048)". The + * last certificate uses UTF8 encoding for its X509 name. + */ + private final byte[][] serviceSprintComCertChain = new byte[][] { + new byte[] { 48, -126, 6, 89, 48, -126, 5, 65, -96, 3, 2, 1, 2, 2, 4, 72, 13, 115, -81, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, 5, 5, 0, 48, -126, 1, 52, 49, 11, 48, 9, 6, 3, 85, 4, 6, 19, 2, 85, 83, 49, 22, 48, 20, 6, 3, 85, 4, 10, 19, 13, 69, 110, 116, 114, 117, 115, 116, 44, 32, 73, 110, 99, 46, 49, 56, 48, 54, 6, 3, 85, 4, 11, 19, 47, 65, 78, 68, 32, 65, 68, 68, 73, 84, 73, 79, 78, 65, 76, 32, 84, 69, 82, 77, 83, 32, 71, 79, 86, 69, 82, 78, 73, 78, 71, 32, 85, 83, 69, 32, 65, 78, 68, 32, 82, 69, 76, 73, 65, 78, 67, 69, 49, 71, 48, 69, 6, 3, 85, 4, 11, 19, 62, 67, 80, 83, 32, 67, 79, 78, 84, 65, 73, 78, 83, 32, 73, 77, 80, 79, 82, 84, 65, 78, 84, 32, 76, 73, 77, 73, 84, 65, 84, 73, 79, 78, 83, 32, 79, 70, 32, 87, 65, 82, 82, 65, 78, 84, 73, 69, 83, 32, 65, 78, 68, 32, 76, 73, 65, 66, 73, 76, 73, 84, 89, 49, 57, 48, 55, 6, 3, 85, 4, 11, 19, 48, 119, 119, 119, 46, 101, 110, 116, 114, 117, 115, 116, 46, 110, 101, 116, 47, 67, 80, 83, 32, 105, 115, 32, 105, 110, 99, 111, 114, 112, 111, 114, 97, 116, 101, 100, 32, 98, 121, 32, 114, 101, 102, 101, 114, 101, 110, 99, 101, 49, 31, 48, 29, 6, 3, 85, 4, 11, 19, 22, 40, 99, 41, 32, 50, 48, 48, 56, 32, 69, 110, 116, 114, 117, 115, 116, 44, 32, 73, 110, 99, 46, 49, 46, 48, 44, 6, 3, 85, 4, 3, 19, 37, 69, 110, 116, 114, 117, 115, 116, 32, 67, 101, 114, 116, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 65, 117, 116, 104, 111, 114, 105, 116, 121, 32, 45, 32, 76, 49, 66, 48, 30, 23, 13, 48, 57, 48, 52, 50, 57, 49, 53, 50, 54, 53, 57, 90, 23, 13, 49, 49, 48, 53, 48, 53, 49, 53, 53, 54, 53, 55, 90, 48, 120, 49, 11, 48, 9, 6, 3, 85, 4, 6, 19, 2, 85, 83, 49, 15, 48, 13, 6, 3, 85, 4, 8, 19, 6, 75, 65, 78, 83, 65, 83, 49, 22, 48, 20, 6, 3, 85, 4, 7, 19, 13, 79, 118, 101, 114, 108, 97, 110, 100, 32, 80, 97, 114, 107, 49, 15, 48, 13, 6, 3, 85, 4, 10, 19, 6, 83, 112, 114, 105, 110, 116, 49, 18, 48, 16, 6, 3, 85, 4, 11, 19, 9, 100, 97, 115, 110, 109, 112, 48, 52, 98, 49, 27, 48, 25, 6, 3, 85, 4, 3, 19, 18, 115, 101, 114, 118, 105, 99, 101, 46, 115, 112, 114, 105, 110, 116, 46, 99, 111, 109, 48, -127, -97, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, 1, 5, 0, 3, -127, -115, 0, 48, -127, -119, 2, -127, -127, 0, -80, 99, 109, 108, 94, -41, -78, 88, 56, -97, 33, -23, 65, -74, -118, 0, 1, 119, 126, 122, -59, -83, -25, -16, -75, -87, 100, 46, 37, -98, 65, -104, 54, -87, 56, -81, 96, -38, -4, -78, 11, 101, -29, 70, -13, -110, -76, -125, -106, -35, 41, 83, 71, 56, 6, 67, -8, 82, -58, -81, -113, 90, 91, 79, 74, -38, 34, 28, 39, -37, -12, 54, 87, 61, 48, 33, -16, 10, 112, -40, -37, -15, 59, -72, 112, 96, 85, 109, 123, -122, 58, 18, 95, 56, -81, 49, 43, -39, 99, 69, -28, -81, -106, -64, 8, -62, 40, -92, 95, -109, -122, 94, 53, -13, -33, 88, -104, 3, -77, -30, -27, 23, 92, -69, 12, -23, -9, 125, 2, 3, 1, 0, 1, -93, -126, 2, -81, 48, -126, 2, -85, 48, 11, 6, 3, 85, 29, 15, 4, 4, 3, 2, 5, -96, 48, 43, 6, 3, 85, 29, 16, 4, 36, 48, 34, -128, 15, 50, 48, 48, 57, 48, 52, 50, 57, 49, 53, 50, 54, 53, 57, 90, -127, 15, 50, 48, 49, 49, 48, 53, 48, 53, 49, 53, 53, 54, 53, 55, 90, 48, 19, 6, 3, 85, 29, 37, 4, 12, 48, 10, 6, 8, 43, 6, 1, 5, 5, 7, 3, 1, 48, 51, 6, 3, 85, 29, 31, 4, 44, 48, 42, 48, 40, -96, 38, -96, 36, -122, 34, 104, 116, 116, 112, 58, 47, 47, 99, 114, 108, 46, 101, 110, 116, 114, 117, 115, 116, 46, 110, 101, 116, 47, 108, 101, 118, 101, 108, 49, 98, 46, 99, 114, 108, 48, 100, 6, 8, 43, 6, 1, 5, 5, 7, 1, 1, 4, 88, 48, 86, 48, 35, 6, 8, 43, 6, 1, 5, 5, 7, 48, 1, -122, 23, 104, 116, 116, 112, 58, 47, 47, 111, 99, 115, 112, 46, 101, 110, 116, 114, 117, 115, 116, 46, 110, 101, 116, 48, 47, 6, 8, 43, 6, 1, 5, 5, 7, 48, 2, -122, 35, 104, 116, 116, 112, 58, 47, 47, 97, 105, 97, 46, 101, 110, 116, 114, 117, 115, 116, 46, 110, 101, 116, 47, 50, 48, 52, 56, 45, 108, 49, 98, 46, 99, 101, 114, 48, -126, 1, 87, 6, 3, 85, 29, 32, 4, -126, 1, 78, 48, -126, 1, 74, 48, -126, 1, 70, 6, 9, 42, -122, 72, -122, -10, 125, 7, 75, 2, 48, -126, 1, 55, 48, 38, 6, 8, 43, 6, 1, 5, 5, 7, 2, 1, 22, 26, 104, 116, 116, 112, 58, 47, 47, 119, 119, 119, 46, 101, 110, 116, 114, 117, 115, 116, 46, 110, 101, 116, 47, 99, 112, 115, 48, -126, 1, 11, 6, 8, 43, 6, 1, 5, 5, 7, 2, 2, 48, -127, -2, 26, -127, -5, 84, 104, 101, 32, 69, 110, 116, 114, 117, 115, 116, 32, 67, 101, 114, 116, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 80, 114, 97, 99, 116, 105, 99, 101, 32, 83, 116, 97, 116, 101, 109, 101, 110, 116, 32, 40, 67, 80, 83, 41, 32, 97, 118, 97, 105, 108, 97, 98, 108, 101, 32, 97, 116, 32, 119, 119, 119, 46, 101, 110, 116, 114, 117, 115, 116, 46, 110, 101, 116, 47, 99, 112, 115, 32, 32, 105, 115, 32, 104, 101, 114, 101, 98, 121, 32, 105, 110, 99, 111, 114, 112, 111, 114, 97, 116, 101, 100, 32, 105, 110, 116, 111, 32, 121, 111, 117, 114, 32, 117, 115, 101, 32, 111, 114, 32, 114, 101, 108, 105, 97, 110, 99, 101, 32, 111, 110, 32, 116, 104, 105, 115, 32, 67, 101, 114, 116, 105, 102, 105, 99, 97, 116, 101, 46, 32, 32, 84, 104, 105, 115, 32, 67, 80, 83, 32, 99, 111, 110, 116, 97, 105, 110, 115, 32, 108, 105, 109, 105, 116, 97, 116, 105, 111, 110, 115, 32, 111, 110, 32, 119, 97, 114, 114, 97, 110, 116, 105, 101, 115, 32, 97, 110, 100, 32, 108, 105, 97, 98, 105, 108, 105, 116, 105, 101, 115, 46, 32, 67, 111, 112, 121, 114, 105, 103, 104, 116, 32, 40, 99, 41, 32, 50, 48, 48, 56, 32, 69, 110, 116, 114, 117, 115, 116, 32, 76, 105, 109, 105, 116, 101, 100, 48, 31, 6, 3, 85, 29, 35, 4, 24, 48, 22, -128, 20, -11, -14, -106, -120, 125, 13, -13, 42, -7, 78, -25, 52, -96, -67, 70, 126, 19, -42, 22, -56, 48, 29, 6, 3, 85, 29, 14, 4, 22, 4, 20, 68, 101, 26, -23, -69, -107, 32, 89, 18, 28, -123, 32, 74, -116, -33, -48, 70, -52, 68, 77, 48, 9, 6, 3, 85, 29, 19, 4, 2, 48, 0, 48, 25, 6, 9, 42, -122, 72, -122, -10, 125, 7, 65, 0, 4, 12, 48, 10, 27, 4, 86, 55, 46, 49, 3, 2, 3, 40, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, 5, 5, 0, 3, -126, 1, 1, 0, 18, 56, 92, -74, -100, -56, 95, 121, 27, -84, -88, -104, -27, -98, -12, 58, 48, -26, 40, -7, 25, -68, -124, -104, -54, -121, 84, 52, 3, 22, -106, 88, 44, -39, 126, 17, 96, 4, -41, -84, -101, 74, -92, -113, -12, -99, 77, 108, -30, 38, 19, 78, 48, 32, -126, 95, -10, -114, 58, 98, -49, -108, -109, -87, 5, -80, -43, 121, 21, -99, 43, -73, 26, 51, 31, 87, -38, -119, 78, -113, -59, -100, -118, -84, -46, -48, 93, 99, 2, 40, -39, 76, -48, -122, -60, -25, -73, 103, 126, 83, -86, -26, 66, 122, -65, -89, -102, 115, 105, -124, -85, -18, -66, 85, 30, -29, -96, 104, 65, -66, 40, 69, -91, 101, -19, 39, -86, -21, -18, 39, 51, -1, 36, -52, 53, -65, 53, 12, -62, -97, -45, -26, 113, -20, 102, 56, 102, 104, 37, 17, 57, -96, -83, -71, 106, 63, -64, -122, 61, 59, 8, -123, 108, 22, 62, -58, -105, 88, 38, 96, -6, -29, -114, 105, 110, -102, -72, 109, -33, 56, 61, 52, 70, -75, -92, 97, -9, -6, -64, 53, -76, 81, -100, 90, -50, 19, -87, 30, -24, -53, 109, -75, 45, -38, 14, 119, -31, 44, -30, -93, -76, 14, 97, -53, -107, 60, 30, -102, 68, 12, 26, 76, -114, 73, -13, -127, 21, 94, -42, 94, 30, -50, -3, 116, 41, -3, -89, 23, -27, -49, -3, -95, 119, -104, -45, 112, 35, 66, 59, 84, 116, 19, -102, -68, -104, 1 }, + new byte[] { 48, -126, 5, -111, 48, -126, 4, 121, -96, 3, 2, 1, 2, 2, 4, 56, 99, -59, -82, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, 5, 5, 0, 48, -127, -76, 49, 20, 48, 18, 6, 3, 85, 4, 10, 19, 11, 69, 110, 116, 114, 117, 115, 116, 46, 110, 101, 116, 49, 64, 48, 62, 6, 3, 85, 4, 11, 20, 55, 119, 119, 119, 46, 101, 110, 116, 114, 117, 115, 116, 46, 110, 101, 116, 47, 67, 80, 83, 95, 50, 48, 52, 56, 32, 105, 110, 99, 111, 114, 112, 46, 32, 98, 121, 32, 114, 101, 102, 46, 32, 40, 108, 105, 109, 105, 116, 115, 32, 108, 105, 97, 98, 46, 41, 49, 37, 48, 35, 6, 3, 85, 4, 11, 19, 28, 40, 99, 41, 32, 49, 57, 57, 57, 32, 69, 110, 116, 114, 117, 115, 116, 46, 110, 101, 116, 32, 76, 105, 109, 105, 116, 101, 100, 49, 51, 48, 49, 6, 3, 85, 4, 3, 19, 42, 69, 110, 116, 114, 117, 115, 116, 46, 110, 101, 116, 32, 67, 101, 114, 116, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 65, 117, 116, 104, 111, 114, 105, 116, 121, 32, 40, 50, 48, 52, 56, 41, 48, 30, 23, 13, 48, 56, 48, 56, 50, 53, 49, 56, 49, 52, 50, 54, 90, 23, 13, 49, 56, 48, 56, 50, 53, 49, 56, 52, 52, 50, 54, 90, 48, -126, 1, 52, 49, 11, 48, 9, 6, 3, 85, 4, 6, 19, 2, 85, 83, 49, 22, 48, 20, 6, 3, 85, 4, 10, 19, 13, 69, 110, 116, 114, 117, 115, 116, 44, 32, 73, 110, 99, 46, 49, 56, 48, 54, 6, 3, 85, 4, 11, 19, 47, 65, 78, 68, 32, 65, 68, 68, 73, 84, 73, 79, 78, 65, 76, 32, 84, 69, 82, 77, 83, 32, 71, 79, 86, 69, 82, 78, 73, 78, 71, 32, 85, 83, 69, 32, 65, 78, 68, 32, 82, 69, 76, 73, 65, 78, 67, 69, 49, 71, 48, 69, 6, 3, 85, 4, 11, 19, 62, 67, 80, 83, 32, 67, 79, 78, 84, 65, 73, 78, 83, 32, 73, 77, 80, 79, 82, 84, 65, 78, 84, 32, 76, 73, 77, 73, 84, 65, 84, 73, 79, 78, 83, 32, 79, 70, 32, 87, 65, 82, 82, 65, 78, 84, 73, 69, 83, 32, 65, 78, 68, 32, 76, 73, 65, 66, 73, 76, 73, 84, 89, 49, 57, 48, 55, 6, 3, 85, 4, 11, 19, 48, 119, 119, 119, 46, 101, 110, 116, 114, 117, 115, 116, 46, 110, 101, 116, 47, 67, 80, 83, 32, 105, 115, 32, 105, 110, 99, 111, 114, 112, 111, 114, 97, 116, 101, 100, 32, 98, 121, 32, 114, 101, 102, 101, 114, 101, 110, 99, 101, 49, 31, 48, 29, 6, 3, 85, 4, 11, 19, 22, 40, 99, 41, 32, 50, 48, 48, 56, 32, 69, 110, 116, 114, 117, 115, 116, 44, 32, 73, 110, 99, 46, 49, 46, 48, 44, 6, 3, 85, 4, 3, 19, 37, 69, 110, 116, 114, 117, 115, 116, 32, 67, 101, 114, 116, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 65, 117, 116, 104, 111, 114, 105, 116, 121, 32, 45, 32, 76, 49, 66, 48, -126, 1, 34, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, 1, 5, 0, 3, -126, 1, 15, 0, 48, -126, 1, 10, 2, -126, 1, 1, 0, -36, 33, -11, 104, -7, 122, -50, -121, -14, 120, -33, -40, 59, 77, 6, 125, -58, 36, -28, -87, -51, -99, 1, 86, -28, -10, 113, 23, -86, 127, 117, 34, 24, -28, 116, 109, 27, 62, 86, -43, -79, -90, 30, -35, 89, 38, 83, -54, 6, -26, -70, 11, 111, 55, -69, -88, -58, -100, 21, 59, 6, 27, -121, 12, -62, 26, 77, -45, -127, -82, -37, 80, 101, -91, 58, 100, 79, 48, 52, -102, 43, -87, 31, -3, 43, -47, 56, 113, 25, 104, -14, -114, -21, 123, -55, 64, 60, 72, -60, 25, -79, -73, 16, 37, -17, 68, -89, -26, 119, -101, 125, 34, -102, -34, -40, 94, -39, -61, -50, -55, 113, 34, -69, -82, -17, 5, -42, -14, 23, -25, 86, 120, -31, 83, 5, 74, 38, 115, -72, -57, 73, 103, -109, 35, 15, 86, -78, -113, -35, -55, 89, 5, -27, 99, 21, -76, -121, 126, 64, 70, -23, -75, 0, 123, 3, -76, 13, -28, -106, 103, 44, -34, 27, 89, 11, 26, 31, -72, 99, 68, -82, -63, -41, 68, -121, -60, -111, 89, -100, 0, 67, 109, -58, -33, 10, -80, -79, 4, -51, -2, -66, 48, 94, 58, 37, 114, -35, -94, 62, -19, 70, 58, -57, -92, 92, 92, -28, 37, -14, 19, 7, -24, -82, -38, -101, 25, -101, -94, -39, 96, -99, -50, -112, 71, 106, 97, 123, 64, -24, 20, -62, -2, 47, -124, 90, 102, 23, -64, -105, -45, 73, 56, -34, 99, 2, -97, 2, 3, 1, 0, 1, -93, -126, 1, 38, 48, -126, 1, 34, 48, 14, 6, 3, 85, 29, 15, 1, 1, -1, 4, 4, 3, 2, 1, 6, 48, 15, 6, 3, 85, 29, 19, 1, 1, -1, 4, 5, 48, 3, 1, 1, -1, 48, 51, 6, 8, 43, 6, 1, 5, 5, 7, 1, 1, 4, 39, 48, 37, 48, 35, 6, 8, 43, 6, 1, 5, 5, 7, 48, 1, -122, 23, 104, 116, 116, 112, 58, 47, 47, 111, 99, 115, 112, 46, 101, 110, 116, 114, 117, 115, 116, 46, 110, 101, 116, 48, 50, 6, 3, 85, 29, 31, 4, 43, 48, 41, 48, 39, -96, 37, -96, 35, -122, 33, 104, 116, 116, 112, 58, 47, 47, 99, 114, 108, 46, 101, 110, 116, 114, 117, 115, 116, 46, 110, 101, 116, 47, 50, 48, 52, 56, 99, 97, 46, 99, 114, 108, 48, 59, 6, 3, 85, 29, 32, 4, 52, 48, 50, 48, 48, 6, 4, 85, 29, 32, 0, 48, 40, 48, 38, 6, 8, 43, 6, 1, 5, 5, 7, 2, 1, 22, 26, 104, 116, 116, 112, 58, 47, 47, 119, 119, 119, 46, 101, 110, 116, 114, 117, 115, 116, 46, 110, 101, 116, 47, 67, 80, 83, 48, 29, 6, 3, 85, 29, 14, 4, 22, 4, 20, -11, -14, -106, -120, 125, 13, -13, 42, -7, 78, -25, 52, -96, -67, 70, 126, 19, -42, 22, -56, 48, 31, 6, 3, 85, 29, 35, 4, 24, 48, 22, -128, 20, 85, -28, -127, -47, 17, -128, -66, -40, -119, -71, 8, -93, 49, -7, -95, 36, 9, 22, -71, 112, 48, 25, 6, 9, 42, -122, 72, -122, -10, 125, 7, 65, 0, 4, 12, 48, 10, 27, 4, 86, 55, 46, 49, 3, 2, 0, -127, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, 5, 5, 0, 3, -126, 1, 1, 0, 11, 37, 60, 88, -6, -114, -36, -94, 66, 59, 118, 113, 110, 108, -44, 79, 43, -71, 83, 92, -78, 88, -71, -79, -36, 111, 26, -28, -29, -60, 80, -14, 65, -126, -70, -12, 125, -57, -63, -7, -6, -116, 83, -65, -71, 98, -73, 73, -29, 29, 10, -4, 31, -42, -60, 118, 106, -109, -53, 119, 30, 44, 127, -48, 63, 22, 99, 76, 114, 76, 103, 96, 15, -8, -128, -42, -89, -102, -54, -94, 51, -111, 15, 68, -78, 102, 61, -114, 104, 12, 64, -123, 18, 55, -111, -71, -126, 119, 52, 89, 45, 92, -33, -126, 110, 44, -74, 122, -46, 4, -112, 103, 104, 75, 112, -4, 45, -72, -1, -112, 100, 111, 126, -111, -9, -47, 71, 51, -13, 91, -72, 88, 46, 33, -40, 117, 96, 27, 19, -52, -8, -78, -88, -6, 106, -87, 42, 90, 79, 69, -123, 64, -76, -35, 52, 5, -73, 112, -54, 1, -17, -31, -127, -25, 17, 80, -37, 62, -30, -41, 16, 46, 106, 21, 127, -73, -44, -93, 98, -78, -119, 105, 97, 87, -58, 127, -114, -98, -44, 36, 122, -13, -95, 67, 95, -96, 122, -119, -36, 89, -51, 125, -41, 117, -89, -68, 83, -43, 71, 53, -58, 49, 48, 32, -97, -101, -70, -75, -125, -26, -119, 85, 1, 77, -111, 59, -42, -119, 53, -121, 60, -125, 107, 122, 41, -126, -44, 75, -44, -26, 22, 116, -80, 1, 16, -85, 105, 6, 20, 55, 123, -9, 102, 48, 58, -59 }, + new byte[] { 48, -126, 4, 92, 48, -126, 3, 68, -96, 3, 2, 1, 2, 2, 4, 56, 99, -71, 102, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, 5, 5, 0, 48, -127, -76, 49, 20, 48, 18, 6, 3, 85, 4, 10, 19, 11, 69, 110, 116, 114, 117, 115, 116, 46, 110, 101, 116, 49, 64, 48, 62, 6, 3, 85, 4, 11, 20, 55, 119, 119, 119, 46, 101, 110, 116, 114, 117, 115, 116, 46, 110, 101, 116, 47, 67, 80, 83, 95, 50, 48, 52, 56, 32, 105, 110, 99, 111, 114, 112, 46, 32, 98, 121, 32, 114, 101, 102, 46, 32, 40, 108, 105, 109, 105, 116, 115, 32, 108, 105, 97, 98, 46, 41, 49, 37, 48, 35, 6, 3, 85, 4, 11, 19, 28, 40, 99, 41, 32, 49, 57, 57, 57, 32, 69, 110, 116, 114, 117, 115, 116, 46, 110, 101, 116, 32, 76, 105, 109, 105, 116, 101, 100, 49, 51, 48, 49, 6, 3, 85, 4, 3, 19, 42, 69, 110, 116, 114, 117, 115, 116, 46, 110, 101, 116, 32, 67, 101, 114, 116, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 65, 117, 116, 104, 111, 114, 105, 116, 121, 32, 40, 50, 48, 52, 56, 41, 48, 30, 23, 13, 57, 57, 49, 50, 50, 52, 49, 55, 53, 48, 53, 49, 90, 23, 13, 49, 57, 49, 50, 50, 52, 49, 56, 50, 48, 53, 49, 90, 48, -127, -76, 49, 20, 48, 18, 6, 3, 85, 4, 10, 19, 11, 69, 110, 116, 114, 117, 115, 116, 46, 110, 101, 116, 49, 64, 48, 62, 6, 3, 85, 4, 11, 20, 55, 119, 119, 119, 46, 101, 110, 116, 114, 117, 115, 116, 46, 110, 101, 116, 47, 67, 80, 83, 95, 50, 48, 52, 56, 32, 105, 110, 99, 111, 114, 112, 46, 32, 98, 121, 32, 114, 101, 102, 46, 32, 40, 108, 105, 109, 105, 116, 115, 32, 108, 105, 97, 98, 46, 41, 49, 37, 48, 35, 6, 3, 85, 4, 11, 19, 28, 40, 99, 41, 32, 49, 57, 57, 57, 32, 69, 110, 116, 114, 117, 115, 116, 46, 110, 101, 116, 32, 76, 105, 109, 105, 116, 101, 100, 49, 51, 48, 49, 6, 3, 85, 4, 3, 19, 42, 69, 110, 116, 114, 117, 115, 116, 46, 110, 101, 116, 32, 67, 101, 114, 116, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 65, 117, 116, 104, 111, 114, 105, 116, 121, 32, 40, 50, 48, 52, 56, 41, 48, -126, 1, 34, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, 1, 5, 0, 3, -126, 1, 15, 0, 48, -126, 1, 10, 2, -126, 1, 1, 0, -83, 77, 75, -87, 18, -122, -78, -22, -93, 32, 7, 21, 22, 100, 42, 43, 75, -47, -65, 11, 74, 77, -114, -19, -128, 118, -91, 103, -73, 120, 64, -64, 115, 66, -56, 104, -64, -37, 83, 43, -35, 94, -72, 118, -104, 53, -109, -117, 26, -99, 124, 19, 58, 14, 31, 91, -73, 30, -49, -27, 36, 20, 30, -79, -127, -87, -115, 125, -72, -52, 107, 75, 3, -15, 2, 12, -36, -85, -91, 64, 36, 0, 127, 116, -108, -95, -99, 8, 41, -77, -120, 11, -11, -121, 119, -99, 85, -51, -28, -61, 126, -41, 106, 100, -85, -123, 20, -122, -107, 91, -105, 50, 80, 111, 61, -56, -70, 102, 12, -29, -4, -67, -72, 73, -63, 118, -119, 73, 25, -3, -64, -88, -67, -119, -93, 103, 47, -58, -97, -68, 113, 25, 96, -72, 45, -23, 44, -55, -112, 118, 102, 123, -108, -30, -81, 120, -42, 101, 83, 93, 60, -42, -100, -78, -49, 41, 3, -7, 47, -92, 80, -78, -44, 72, -50, 5, 50, 85, -118, -3, -78, 100, 76, 14, -28, -104, 7, 117, -37, 127, -33, -71, 8, 85, 96, -123, 48, 41, -7, 123, 72, -92, 105, -122, -29, 53, 63, 30, -122, 93, 122, 122, 21, -67, -17, 0, -114, 21, 34, 84, 23, 0, -112, 38, -109, -68, 14, 73, 104, -111, -65, -8, 71, -45, -99, -107, 66, -63, 14, 77, -33, 111, 38, -49, -61, 24, 33, 98, 102, 67, 112, -42, -43, -64, 7, -31, 2, 3, 1, 0, 1, -93, 116, 48, 114, 48, 17, 6, 9, 96, -122, 72, 1, -122, -8, 66, 1, 1, 4, 4, 3, 2, 0, 7, 48, 31, 6, 3, 85, 29, 35, 4, 24, 48, 22, -128, 20, 85, -28, -127, -47, 17, -128, -66, -40, -119, -71, 8, -93, 49, -7, -95, 36, 9, 22, -71, 112, 48, 29, 6, 3, 85, 29, 14, 4, 22, 4, 20, 85, -28, -127, -47, 17, -128, -66, -40, -119, -71, 8, -93, 49, -7, -95, 36, 9, 22, -71, 112, 48, 29, 6, 9, 42, -122, 72, -122, -10, 125, 7, 65, 0, 4, 16, 48, 14, 27, 8, 86, 53, 46, 48, 58, 52, 46, 48, 3, 2, 4, -112, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, 5, 5, 0, 3, -126, 1, 1, 0, 89, 71, -84, 33, -124, -118, 23, -55, -100, -119, 83, 30, -70, -128, -123, 26, -58, 60, 78, 62, -79, -100, -74, 124, -58, -110, 93, 24, 100, 2, -29, -45, 6, 8, 17, 97, 124, 99, -29, 43, -99, 49, 3, 112, 118, -46, -93, 40, -96, -12, -69, -102, 99, 115, -19, 109, -27, 42, -37, -19, 20, -87, 43, -58, 54, 17, -48, 43, -21, 7, -117, -91, -38, -98, 92, 25, -99, 86, 18, -11, 84, 41, -56, 5, -19, -78, 18, 42, -115, -12, 3, 27, -1, -25, -110, 16, -121, -80, 58, -75, -61, -99, 5, 55, 18, -93, -57, -12, 21, -71, -43, -92, 57, 22, -101, 83, 58, 35, -111, -15, -88, -126, -94, 106, -120, 104, -63, 121, 2, 34, -68, -86, -90, -42, -82, -33, -80, 20, 95, -72, -121, -48, -35, 124, 127, 123, -1, -81, 28, -49, -26, -37, 7, -83, 94, -37, -123, -99, -48, 43, 13, 51, -37, 4, -47, -26, 73, 64, 19, 43, 118, -5, 62, -23, -100, -119, 15, 21, -50, 24, -80, -123, 120, 33, 79, 107, 79, 14, -6, 54, 103, -51, 7, -14, -1, 8, -48, -30, -34, -39, -65, 42, -81, -72, -121, -122, 33, 60, 4, -54, -73, -108, 104, 127, -49, 60, -23, -104, -41, 56, -1, -20, -64, -39, 80, -16, 46, 75, 88, -82, 70, 111, -48, 46, -61, 96, -38, 114, 85, 114, -67, 76, 69, -98, 97, -70, -65, -124, -127, -110, 3, -47, -46, 105, 124, -59 }, + }; + + /** + * ASN1-encoded trusted certificate #946059622 for Entrust.net. This + * certificate uses the TELETEX encoding for its X509 name. + */ + private final byte[] trustedCert = new byte[] { 48, -126, 4, 92, 48, -126, 3, 68, -96, 3, 2, 1, 2, 2, 4, 56, 99, -71, 102, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, 5, 5, 0, 48, -127, -76, 49, 20, 48, 18, 6, 3, 85, 4, 10, 19, 11, 69, 110, 116, 114, 117, 115, 116, 46, 110, 101, 116, 49, 64, 48, 62, 6, 3, 85, 4, 11, 20, 55, 119, 119, 119, 46, 101, 110, 116, 114, 117, 115, 116, 46, 110, 101, 116, 47, 67, 80, 83, 95, 50, 48, 52, 56, 32, 105, 110, 99, 111, 114, 112, 46, 32, 98, 121, 32, 114, 101, 102, 46, 32, 40, 108, 105, 109, 105, 116, 115, 32, 108, 105, 97, 98, 46, 41, 49, 37, 48, 35, 6, 3, 85, 4, 11, 19, 28, 40, 99, 41, 32, 49, 57, 57, 57, 32, 69, 110, 116, 114, 117, 115, 116, 46, 110, 101, 116, 32, 76, 105, 109, 105, 116, 101, 100, 49, 51, 48, 49, 6, 3, 85, 4, 3, 19, 42, 69, 110, 116, 114, 117, 115, 116, 46, 110, 101, 116, 32, 67, 101, 114, 116, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 65, 117, 116, 104, 111, 114, 105, 116, 121, 32, 40, 50, 48, 52, 56, 41, 48, 30, 23, 13, 57, 57, 49, 50, 50, 52, 49, 55, 53, 48, 53, 49, 90, 23, 13, 49, 57, 49, 50, 50, 52, 49, 56, 50, 48, 53, 49, 90, 48, -127, -76, 49, 20, 48, 18, 6, 3, 85, 4, 10, 19, 11, 69, 110, 116, 114, 117, 115, 116, 46, 110, 101, 116, 49, 64, 48, 62, 6, 3, 85, 4, 11, 20, 55, 119, 119, 119, 46, 101, 110, 116, 114, 117, 115, 116, 46, 110, 101, 116, 47, 67, 80, 83, 95, 50, 48, 52, 56, 32, 105, 110, 99, 111, 114, 112, 46, 32, 98, 121, 32, 114, 101, 102, 46, 32, 40, 108, 105, 109, 105, 116, 115, 32, 108, 105, 97, 98, 46, 41, 49, 37, 48, 35, 6, 3, 85, 4, 11, 19, 28, 40, 99, 41, 32, 49, 57, 57, 57, 32, 69, 110, 116, 114, 117, 115, 116, 46, 110, 101, 116, 32, 76, 105, 109, 105, 116, 101, 100, 49, 51, 48, 49, 6, 3, 85, 4, 3, 19, 42, 69, 110, 116, 114, 117, 115, 116, 46, 110, 101, 116, 32, 67, 101, 114, 116, 105, 102, 105, 99, 97, 116, 105, 111, 110, 32, 65, 117, 116, 104, 111, 114, 105, 116, 121, 32, 40, 50, 48, 52, 56, 41, 48, -126, 1, 34, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, 1, 5, 0, 3, -126, 1, 15, 0, 48, -126, 1, 10, 2, -126, 1, 1, 0, -83, 77, 75, -87, 18, -122, -78, -22, -93, 32, 7, 21, 22, 100, 42, 43, 75, -47, -65, 11, 74, 77, -114, -19, -128, 118, -91, 103, -73, 120, 64, -64, 115, 66, -56, 104, -64, -37, 83, 43, -35, 94, -72, 118, -104, 53, -109, -117, 26, -99, 124, 19, 58, 14, 31, 91, -73, 30, -49, -27, 36, 20, 30, -79, -127, -87, -115, 125, -72, -52, 107, 75, 3, -15, 2, 12, -36, -85, -91, 64, 36, 0, 127, 116, -108, -95, -99, 8, 41, -77, -120, 11, -11, -121, 119, -99, 85, -51, -28, -61, 126, -41, 106, 100, -85, -123, 20, -122, -107, 91, -105, 50, 80, 111, 61, -56, -70, 102, 12, -29, -4, -67, -72, 73, -63, 118, -119, 73, 25, -3, -64, -88, -67, -119, -93, 103, 47, -58, -97, -68, 113, 25, 96, -72, 45, -23, 44, -55, -112, 118, 102, 123, -108, -30, -81, 120, -42, 101, 83, 93, 60, -42, -100, -78, -49, 41, 3, -7, 47, -92, 80, -78, -44, 72, -50, 5, 50, 85, -118, -3, -78, 100, 76, 14, -28, -104, 7, 117, -37, 127, -33, -71, 8, 85, 96, -123, 48, 41, -7, 123, 72, -92, 105, -122, -29, 53, 63, 30, -122, 93, 122, 122, 21, -67, -17, 0, -114, 21, 34, 84, 23, 0, -112, 38, -109, -68, 14, 73, 104, -111, -65, -8, 71, -45, -99, -107, 66, -63, 14, 77, -33, 111, 38, -49, -61, 24, 33, 98, 102, 67, 112, -42, -43, -64, 7, -31, 2, 3, 1, 0, 1, -93, 116, 48, 114, 48, 17, 6, 9, 96, -122, 72, 1, -122, -8, 66, 1, 1, 4, 4, 3, 2, 0, 7, 48, 31, 6, 3, 85, 29, 35, 4, 24, 48, 22, -128, 20, 85, -28, -127, -47, 17, -128, -66, -40, -119, -71, 8, -93, 49, -7, -95, 36, 9, 22, -71, 112, 48, 29, 6, 3, 85, 29, 14, 4, 22, 4, 20, 85, -28, -127, -47, 17, -128, -66, -40, -119, -71, 8, -93, 49, -7, -95, 36, 9, 22, -71, 112, 48, 29, 6, 9, 42, -122, 72, -122, -10, 125, 7, 65, 0, 4, 16, 48, 14, 27, 8, 86, 53, 46, 48, 58, 52, 46, 48, 3, 2, 4, -112, 48, 13, 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, 5, 5, 0, 3, -126, 1, 1, 0, 89, 71, -84, 33, -124, -118, 23, -55, -100, -119, 83, 30, -70, -128, -123, 26, -58, 60, 78, 62, -79, -100, -74, 124, -58, -110, 93, 24, 100, 2, -29, -45, 6, 8, 17, 97, 124, 99, -29, 43, -99, 49, 3, 112, 118, -46, -93, 40, -96, -12, -69, -102, 99, 115, -19, 109, -27, 42, -37, -19, 20, -87, 43, -58, 54, 17, -48, 43, -21, 7, -117, -91, -38, -98, 92, 25, -99, 86, 18, -11, 84, 41, -56, 5, -19, -78, 18, 42, -115, -12, 3, 27, -1, -25, -110, 16, -121, -80, 58, -75, -61, -99, 5, 55, 18, -93, -57, -12, 21, -71, -43, -92, 57, 22, -101, 83, 58, 35, -111, -15, -88, -126, -94, 106, -120, 104, -63, 121, 2, 34, -68, -86, -90, -42, -82, -33, -80, 20, 95, -72, -121, -48, -35, 124, 127, 123, -1, -81, 28, -49, -26, -37, 7, -83, 94, -37, -123, -99, -48, 43, 13, 51, -37, 4, -47, -26, 73, 64, 19, 43, 118, -5, 62, -23, -100, -119, 15, 21, -50, 24, -80, -123, 120, 33, 79, 107, 79, 14, -6, 54, 103, -51, 7, -14, -1, 8, -48, -30, -34, -39, -65, 42, -81, -72, -121, -122, 33, 60, 4, -54, -73, -108, 104, 127, -49, 60, -23, -104, -41, 56, -1, -20, -64, -39, 80, -16, 46, 75, 88, -82, 70, 111, -48, 46, -61, 96, -38, 114, 85, 114, -67, 76, 69, -98, 97, -70, -65, -124, -127, -110, 3, -47, -46, 105, 124, -59 }; + + public void testTrustAndRemoteCertificatesWithDifferentEncodings() + throws IOException, CertificateException, KeyStoreException, + InvalidAlgorithmParameterException, CertPathValidatorException { + + X509CertPathImpl certPath = new X509CertPathImpl(Arrays.asList( + new X509CertImpl(serviceSprintComCertChain[0]), + new X509CertImpl(serviceSprintComCertChain[1]), + new X509CertImpl(serviceSprintComCertChain[2]))); + + Set trustAnchors = new HashSet(); + trustAnchors.add(new TrustAnchor(new X509CertificateObject( + new X509CertificateStructure( + (ASN1Sequence) new ASN1InputStream(trustedCert).readObject())), null)); + + IndexedPKIXParameters indexedPKIXParameters = new IndexedPKIXParameters(trustAnchors); + indexedPKIXParameters.setRevocationEnabled(false); + + new PKIXCertPathValidatorSpi().engineValidate(certPath, indexedPKIXParameters); + // completing normally indicates that the certificate was valid + } +} diff --git a/libcore/security/src/test/java/tests/security/AllTests.java b/libcore/security/src/test/java/tests/security/AllTests.java index 2a69b592a..a88cc0e4b 100644 --- a/libcore/security/src/test/java/tests/security/AllTests.java +++ b/libcore/security/src/test/java/tests/security/AllTests.java @@ -32,6 +32,7 @@ public class AllTests { TestSuite suite = tests.TestSuiteFactory.createTestSuite("All security test suites"); // $JUnit-BEGIN$ suite.addTest(org.apache.harmony.security.tests.java.security.AllTests.suite()); + suite.addTest(org.bouncycastle.jce.provider.AllTests.suite()); suite.addTest(tests.api.java.security.AllTests.suite()); suite.addTest(tests.java.security.AllTests.suite()); diff --git a/libcore/sql/src/main/java/java/sql/Array.java b/libcore/sql/src/main/java/java/sql/Array.java index 6113c46e5..fc9debe31 100644 --- a/libcore/sql/src/main/java/java/sql/Array.java +++ b/libcore/sql/src/main/java/java/sql/Array.java @@ -21,8 +21,6 @@ import java.util.Map; /** * A Java representation of the SQL {@code ARRAY} type. - * - * @since Android 1.0 */ public interface Array { @@ -33,7 +31,6 @@ public interface Array { * @return A Java array containing the elements of this Array * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public Object getArray() throws SQLException; @@ -50,7 +47,6 @@ public interface Array { * @return A Java array containing the desired set of elements from this Array * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public Object getArray(long index, int count) throws SQLException; @@ -70,7 +66,6 @@ public interface Array { * @return A Java array containing the desired set of elements from this Array * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public Object getArray(long index, int count, Map> map) throws SQLException; @@ -84,7 +79,6 @@ public interface Array { * @return A Java array containing the elements of this array * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public Object getArray(Map> map) throws SQLException; @@ -95,7 +89,6 @@ public interface Array { * @return An integer constant from the {@code java.sql.Types} class * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public int getBaseType() throws SQLException; @@ -106,7 +99,6 @@ public interface Array { * @return The database specific name or a fully-qualified SQL type name. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public String getBaseTypeName() throws SQLException; @@ -117,7 +109,6 @@ public interface Array { * @return the elements of the array as a {@code ResultSet}. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public ResultSet getResultSet() throws SQLException; @@ -134,7 +125,6 @@ public interface Array { * @return the elements of the array as a {@code ResultSet}. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public ResultSet getResultSet(long index, int count) throws SQLException; @@ -155,7 +145,6 @@ public interface Array { * database error has occurred. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public ResultSet getResultSet(long index, int count, Map> map) throws SQLException; @@ -170,7 +159,6 @@ public interface Array { * @return the array as a {@code ResultSet}. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public ResultSet getResultSet(Map> map) throws SQLException; diff --git a/libcore/sql/src/main/java/java/sql/BatchUpdateException.java b/libcore/sql/src/main/java/java/sql/BatchUpdateException.java index 36a7ef919..0a616db7d 100644 --- a/libcore/sql/src/main/java/java/sql/BatchUpdateException.java +++ b/libcore/sql/src/main/java/java/sql/BatchUpdateException.java @@ -26,7 +26,7 @@ import java.io.Serializable; * problem that occurred, compared with a standard {@code SQLException}. It * supplies update counts for successful commands which were executed before the * exception was encountered. - *

          + *

          * The element order in the array of update counts matches the order that the * commands were added to the batch operation. *

          @@ -37,9 +37,6 @@ import java.io.Serializable; * for every command in the batch, not only those that executed successfully. In * this case, the array element for any command which encountered a problem is * set to {@code Statement.EXECUTE_FAILED}. - *

          - * - * @since Android 1.0 */ public class BatchUpdateException extends SQLException implements Serializable { @@ -51,8 +48,6 @@ public class BatchUpdateException extends SQLException implements Serializable { * Creates a default {@code BatchUpdateException} with the parameters * reason, SQLState, and update counts set to {@code * null} and the vendor code set to 0. - * - * @since Android 1.0 */ public BatchUpdateException() { super(); @@ -67,7 +62,6 @@ public class BatchUpdateException extends SQLException implements Serializable { * the array of {@code updateCounts} giving the number of * successful updates (or another status code) for each command * in the batch that was attempted. - * @since Android 1.0 */ public BatchUpdateException(int[] updateCounts) { super(); @@ -86,7 +80,6 @@ public class BatchUpdateException extends SQLException implements Serializable { * the array of {@code updateCounts} giving the number of * successful updates (or another status code) for each command * in the batch that was attempted. - * @since Android 1.0 */ public BatchUpdateException(String reason, int[] updateCounts) { super(reason); @@ -107,7 +100,6 @@ public class BatchUpdateException extends SQLException implements Serializable { * the array of {@code updateCounts} giving the number of * successful updates (or another status code) for each command * in the batch that was attempted. - * @since Android 1.0 */ public BatchUpdateException(String reason, String SQLState, int[] updateCounts) { @@ -130,7 +122,6 @@ public class BatchUpdateException extends SQLException implements Serializable { * the array of {@code updateCounts} giving the number of * successful updates (or another status code) for each command * in the batch that was attempted. - * @since Android 1.0 */ public BatchUpdateException(String reason, String SQLState, int vendorCode, int[] updateCounts) { @@ -162,7 +153,6 @@ public class BatchUpdateException extends SQLException implements Serializable { *
        • {@code Statement.EXECUTE_FAILED} indicating that the command * was unsuccessful.
        • * - * @since Android 1.0 */ public int[] getUpdateCounts() { return updateCounts; diff --git a/libcore/sql/src/main/java/java/sql/Blob.java b/libcore/sql/src/main/java/java/sql/Blob.java index e6d9b1982..0074b9802 100644 --- a/libcore/sql/src/main/java/java/sql/Blob.java +++ b/libcore/sql/src/main/java/java/sql/Blob.java @@ -25,12 +25,10 @@ import java.io.InputStream; *

          * An SQL {@code BLOB} type stores a large array of binary data (bytes) as the * value in a column of a database. - *

          + *

          * The {@code java.sql.Blob} interface provides methods for setting and * retrieving data in the {@code Blob}, for querying {@code Blob} data length, * and for searching for data within the {@code Blob}. - * - * @since Android 1.0 */ public interface Blob { @@ -41,7 +39,6 @@ public interface Blob { * data. * @throws SQLException * if an error occurs accessing the {@code Blob}. - * @since Android 1.0 */ public InputStream getBinaryStream() throws SQLException; @@ -57,7 +54,6 @@ public interface Blob { * at {@code pos} and is up to {@code length} bytes long. * @throws SQLException * if an error occurs accessing the {@code Blob}. - * @since Android 1.0 */ public byte[] getBytes(long pos, int length) throws SQLException; @@ -68,7 +64,6 @@ public interface Blob { * bytes. * @throws SQLException * if an error occurs accessing the {@code Blob}. - * @since Android 1.0 */ public long length() throws SQLException; @@ -87,7 +82,6 @@ public interface Blob { * {@code Blob}. * @throws SQLException * if an error occurs accessing the {@code Blob}. - * @since Android 1.0 */ public long position(Blob pattern, long start) throws SQLException; @@ -106,7 +100,6 @@ public interface Blob { * {@code Blob}. * @throws SQLException * if an error occurs accessing the {@code Blob}. - * @since Android 1.0 */ public long position(byte[] pattern, long start) throws SQLException; @@ -121,7 +114,6 @@ public interface Blob { * the {@code Blob} starting at the specified position. * @throws SQLException * if an error occurs accessing the {@code Blob}. - * @since Android 1.0 */ public OutputStream setBinaryStream(long pos) throws SQLException; @@ -139,7 +131,6 @@ public interface Blob { * Blob}. * @throws SQLException * if an error occurs accessing the {@code Blob}. - * @since Android 1.0 */ public int setBytes(long pos, byte[] theBytes) throws SQLException; @@ -162,7 +153,6 @@ public interface Blob { * Blob}. * @throws SQLException * if an error occurs accessing the {@code Blob}. - * @since Android 1.0 */ public int setBytes(long pos, byte[] theBytes, int offset, int len) throws SQLException; @@ -176,7 +166,6 @@ public interface Blob { * is to be truncated. * @throws SQLException * if an error occurs accessing the {@code Blob}. - * @since Android 1.0 */ public void truncate(long len) throws SQLException; } diff --git a/libcore/sql/src/main/java/java/sql/CallableStatement.java b/libcore/sql/src/main/java/java/sql/CallableStatement.java index 7a9004184..be5463bc4 100644 --- a/libcore/sql/src/main/java/java/sql/CallableStatement.java +++ b/libcore/sql/src/main/java/java/sql/CallableStatement.java @@ -36,7 +36,6 @@ import java.io.Reader; * name or by a numerical index starting at 1. *

          * The correct syntax is: - *

          *
          *
          * { ?= call <procedurename> [( [parameter1,parameter2,...] )] } @@ -44,7 +43,8 @@ import java.io.Reader; *
          * { call <procedurename> [( [parameter1,parameter2,...] )] } *
          - *

          {@code IN} parameters are set before calling the procedure, + * + * {@code IN} parameters are set before calling the procedure, * using the setter methods which are inherited from {@code PreparedStatement}. * For {@code OUT} parameters, their type must be registered before executing * the stored procedure. The values are retrieved using the getter methods @@ -53,9 +53,6 @@ import java.io.Reader; * {@code CallableStatement}s can return one or more {@code ResultSets}. In the * event that multiple {@code ResultSets} are returned, they are accessed using * the methods inherited from the {@code Statement} interface. - *

          - * - * @since Android 1.0 */ public interface CallableStatement extends PreparedStatement { @@ -69,7 +66,6 @@ public interface CallableStatement extends PreparedStatement { * @return a {@code java.sql.Array} containing the parameter value. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public Array getArray(int parameterIndex) throws SQLException; @@ -82,7 +78,6 @@ public interface CallableStatement extends PreparedStatement { * @return a {@code java.sql.Array} containing the parameter's value. * @throws SQLException * if there is a problem accessing the database. - * @since Android 1.0 */ public Array getArray(String parameterName) throws SQLException; @@ -98,7 +93,6 @@ public interface CallableStatement extends PreparedStatement { * the parameter in question is an SQL {@code NULL}. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public BigDecimal getBigDecimal(int parameterIndex) throws SQLException; @@ -119,7 +113,6 @@ public interface CallableStatement extends PreparedStatement { * if a database error occurs. * @deprecated Use {@link #getBigDecimal(int)} or * {@link #getBigDecimal(String)} - * @since Android 1.0 */ @Deprecated public BigDecimal getBigDecimal(int parameterIndex, int scale) @@ -136,7 +129,6 @@ public interface CallableStatement extends PreparedStatement { * the parameter in question is an SQL {@code NULL}. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public BigDecimal getBigDecimal(String parameterName) throws SQLException; @@ -152,7 +144,6 @@ public interface CallableStatement extends PreparedStatement { * the parameter in question is an SQL {@code NULL}. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public Blob getBlob(int parameterIndex) throws SQLException; @@ -167,7 +158,6 @@ public interface CallableStatement extends PreparedStatement { * the parameter in question is an SQL {@code NULL}. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public Blob getBlob(String parameterName) throws SQLException; @@ -181,7 +171,6 @@ public interface CallableStatement extends PreparedStatement { * is returned if the value is SQL {@code NULL}. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public boolean getBoolean(int parameterIndex) throws SQLException; @@ -195,7 +184,6 @@ public interface CallableStatement extends PreparedStatement { * {@code false} is returned if the SQL value is {@code NULL}. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public boolean getBoolean(String parameterName) throws SQLException; @@ -210,7 +198,6 @@ public interface CallableStatement extends PreparedStatement { * {@code 0} is returned if the value is SQL {@code NULL}. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public byte getByte(int parameterIndex) throws SQLException; @@ -224,7 +211,6 @@ public interface CallableStatement extends PreparedStatement { * {@code 0} is returned if the SQL value is {@code NULL}. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public byte getByte(String parameterName) throws SQLException; @@ -239,7 +225,6 @@ public interface CallableStatement extends PreparedStatement { * is returned if the value is SQL {@code NULL}. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public byte[] getBytes(int parameterIndex) throws SQLException; @@ -253,7 +238,6 @@ public interface CallableStatement extends PreparedStatement { * is returned if the value is SQL {@code NULL}. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public byte[] getBytes(String parameterName) throws SQLException; @@ -270,7 +254,6 @@ public interface CallableStatement extends PreparedStatement { * @throws SQLException * if a database error occurs. * @see Clob - * @since Android 1.0 */ public Clob getClob(int parameterIndex) throws SQLException; @@ -285,7 +268,6 @@ public interface CallableStatement extends PreparedStatement { * @throws SQLException * if a database error occurs. * @see Clob - * @since Android 1.0 */ public Clob getClob(String parameterName) throws SQLException; @@ -301,7 +283,6 @@ public interface CallableStatement extends PreparedStatement { * @throws SQLException * if a database error occurs. * @see Date - * @since Android 1.0 */ public Date getDate(int parameterIndex) throws SQLException; @@ -312,8 +293,7 @@ public interface CallableStatement extends PreparedStatement { * The JDBC driver uses the calendar to create the Date using a particular * timezone and locale. The default behavior of the driver is to use the Java * virtual machine default settings. - *

          - * + * * @param parameterIndex * the parameter number index, where the first parameter has * index 1. @@ -324,7 +304,6 @@ public interface CallableStatement extends PreparedStatement { * @throws SQLException * if a database error occurs. * @see Date - * @since Android 1.0 */ public Date getDate(int parameterIndex, Calendar cal) throws SQLException; @@ -339,7 +318,6 @@ public interface CallableStatement extends PreparedStatement { * @throws SQLException * if a database error occurs. * @see Date - * @since Android 1.0 */ public Date getDate(String parameterName) throws SQLException; @@ -350,8 +328,7 @@ public interface CallableStatement extends PreparedStatement { * The JDBC driver uses the calendar to create the date using a particular * timezone and locale. The default behavior of the driver is to use the Java * virtual machine default settings. - *

          - * + * * @param parameterName * the name of the desired parameter. * @param cal @@ -361,7 +338,6 @@ public interface CallableStatement extends PreparedStatement { * @throws SQLException * if a database error occurs. * @see Date - * @since Android 1.0 */ public Date getDate(String parameterName, Calendar cal) throws SQLException; @@ -376,7 +352,6 @@ public interface CallableStatement extends PreparedStatement { * is returned if the value is SQL {@code NULL}. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public double getDouble(int parameterIndex) throws SQLException; @@ -390,7 +365,6 @@ public interface CallableStatement extends PreparedStatement { * is returned if the value is SQL {@code NULL}. * @throws SQLException * if there is a problem accessing the database. - * @since Android 1.0 */ public double getDouble(String parameterName) throws SQLException; @@ -405,7 +379,6 @@ public interface CallableStatement extends PreparedStatement { * is returned if the value is SQL {@code NULL}. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public float getFloat(int parameterIndex) throws SQLException; @@ -419,7 +392,6 @@ public interface CallableStatement extends PreparedStatement { * is returned if the value is SQL {@code NULL}. * @throws SQLException * if there is a problem accessing the database. - * @since Android 1.0 */ public float getFloat(String parameterName) throws SQLException; @@ -434,7 +406,6 @@ public interface CallableStatement extends PreparedStatement { * is returned if the value is SQL {@code NULL}. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public int getInt(int parameterIndex) throws SQLException; @@ -448,7 +419,6 @@ public interface CallableStatement extends PreparedStatement { * is returned if the value is SQL {@code NULL}. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public int getInt(String parameterName) throws SQLException; @@ -463,7 +433,6 @@ public interface CallableStatement extends PreparedStatement { * is returned if the value is SQL {@code NULL}. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public long getLong(int parameterIndex) throws SQLException; @@ -477,7 +446,6 @@ public interface CallableStatement extends PreparedStatement { * is returned if the value is SQL {@code NULL}. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public long getLong(String parameterName) throws SQLException; @@ -488,15 +456,13 @@ public interface CallableStatement extends PreparedStatement { * with a {@code registerOutParameter} call. If a parameter was registered * as a {@code java.sql.Types.OTHER} then it may hold abstract types that * are particular to the connected database. - *

          - * + * * @param parameterIndex * the parameter number index, where the first parameter has * index 1. * @return an Object holding the value of the parameter. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public Object getObject(int parameterIndex) throws SQLException; @@ -513,7 +479,6 @@ public interface CallableStatement extends PreparedStatement { * @return an Object holding the value of the parameter. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public Object getObject(int parameterIndex, Map> map) throws SQLException; @@ -526,15 +491,13 @@ public interface CallableStatement extends PreparedStatement { * If a parameter was registered as a {@code java.sql.Types.OTHER} * then it may hold abstract types that are particular to the * connected database. - *

          - * + * * @param parameterName * the parameter name. * @return the Java {@code Object} representation of the value of the * parameter. * @throws SQLException * if there is a problem accessing the database. - * @since Android 1.0 */ public Object getObject(String parameterName) throws SQLException; @@ -550,7 +513,6 @@ public interface CallableStatement extends PreparedStatement { * @return an {@code Object} holding the value of the parameter. * @throws SQLException * if there is a problem accessing the database. - * @since Android 1.0 */ public Object getObject(String parameterName, Map> map) throws SQLException; @@ -566,7 +528,6 @@ public interface CallableStatement extends PreparedStatement { * is returned if the value is SQL {@code NULL}. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public Ref getRef(int parameterIndex) throws SQLException; @@ -582,7 +543,6 @@ public interface CallableStatement extends PreparedStatement { * @throws SQLException * if there is a problem accessing the database. * @see Ref - * @since Android 1.0 */ public Ref getRef(String parameterName) throws SQLException; @@ -597,7 +557,6 @@ public interface CallableStatement extends PreparedStatement { * if the parameter's value is SQL {@code NULL}. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public short getShort(int parameterIndex) throws SQLException; @@ -611,7 +570,6 @@ public interface CallableStatement extends PreparedStatement { * if the parameter's value is SQL {@code NULL}. * @throws SQLException * if there is a problem accessing the database. - * @since Android 1.0 */ public short getShort(String parameterName) throws SQLException; @@ -623,8 +581,7 @@ public interface CallableStatement extends PreparedStatement { * The {@code String} corresponding to a {@code CHAR} of fixed length * will be of identical length to the value in the database inclusive * of padding characters. - *

          - * + * * @param parameterIndex * the parameter number index, where the first parameter has * index 1. @@ -632,7 +589,6 @@ public interface CallableStatement extends PreparedStatement { * is returned if the value is SQL {@code NULL}. * @throws SQLException * if there is a problem accessing the database. - * @since Android 1.0 */ public String getString(int parameterIndex) throws SQLException; @@ -644,15 +600,13 @@ public interface CallableStatement extends PreparedStatement { * The string corresponding to a {@code CHAR} of fixed length will be of * identical length to the value in the database inclusive of padding * characters. - *

          - * + * * @param parameterName * the desired parameter's name. * @return the parameter's value as a {@code String}. {@code null} * is returned if the value is SQL {@code NULL}. * @throws SQLException * if there is a problem accessing the database. - * @since Android 1.0 */ public String getString(String parameterName) throws SQLException; @@ -668,7 +622,6 @@ public interface CallableStatement extends PreparedStatement { * @throws SQLException * if a database error occurs. * @see Time - * @since Android 1.0 */ public Time getTime(int parameterIndex) throws SQLException; @@ -689,7 +642,6 @@ public interface CallableStatement extends PreparedStatement { * if a database error occurs. * @see Time * @see java.util.Calendar - * @since Android 1.0 */ public Time getTime(int parameterIndex, Calendar cal) throws SQLException; @@ -704,7 +656,6 @@ public interface CallableStatement extends PreparedStatement { * @throws SQLException * if a database error occurs. * @see Time - * @since Android 1.0 */ public Time getTime(String parameterName) throws SQLException; @@ -724,7 +675,6 @@ public interface CallableStatement extends PreparedStatement { * if a database error occurs. * @see Time * @see java.util.Calendar - * @since Android 1.0 */ public Time getTime(String parameterName, Calendar cal) throws SQLException; @@ -741,7 +691,6 @@ public interface CallableStatement extends PreparedStatement { * @throws SQLException * if a database error occurs. * @see Timestamp - * @since Android 1.0 */ public Timestamp getTimestamp(int parameterIndex) throws SQLException; @@ -761,7 +710,6 @@ public interface CallableStatement extends PreparedStatement { * @throws SQLException * if a database error occurs. * @see Timestamp - * @since Android 1.0 */ public Timestamp getTimestamp(int parameterIndex, Calendar cal) throws SQLException; @@ -778,7 +726,6 @@ public interface CallableStatement extends PreparedStatement { * @throws SQLException * if a database error occurs. * @see Timestamp - * @since Android 1.0 */ public Timestamp getTimestamp(String parameterName) throws SQLException; @@ -797,7 +744,6 @@ public interface CallableStatement extends PreparedStatement { * @throws SQLException * if a database error occurs. * @see Timestamp - * @since Android 1.0 */ public Timestamp getTimestamp(String parameterName, Calendar cal) throws SQLException; @@ -814,7 +760,6 @@ public interface CallableStatement extends PreparedStatement { * @throws SQLException * if a database error occurs. * @see java.net.URL - * @since Android 1.0 */ public URL getURL(int parameterIndex) throws SQLException; @@ -830,7 +775,6 @@ public interface CallableStatement extends PreparedStatement { * @throws SQLException * if a database error occurs. * @see java.net.URL - * @since Android 1.0 */ public URL getURL(String parameterName) throws SQLException; @@ -845,8 +789,7 @@ public interface CallableStatement extends PreparedStatement { * If a database specific type is expected for a parameter, the Type {@code * java.sql.Types.OTHER} should be used. Note that there is another variant * of this method for User Defined Types or a {@code REF} type. - *

          - * + * * @param parameterIndex * the parameter number index, where the first parameter has * index 1 @@ -857,7 +800,6 @@ public interface CallableStatement extends PreparedStatement { * @throws SQLException * if a database error occurs. * @see Types - * @since Android 1.0 */ public void registerOutParameter(int parameterIndex, int sqlType) throws SQLException; @@ -873,8 +815,7 @@ public interface CallableStatement extends PreparedStatement { * The type supplied in the {@code sqlType} parameter fixes the * type that will be returned by the getter methods of * {@code CallableStatement}. - *

          - * + * * @param parameterIndex * the parameter number index, where the first parameter has * index 1 @@ -886,7 +827,6 @@ public interface CallableStatement extends PreparedStatement { * @throws SQLException * if a database error occurs. * @see Types - * @since Android 1.0 */ public void registerOutParameter(int parameterIndex, int sqlType, int scale) throws SQLException; @@ -908,7 +848,6 @@ public interface CallableStatement extends PreparedStatement { * @throws SQLException * if a database error occurs. * @see Ref - * @since Android 1.0 */ public void registerOutParameter(int paramIndex, int sqlType, String typeName) throws SQLException; @@ -923,8 +862,7 @@ public interface CallableStatement extends PreparedStatement { * If a database-specific type is expected for a parameter, the Type {@code * java.sql.Types.OTHER} should be used. Note that there is another variant * of this method for User Defined Types or a {@code REF} type. - *

          - * + * * @param parameterName * the parameter name. * @param sqlType @@ -934,7 +872,6 @@ public interface CallableStatement extends PreparedStatement { * parameter. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public void registerOutParameter(String parameterName, int sqlType) throws SQLException; @@ -950,8 +887,7 @@ public interface CallableStatement extends PreparedStatement { * The type supplied in the {@code sqlType} parameter fixes the * type that will be returned by the getter methods of * {@code CallableStatement}. - *

          - * + * * @param parameterName * the parameter name. * @param sqlType @@ -961,7 +897,6 @@ public interface CallableStatement extends PreparedStatement { * than or equal to 0. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public void registerOutParameter(String parameterName, int sqlType, int scale) throws SQLException; @@ -982,7 +917,6 @@ public interface CallableStatement extends PreparedStatement { * of the referenced type. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public void registerOutParameter(String parameterName, int sqlType, String typeName) throws SQLException; @@ -991,12 +925,11 @@ public interface CallableStatement extends PreparedStatement { * Sets the value of a specified parameter to the content of a supplied * {@code InputStream}, which has a specified number of bytes. *

          - * This is a good method for setting an SQL {@code LONVARCHAR} parameter + * This is a good method for setting an SQL {@code LONGVARCHAR} parameter * where the length of the data is large. Data is read from the {@code * InputStream} until end-of-file is reached or the specified number of * bytes is copied. - *

          - * + * * @param parameterName * the parameter name * @param theInputStream @@ -1007,7 +940,6 @@ public interface CallableStatement extends PreparedStatement { * parameter. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public void setAsciiStream(String parameterName, InputStream theInputStream, int length) throws SQLException; @@ -1022,7 +954,6 @@ public interface CallableStatement extends PreparedStatement { * the {@code java.math.BigInteger} value to set. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public void setBigDecimal(String parameterName, BigDecimal theBigDecimal) throws SQLException; @@ -1033,8 +964,7 @@ public interface CallableStatement extends PreparedStatement { *

          * Use this method when a large amount of data needs to be set into a * {@code LONGVARBINARY} parameter. - *

          - * + * * @param parameterName * the name of the parameter. * @param theInputStream @@ -1045,7 +975,6 @@ public interface CallableStatement extends PreparedStatement { * parameter. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public void setBinaryStream(String parameterName, InputStream theInputStream, int length) throws SQLException; @@ -1060,7 +989,6 @@ public interface CallableStatement extends PreparedStatement { * the new value with which to update the parameter. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public void setBoolean(String parameterName, boolean theBoolean) throws SQLException; @@ -1074,7 +1002,6 @@ public interface CallableStatement extends PreparedStatement { * the new value with which to update the parameter. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public void setByte(String parameterName, byte theByte) throws SQLException; @@ -1089,7 +1016,6 @@ public interface CallableStatement extends PreparedStatement { * the new value with which to update the parameter. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public void setBytes(String parameterName, byte[] theBytes) throws SQLException; @@ -1106,7 +1032,6 @@ public interface CallableStatement extends PreparedStatement { * a count of the characters contained in {@code reader}. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public void setCharacterStream(String parameterName, Reader reader, int length) throws SQLException; @@ -1121,7 +1046,6 @@ public interface CallableStatement extends PreparedStatement { * the new value with which to update the parameter. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public void setDate(String parameterName, Date theDate) throws SQLException; @@ -1144,7 +1068,6 @@ public interface CallableStatement extends PreparedStatement { * if a database error occurs. * @see java.util.Calendar * @see Date - * @since Android 1.0 */ public void setDate(String parameterName, Date theDate, Calendar cal) throws SQLException; @@ -1159,7 +1082,6 @@ public interface CallableStatement extends PreparedStatement { * the new value with which to update the parameter. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public void setDouble(String parameterName, double theDouble) throws SQLException; @@ -1174,7 +1096,6 @@ public interface CallableStatement extends PreparedStatement { * the new value with which to update the parameter. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public void setFloat(String parameterName, float theFloat) throws SQLException; @@ -1188,7 +1109,6 @@ public interface CallableStatement extends PreparedStatement { * the new value with which to update the parameter. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public void setInt(String parameterName, int theInt) throws SQLException; @@ -1201,7 +1121,6 @@ public interface CallableStatement extends PreparedStatement { * the new value with which to update the parameter. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public void setLong(String parameterName, long theLong) throws SQLException; @@ -1216,7 +1135,6 @@ public interface CallableStatement extends PreparedStatement { * a JDBC type expressed as a constant from {@link Types}. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public void setNull(String parameterName, int sqlType) throws SQLException; @@ -1229,8 +1147,7 @@ public interface CallableStatement extends PreparedStatement { * type code and type name (which is just the parameter name if the type is * user defined, referred to as a {@code UDT}, or the name of the referenced * type in case of a {@code REF} type). - *

          - * + * * @param parameterName * the parameter name. * @param sqlType @@ -1243,7 +1160,6 @@ public interface CallableStatement extends PreparedStatement { * @throws SQLException * if a database error occurs. * @see Types - * @since Android 1.0 */ public void setNull(String parameterName, int sqlType, String typeName) throws SQLException; @@ -1263,8 +1179,7 @@ public interface CallableStatement extends PreparedStatement { *
        • {@link Array}
        • *
        • {@link Clob}
        • *
        • {@link Blob}
        • - *

          - * + * * @param parameterName * the parameter name * @param theObject @@ -1272,7 +1187,6 @@ public interface CallableStatement extends PreparedStatement { * @throws SQLException * if a database error occurs. * @see SQLData - * @since Android 1.0 */ public void setObject(String parameterName, Object theObject) throws SQLException; @@ -1295,8 +1209,7 @@ public interface CallableStatement extends PreparedStatement { * * then the driver is in charge of mapping the value to the appropriate * SQL type and deliver it to the database. - *

          - * + * * @param parameterName * the parameter name. * @param theObject @@ -1306,7 +1219,6 @@ public interface CallableStatement extends PreparedStatement { * @throws SQLException * if a database error occurs. * @see SQLData - * @since Android 1.0 */ public void setObject(String parameterName, Object theObject, int targetSqlType) throws SQLException; @@ -1328,8 +1240,7 @@ public interface CallableStatement extends PreparedStatement { * * then the driver is charge of mapping the value to the appropriate * SQL type. - *

          - * + * * @param parameterName * the parameter name. * @param theObject @@ -1342,7 +1253,6 @@ public interface CallableStatement extends PreparedStatement { * @throws SQLException * if a database error occurs. * @see SQLData - * @since Android 1.0 */ public void setObject(String parameterName, Object theObject, int targetSqlType, int scale) throws SQLException; @@ -1357,7 +1267,6 @@ public interface CallableStatement extends PreparedStatement { * a short value to update the parameter. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public void setShort(String parameterName, short theShort) throws SQLException; @@ -1371,7 +1280,6 @@ public interface CallableStatement extends PreparedStatement { * a {@code String} value to update the parameter. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public void setString(String parameterName, String theString) throws SQLException; @@ -1387,7 +1295,6 @@ public interface CallableStatement extends PreparedStatement { * @throws SQLException * if a database error occurs. * @see Time - * @since Android 1.0 */ public void setTime(String parameterName, Time theTime) throws SQLException; @@ -1409,7 +1316,6 @@ public interface CallableStatement extends PreparedStatement { * @throws SQLException * if a database error occurs. * @see Time - * @since Android 1.0 */ public void setTime(String parameterName, Time theTime, Calendar cal) throws SQLException; @@ -1425,7 +1331,6 @@ public interface CallableStatement extends PreparedStatement { * @throws SQLException * if a database error occurs. * @see Timestamp - * @since Android 1.0 */ public void setTimestamp(String parameterName, Timestamp theTimestamp) throws SQLException; @@ -1437,8 +1342,7 @@ public interface CallableStatement extends PreparedStatement { * The driver uses the supplied calendar to create the SQL {@code TIMESTAMP} * value, which allows it to use a custom timezone - otherwise the driver * uses the default timezone of the Java virtual machine. - *

          - * + * * @param parameterName * the parameter name. * @param theTimestamp @@ -1449,7 +1353,6 @@ public interface CallableStatement extends PreparedStatement { * if a database error occurs. * @see Timestamp * @see java.util.Calendar - * @since Android 1.0 */ public void setTimestamp(String parameterName, Timestamp theTimestamp, Calendar cal) throws SQLException; @@ -1465,7 +1368,6 @@ public interface CallableStatement extends PreparedStatement { * @throws SQLException * if a database error occurs. * @see java.net.URL - * @since Android 1.0 */ public void setURL(String parameterName, URL theURL) throws SQLException; @@ -1477,7 +1379,6 @@ public interface CallableStatement extends PreparedStatement { * otherwise. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public boolean wasNull() throws SQLException; } diff --git a/libcore/sql/src/main/java/java/sql/Clob.java b/libcore/sql/src/main/java/java/sql/Clob.java index 339d4e574..73fe7fbe6 100644 --- a/libcore/sql/src/main/java/java/sql/Clob.java +++ b/libcore/sql/src/main/java/java/sql/Clob.java @@ -31,8 +31,6 @@ import java.io.Writer; * The {@code java.sql.Clob} interface provides methods for setting and * retrieving data in the {@code Clob}, for querying {@code Clob} data length, * for searching for data within the {@code Clob}. - * - * @since Android 1.0 */ public interface Clob { @@ -43,7 +41,6 @@ public interface Clob { * {@code Clob} data. * @throws SQLException * if an error occurs accessing the {@code Clob}. - * @since Android 1.0 */ public InputStream getAsciiStream() throws SQLException; @@ -54,7 +51,6 @@ public interface Clob { * Clob} data. * @throws SQLException * if an error occurs accessing the {@code Clob}. - * @since Android 1.0 */ public Reader getCharacterStream() throws SQLException; @@ -68,7 +64,6 @@ public interface Clob { * @return A string containing the requested data. * @throws SQLException * if an error occurs accessing the {@code Clob}. - * @since Android 1.0 */ public String getSubString(long pos, int length) throws SQLException; @@ -78,7 +73,6 @@ public interface Clob { * @return a long value with the number of character in this {@code Clob}. * @throws SQLException * if an error occurs accessing the {@code Clob}. - * @since Android 1.0 */ public long length() throws SQLException; @@ -94,7 +88,6 @@ public interface Clob { * Clob} occurs within this {@code Clob}. * @throws SQLException * if an error occurs accessing the {@code Clob}. - * @since Android 1.0 */ public long position(Clob searchstr, long start) throws SQLException; @@ -111,7 +104,6 @@ public interface Clob { * occurs within this {@code Clob}. * @throws SQLException * if an error occurs accessing the {@code Clob}. - * @since Android 1.0 */ public long position(String searchstr, long start) throws SQLException; @@ -125,7 +117,6 @@ public interface Clob { * this {@code Clob}. * @throws SQLException * if an error occurs accessing the {@code Clob}. - * @since Android 1.0 */ public OutputStream setAsciiStream(long pos) throws SQLException; @@ -139,7 +130,6 @@ public interface Clob { * {@code Clob}. * @throws SQLException * if an error occurs accessing the {@code Clob}. - * @since Android 1.0 */ public Writer setCharacterStream(long pos) throws SQLException; @@ -154,7 +144,6 @@ public interface Clob { * @return the number of characters written. * @throws SQLException * if an error occurs accessing the {@code Clob}. - * @since Android 1.0 */ public int setString(long pos, String str) throws SQLException; @@ -173,7 +162,6 @@ public interface Clob { * @return the number of characters written. * @throws SQLException * if an error occurs accessing the {@code Clob}. - * @since Android 1.0 */ public int setString(long pos, String str, int offset, int len) throws SQLException; @@ -186,7 +174,6 @@ public interface Clob { * truncate this {@code Clob}. * @throws SQLException * if an error occurs accessing the {@code Clob}. - * @since Android 1.0 */ public void truncate(long len) throws SQLException; } diff --git a/libcore/sql/src/main/java/java/sql/Connection.java b/libcore/sql/src/main/java/java/sql/Connection.java index 523071c69..0704cca75 100644 --- a/libcore/sql/src/main/java/java/sql/Connection.java +++ b/libcore/sql/src/main/java/java/sql/Connection.java @@ -23,8 +23,10 @@ import java.util.Map; * A connection represents a link from a Java application to a database. All SQL * statements and results are returned within the context of a connection. * Database statements that are executed within this context form a - * database session which forms one or more closed transactions. Especially In distributed applications, multiple concurrent connections may exist accessing the same values of the database. - * which may lead to the following phenomena (referred to as transaction isolation levels): + * database session which forms one or more closed transactions. Especially in + * distributed applications, multiple concurrent connections may exist accessing + * the same values of the database. which may lead to the following phenomena + * (referred to as transaction isolation levels): *
            *
          • dirty reads:
            * reading values from table rows that are not committed.
          • @@ -36,15 +38,11 @@ import java.util.Map; * because other transactions have inserted additional rows that satisfy an * SQL {@code WHERE} clause
            *
          - * - * @since Android 1.0 */ public interface Connection { /** * A constant indicating that transactions are not supported. - * - * @since Android 1.0 */ public static final int TRANSACTION_NONE = 0; @@ -52,8 +50,6 @@ public interface Connection { * No dirty reads are permitted, therefore transactions may not read * a row containing uncommitted values - but does not prevent an application * from non-repeatable reads and phantom reads. - * - * @since Android 1.0 */ public static final int TRANSACTION_READ_COMMITTED = 2; @@ -65,16 +61,12 @@ public interface Connection { *
        • non-repeatable reads
        • *
        • phantom reads
        • * - * - * @since Android 1.0 */ public static final int TRANSACTION_READ_UNCOMMITTED = 1; /** * A constant indicating that dirty reads and non-repeatable * reads are prevented but phantom reads can occur. - * - * @since Android 1.0 */ public static final int TRANSACTION_REPEATABLE_READ = 4; @@ -86,8 +78,6 @@ public interface Connection { *
        • non-repeatable reads
        • *
        • phantom reads
        • * - * - * @since Android 1.0 */ public static final int TRANSACTION_SERIALIZABLE = 8; @@ -112,8 +102,7 @@ public interface Connection { * connection before garbage collection takes place, it is not advisable to * leave the {@code close} operation to take place in this way. Mainly * because undesired side-effects may appear. - *

          - * + * * @throws SQLException * if there is a problem accessing the database. */ @@ -217,7 +206,6 @@ public interface Connection { * false}. * @throws SQLException * if there is a problem accessing the database. - * @since Android 1.0 */ public boolean getAutoCommit() throws SQLException; @@ -242,7 +230,6 @@ public interface Connection { * * @throws SQLException * if there is a problem accessing the a database. - * @since Android 1.0 */ public int getHoldability() throws SQLException; @@ -255,7 +242,6 @@ public interface Connection { * description. * @throws SQLException * if there is a problem accessing the a database. - * @since Android 1.0 */ public DatabaseMetaData getMetaData() throws SQLException; @@ -270,7 +256,6 @@ public interface Connection { * @see #TRANSACTION_READ_UNCOMMITTED * @see #TRANSACTION_REPEATABLE_READ * @see #TRANSACTION_SERIALIZABLE - * @since Android 1.0 */ public int getTransactionIsolation() throws SQLException; @@ -281,7 +266,6 @@ public interface Connection { * @return the Type Map as a {@code java.util.Map}. * @throws SQLException * if there is a problem accessing the database. - * @since Android 1.0 */ public Map> getTypeMap() throws SQLException; @@ -294,14 +278,12 @@ public interface Connection { * By invoking the {@link SQLWarning#getNextWarning()} method of the * returned {@code SQLWarning} object it is possible to obtain all of * this connection's warning objects. - *

          - * + * * @return the first warning as an SQLWarning object (may be {@code null}). * @throws SQLException * if there is a problem accessing the database or if the call * has been made on a connection which has been previously * closed. - * @since Android 1.0 */ public SQLWarning getWarnings() throws SQLException; @@ -315,7 +297,6 @@ public interface Connection { * @return {@code true} if closed, otherwise {@code false}. * @throws SQLException * if there is a problem accessing the database. - * @since Android 1.0 */ public boolean isClosed() throws SQLException; @@ -326,7 +307,6 @@ public interface Connection { * @return {@code true} if in read-only state, otherwise {@code false}. * @throws SQLException * if there is a problem accessing the database. - * @since Android 1.0 */ public boolean isReadOnly() throws SQLException; @@ -356,7 +336,6 @@ public interface Connection { * {@link ResultSet#CONCUR_READ_ONLY}. * @throws SQLException * if a problem occurs accessing the database. - * @since Android 1.0 */ public CallableStatement prepareCall(String sql) throws SQLException; @@ -387,7 +366,6 @@ public interface Connection { * resultSetType} and {@code resultSetConcurrency} values. * @throws SQLException * if a problem occurs accessing the database - * @since Android 1.0 */ public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException; @@ -426,7 +404,6 @@ public interface Connection { * resultSetHoldability} values. * @throws SQLException * if a problem occurs accessing the database. - * @since Android 1.0 */ public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) @@ -441,7 +418,7 @@ public interface Connection { * the driver does not support precompiled statements, the statement will * not reach the database server until it is executed. This distinction * determines the moment when {@code SQLException}s get raised. - *

          + *

          * By default, {@code ResultSet}s from the returned object will be * {@link ResultSet#TYPE_FORWARD_ONLY} type with a * {@link ResultSet#CONCUR_READ_ONLY} mode of concurrency. @@ -452,7 +429,6 @@ public interface Connection { * statement. * @throws SQLException * if there is a problem accessing the database. - * @since Android 1.0 */ public PreparedStatement prepareStatement(String sql) throws SQLException; @@ -467,7 +443,7 @@ public interface Connection { * precompiled in a {@code PreparedStatement}. The {@code PreparedStatement} * can then be then be used to execute the statement multiple times in an * efficient way. - *

          + *

          * Subject to JDBC driver support, this operation will attempt to send the * precompiled version of the statement to the database. If * the driver does not support precompiled statements, the statement will @@ -477,8 +453,7 @@ public interface Connection { * By default, {@code ResultSet}s from the returned object will be * {@link ResultSet#TYPE_FORWARD_ONLY} type with a * {@link ResultSet#CONCUR_READ_ONLY} mode of concurrency. - *

          - * + * * @param sql * the SQL statement. * @param autoGeneratedKeys @@ -491,7 +466,6 @@ public interface Connection { * SQL statement. * @throws SQLException * if there is a problem accessing the database. - * @since Android 1.0 */ public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException; @@ -509,13 +483,11 @@ public interface Connection { * the driver does not support precompiled statements, the statement will * not reach the database server until it is executed. This distinction * determines the moment when {@code SQLException}s get raised. - *

          *

          * By default, {@code ResultSet}s from the returned object will be * {@link ResultSet#TYPE_FORWARD_ONLY} type with a * {@link ResultSet#CONCUR_READ_ONLY} concurrency mode. - *

          - * + * * @param sql * the SQL statement. * @param columnIndexes @@ -524,7 +496,6 @@ public interface Connection { * @return the PreparedStatement containing the supplied SQL statement. * @throws SQLException * if a problem occurs accessing the database. - * @since Android 1.0 */ public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException; @@ -556,7 +527,6 @@ public interface Connection { * resultSetType} and {@code resultSetConcurrency} values. * @throws SQLException * if a problem occurs accessing the database. - * @since Android 1.0 */ public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException; @@ -594,7 +564,6 @@ public interface Connection { * resultSetHoldability} values. * @throws SQLException * if a problem occurs accessing the database. - * @since Android 1.0 */ public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) @@ -614,13 +583,11 @@ public interface Connection { * statement will not reach the database server until it is executed. This * will have a bearing on precisely when {@code SQLException} * instances get raised. - *

          *

          * By default, ResultSets from the returned object will be * {@link ResultSet#TYPE_FORWARD_ONLY} type with a * {@link ResultSet#CONCUR_READ_ONLY} concurrency mode. - *

          - * + * * @param sql * the SQL statement. * @param columnNames @@ -629,7 +596,6 @@ public interface Connection { * @return the PreparedStatement containing the supplied SQL statement. * @throws SQLException * if a problem occurs accessing the database. - * @since Android 1.0 */ public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException; @@ -645,7 +611,6 @@ public interface Connection { * if there is a problem with accessing the database or if * {@code savepoint} is considered not valid in this * transaction. - * @since Android 1.0 */ public void releaseSavepoint(Savepoint savepoint) throws SQLException; @@ -657,7 +622,6 @@ public interface Connection { * @throws SQLException * if there is a problem with the database or if the method is * called while in auto-commit mode of operation. - * @since Android 1.0 */ public void rollback() throws SQLException; @@ -669,7 +633,6 @@ public interface Connection { * the Savepoint to roll back to * @throws SQLException * if there is a problem accessing the database. - * @since Android 1.0 */ public void rollback(Savepoint savepoint) throws SQLException; @@ -682,26 +645,23 @@ public interface Connection { * statements get grouped into transactions that need to be completed by * explicit calls to either the {@link #commit()} or {@link #rollback()} * methods. - *

          + *

          * Auto-commit is the default mode for new connection instances. *

          * When in this mode, commits will automatically occur upon successful SQL * statement completion or upon successful completion of an execute. * Statements are not considered successfully completed until all associated * {@code ResultSet}s and output parameters have been obtained or closed. - *

          *

          * Calling this operation during an uncommitted transaction will result in * it being committed. - *

          - * + * * @param autoCommit * {@code boolean} indication of whether to put the target * connection into auto-commit mode ({@code true}) or not ( * {@code false}). * @throws SQLException * if there is a problem accessing the database. - * @since Android 1.0 */ public void setAutoCommit(boolean autoCommit) throws SQLException; @@ -714,7 +674,6 @@ public interface Connection { * the catalog name to use. * @throws SQLException * if there is a problem accessing the database. - * @since Android 1.0 */ public void setCatalog(String catalog) throws SQLException; @@ -738,14 +697,12 @@ public interface Connection { *

          * This serves as a hint to the driver, which can enable database * optimizations. - *

          - * + * * @param readOnly * {@code true} to set the Connection to read only mode. {@code * false} disables read-only mode. * @throws SQLException * if there is a problem accessing the database. - * @since Android 1.0 */ public void setReadOnly(boolean readOnly) throws SQLException; @@ -755,7 +712,6 @@ public interface Connection { * @return a {@code Savepoint} object for this savepoint. * @throws SQLException * if there is a problem accessing the database. - * @since Android 1.0 */ public Savepoint setSavepoint() throws SQLException; @@ -767,7 +723,6 @@ public interface Connection { * @return a {@code Savepoint} object for this savepoint. * @throws SQLException * if there is a problem accessing the database. - * @since Android 1.0 */ public Savepoint setSavepoint(String name) throws SQLException; @@ -776,8 +731,7 @@ public interface Connection { *

          * If this method is called during a transaction, the results are * implementation defined. - *

          - * + * * @param level * the new transaction isolation level to use from the following * list of possible values: @@ -790,7 +744,6 @@ public interface Connection { * @throws SQLException * if there is a problem with the database or if the value of * {@code level} is not one of the expected constant values. - * @since Android 1.0 */ public void setTransactionIsolation(int level) throws SQLException; @@ -803,7 +756,6 @@ public interface Connection { * @throws SQLException * if there is a problem accessing the database or if {@code * map} is not an instance of {@link Map}. - * @since Android 1.0 */ public void setTypeMap(Map> map) throws SQLException; } diff --git a/libcore/sql/src/main/java/java/sql/DataTruncation.java b/libcore/sql/src/main/java/java/sql/DataTruncation.java index a472cc531..20da727b6 100644 --- a/libcore/sql/src/main/java/java/sql/DataTruncation.java +++ b/libcore/sql/src/main/java/java/sql/DataTruncation.java @@ -24,8 +24,6 @@ import java.io.Serializable; * value either when reading (resulting in warning), or when writing data * (resulting in an error). The {@code SQLState} error code for truncated data * is {@code 01004}. - * - * @since Android 1.0 */ public class DataTruncation extends SQLWarning implements Serializable { @@ -66,7 +64,6 @@ public class DataTruncation extends SQLWarning implements Serializable { * the original size of the truncated data. * @param transferSize * the size of the data after truncation. - * @since Android 1.0 */ public DataTruncation(int index, boolean parameter, boolean read, int dataSize, int transferSize) { @@ -83,7 +80,6 @@ public class DataTruncation extends SQLWarning implements Serializable { * * @return the number of bytes that should have been read or written. The * value is set to {@code -1} if the size is unknown. - * @since Android 1.0 */ public int getDataSize() { return dataSize; @@ -93,7 +89,6 @@ public class DataTruncation extends SQLWarning implements Serializable { * Gets the index of the column or of the parameter that was truncated. * * @return the index number of the column or of the parameter. - * @since Android 1.0 */ public int getIndex() { return index; @@ -104,7 +99,6 @@ public class DataTruncation extends SQLWarning implements Serializable { * * @return {@code true} if the value truncated was a parameter value, * {@code false} if it was a column value. - * @since Android 1.0 */ public boolean getParameter() { return parameter; @@ -116,7 +110,6 @@ public class DataTruncation extends SQLWarning implements Serializable { * * @return {@code true} if the value was truncated on a read operation, * {@code false} otherwise. - * @since Android 1.0 */ public boolean getRead() { return read; @@ -127,7 +120,6 @@ public class DataTruncation extends SQLWarning implements Serializable { * * @return the number of bytes actually read/written. The value may be set * to {@code -1} if the size is unknown. - * @since Android 1.0 */ public int getTransferSize() { return transferSize; diff --git a/libcore/sql/src/main/java/java/sql/DatabaseMetaData.java b/libcore/sql/src/main/java/java/sql/DatabaseMetaData.java index 82219c522..aca03c05e 100644 --- a/libcore/sql/src/main/java/java/sql/DatabaseMetaData.java +++ b/libcore/sql/src/main/java/java/sql/DatabaseMetaData.java @@ -24,7 +24,6 @@ package java.sql; * This interface is implemented by JDBC driver vendors in order to provide * information about the underlying database capabilities in association with * the JDBC driver. - *

          *

          * Some of the methods in this interface take string parameters which are * patterns. Within these string patterns, {@code '%'} and {@code '_'} @@ -33,144 +32,105 @@ package java.sql; * "match any character". Only metadata entries that match the pattern are * returned. If such a search pattern string is set to {@code null}, that * argument's criteria are dropped from the search. - *

          - * - * @since Android 1.0 */ public interface DatabaseMetaData { /** * States that it may not be permitted to store {@code NULL} values. - * - * @since Android 1.0 */ public static final short attributeNoNulls = 0; /** * States that {@code NULL} values are definitely permitted. - * - * @since Android 1.0 */ public static final short attributeNullable = 1; /** * States that whether {@code NULL} values are permitted is unknown. - * - * @since Android 1.0 */ public static final short attributeNullableUnknown = 2; /** * States the best row identifier is NOT a pseudo column. - * - * @since Android 1.0 */ public static final int bestRowNotPseudo = 1; /** * States that the best row identifier is a pseudo column. - * - * @since Android 1.0 */ public static final int bestRowPseudo = 2; /** * States that the remainder of the current session is used as the scope for * the best row identifier. - * - * @since Android 1.0 */ public static final int bestRowSession = 2; /** * States that best row identifier scope lasts only while the row is being * used. - * - * @since Android 1.0 */ public static final int bestRowTemporary = 0; /** * States that the remainder of the current transaction is used as the scope * for the best row identifier. - * - * @since Android 1.0 */ public static final int bestRowTransaction = 1; /** * States that the best row identifier may or may not be a pseudo column. - * - * @since Android 1.0 */ public static final int bestRowUnknown = 0; /** * States that the column must not allow {@code NULL} values. - * - * @since Android 1.0 */ public static final int columnNoNulls = 0; /** * States that the column definitely allows {@code NULL} values. - * - * @since Android 1.0 */ public static final int columnNullable = 1; /** * States that it is unknown whether the columns may be nulled. - * - * @since Android 1.0 */ public static final int columnNullableUnknown = 2; /** * For the column {@code UPDATE_RULE}, states that when the primary key is * updated, the foreign key (imported key) is changed accordingly. - * - * @since Android 1.0 */ public static final int importedKeyCascade = 0; /** * States that the evaluation of foreign key constraints is deferred (delayed * until commit). - * - * @since Android 1.0 */ public static final int importedKeyInitiallyDeferred = 5; /** * States that the evaluation of foreign key constraint is {@code IMMEDIATE} * . - * - * @since Android 1.0 */ public static final int importedKeyInitiallyImmediate = 6; /** * For the columns {@code UPDATE_RULE} and {@code DELETE_RULE}, states that * if the primary key has been imported, it cannot be updated or deleted. - * - * @since Android 1.0 */ public static final int importedKeyNoAction = 3; /** * States that the evaluation of foreign key constraint must not be {@code * DEFERRED}. - * - * @since Android 1.0 */ public static final int importedKeyNotDeferrable = 7; /** * States that a primary key must not be updated when imported as a foreign * key by some other table. Used for the column {@code UPDATE_RULE}. - * - * @since Android 1.0 */ public static final int importedKeyRestrict = 1; @@ -178,8 +138,6 @@ public interface DatabaseMetaData { * States that when the primary key is modified (updated or deleted) the * foreign (imported) key is changed to its default value. Applies to the * {@code UPDATE_RULE} and {@code DELETE_RULE} columns. - * - * @since Android 1.0 */ public static final int importedKeySetDefault = 4; @@ -187,190 +145,138 @@ public interface DatabaseMetaData { * States that when the primary key is modified (updated or deleted) the * foreign (imported) key is changed to {@code NULL}. Applies to the {@code * UPDATE_RULE} and {@code DELETE_RULE} columns. - * - * @since Android 1.0 */ public static final int importedKeySetNull = 2; /** * States that the column stores {@code IN} type parameters. - * - * @since Android 1.0 */ public static final int procedureColumnIn = 1; /** * States that this column stores {@code INOUT} type parameters. - * - * @since Android 1.0 */ public static final int procedureColumnInOut = 2; /** * States that this column stores {@code OUT} type parameters. - * - * @since Android 1.0 */ public static final int procedureColumnOut = 4; /** * States that the column stores results. - * - * @since Android 1.0 */ public static final int procedureColumnResult = 3; /** * States that the column stores return values. - * - * @since Android 1.0 */ public static final int procedureColumnReturn = 5; /** * States that type of the column is unknown. - * - * @since Android 1.0 */ public static final int procedureColumnUnknown = 0; /** * States that {@code NULL} values are not permitted. - * - * @since Android 1.0 */ public static final int procedureNoNulls = 0; /** * States that the procedure does not return a result. - * - * @since Android 1.0 */ public static final int procedureNoResult = 1; /** * States that {@code NULL} values are permitted. - * - * @since Android 1.0 */ public static final int procedureNullable = 1; /** * States that it is unknown whether {@code NULL} values are permitted. - * - * @since Android 1.0 */ public static final int procedureNullableUnknown = 2; /** * States that it is unknown whether or not the procedure returns a result. - * - * @since Android 1.0 */ public static final int procedureResultUnknown = 0; /** * States that the procedure returns a result. - * - * @since Android 1.0 */ public static final int procedureReturnsResult = 2; /** * States that the value is an SQL99 {@code SQLSTATE} value. - * - * @since Android 1.0 */ public static final int sqlStateSQL99 = 2; /** * States that the value is an SQL {@code CLI SQLSTATE} value as defined by * the X/Open standard. - * - * @since Android 1.0 */ public static final int sqlStateXOpen = 1; /** * States that this table index is a clustered index. - * - * @since Android 1.0 */ public static final short tableIndexClustered = 1; /** * States that this table index is a hashed index. - * - * @since Android 1.0 */ public static final short tableIndexHashed = 2; /** * States this table's index is neither a clustered index, not a hashed * index, and not a table statistics index; i.e. it is something else. - * - * @since Android 1.0 */ public static final short tableIndexOther = 3; /** * States this column has the table's statistics, and that it is returned in * conjunction with the table's index description. - * - * @since Android 1.0 */ public static final short tableIndexStatistic = 0; /** * States that a {@code NULL} value is NOT permitted for * this data type. - * - * @since Android 1.0 */ public static final int typeNoNulls = 0; /** * States that a {@code NULL} value is permitted for this data type. - * - * @since Android 1.0 */ public static final int typeNullable = 1; /** * States that it is unknown if a {@code NULL} value is permitted for * this data type. - * - * @since Android 1.0 */ public static final int typeNullableUnknown = 2; /** * States that this column shall not be used for {@code WHERE} statements * with a {@code LIKE} clause. - * - * @since Android 1.0 */ public static final int typePredBasic = 2; /** * States that this column can only be used in a {@code WHERE...LIKE} * statement. - * - * @since Android 1.0 */ public static final int typePredChar = 1; /** * States that this column does not support searches. - * - * @since Android 1.0 */ public static final int typePredNone = 0; /** * States that the column is searchable. - * - * @since Android 1.0 */ public static final int typeSearchable = 3; @@ -397,7 +303,6 @@ public interface DatabaseMetaData { * {@code false} otherwise. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public boolean allProceduresAreCallable() throws SQLException; @@ -409,7 +314,6 @@ public interface DatabaseMetaData { * otherwise. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public boolean allTablesAreSelectable() throws SQLException; @@ -421,7 +325,6 @@ public interface DatabaseMetaData { * otherwise. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public boolean dataDefinitionCausesTransactionCommit() throws SQLException; @@ -433,7 +336,6 @@ public interface DatabaseMetaData { * {@code false} otherwise. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public boolean dataDefinitionIgnoredInTransactions() throws SQLException; @@ -450,7 +352,6 @@ public interface DatabaseMetaData { * false} otherwise. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public boolean deletesAreDetected(int type) throws SQLException; @@ -462,7 +363,6 @@ public interface DatabaseMetaData { * and {@code LONGVARCHAR}, otherwise {@code false}. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public boolean doesMaxRowSizeIncludeBlobs() throws SQLException; @@ -530,8 +430,7 @@ public interface DatabaseMetaData { * generated REF type or for a Distinct type. ({@code NULL} if {@code * DATA_TYPE} is not DISTINCT or a user generated REF) * - *

          - * + * * @param catalog * a catalog name. {@code null} is used to imply no narrowing of * the search by catalog name. Otherwise, the name must match a @@ -551,7 +450,6 @@ public interface DatabaseMetaData { * @return a {@code ResultSet}, where each row is an attribute description. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public ResultSet getAttributes(String catalog, String schemaPattern, String typeNamePattern, String attributeNamePattern) @@ -592,8 +490,7 @@ public interface DatabaseMetaData { * * * - *

          - * + * * @param catalog * a catalog name. {@code null} is used to imply no narrowing of * the search by catalog name. Otherwise, the name must match a @@ -616,7 +513,6 @@ public interface DatabaseMetaData { * and the complete set of rows is the optimal set for this table. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public ResultSet getBestRowIdentifier(String catalog, String schema, String table, int scope, boolean nullable) throws SQLException; @@ -630,7 +526,6 @@ public interface DatabaseMetaData { * single column named {@code TABLE_CAT}. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public ResultSet getCatalogs() throws SQLException; @@ -641,7 +536,6 @@ public interface DatabaseMetaData { * @return a String containing the separator. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public String getCatalogSeparator() throws SQLException; @@ -651,7 +545,6 @@ public interface DatabaseMetaData { * @return a String with the vendor's term for "catalog". * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public String getCatalogTerm() throws SQLException; @@ -674,8 +567,7 @@ public interface DatabaseMetaData { * receiver can grant access to others, {@code "NO"} if the receiver cannot * grant access to others, {@code null} if unknown. * - *

          - * + * * @param catalog * a catalog name. {@code null} is used to imply no narrowing of * the search by catalog name. Otherwise, the name must match a @@ -696,7 +588,6 @@ public interface DatabaseMetaData { * each privilege description. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public ResultSet getColumnPrivileges(String catalog, String schema, String table, String columnNamePattern) throws SQLException; @@ -754,8 +645,7 @@ public interface DatabaseMetaData { * generated REF type or for a Distinct type. ({@code NULL} if {@code * DATA_TYPE} is not DISTINCT or a user generated REF) * - *

          - * + * * @param catalog * a catalog name. {@code null} is used to imply no narrowing of * the search by catalog name. Otherwise, the name must match a @@ -776,7 +666,6 @@ public interface DatabaseMetaData { * defined above. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public ResultSet getColumns(String catalog, String schemaPattern, String tableNamePattern, String columnNamePattern) @@ -788,7 +677,6 @@ public interface DatabaseMetaData { * @return the connection to the database. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public Connection getConnection() throws SQLException; @@ -857,8 +745,7 @@ public interface DatabaseMetaData { * * * - *

          - * + * * @param primaryCatalog * a catalog name for the primary key table. {@code null} is used to imply no narrowing of * the search by catalog name. Otherwise, the name must match a @@ -889,7 +776,6 @@ public interface DatabaseMetaData { * foreign keys laid out according to the format defined above. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public ResultSet getCrossReference(String primaryCatalog, String primarySchema, String primaryTable, String foreignCatalog, @@ -901,7 +787,6 @@ public interface DatabaseMetaData { * @return the major version number of the database software. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public int getDatabaseMajorVersion() throws SQLException; @@ -911,7 +796,6 @@ public interface DatabaseMetaData { * @return the minor version number of the database software. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public int getDatabaseMinorVersion() throws SQLException; @@ -921,7 +805,6 @@ public interface DatabaseMetaData { * @return a {@code String} with the name of the database software. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public String getDatabaseProductName() throws SQLException; @@ -932,7 +815,6 @@ public interface DatabaseMetaData { * software. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public String getDatabaseProductVersion() throws SQLException; @@ -949,7 +831,6 @@ public interface DatabaseMetaData { * * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public int getDefaultTransactionIsolation() throws SQLException; @@ -957,7 +838,6 @@ public interface DatabaseMetaData { * Returns the JDBC driver's major version number. * * @return the driver's major version number. - * @since Android 1.0 */ public int getDriverMajorVersion(); @@ -965,7 +845,6 @@ public interface DatabaseMetaData { * Returns the JDBC driver's minor version number. * * @return the driver's minor version number. - * @since Android 1.0 */ public int getDriverMinorVersion(); @@ -975,7 +854,6 @@ public interface DatabaseMetaData { * @return a {@code String} containing the name of the JDBC driver * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public String getDriverName() throws SQLException; @@ -986,7 +864,6 @@ public interface DatabaseMetaData { * JDBC driver. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public String getDriverVersion() throws SQLException; @@ -1056,8 +933,7 @@ public interface DatabaseMetaData { * * * - *

          - * + * * @param catalog * a catalog name. {@code null} is used to imply no narrowing of * the search by catalog name. Otherwise, the name must match a @@ -1075,7 +951,6 @@ public interface DatabaseMetaData { * columns, as defined above * @throws SQLException * a database error occurred - * @since Android 1.0 */ public ResultSet getExportedKeys(String catalog, String schema, String table) throws SQLException; @@ -1088,7 +963,6 @@ public interface DatabaseMetaData { * @return a String containing all the additional permitted characters. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public String getExtraNameCharacters() throws SQLException; @@ -1099,7 +973,6 @@ public interface DatabaseMetaData { * @return the String used to quote SQL identifiers. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public String getIdentifierQuoteString() throws SQLException; @@ -1169,8 +1042,7 @@ public interface DatabaseMetaData { * * * - *

          - * + * * @param catalog * a catalog name. {@code null} is used to imply no narrowing of * the search by catalog name. Otherwise, the name must match a @@ -1188,7 +1060,6 @@ public interface DatabaseMetaData { * rows in the format defined above. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public ResultSet getImportedKeys(String catalog, String schema, String table) throws SQLException; @@ -1235,8 +1106,7 @@ public interface DatabaseMetaData { *
        • {@code FILTER_CONDITION} - String - Filter condition. (possibly null) *
        • * - *

          - * + * * @param catalog * a catalog name. {@code null} is used to imply no narrowing of * the search by catalog name. Otherwise, the name must match a @@ -1262,7 +1132,6 @@ public interface DatabaseMetaData { * for the table, in the format defined above. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public ResultSet getIndexInfo(String catalog, String schema, String table, boolean unique, boolean approximate) throws SQLException; @@ -1273,7 +1142,6 @@ public interface DatabaseMetaData { * @return the major JDBC version number. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public int getJDBCMajorVersion() throws SQLException; @@ -1283,7 +1151,6 @@ public interface DatabaseMetaData { * @return the Minor JDBC Version Number. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public int getJDBCMinorVersion() throws SQLException; @@ -1295,7 +1162,6 @@ public interface DatabaseMetaData { * literal. If the number is unlimited then the result is zero. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public int getMaxBinaryLiteralLength() throws SQLException; @@ -1306,7 +1172,6 @@ public interface DatabaseMetaData { * is unknown, or the value is unlimited, then the result is zero. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public int getMaxCatalogNameLength() throws SQLException; @@ -1318,7 +1183,6 @@ public interface DatabaseMetaData { * zero. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public int getMaxCharLiteralLength() throws SQLException; @@ -1329,7 +1193,6 @@ public interface DatabaseMetaData { * is unknown, or the value is unlimited, then the result is zero. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public int getMaxColumnNameLength() throws SQLException; @@ -1342,7 +1205,6 @@ public interface DatabaseMetaData { * is zero. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public int getMaxColumnsInGroupBy() throws SQLException; @@ -1353,7 +1215,6 @@ public interface DatabaseMetaData { * unknown, or the value is unlimited, then the result is zero. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public int getMaxColumnsInIndex() throws SQLException; @@ -1366,7 +1227,6 @@ public interface DatabaseMetaData { * is zero. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public int getMaxColumnsInOrderBy() throws SQLException; @@ -1379,7 +1239,6 @@ public interface DatabaseMetaData { * zero. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public int getMaxColumnsInSelect() throws SQLException; @@ -1390,7 +1249,6 @@ public interface DatabaseMetaData { * unknown, or the value is unlimited, then the result is zero. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public int getMaxColumnsInTable() throws SQLException; @@ -1401,7 +1259,6 @@ public interface DatabaseMetaData { * the value is unlimited, then the result is zero. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public int getMaxConnections() throws SQLException; @@ -1412,7 +1269,6 @@ public interface DatabaseMetaData { * is unknown, or the value is unlimited, then the result is zero. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public int getMaxCursorNameLength() throws SQLException; @@ -1424,7 +1280,6 @@ public interface DatabaseMetaData { * unknown, or the value is unlimited, then the result is zero. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public int getMaxIndexLength() throws SQLException; @@ -1437,7 +1292,6 @@ public interface DatabaseMetaData { * zero. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public int getMaxProcedureNameLength() throws SQLException; @@ -1449,7 +1303,6 @@ public interface DatabaseMetaData { * unknown, or the value is unlimited, then the result is zero. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public int getMaxRowSize() throws SQLException; @@ -1461,7 +1314,6 @@ public interface DatabaseMetaData { * is unknown, or the value is unlimited, then the result is zero. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public int getMaxSchemaNameLength() throws SQLException; @@ -1474,7 +1326,6 @@ public interface DatabaseMetaData { * zero. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public int getMaxStatementLength() throws SQLException; @@ -1486,7 +1337,6 @@ public interface DatabaseMetaData { * unknown, or the value is unlimited, then the result is zero. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public int getMaxStatements() throws SQLException; @@ -1497,7 +1347,6 @@ public interface DatabaseMetaData { * unknown, or the value is unlimited, then the result is zero. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public int getMaxTableNameLength() throws SQLException; @@ -1510,7 +1359,6 @@ public interface DatabaseMetaData { * then the result is zero. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public int getMaxTablesInSelect() throws SQLException; @@ -1521,7 +1369,6 @@ public interface DatabaseMetaData { * unknown, or the value is unlimited, then the result is zero. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public int getMaxUserNameLength() throws SQLException; @@ -1534,7 +1381,6 @@ public interface DatabaseMetaData { * separated list. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public String getNumericFunctions() throws SQLException; @@ -1553,8 +1399,7 @@ public interface DatabaseMetaData { * primary key *
        • {@code PK_NAME} - String - the primary key name (possibly null)
        • * - *

          - * + * * @param catalog * a catalog name. {@code null} is used to imply no narrowing of * the search by catalog name. Otherwise, the name must match a @@ -1572,7 +1417,6 @@ public interface DatabaseMetaData { * defined above. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public ResultSet getPrimaryKeys(String catalog, String schema, String table) throws SQLException; @@ -1630,8 +1474,7 @@ public interface DatabaseMetaData { *
        • {@code REMARKS} - String - an explanatory comment about the data item *
        • * - *

          - * + * * @param catalog * a catalog name. {@code null} is used to imply no narrowing of * the search by catalog name. Otherwise, the name must match a @@ -1652,7 +1495,6 @@ public interface DatabaseMetaData { * in the format defined above. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public ResultSet getProcedureColumns(String catalog, String schemaPattern, String procedureNamePattern, String columnNamePattern) @@ -1702,7 +1544,6 @@ public interface DatabaseMetaData { * procedure in the format defined above. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public ResultSet getProcedures(String catalog, String schemaPattern, String procedureNamePattern) throws SQLException; @@ -1713,7 +1554,6 @@ public interface DatabaseMetaData { * @return a String with the vendor's preferred name for "procedure". * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public String getProcedureTerm() throws SQLException; @@ -1724,7 +1564,6 @@ public interface DatabaseMetaData { * ResultSet.CLOSE_CURSORS_AT_COMMIT}. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public int getResultSetHoldability() throws SQLException; @@ -1741,7 +1580,6 @@ public interface DatabaseMetaData { * defined above. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public ResultSet getSchemas() throws SQLException; @@ -1751,7 +1589,6 @@ public interface DatabaseMetaData { * @return a String which is the vendor's preferred term for schema. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public String getSchemaTerm() throws SQLException; @@ -1765,7 +1602,6 @@ public interface DatabaseMetaData { * @return a String used to escape the wildcard characters. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public String getSearchStringEscape() throws SQLException; @@ -1777,7 +1613,6 @@ public interface DatabaseMetaData { * format. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public String getSQLKeywords() throws SQLException; @@ -1791,7 +1626,6 @@ public interface DatabaseMetaData { * DatabaseMetaData.sqlStateXOpen}. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public int getSQLStateType() throws SQLException; @@ -1804,7 +1638,6 @@ public interface DatabaseMetaData { * separated format. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public String getStringFunctions() throws SQLException; @@ -1824,8 +1657,7 @@ public interface DatabaseMetaData { *
        • {@code TABLE_NAME} - String - The table name
        • *
        • SUPER{@code TABLE_NAME} - String - The super table name
        • * - *

          - * + * * @param catalog * a catalog name. {@code null} is used to imply no narrowing of * the search by catalog name. Otherwise, the name must match a @@ -1846,7 +1678,6 @@ public interface DatabaseMetaData { * returned if the database does not support table hierarchies. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public ResultSet getSuperTables(String catalog, String schemaPattern, String tableNamePattern) throws SQLException; @@ -1871,8 +1702,7 @@ public interface DatabaseMetaData { * (possibly {@code null}) *
        • SUPER{@code TYPE_NAME} - String - direct supertype's name
        • * - *

          - * + * * @param catalog * the catalog name. "" means get the UDTs without a catalog. * {@code null} means don't use the catalog name to restrict the @@ -1889,7 +1719,6 @@ public interface DatabaseMetaData { * returned for a database that does not support type hierarchies. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public ResultSet getSuperTypes(String catalog, String schemaPattern, String typeNamePattern) throws SQLException; @@ -1903,7 +1732,6 @@ public interface DatabaseMetaData { * separated format. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public String getSystemFunctions() throws SQLException; @@ -1930,8 +1758,7 @@ public interface DatabaseMetaData { * access to others, {@code "NO"} implies guarantee cannot grant access to * others, {@code null} means this status is unknown * - *

          - * + * * @param catalog * a catalog name. {@code null} is used to imply no narrowing of * the search by catalog name. Otherwise, the name must match a @@ -1949,7 +1776,6 @@ public interface DatabaseMetaData { * in the format defined above. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public ResultSet getTablePrivileges(String catalog, String schemaPattern, String tableNamePattern) throws SQLException; @@ -1982,8 +1808,7 @@ public interface DatabaseMetaData { * "USER" | "DERIVED" - specifies how values in the {@code * SELF_REFERENCING_COL_NAME} are created (possibly {@code null}) * - *

          - * + * * @param catalog * a catalog name. {@code null} is used to imply no narrowing of * the search by catalog name. Otherwise, the name must match a @@ -2004,7 +1829,6 @@ public interface DatabaseMetaData { * above. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public ResultSet getTables(String catalog, String schemaPattern, String tableNamePattern, String[] types) throws SQLException; @@ -2020,13 +1844,11 @@ public interface DatabaseMetaData { * {@code "TABLE"}, {@code "VIEW"}, "{@code SYSTEM TABLE"}, {@code "ALIAS"}, * {@code "SYNONYM"}, {@code "GLOBAL TEMPORARY"} * - *

          - * + * * @return a {@code ResultSet} with one row per table type in the format * defined above. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public ResultSet getTableTypes() throws SQLException; @@ -2037,7 +1859,6 @@ public interface DatabaseMetaData { * functions. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public String getTimeDateFunctions() throws SQLException; @@ -2096,7 +1917,6 @@ public interface DatabaseMetaData { * @return a {@code ResultSet} which is structured as described above. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public ResultSet getTypeInfo() throws SQLException; @@ -2122,11 +1942,9 @@ public interface DatabaseMetaData { * This is defined in {@code java.sql.Types}, and will be {@code null} if * the {@code DATA_TYPE} does not match these criteria. * - *

          *

          * If the driver does not support UDTs, the {@code ResultSet} is empty. - *

          - * + * * @param catalog * a catalog name. {@code null} is used to imply no narrowing of * the search by catalog name. Otherwise, the name must match a @@ -2146,7 +1964,6 @@ public interface DatabaseMetaData { * @return a {@code ResultSet} in the format described above. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public ResultSet getUDTs(String catalog, String schemaPattern, String typeNamePattern, int[] types) throws SQLException; @@ -2157,7 +1974,6 @@ public interface DatabaseMetaData { * @return the URL for the database. {@code null} if it cannot be generated. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public String getURL() throws SQLException; @@ -2167,7 +1983,6 @@ public interface DatabaseMetaData { * @return the user name. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public String getUserName() throws SQLException; @@ -2197,8 +2012,7 @@ public interface DatabaseMetaData { * * * - *

          - * + * * @param catalog * a catalog name. {@code null} is used to imply no narrowing of * the search using catalog name. Otherwise, the name must match @@ -2216,7 +2030,6 @@ public interface DatabaseMetaData { * column, in the format defined above. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public ResultSet getVersionColumns(String catalog, String schema, String table) throws SQLException; @@ -2235,7 +2048,6 @@ public interface DatabaseMetaData { * @throws SQLException * a database error occurred. * @see ResultSet#rowInserted() - * @since Android 1.0 */ public boolean insertsAreDetected(int type) throws SQLException; @@ -2247,7 +2059,6 @@ public interface DatabaseMetaData { * qualified table name, {@code false} otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean isCatalogAtStart() throws SQLException; @@ -2258,7 +2069,6 @@ public interface DatabaseMetaData { * otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean isReadOnly() throws SQLException; @@ -2270,7 +2080,6 @@ public interface DatabaseMetaData { * {@code false} otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean locatorsUpdateCopy() throws SQLException; @@ -2282,7 +2091,6 @@ public interface DatabaseMetaData { * produce a {@code NULL} result, {@code false} otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean nullPlusNonNullIsNull() throws SQLException; @@ -2296,7 +2104,6 @@ public interface DatabaseMetaData { * {@code false} otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean nullsAreSortedAtEnd() throws SQLException; @@ -2309,7 +2116,6 @@ public interface DatabaseMetaData { * {@code false} otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean nullsAreSortedAtStart() throws SQLException; @@ -2321,7 +2127,6 @@ public interface DatabaseMetaData { * false} otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean nullsAreSortedHigh() throws SQLException; @@ -2333,7 +2138,6 @@ public interface DatabaseMetaData { * otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean nullsAreSortedLow() throws SQLException; @@ -2350,7 +2154,6 @@ public interface DatabaseMetaData { * otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean othersDeletesAreVisible(int type) throws SQLException; @@ -2367,7 +2170,6 @@ public interface DatabaseMetaData { * false}. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean othersInsertsAreVisible(int type) throws SQLException; @@ -2384,7 +2186,6 @@ public interface DatabaseMetaData { * false}. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean othersUpdatesAreVisible(int type) throws SQLException; @@ -2401,7 +2202,6 @@ public interface DatabaseMetaData { * ResultSet} itself, otherwise {@code false}. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean ownDeletesAreVisible(int type) throws SQLException; @@ -2418,7 +2218,6 @@ public interface DatabaseMetaData { * ResultSet} itself, otherwise {@code false}. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean ownInsertsAreVisible(int type) throws SQLException; @@ -2435,7 +2234,6 @@ public interface DatabaseMetaData { * ResultSet} itself, otherwise {@code false}. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean ownUpdatesAreVisible(int type) throws SQLException; @@ -2448,7 +2246,6 @@ public interface DatabaseMetaData { * case, {@code false} otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean storesLowerCaseIdentifiers() throws SQLException; @@ -2460,7 +2257,6 @@ public interface DatabaseMetaData { * {@code false} otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean storesLowerCaseQuotedIdentifiers() throws SQLException; @@ -2472,7 +2268,6 @@ public interface DatabaseMetaData { * {@code false} otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean storesMixedCaseIdentifiers() throws SQLException; @@ -2485,7 +2280,6 @@ public interface DatabaseMetaData { * {@code false} otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean storesMixedCaseQuotedIdentifiers() throws SQLException; @@ -2497,7 +2291,6 @@ public interface DatabaseMetaData { * case, {@code false} otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean storesUpperCaseIdentifiers() throws SQLException; @@ -2509,7 +2302,6 @@ public interface DatabaseMetaData { * {@code false} otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean storesUpperCaseQuotedIdentifiers() throws SQLException; @@ -2521,7 +2313,6 @@ public interface DatabaseMetaData { * supported, {@code false} otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsAlterTableWithAddColumn() throws SQLException; @@ -2533,7 +2324,6 @@ public interface DatabaseMetaData { * supported, {@code false} otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsAlterTableWithDropColumn() throws SQLException; @@ -2544,7 +2334,6 @@ public interface DatabaseMetaData { * {@code false} otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsANSI92EntryLevelSQL() throws SQLException; @@ -2555,7 +2344,6 @@ public interface DatabaseMetaData { * false} otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsANSI92FullSQL() throws SQLException; @@ -2566,7 +2354,6 @@ public interface DatabaseMetaData { * {@code false} otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsANSI92IntermediateSQL() throws SQLException; @@ -2577,7 +2364,6 @@ public interface DatabaseMetaData { * otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsBatchUpdates() throws SQLException; @@ -2589,7 +2375,6 @@ public interface DatabaseMetaData { * statements, {@code false} otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsCatalogsInDataManipulation() throws SQLException; @@ -2600,7 +2385,6 @@ public interface DatabaseMetaData { * statements, {@code false} otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsCatalogsInIndexDefinitions() throws SQLException; @@ -2612,7 +2396,6 @@ public interface DatabaseMetaData { * statements, {@code false} otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsCatalogsInPrivilegeDefinitions() throws SQLException; @@ -2623,7 +2406,6 @@ public interface DatabaseMetaData { * statements. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsCatalogsInProcedureCalls() throws SQLException; @@ -2634,7 +2416,6 @@ public interface DatabaseMetaData { * statements, {@code false} otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsCatalogsInTableDefinitions() throws SQLException; @@ -2643,8 +2424,7 @@ public interface DatabaseMetaData { *

          * If aliasing is supported, then the SQL AS clause is used to provide names * for computed columns and provide alias names for columns. - *

          - * + * * @return {@code true} if column aliasing is supported, {@code false} * otherwise. * @throws SQLException @@ -2660,7 +2440,6 @@ public interface DatabaseMetaData { * {@code false} otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsConvert() throws SQLException; @@ -2676,7 +2455,6 @@ public interface DatabaseMetaData { * these types, {@code false} otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsConvert(int fromType, int toType) throws SQLException; @@ -2688,7 +2466,6 @@ public interface DatabaseMetaData { * otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsCoreSQLGrammar() throws SQLException; @@ -2699,7 +2476,6 @@ public interface DatabaseMetaData { * and {@code false} otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsCorrelatedSubqueries() throws SQLException; @@ -2711,7 +2487,6 @@ public interface DatabaseMetaData { * false} otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsDataDefinitionAndDataManipulationTransactions() throws SQLException; @@ -2724,7 +2499,6 @@ public interface DatabaseMetaData { * {@code false} otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsDataManipulationTransactionsOnly() throws SQLException; @@ -2737,7 +2511,6 @@ public interface DatabaseMetaData { * names, {@code false} otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsDifferentTableCorrelationNames() throws SQLException; @@ -2748,7 +2521,6 @@ public interface DatabaseMetaData { * supported. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsExpressionsInOrderBy() throws SQLException; @@ -2759,7 +2531,6 @@ public interface DatabaseMetaData { * false} otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsExtendedSQLGrammar() throws SQLException; @@ -2770,7 +2541,6 @@ public interface DatabaseMetaData { * false} otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsFullOuterJoins() throws SQLException; @@ -2782,7 +2552,6 @@ public interface DatabaseMetaData { * false} otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsGetGeneratedKeys() throws SQLException; @@ -2793,7 +2562,6 @@ public interface DatabaseMetaData { * false} otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsGroupBy() throws SQLException; @@ -2807,7 +2575,6 @@ public interface DatabaseMetaData { * this way, {@code false} otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsGroupByBeyondSelect() throws SQLException; @@ -2819,7 +2586,6 @@ public interface DatabaseMetaData { * in the {@code SELECT} statement, {@code false} otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsGroupByUnrelated() throws SQLException; @@ -2831,7 +2597,6 @@ public interface DatabaseMetaData { * {@code false} otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsIntegrityEnhancementFacility() throws SQLException; @@ -2842,7 +2607,6 @@ public interface DatabaseMetaData { * otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsLikeEscapeClause() throws SQLException; @@ -2855,7 +2619,6 @@ public interface DatabaseMetaData { * {@code supportsFullOuterJoins} returns {@code true}. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsLimitedOuterJoins() throws SQLException; @@ -2866,7 +2629,6 @@ public interface DatabaseMetaData { * false} otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsMinimumSQLGrammar() throws SQLException; @@ -2878,7 +2640,6 @@ public interface DatabaseMetaData { * case, {@code false} otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsMixedCaseIdentifiers() throws SQLException; @@ -2890,7 +2651,6 @@ public interface DatabaseMetaData { * {@code false} otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsMixedCaseQuotedIdentifiers() throws SQLException; @@ -2903,7 +2663,6 @@ public interface DatabaseMetaData { * otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsMultipleOpenResults() throws SQLException; @@ -2915,7 +2674,6 @@ public interface DatabaseMetaData { * {@code false} otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsMultipleResultSets() throws SQLException; @@ -2927,7 +2685,6 @@ public interface DatabaseMetaData { * false} otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsMultipleTransactions() throws SQLException; @@ -2938,7 +2695,6 @@ public interface DatabaseMetaData { * statements, {@code false} otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsNamedParameters() throws SQLException; @@ -2949,7 +2705,6 @@ public interface DatabaseMetaData { * false} otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsNonNullableColumns() throws SQLException; @@ -2972,7 +2727,6 @@ public interface DatabaseMetaData { * operations, {@code false} if they might get closed. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsOpenCursorsAcrossRollback() throws SQLException; @@ -2984,7 +2738,6 @@ public interface DatabaseMetaData { * they might not. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsOpenStatementsAcrossCommit() throws SQLException; @@ -2996,7 +2749,6 @@ public interface DatabaseMetaData { * they might not. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsOpenStatementsAcrossRollback() throws SQLException; @@ -3008,7 +2760,6 @@ public interface DatabaseMetaData { * not in the {@code SELECT}, {@code false} otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsOrderByUnrelated() throws SQLException; @@ -3019,7 +2770,6 @@ public interface DatabaseMetaData { * false} otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsOuterJoins() throws SQLException; @@ -3030,7 +2780,6 @@ public interface DatabaseMetaData { * statements. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsPositionedDelete() throws SQLException; @@ -3041,7 +2790,6 @@ public interface DatabaseMetaData { * statements, {@code false} otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsPositionedUpdate() throws SQLException; @@ -3065,7 +2813,6 @@ public interface DatabaseMetaData { * pairing is supported otherwise {@code false}. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsResultSetConcurrency(int type, int concurrency) throws SQLException; @@ -3082,7 +2829,6 @@ public interface DatabaseMetaData { * if it isn't then {@code false}. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsResultSetHoldability(int holdability) throws SQLException; @@ -3099,7 +2845,6 @@ public interface DatabaseMetaData { * false} otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsResultSetType(int type) throws SQLException; @@ -3110,7 +2855,6 @@ public interface DatabaseMetaData { * otherwise. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsSavepoints() throws SQLException; @@ -3122,7 +2866,6 @@ public interface DatabaseMetaData { * otherwise {@code false}. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsSchemasInDataManipulation() throws SQLException; @@ -3134,7 +2877,6 @@ public interface DatabaseMetaData { * otherwise {@code false}. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsSchemasInIndexDefinitions() throws SQLException; @@ -3146,7 +2888,6 @@ public interface DatabaseMetaData { * definition, otherwise {@code false} * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsSchemasInPrivilegeDefinitions() throws SQLException; @@ -3157,7 +2898,6 @@ public interface DatabaseMetaData { * otherwise {@code false}. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsSchemasInProcedureCalls() throws SQLException; @@ -3168,7 +2908,6 @@ public interface DatabaseMetaData { * otherwise {@code false}. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsSchemasInTableDefinitions() throws SQLException; @@ -3179,7 +2918,6 @@ public interface DatabaseMetaData { * supported, otherwise {@code false}. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsSelectForUpdate() throws SQLException; @@ -3190,7 +2928,6 @@ public interface DatabaseMetaData { * otherwise {@code false}. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsStatementPooling() throws SQLException; @@ -3202,7 +2939,6 @@ public interface DatabaseMetaData { * escape syntax are supported, otherwise {@code false}. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsStoredProcedures() throws SQLException; @@ -3213,7 +2949,6 @@ public interface DatabaseMetaData { * expressions. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsSubqueriesInComparisons() throws SQLException; @@ -3224,7 +2959,6 @@ public interface DatabaseMetaData { * expressions, otherwise {@code false}. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsSubqueriesInExists() throws SQLException; @@ -3235,7 +2969,6 @@ public interface DatabaseMetaData { * otherwise {@code false}. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsSubqueriesInIns() throws SQLException; @@ -3245,7 +2978,6 @@ public interface DatabaseMetaData { * @return {@code true} if subqueries are supported, otherwise {@code false}. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsSubqueriesInQuantifieds() throws SQLException; @@ -3256,7 +2988,6 @@ public interface DatabaseMetaData { * {@code false}. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsTableCorrelationNames() throws SQLException; @@ -3273,7 +3004,6 @@ public interface DatabaseMetaData { * otherwise {@code false}. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsTransactionIsolationLevel(int level) throws SQLException; @@ -3284,13 +3014,11 @@ public interface DatabaseMetaData { * If transactions are not supported, then the {@code commit} method does * nothing and the transaction isolation level is always {@code * TRANSACTION_NONE}. - *

          - * + * * @return {@code true} if transactions are supported, otherwise {@code * false}. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsTransactions() throws SQLException; @@ -3301,7 +3029,6 @@ public interface DatabaseMetaData { * {@code false}. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsUnion() throws SQLException; @@ -3312,7 +3039,6 @@ public interface DatabaseMetaData { * otherwise {@code false}. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean supportsUnionAll() throws SQLException; @@ -3328,7 +3054,6 @@ public interface DatabaseMetaData { * false}. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean updatesAreDetected(int type) throws SQLException; @@ -3339,7 +3064,6 @@ public interface DatabaseMetaData { * otherwise {@code false}. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean usesLocalFilePerTable() throws SQLException; @@ -3350,7 +3074,6 @@ public interface DatabaseMetaData { * otherwise {@code false}. * @throws SQLException * a database error occurred. - * @since Android 1.0 */ public boolean usesLocalFiles() throws SQLException; } diff --git a/libcore/sql/src/main/java/java/sql/Date.java b/libcore/sql/src/main/java/java/sql/Date.java index e506a4398..90b5246f5 100644 --- a/libcore/sql/src/main/java/java/sql/Date.java +++ b/libcore/sql/src/main/java/java/sql/Date.java @@ -17,25 +17,20 @@ package java.sql; -import java.text.SimpleDateFormat; - /** * A class which can consume and produce dates in SQL {@code Date} format. *

          * Dates are represented in SQL as {@code yyyy-mm-dd}. Note that this date * format only deals with year, month and day values. There are no values for * hours, minutes, seconds. - *

          - * This is unlike the familiar {@code java.util.Date} object, which also includes + *

          + * This is unlike the familiar {@code java.util.Date} object, which also includes * values for hours, minutes, seconds, and milliseconds. *

          * Time points are handled as millisecond values - milliseconds since the Epoch, * January 1st 1970, 00:00:00.000 GMT. Time values passed to the {@code * java.sql.Date} class are "normalized" to the time 00:00:00.000 GMT on the * date implied by the time value. - *

          - * - * @since Android 1.0 */ public class Date extends java.util.Date { @@ -44,8 +39,8 @@ public class Date extends java.util.Date { /** * Constructs a {@code Date} object corresponding to the supplied year, * month and day. - * - * @deprecated Please use the constructor {@link #Date(long)}. + * + * @deprecated Use the constructor {@link #Date(long)}. * @param theYear * the year, specified as the year minus 1900. Must be in the * range {@code [0,8099]}. @@ -54,9 +49,7 @@ public class Date extends java.util.Date { * the range {@code [0,11]}. * @param theDay * the day in the month. Must be in the range {@code [1,31]}. - * @since Android 1.0 */ - @SuppressWarnings("deprecation") @Deprecated public Date(int theYear, int theMonth, int theDay) { super(theYear, theMonth, theDay); @@ -72,7 +65,6 @@ public class Date extends java.util.Date { * milliseconds) stored in the {@code Date} object is adjusted to * correspond to 00:00:00 GMT on the day determined by the supplied * time value. - * @since Android 1.0 */ public Date(long theDate) { super(normalizeTime(theDate)); @@ -84,9 +76,7 @@ public class Date extends java.util.Date { * @return does not return anything. * @throws IllegalArgumentException * if this method is called. - * @since Android 1.0 */ - @SuppressWarnings("deprecation") @Deprecated @Override public int getHours() { @@ -99,9 +89,7 @@ public class Date extends java.util.Date { * @return does not return anything. * @throws IllegalArgumentException * if this method is called. - * @since Android 1.0 */ - @SuppressWarnings("deprecation") @Deprecated @Override public int getMinutes() { @@ -114,9 +102,7 @@ public class Date extends java.util.Date { * @return does not return anything. * @throws IllegalArgumentException * if this method is called. - * @since Android 1.0 */ - @SuppressWarnings("deprecation") @Deprecated @Override public int getSeconds() { @@ -130,9 +116,7 @@ public class Date extends java.util.Date { * the number of hours to set. * @throws IllegalArgumentException * if this method is called. - * @since Android 1.0 */ - @SuppressWarnings("deprecation") @Deprecated @Override public void setHours(int theHours) { @@ -146,9 +130,7 @@ public class Date extends java.util.Date { * the number of minutes to set. * @throws IllegalArgumentException * if this method is called. - * @since Android 1.0 */ - @SuppressWarnings("deprecation") @Deprecated @Override public void setMinutes(int theMinutes) { @@ -162,9 +144,7 @@ public class Date extends java.util.Date { * the number of seconds to set. * @throws IllegalArgumentException * if this method is called. - * @since Android 1.0 */ - @SuppressWarnings("deprecation") @Deprecated @Override public void setSeconds(int theSeconds) { @@ -177,7 +157,6 @@ public class Date extends java.util.Date { * * @param theTime * the time in milliseconds since the Epoch. - * @since Android 1.0 */ @Override public void setTime(long theTime) { @@ -193,12 +172,31 @@ public class Date extends java.util.Date { * * @return a string representation of the date in SQL format - {@code * "yyyy-mm-dd"}. - * @since Android 1.0 */ @Override public String toString() { - SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); //$NON-NLS-1$ - return dateFormat.format(this); + StringBuilder sb = new StringBuilder(10); + + format((getYear() + 1900), 4, sb); + sb.append('-'); + format((getMonth() + 1), 2, sb); + sb.append('-'); + format(getDate(), 2, sb); + + return sb.toString(); + } + + private static final String PADDING = "0000"; //$NON-NLS-1$ + + /* + * Private method to format the time + */ + private void format(int date, int digits, StringBuilder sb) { + String str = String.valueOf(date); + if (digits - str.length() > 0) { + sb.append(PADDING.substring(0, digits - str.length())); + } + sb.append(str); } /** @@ -212,7 +210,6 @@ public class Date extends java.util.Date { * @throws IllegalArgumentException * if the format of the supplied string does not match the SQL * format. - * @since Android 1.0 */ public static Date valueOf(String dateString) { if (dateString == null) { diff --git a/libcore/sql/src/main/java/java/sql/Driver.java b/libcore/sql/src/main/java/java/sql/Driver.java index c0499cb12..207aec5a2 100644 --- a/libcore/sql/src/main/java/java/sql/Driver.java +++ b/libcore/sql/src/main/java/java/sql/Driver.java @@ -27,15 +27,12 @@ import java.util.Properties; * {@code xxxx:yyyy}" is referred to as the subprotocol and is normally * the same for all of a particular driver. " {@code SpecificData}" is a string * which identifies the particular data source that the driver should use. - *

          *

          * A driver needs to be registered with a {@link DriverManager}. It is * registered and instantiated by calling {@code Class.forName("DriverURL")} * with the URL string as argument. - *

          + * * @see DriverManager - * - * @since Android 1.0 */ public interface Driver { @@ -51,7 +48,6 @@ public interface Driver { * the subprotocol specified by the driver. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public boolean acceptsURL(String url) throws SQLException; @@ -70,7 +66,6 @@ public interface Driver { * @return the connection to the database. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public Connection connect(String url, Properties info) throws SQLException; @@ -78,7 +73,6 @@ public interface Driver { * Gets the driver's major version number. * * @return the major version number of the driver - typically starts at 1. - * @since Android 1.0 */ public int getMajorVersion(); @@ -86,7 +80,6 @@ public interface Driver { * Gets the driver's minor version number. * * @return the minor version number of the driver - typically starts at 0. - * @since Android 1.0 */ public int getMinorVersion(); @@ -97,8 +90,7 @@ public interface Driver { * the client of the driver must supply in order to establish a connection * to a database. Note that the returned array of properties may change * depending on the supplied list of property values. - *

          - * + * * @param url * the URL of the database. An application may call this method * iteratively as the property list is built up - for example, @@ -113,7 +105,6 @@ public interface Driver { * connect to the database. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException; @@ -125,11 +116,9 @@ public interface Driver { *

          * A driver may not be fully compliant if the underlying database has * limited functionality. - *

          - * + * * @return {@code true} if the driver is fully JDBC compliant, {@code false} * otherwise. - * @since Android 1.0 */ public boolean jdbcCompliant(); diff --git a/libcore/sql/src/main/java/java/sql/DriverManager.java b/libcore/sql/src/main/java/java/sql/DriverManager.java index afcf2f5b8..1c41a4690 100644 --- a/libcore/sql/src/main/java/java/sql/DriverManager.java +++ b/libcore/sql/src/main/java/java/sql/DriverManager.java @@ -17,14 +17,16 @@ package java.sql; +import java.util.ArrayList; +import java.util.List; import java.util.Properties; import java.util.Enumeration; import java.util.Iterator; -import java.util.Set; import java.io.PrintStream; import java.io.PrintWriter; -import java.util.HashSet; import java.util.Vector; +import java.security.AccessController; +import org.apache.harmony.luni.util.PriviAction; import org.apache.harmony.sql.internal.nls.Messages; // BEGIN android-changed import dalvik.system.VMStack; @@ -36,9 +38,6 @@ import dalvik.system.VMStack; * The {@code DriverManager} class loads JDBC drivers during its initialization, * from the list of drivers referenced by the system property {@code * "jdbc.drivers"}. - *

          - * - * @since Android 1.0 */ public class DriverManager { @@ -57,10 +56,11 @@ public class DriverManager { * Set to hold Registered Drivers - initial capacity 10 drivers (will expand * automatically if necessary. */ - private static final Set theDriverSet = new HashSet(10); + private static final List theDrivers = new ArrayList(10); // Permission for setting log - private static final SQLPermission logPermission = new SQLPermission("setLog"); //$NON-NLS-1$ + private static final SQLPermission logPermission = new SQLPermission( + "setLog"); //$NON-NLS-1$ /* * Load drivers on initialization @@ -74,7 +74,9 @@ public class DriverManager { * it is defined. */ private static void loadInitialDrivers() { - String theDriverList = System.getProperty("jdbc.drivers", null); //$NON-NLS-1$ + String theDriverList = AccessController + .doPrivileged(new PriviAction("jdbc.drivers", null)); //$NON-NLS-1$ + if (theDriverList == null) { return; } @@ -112,14 +114,12 @@ public class DriverManager { *

          * If the removal succeeds, the {@code DriverManager} will not use this * driver in the future when asked to get a {@code Connection}. - *

          - * + * * @param driver * the JDBC driver to remove. * @throws SQLException * if there is a problem interfering with accessing the * database. - * @since Android 1.0 */ public static void deregisterDriver(Driver driver) throws SQLException { if (driver == null) { @@ -130,11 +130,12 @@ public class DriverManager { // END android-changed if (!DriverManager.isClassFromClassLoader(driver, callerClassLoader)) { - // sql.1=DriverManager: calling class not authorized to deregister JDBC driver + // sql.1=DriverManager: calling class not authorized to deregister + // JDBC driver throw new SecurityException(Messages.getString("sql.1")); //$NON-NLS-1$ } // end if - synchronized (theDriverSet) { - theDriverSet.remove(driver); + synchronized (theDrivers) { + theDrivers.remove(driver); } } @@ -148,7 +149,6 @@ public class DriverManager { * @throws SQLException * if there is an error while attempting to connect to the * database identified by the URL. - * @since Android 1.0 */ public static Connection getConnection(String url) throws SQLException { return getConnection(url, new Properties()); @@ -171,7 +171,6 @@ public class DriverManager { * @throws SQLException * if there is an error while attempting to connect to the * database identified by the URL. - * @since Android 1.0 */ public static Connection getConnection(String url, Properties info) throws SQLException { @@ -182,13 +181,13 @@ public class DriverManager { // sql.5=The url cannot be null throw new SQLException(Messages.getString("sql.5"), sqlState); //$NON-NLS-1$ } - synchronized (theDriverSet) { + synchronized (theDrivers) { /* * Loop over the drivers in the DriverSet checking to see if one can * open a connection to the supplied URL - return the first * connection which is returned */ - for (Driver theDriver : theDriverSet) { + for (Driver theDriver : theDrivers) { Connection theConnection = theDriver.connect(url, info); if (theConnection != null) { return theConnection; @@ -214,15 +213,14 @@ public class DriverManager { * @throws SQLException * if there is an error while attempting to connect to the * database identified by the URL. - * @since Android 1.0 */ public static Connection getConnection(String url, String user, String password) throws SQLException { Properties theProperties = new Properties(); - if(null != user){ + if (null != user) { theProperties.setProperty("user", user); //$NON-NLS-1$ } - if(null != password){ + if (null != password) { theProperties.setProperty("password", password); //$NON-NLS-1$ } return getConnection(url, theProperties); @@ -243,13 +241,13 @@ public class DriverManager { ClassLoader callerClassLoader = VMStack.getCallingClassLoader(); // END android-changed - synchronized (theDriverSet) { + synchronized (theDrivers) { /* * Loop over the drivers in the DriverSet checking to see if one * does understand the supplied URL - return the first driver which * does understand the URL */ - Iterator theIterator = theDriverSet.iterator(); + Iterator theIterator = theDrivers.iterator(); while (theIterator.hasNext()) { Driver theDriver = theIterator.next(); if (theDriver.acceptsURL(url) @@ -261,8 +259,8 @@ public class DriverManager { } // If no drivers understand the URL, throw an SQLException // sql.6=No suitable driver - //SQLState: 08 - connection exception - //001 - SQL-client unable to establish SQL-connection + // SQLState: 08 - connection exception + // 001 - SQL-client unable to establish SQL-connection throw new SQLException(Messages.getString("sql.6"), "08001"); //$NON-NLS-1$ //$NON-NLS-2$ } @@ -272,7 +270,6 @@ public class DriverManager { * * @return An {@code Enumeration} containing all the currently loaded JDBC * {@code Drivers}. - * @since Android 1.0 */ public static Enumeration getDrivers() { // BEGIN android-changed @@ -282,13 +279,13 @@ public class DriverManager { * Synchronize to avoid clashes with additions and removals of drivers * in the DriverSet */ - synchronized (theDriverSet) { + synchronized (theDrivers) { /* * Create the Enumeration by building a Vector from the elements of * the DriverSet */ Vector theVector = new Vector(); - Iterator theIterator = theDriverSet.iterator(); + Iterator theIterator = theDrivers.iterator(); while (theIterator.hasNext()) { Driver theDriver = theIterator.next(); if (DriverManager.isClassFromClassLoader(theDriver, @@ -304,7 +301,6 @@ public class DriverManager { * Returns the login timeout when connecting to a database in seconds. * * @return the login timeout in seconds. - * @since Android 1.0 */ public static int getLoginTimeout() { return loginTimeout; @@ -313,10 +309,9 @@ public class DriverManager { /** * Gets the log {@code PrintStream} used by the {@code DriverManager} and * all the JDBC Drivers. - * + * * @deprecated use {@link #getLogWriter()} instead. * @return the {@code PrintStream} used for logging activities. - * @since Android 1.0 */ @Deprecated public static PrintStream getLogStream() { @@ -328,7 +323,6 @@ public class DriverManager { * * @return A {@code PrintWriter} object used as the log writer. {@code null} * if no log writer is set. - * @since Android 1.0 */ public static PrintWriter getLogWriter() { return thePrintWriter; @@ -340,7 +334,6 @@ public class DriverManager { * * @param message * the message to print to the JDBC log stream. - * @since Android 1.0 */ public static void println(String message) { if (thePrintWriter != null) { @@ -362,8 +355,7 @@ public class DriverManager { *

          * A newly loaded JDBC driver class should register itself with the * {@code DriverManager} by calling this method. - *

          - * + * * @param driver * the {@code Driver} to register with the {@code DriverManager}. * @throws SQLException @@ -373,8 +365,8 @@ public class DriverManager { if (driver == null) { throw new NullPointerException(); } - synchronized (theDriverSet) { - theDriverSet.add(driver); + synchronized (theDrivers) { + theDrivers.add(driver); } } @@ -383,7 +375,6 @@ public class DriverManager { * * @param seconds * seconds until timeout. 0 indicates wait forever. - * @since Android 1.0 */ public static void setLoginTimeout(int seconds) { loginTimeout = seconds; @@ -393,11 +384,10 @@ public class DriverManager { /** * Sets the print stream to use for logging data from the {@code * DriverManager} and the JDBC drivers. - * + * * @deprecated Use {@link #setLogWriter} instead. * @param out * the {@code PrintStream} to use for logging. - * @since Android 1.0 */ @Deprecated public static void setLogStream(PrintStream out) { @@ -411,7 +401,6 @@ public class DriverManager { * * @param out * the {@code PrintWriter} to be used. - * @since Android 1.0 */ public static void setLogWriter(PrintWriter out) { checkLogSecurity(); @@ -442,13 +431,13 @@ public class DriverManager { */ private static boolean isClassFromClassLoader(Object theObject, ClassLoader theClassLoader) { - + if ((theObject == null) || (theClassLoader == null)) { return false; } - + Class objectClass = theObject.getClass(); - + try { Class checkClass = Class.forName(objectClass.getName(), true, theClassLoader); diff --git a/libcore/sql/src/main/java/java/sql/DriverPropertyInfo.java b/libcore/sql/src/main/java/java/sql/DriverPropertyInfo.java index 3875abb13..aa1858579 100644 --- a/libcore/sql/src/main/java/java/sql/DriverPropertyInfo.java +++ b/libcore/sql/src/main/java/java/sql/DriverPropertyInfo.java @@ -22,38 +22,28 @@ package java.sql; * This class is returned by the * {@link Driver#getPropertyInfo(String, java.util.Properties)} method and * allows for the advanced connection handling. - * - * @since Android 1.0 */ public class DriverPropertyInfo { /** * If the value member can be chosen from a set of possible values, they are * contained here. Otherwise choices is {@code null}. - * - * @since Android 1.0 */ public String[] choices; /** * A description of the property. May be {@code null}. - * - * @since Android 1.0 */ public String description; /** * The name of the property. - * - * @since Android 1.0 */ public String name; /** - * {@code True} when the value member must be provided during {@code - * Driver.connect}. {@code False} otherwise. - * - * @since Android 1.0 + * {@code true} when the value member must be provided during {@code + * Driver.connect}. {@code false} otherwise. */ public boolean required; @@ -61,8 +51,6 @@ public class DriverPropertyInfo { * The current value associated with this property. It is depending on the * data gathered by the {@code getPropertyInfo} method, the general Java * environment and the driver's default values. - * - * @since Android 1.0 */ public String value; @@ -74,7 +62,6 @@ public class DriverPropertyInfo { * The property name. * @param value * The property value. - * @since Android 1.0 */ public DriverPropertyInfo(String name, String value) { this.name = name; diff --git a/libcore/sql/src/main/java/java/sql/ParameterMetaData.java b/libcore/sql/src/main/java/java/sql/ParameterMetaData.java index 94901aebc..1e241c61e 100644 --- a/libcore/sql/src/main/java/java/sql/ParameterMetaData.java +++ b/libcore/sql/src/main/java/java/sql/ParameterMetaData.java @@ -20,58 +20,42 @@ package java.sql; /** * An interface used to get information about the types and properties of * parameters in a {@code PreparedStatement}. - * - * @since Android 1.0 */ public interface ParameterMetaData { /** * Indicates that the parameter mode is {@code IN}. - * - * @since Android 1.0 */ public static final int parameterModeIn = 1; /** * Indicates that the parameter mode is {@code INOUT}. - * - * @since Android 1.0 */ public static final int parameterModeInOut = 2; /** * Indicates that the parameter mode is {@code OUT}. - * - * @since Android 1.0 */ public static final int parameterModeOut = 4; /** * Indicates that the parameter mode is not known. - * - * @since Android 1.0 */ public static final int parameterModeUnknown = 0; /** * Indicates that a parameter is not permitted to be {@code NULL}. - * - * @since Android 1.0 */ public static final int parameterNoNulls = 0; /** * Indicates that a parameter is permitted to be {@code NULL}. - * - * @since Android 1.0 */ public static final int parameterNullable = 1; /** * Indicates that whether a parameter is allowed to be {@code null} or not * is not known. - * - * @since Android 1.0 */ public static final int parameterNullableUnknown = 2; @@ -87,7 +71,6 @@ public interface ParameterMetaData { * between SQL types and Java objects. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public String getParameterClassName(int paramIndex) throws SQLException; @@ -98,7 +81,6 @@ public interface ParameterMetaData { * @return the number of parameters. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public int getParameterCount() throws SQLException; @@ -117,7 +99,6 @@ public interface ParameterMetaData { * @return the parameter's mode. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public int getParameterMode(int paramIndex) throws SQLException; @@ -131,7 +112,6 @@ public interface ParameterMetaData { * java.sql.Types}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public int getParameterType(int paramIndex) throws SQLException; @@ -146,7 +126,6 @@ public interface ParameterMetaData { * Defined Type (UDT). * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public String getParameterTypeName(int paramIndex) throws SQLException; @@ -160,7 +139,6 @@ public interface ParameterMetaData { * {@code 0} if the parameter is not a numeric type. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public int getPrecision(int paramIndex) throws SQLException; @@ -175,7 +153,6 @@ public interface ParameterMetaData { * the parameter. {@code 0} if the parameter is not a numeric type. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public int getScale(int paramIndex) throws SQLException; @@ -194,7 +171,6 @@ public interface ParameterMetaData { * @return the int code indicating the nullability of the parameter. * @throws SQLException * if a database error is encountered. - * @since Android 1.0 */ public int isNullable(int paramIndex) throws SQLException; @@ -208,7 +184,6 @@ public interface ParameterMetaData { * {@code false} otherwise. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public boolean isSigned(int paramIndex) throws SQLException; } diff --git a/libcore/sql/src/main/java/java/sql/PreparedStatement.java b/libcore/sql/src/main/java/java/sql/PreparedStatement.java index ab8187132..1cd966807 100644 --- a/libcore/sql/src/main/java/java/sql/PreparedStatement.java +++ b/libcore/sql/src/main/java/java/sql/PreparedStatement.java @@ -28,14 +28,10 @@ import java.math.BigDecimal; *

          * An SQL Statement is put into a {@code PreparedStatement} and is precompiled * so that it can be executed efficiently multiple times. - *

          *

          * Setter methods are supplied in the {@code PreparedStatement} interface for * the setting of {@code IN} parameters for the statement. The setter method * used for each {@code IN} parameter must match the parameter's type. - *

          - * - * @since Android 1.0 */ public interface PreparedStatement extends Statement { @@ -44,7 +40,6 @@ public interface PreparedStatement extends Statement { * * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void addBatch() throws SQLException; @@ -55,11 +50,9 @@ public interface PreparedStatement extends Statement { * {@code Statement}. Setting a parameter value replaces the previous value. This * method clears the values for all parameters, releasing all resources used * by those parameters. - *

          - * + * * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void clearParameters() throws SQLException; @@ -72,14 +65,12 @@ public interface PreparedStatement extends Statement { * {@code getResultSet} or {@code getUpdateCount} are used to retrieve * the first result, and the second and subsequent results are * retrieved with {@code getMoreResults}. - *

          - * + * * @return {@code true} if the result of the execution is a {@code * ResultSet}, {@code false} if there is no result or if the result * is an update count. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public boolean execute() throws SQLException; @@ -91,7 +82,6 @@ public interface PreparedStatement extends Statement { * @throws SQLException * if a database error happens or if the SQL statement does not * produce a {@code ResultSet}. - * @since Android 1.0 */ public ResultSet executeQuery() throws SQLException; @@ -105,7 +95,6 @@ public interface PreparedStatement extends Statement { * @throws SQLException * if a database error happens or if the SQL statement returns a * {@code ResultSet}. - * @since Android 1.0 */ public int executeUpdate() throws SQLException; @@ -117,14 +106,12 @@ public interface PreparedStatement extends Statement { * executing the {@code PreparedStatement}, because the {@code * PreparedStatement} is precompiled. As a result the metadata can be * queried ahead of time without actually executing the statement. - *

          - * + * * @return a {@code ResultSetMetaData} object with the information about the * columns of the {@code ResultSet}, if the driver can return a * {@code ResultSetMetaData}. {@code null} otherwise. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public ResultSetMetaData getMetaData() throws SQLException; @@ -136,7 +123,6 @@ public interface PreparedStatement extends Statement { * PreparedStatement}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public ParameterMetaData getParameterMetaData() throws SQLException; @@ -152,7 +138,6 @@ public interface PreparedStatement extends Statement { * @throws SQLException * if a database error happens. * @see Array - * @since Android 1.0 */ public void setArray(int parameterIndex, Array theArray) throws SQLException; @@ -161,12 +146,11 @@ public interface PreparedStatement extends Statement { * Sets the value of a specified parameter to the content of a supplied * {@code InputStream}, which has a specified number of bytes. *

          - * This is a good method for setting an SQL {@code LONVARCHAR} parameter + * This is a good method for setting an SQL {@code LONGVARCHAR} parameter * where the length of the data is large. Data is read from the {@code * InputStream} until end-of-file is reached or the specified number of * bytes is copied. - *

          - * + * * @param parameterIndex * the parameter number index, where the first parameter has * index 1. @@ -178,7 +162,6 @@ public interface PreparedStatement extends Statement { * parameter. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void setAsciiStream(int parameterIndex, InputStream theInputStream, int length) throws SQLException; @@ -196,7 +179,6 @@ public interface PreparedStatement extends Statement { * @throws SQLException * if a database error happens. * @see java.math.BigDecimal - * @since Android 1.0 */ public void setBigDecimal(int parameterIndex, BigDecimal theBigDecimal) throws SQLException; @@ -207,8 +189,7 @@ public interface PreparedStatement extends Statement { *

          * Use this method when a large amount of data needs to be set into a * {@code LONGVARBINARY} parameter. - *

          - * + * * @param parameterIndex * the parameter number index, where the first parameter has * index 1. @@ -220,7 +201,6 @@ public interface PreparedStatement extends Statement { * parameter. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void setBinaryStream(int parameterIndex, InputStream theInputStream, int length) throws SQLException; @@ -236,7 +216,6 @@ public interface PreparedStatement extends Statement { * parameterIndex} is set. * @throws SQLException * if a database error happens. - * @since Android 1.0 * @see Blob */ public void setBlob(int parameterIndex, Blob theBlob) throws SQLException; @@ -253,7 +232,6 @@ public interface PreparedStatement extends Statement { * parameterIndex} is set. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void setBoolean(int parameterIndex, boolean theBoolean) throws SQLException; @@ -269,7 +247,6 @@ public interface PreparedStatement extends Statement { * parameterIndex} is set. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void setByte(int parameterIndex, byte theByte) throws SQLException; @@ -286,7 +263,6 @@ public interface PreparedStatement extends Statement { * parameterIndex} is set. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void setBytes(int parameterIndex, byte[] theBytes) throws SQLException; @@ -298,8 +274,7 @@ public interface PreparedStatement extends Statement { * Data is read from the {@code * Reader} until end-of-file is reached or the specified number of * characters are copied. - *

          - * + * * @param parameterIndex * the parameter number index, where the first parameter has * index 1 @@ -309,7 +284,6 @@ public interface PreparedStatement extends Statement { * the number of characters to be read. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void setCharacterStream(int parameterIndex, Reader reader, int length) throws SQLException; @@ -325,7 +299,6 @@ public interface PreparedStatement extends Statement { * parameter at {@code parameterIndex} is set. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void setClob(int parameterIndex, Clob theClob) throws SQLException; @@ -341,7 +314,6 @@ public interface PreparedStatement extends Statement { * parameterIndex} is set. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void setDate(int parameterIndex, Date theDate) throws SQLException; @@ -366,7 +338,6 @@ public interface PreparedStatement extends Statement { * if a database error happens. * @see Date * @see java.util.Calendar - * @since Android 1.0 */ public void setDate(int parameterIndex, Date theDate, Calendar cal) throws SQLException; @@ -383,7 +354,6 @@ public interface PreparedStatement extends Statement { * parameterIndex} is set. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void setDouble(int parameterIndex, double theDouble) throws SQLException; @@ -399,7 +369,6 @@ public interface PreparedStatement extends Statement { * the {@code float} value to update the parameter. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void setFloat(int parameterIndex, float theFloat) throws SQLException; @@ -415,7 +384,6 @@ public interface PreparedStatement extends Statement { * parameterIndex} is set. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void setInt(int parameterIndex, int theInt) throws SQLException; @@ -430,7 +398,6 @@ public interface PreparedStatement extends Statement { * parameterIndex} is set. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void setLong(int parameterIndex, long theLong) throws SQLException; @@ -447,7 +414,6 @@ public interface PreparedStatement extends Statement { * java.sql.Types}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void setNull(int parameterIndex, int sqlType) throws SQLException; @@ -461,8 +427,7 @@ public interface PreparedStatement extends Statement { * SQL type name when supplying a {@code NULL} UDT or REF. For a UDT, the * type name is the type name of the parameter itself, but for a REF * parameter the type name is the type name of the referenced type. - *

          - * + * * @param paramIndex * the parameter number index, where the first parameter has * index 1. @@ -475,7 +440,6 @@ public interface PreparedStatement extends Statement { * @throws SQLException * if a database error happens. * @see Types - * @since Android 1.0 */ public void setNull(int paramIndex, int sqlType, String typeName) throws SQLException; @@ -493,8 +457,7 @@ public interface PreparedStatement extends Statement { * object's class implements {@code Ref}, {@code Blob}, {@code Clob}, * {@code Struct}, or {@code Array}, the driver passes it to the database as * a value of the corresponding SQL type. - *

          - * + * * @param parameterIndex * the parameter number index, where the first parameter has * index 1. @@ -503,7 +466,6 @@ public interface PreparedStatement extends Statement { * {@code parameterIndex} is set. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void setObject(int parameterIndex, Object theObject) throws SQLException; @@ -518,8 +480,7 @@ public interface PreparedStatement extends Statement { * object's class implements {@code Ref}, {@code Blob}, {@code Clob}, * {@code Struct}, or {@code Array}, the driver will pass it to the database * in the form of the relevant SQL type. - *

          - * + * * @param parameterIndex * the parameter index, where the first parameter has index 1. * @param theObject @@ -530,7 +491,6 @@ public interface PreparedStatement extends Statement { * java.sql.Types}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void setObject(int parameterIndex, Object theObject, int targetSqlType) throws SQLException; @@ -545,8 +505,7 @@ public interface PreparedStatement extends Statement { * object's class implements {@code Ref}, {@code Blob}, {@code Clob}, * {@code Struct}, or {@code Array}, the driver will pass it to the database * in the form of the relevant SQL type. - *

          - * + * * @param parameterIndex * the parameter index, where the first parameter has index 1. * @param theObject @@ -561,7 +520,6 @@ public interface PreparedStatement extends Statement { * java.sql.Types.NUMERIC} - ignored for all other types. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void setObject(int parameterIndex, Object theObject, int targetSqlType, int scale) throws SQLException; @@ -579,7 +537,6 @@ public interface PreparedStatement extends Statement { * @throws SQLException * if a database error happens. * @see Ref - * @since Android 1.0 */ public void setRef(int parameterIndex, Ref theRef) throws SQLException; @@ -595,7 +552,6 @@ public interface PreparedStatement extends Statement { * parameterIndex} is set. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void setShort(int parameterIndex, short theShort) throws SQLException; @@ -611,7 +567,6 @@ public interface PreparedStatement extends Statement { * set. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void setString(int parameterIndex, String theString) throws SQLException; @@ -628,7 +583,6 @@ public interface PreparedStatement extends Statement { * {@code parameterIndex} is set. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void setTime(int parameterIndex, Time theTime) throws SQLException; @@ -639,8 +593,7 @@ public interface PreparedStatement extends Statement { * The driver uses the supplied {@code Calendar} to create the SQL {@code * TIME} value, which allows it to use a custom timezone - otherwise the * driver uses the default timezone of the Java virtual machine. - *

          - * + * * @param parameterIndex * the parameter number index, where the first parameter has * index 1. @@ -654,7 +607,6 @@ public interface PreparedStatement extends Statement { * if a database error happens. * @see Time * @see java.util.Calendar - * @since Android 1.0 */ public void setTime(int parameterIndex, Time theTime, Calendar cal) throws SQLException; @@ -671,7 +623,6 @@ public interface PreparedStatement extends Statement { * parameterIndex} is set. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void setTimestamp(int parameterIndex, Timestamp theTimestamp) throws SQLException; @@ -683,8 +634,7 @@ public interface PreparedStatement extends Statement { * The driver uses the supplied {@code Calendar} to create the SQL {@code * TIMESTAMP} value, which allows it to use a custom timezone - otherwise * the driver uses the default timezone of the Java virtual machine. - *

          - * + * * @param parameterIndex * the parameter number index, where the first parameter has * index 1. @@ -698,7 +648,6 @@ public interface PreparedStatement extends Statement { * if a database error happens. * @see Timestamp * @see java.util.Calendar - * @since Android 1.0 */ public void setTimestamp(int parameterIndex, Timestamp theTimestamp, Calendar cal) throws SQLException; @@ -736,7 +685,6 @@ public interface PreparedStatement extends Statement { * @throws SQLException * if a database error happens. * @see URL - * @since Android 1.0 */ public void setURL(int parameterIndex, URL theURL) throws SQLException; } diff --git a/libcore/sql/src/main/java/java/sql/Ref.java b/libcore/sql/src/main/java/java/sql/Ref.java index 2ceac8b1d..cbc5454ee 100644 --- a/libcore/sql/src/main/java/java/sql/Ref.java +++ b/libcore/sql/src/main/java/java/sql/Ref.java @@ -29,11 +29,9 @@ import java.util.Map; * the database supports the {@code Ref} type, it is not typically * necessary to get the underlying object before using it in a method call - * the {@code Ref} object can be used in place of the data structure. - *

          + *

          * A {@code Ref} object is stored into the database using the * {@link PreparedStatement#setRef(int, Ref)} method. - * - * @since Android 1.0 */ public interface Ref { @@ -44,7 +42,6 @@ public interface Ref { * @return the fully qualified name of the SQL structured type. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public String getBaseTypeName() throws SQLException; @@ -55,7 +52,6 @@ public interface Ref { * structured type. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public Object getObject() throws SQLException; @@ -69,7 +65,6 @@ public interface Ref { * structured type. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public Object getObject(Map> map) throws SQLException; @@ -82,7 +77,6 @@ public interface Ref { * that this {@code Ref} references. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public void setObject(Object value) throws SQLException; } diff --git a/libcore/sql/src/main/java/java/sql/ResultSet.java b/libcore/sql/src/main/java/java/sql/ResultSet.java index f33f9de51..8ecbe6c5c 100644 --- a/libcore/sql/src/main/java/java/sql/ResultSet.java +++ b/libcore/sql/src/main/java/java/sql/ResultSet.java @@ -34,7 +34,6 @@ import java.net.URL; * use the {@code next} method. The {@code next} method returns {@code true} as * long as there are more rows in the {@code ResultSet}, otherwise it returns * {@code false}. - *

          *

          * The default type of {@code ResultSet} can not be updated and its cursor can * only advance forward through the rows of data. This means that it is only @@ -42,7 +41,6 @@ import java.net.URL; * are implemented: an updatable type and also types where the cursor can * be scrolled forward and backward through the rows of data. How such a * {@code ResultSet} is created is demonstrated in the following example: - *

          *
            *
            * {@code Connection con;}
            @@ -63,13 +61,11 @@ import java.net.URL; * is better to use column indexes. Ideally the columns should be read * left-to-right and read once only, since not all databases are optimized to * handle other techniques of reading the data. - *

            *

            * When reading data via the appropriate getter methods, the JDBC driver maps * the SQL data retrieved from the database to the Java type implied by the * method invoked by the application. The JDBC specification has a table for the * mappings from SQL types to Java types. - *

            *

            * There are also methods for writing data into the {@code ResultSet}, such as * {@code updateInt} and {@code updateString}. The update methods can be used @@ -80,94 +76,70 @@ import java.net.URL; * method. For insertion of new rows, the cursor is first moved to a special row * called the Insert Row, data is added using the update methods, * followed by calling the {@code ResultSet.insertRow} method. - *

            *

            * A {@code ResultSet} is closed if the statement which generated it closes, the * statement is executed again, or the same statement's next {@code ResultSet} * is retrieved (if the statement returned of multiple results). - *

            - * - * @since Android 1.0 */ public interface ResultSet { /** * A constant used to indicate that a {@code ResultSet} object must be * closed when the method {@code Connection.commit} is invoked. - * - * @since Android 1.0 */ public static final int CLOSE_CURSORS_AT_COMMIT = 2; /** * A constant used to indicate that a {@code ResultSet} object must not be * closed when the method {@code Connection.commit} is invoked. - * - * @since Android 1.0 */ public static final int HOLD_CURSORS_OVER_COMMIT = 1; /** * A constant used to indicate the concurrency mode for a {@code ResultSet} * object that cannot be updated. - * - * @since Android 1.0 */ public static final int CONCUR_READ_ONLY = 1007; /** * A constant used to indicate the concurrency mode for a {@code ResultSet} * object that can be updated. - * - * @since Android 1.0 */ public static final int CONCUR_UPDATABLE = 1008; /** * A constant used to indicate processing of the rows of a {@code ResultSet} * in the forward direction, first to last. - * - * @since Android 1.0 */ public static final int FETCH_FORWARD = 1000; /** * A constant used to indicate processing of the rows of a {@code ResultSet} * in the reverse direction, last to first. - * - * @since Android 1.0 */ public static final int FETCH_REVERSE = 1001; /** * A constant used to indicate that the order of processing of the rows of a * {@code ResultSet} is unknown. - * - * @since Android 1.0 */ public static final int FETCH_UNKNOWN = 1002; /** * A constant used to indicate a {@code ResultSet} object whose cursor can * only move forward. - * - * @since Android 1.0 */ public static final int TYPE_FORWARD_ONLY = 1003; /** * A constant used to indicate a {@code ResultSet} object which is * scrollable but is insensitive to changes made by others. - * - * @since Android 1.0 */ public static final int TYPE_SCROLL_INSENSITIVE = 1004; /** * A constant used to indicate a {@code ResultSet} object which is * scrollable and sensitive to changes made by others. - * - * @since Android 1.0 */ public static final int TYPE_SCROLL_SENSITIVE = 1005; @@ -181,7 +153,6 @@ public interface ResultSet { * ResultSet}, {@code false} otherwise. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public boolean absolute(int row) throws SQLException; @@ -190,7 +161,6 @@ public interface ResultSet { * * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void afterLast() throws SQLException; @@ -200,7 +170,6 @@ public interface ResultSet { * * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void beforeFirst() throws SQLException; @@ -209,7 +178,6 @@ public interface ResultSet { * * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void cancelRowUpdates() throws SQLException; @@ -218,7 +186,6 @@ public interface ResultSet { * * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void clearWarnings() throws SQLException; @@ -232,7 +199,6 @@ public interface ResultSet { * * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void close() throws SQLException; @@ -242,7 +208,6 @@ public interface ResultSet { * * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void deleteRow() throws SQLException; @@ -256,7 +221,6 @@ public interface ResultSet { * name. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public int findColumn(String columnName) throws SQLException; @@ -267,7 +231,6 @@ public interface ResultSet { * false} if the {@code ResultSet} contains no rows. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public boolean first() throws SQLException; @@ -280,7 +243,6 @@ public interface ResultSet { * @return a {@code java.sql.Array} with the data from the column. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public Array getArray(int columnIndex) throws SQLException; @@ -293,7 +255,6 @@ public interface ResultSet { * @return a {@code java.sql.Array} with the data from the specified column. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public Array getArray(String colName) throws SQLException; @@ -306,7 +267,6 @@ public interface ResultSet { * @return an {@code InputStream} with the data from the column. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public InputStream getAsciiStream(int columnIndex) throws SQLException; @@ -319,7 +279,6 @@ public interface ResultSet { * @return an {@code InputStream} with the data from the column. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public InputStream getAsciiStream(String columnName) throws SQLException; @@ -332,7 +291,6 @@ public interface ResultSet { * @return a {@code BigDecimal} with the value of the column. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public BigDecimal getBigDecimal(int columnIndex) throws SQLException; @@ -349,7 +307,6 @@ public interface ResultSet { * @return a {@code BigDecimal} with the value of the column. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ @Deprecated public BigDecimal getBigDecimal(int columnIndex, int scale) @@ -364,7 +321,6 @@ public interface ResultSet { * @return a BigDecimal with value of the column. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public BigDecimal getBigDecimal(String columnName) throws SQLException; @@ -381,7 +337,6 @@ public interface ResultSet { * @return a BigDecimal with value of the column. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ @Deprecated public BigDecimal getBigDecimal(String columnName, int scale) @@ -395,15 +350,13 @@ public interface ResultSet { * data in the {@code InputStream} should be read before getting data from * any other column. A further call to a getter method will implicitly close * the {@code InputStream}. - *

            - * + * * @param columnIndex * the index of the column to read. * @return an {@code InputStream} with the data from the column. If the * column value is SQL {@code NULL}, {@code null} is returned. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public InputStream getBinaryStream(int columnIndex) throws SQLException; @@ -414,15 +367,13 @@ public interface ResultSet { * data in the {@code InputStream} should be read before getting data from * any other column. A further call to a getter method will implicitly close * the {@code InputStream}. - *

            - * + * * @param columnName * the name of the column to read. * @return an {@code InputStream} with the data from the column if the * column value is SQL {@code NULL}, {@code null} is returned. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public InputStream getBinaryStream(String columnName) throws SQLException; @@ -435,7 +386,6 @@ public interface ResultSet { * @return a {@code java.sql.Blob} with the value of the column. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public Blob getBlob(int columnIndex) throws SQLException; @@ -448,7 +398,6 @@ public interface ResultSet { * @return a {@code java.sql.Blob} with the value of the column. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public Blob getBlob(String columnName) throws SQLException; @@ -462,7 +411,6 @@ public interface ResultSet { * {@code NULL}, {@code false} is returned. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public boolean getBoolean(int columnIndex) throws SQLException; @@ -476,7 +424,6 @@ public interface ResultSet { * {@code NULL}, {@code false} is returned. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public boolean getBoolean(String columnName) throws SQLException; @@ -489,7 +436,6 @@ public interface ResultSet { * is SQL {@code NULL}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public byte getByte(int columnIndex) throws SQLException; @@ -502,7 +448,6 @@ public interface ResultSet { * is SQL {@code NULL}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public byte getByte(String columnName) throws SQLException; @@ -515,7 +460,6 @@ public interface ResultSet { * the column contains SQL {@code NULL}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public byte[] getBytes(int columnIndex) throws SQLException; @@ -528,7 +472,6 @@ public interface ResultSet { * the column contains SQL {@code NULL}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public byte[] getBytes(String columnName) throws SQLException; @@ -543,7 +486,6 @@ public interface ResultSet { * @throws SQLException * if a database error happens. * @see java.io.Reader - * @since Android 1.0 */ public Reader getCharacterStream(int columnIndex) throws SQLException; @@ -557,7 +499,6 @@ public interface ResultSet { * the column value is SQL {@code NULL}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public Reader getCharacterStream(String columnName) throws SQLException; @@ -571,7 +512,6 @@ public interface ResultSet { * {@code null} if the value is SQL {@code NULL}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public Clob getClob(int columnIndex) throws SQLException; @@ -585,7 +525,6 @@ public interface ResultSet { * {@code null} if the value is SQL {@code NULL}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public Clob getClob(String colName) throws SQLException; @@ -596,7 +535,6 @@ public interface ResultSet { * , {@code ResultSet.CONCUR_UPDATABLE}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public int getConcurrency() throws SQLException; @@ -606,7 +544,6 @@ public interface ResultSet { * @return the SQL cursor name. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public String getCursorName() throws SQLException; @@ -620,7 +557,6 @@ public interface ResultSet { * if the column is SQL {@code NULL}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public Date getDate(int columnIndex) throws SQLException; @@ -636,7 +572,6 @@ public interface ResultSet { * if the column is SQL {@code NULL}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public Date getDate(int columnIndex, Calendar cal) throws SQLException; @@ -650,7 +585,6 @@ public interface ResultSet { * if the column is SQL {@code NULL}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public Date getDate(String columnName) throws SQLException; @@ -666,7 +600,6 @@ public interface ResultSet { * if the column is SQL {@code NULL}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public Date getDate(String columnName, Calendar cal) throws SQLException; @@ -680,7 +613,6 @@ public interface ResultSet { * column is SQL {@code NULL}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public double getDouble(int columnIndex) throws SQLException; @@ -694,7 +626,6 @@ public interface ResultSet { * column is SQL {@code NULL}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public double getDouble(String columnName) throws SQLException; @@ -709,7 +640,6 @@ public interface ResultSet { *
          * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public int getFetchDirection() throws SQLException; @@ -719,7 +649,6 @@ public interface ResultSet { * @return the fetch size as an int * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public int getFetchSize() throws SQLException; @@ -733,7 +662,6 @@ public interface ResultSet { * column is SQL {@code NULL}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public float getFloat(int columnIndex) throws SQLException; @@ -747,7 +675,6 @@ public interface ResultSet { * column is SQL {@code NULL}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public float getFloat(String columnName) throws SQLException; @@ -761,7 +688,6 @@ public interface ResultSet { * column is SQL {@code NULL}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public int getInt(int columnIndex) throws SQLException; @@ -775,7 +701,6 @@ public interface ResultSet { * column is SQL {@code NULL}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public int getInt(String columnName) throws SQLException; @@ -789,7 +714,6 @@ public interface ResultSet { * column is SQL {@code NULL}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public long getLong(int columnIndex) throws SQLException; @@ -803,7 +727,6 @@ public interface ResultSet { * column is SQL {@code NULL}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public long getLong(String columnName) throws SQLException; @@ -815,7 +738,6 @@ public interface ResultSet { * {@code ResultSet}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public ResultSetMetaData getMetaData() throws SQLException; @@ -827,15 +749,13 @@ public interface ResultSet { * For SQL User Defined Types, if a column value is Structured or Distinct, * this method behaves the same as a call to: {@code * getObject(columnIndex,this.getStatement().getConnection().getTypeMap())} - *

          - * + * * @param columnIndex * the index of the column to read. * @return an {@code Object} containing the value of the column. {@code * null} if the column value is SQL {@code NULL}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public Object getObject(int columnIndex) throws SQLException; @@ -846,8 +766,7 @@ public interface ResultSet { * The type of the Java object will be determined by the supplied Map to * perform the mapping of SQL {@code Struct} or Distinct types into Java * objects. - *

          - * + * * @param columnIndex * the index of the column to read. * @param map @@ -857,7 +776,6 @@ public interface ResultSet { * null} if the column value is SQL {@code NULL}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public Object getObject(int columnIndex, Map> map) throws SQLException; @@ -870,15 +788,13 @@ public interface ResultSet { * For SQL User Defined Types, if a column value is structured or distinct, * this method behaves the same as a call to: {@code * getObject(columnIndex,this.getStatement().getConnection().getTypeMap())} - *

          - * + * * @param columnName * the name of the column to read. * @return an {@code Object} containing the value of the column. {@code * null} if the column value is SQL {@code NULL}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public Object getObject(String columnName) throws SQLException; @@ -888,8 +804,7 @@ public interface ResultSet { *

          * The type of the Java object will be determined by the supplied Map to * perform the mapping of SQL Struct or Distinct types into Java objects. - *

          - * + * * @param columnName * the name of the column to read. * @param map @@ -899,7 +814,6 @@ public interface ResultSet { * null} if the column value is SQL {@code NULL}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public Object getObject(String columnName, Map> map) throws SQLException; @@ -913,7 +827,6 @@ public interface ResultSet { * @return a Ref representing the value of the SQL REF in the column * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public Ref getRef(int columnIndex) throws SQLException; @@ -926,7 +839,6 @@ public interface ResultSet { * @return a Ref representing the value of the SQL {@code REF} in the column * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public Ref getRef(String colName) throws SQLException; @@ -938,7 +850,6 @@ public interface ResultSet { * there is no current row. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public int getRow() throws SQLException; @@ -951,7 +862,6 @@ public interface ResultSet { * the value is SQL {@code NULL}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public short getShort(int columnIndex) throws SQLException; @@ -964,7 +874,6 @@ public interface ResultSet { * the value is SQL {@code NULL}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public short getShort(String columnName) throws SQLException; @@ -978,7 +887,6 @@ public interface ResultSet { * null} if the {@code ResultSet} was not created by a Statement. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public Statement getStatement() throws SQLException; @@ -991,7 +899,6 @@ public interface ResultSet { * the column is SQL {@code NULL}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public String getString(int columnIndex) throws SQLException; @@ -1004,7 +911,6 @@ public interface ResultSet { * the column is SQL {@code NULL}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public String getString(String columnName) throws SQLException; @@ -1018,7 +924,6 @@ public interface ResultSet { * value is SQL {@code NULL}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public Time getTime(int columnIndex) throws SQLException; @@ -1035,7 +940,6 @@ public interface ResultSet { * value is SQL {@code NULL}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public Time getTime(int columnIndex, Calendar cal) throws SQLException; @@ -1049,7 +953,6 @@ public interface ResultSet { * NULL}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public Time getTime(String columnName) throws SQLException; @@ -1066,7 +969,6 @@ public interface ResultSet { * value is SQL {@code NULL}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public Time getTime(String columnName, Calendar cal) throws SQLException; @@ -1080,7 +982,6 @@ public interface ResultSet { * column value is SQL {@code NULL}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public Timestamp getTimestamp(int columnIndex) throws SQLException; @@ -1097,7 +998,6 @@ public interface ResultSet { * column value is SQL NULL. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public Timestamp getTimestamp(int columnIndex, Calendar cal) throws SQLException; @@ -1112,7 +1012,6 @@ public interface ResultSet { * column value is SQL {@code NULL}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public Timestamp getTimestamp(String columnName) throws SQLException; @@ -1129,7 +1028,6 @@ public interface ResultSet { * column value is SQL {@code NULL}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public Timestamp getTimestamp(String columnName, Calendar cal) throws SQLException; @@ -1145,7 +1043,6 @@ public interface ResultSet { * * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public int getType() throws SQLException; @@ -1160,7 +1057,6 @@ public interface ResultSet { * null} if the column value is SQL {@code NULL}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ @Deprecated public InputStream getUnicodeStream(int columnIndex) throws SQLException; @@ -1176,7 +1072,6 @@ public interface ResultSet { * null} if the column value is SQL {@code NULL}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ @Deprecated public InputStream getUnicodeStream(String columnName) throws SQLException; @@ -1190,7 +1085,6 @@ public interface ResultSet { * @return a URL. {@code null} if the column value is SQL {@code NULL}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public URL getURL(int columnIndex) throws SQLException; @@ -1203,7 +1097,6 @@ public interface ResultSet { * @return the column vaule as a URL. {@code null} if the column value is SQL {@code NULL}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public URL getURL(String columnName) throws SQLException; @@ -1217,15 +1110,13 @@ public interface ResultSet { * generated by {@code ResultSet} method calls - warnings generated by * Statement methods are held by the Statement. *

          - *

          * An {@code SQLException} is generated if this method is called on a closed - * {@code ResultSet}.

          + * {@code ResultSet}. * * @return an SQLWarning which is the first warning for this {@code * ResultSet}. {@code null} if there are no warnings. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public SQLWarning getWarnings() throws SQLException; @@ -1239,7 +1130,6 @@ public interface ResultSet { * cursor not being on the Insert Row or if any columns in the * row do not have a value where the column is declared as * not-nullable. - * @since Android 1.0 */ public void insertRow() throws SQLException; @@ -1251,7 +1141,6 @@ public interface ResultSet { * in the {@code ResultSet}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public boolean isAfterLast() throws SQLException; @@ -1263,7 +1152,6 @@ public interface ResultSet { * in the {@code ResultSet}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public boolean isBeforeFirst() throws SQLException; @@ -1275,7 +1163,6 @@ public interface ResultSet { * in the {@code ResultSet}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public boolean isFirst() throws SQLException; @@ -1287,7 +1174,6 @@ public interface ResultSet { * in the {@code ResultSet}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public boolean isLast() throws SQLException; @@ -1298,7 +1184,6 @@ public interface ResultSet { * false} if the {@code ResultSet} contains no rows. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public boolean last() throws SQLException; @@ -1309,7 +1194,6 @@ public interface ResultSet { * * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void moveToCurrentRow() throws SQLException; @@ -1321,7 +1205,6 @@ public interface ResultSet { * * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void moveToInsertRow() throws SQLException; @@ -1330,14 +1213,12 @@ public interface ResultSet { *

          * Any input streams associated with the current row are closed and any * warnings are cleared. - *

          - * + * * @return {@code true} if the updated cursor position is pointing to a * valid row, {@code false} otherwise (i.e. when the cursor is after * the last row in the {@code ResultSet}). * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public boolean next() throws SQLException; @@ -1349,7 +1230,6 @@ public interface ResultSet { * false} if the cursor is now before the first row. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public boolean previous() throws SQLException; @@ -1360,12 +1240,10 @@ public interface ResultSet { * If any columns in the current row have been updated but the {@code * updateRow} has not been called, then the updates are lost when this * method is called. - *

          - * + * * @throws SQLException * if a database error happens., including if the current row is * the Insert row. - * @since Android 1.0 */ public void refreshRow() throws SQLException; @@ -1381,7 +1259,6 @@ public interface ResultSet { * false} otherwise * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public boolean relative(int rows) throws SQLException; @@ -1393,7 +1270,6 @@ public interface ResultSet { * detected, {@code false} otherwise. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public boolean rowDeleted() throws SQLException; @@ -1406,7 +1282,6 @@ public interface ResultSet { * detected, {@code false} otherwise. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public boolean rowInserted() throws SQLException; @@ -1418,7 +1293,6 @@ public interface ResultSet { * can be detected, {@code false} otherwise. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public boolean rowUpdated() throws SQLException; @@ -1432,7 +1306,6 @@ public interface ResultSet { * ResultSet.FETCH_REVERSE}, or {@code ResultSet.FETCH_UNKNOWN} * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public void setFetchDirection(int direction) throws SQLException; @@ -1449,7 +1322,6 @@ public interface ResultSet { * ResultSet}. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void setFetchSize(int rows) throws SQLException; @@ -1463,7 +1335,6 @@ public interface ResultSet { * the new value for the specified column. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void updateArray(int columnIndex, Array x) throws SQLException; @@ -1477,7 +1348,6 @@ public interface ResultSet { * the new value for the specified column. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void updateArray(String columnName, Array x) throws SQLException; @@ -1492,7 +1362,6 @@ public interface ResultSet { * the length of the data to write from the stream * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void updateAsciiStream(int columnIndex, InputStream x, int length) throws SQLException; @@ -1508,7 +1377,6 @@ public interface ResultSet { * the length of the data to write from the stream * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void updateAsciiStream(String columnName, InputStream x, int length) throws SQLException; @@ -1523,7 +1391,6 @@ public interface ResultSet { * the new value for the specified column. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void updateBigDecimal(int columnIndex, BigDecimal x) throws SQLException; @@ -1538,7 +1405,6 @@ public interface ResultSet { * the new value for the specified column. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void updateBigDecimal(String columnName, BigDecimal x) throws SQLException; @@ -1554,7 +1420,6 @@ public interface ResultSet { * the number of bytes to be read from the the stream. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void updateBinaryStream(int columnIndex, InputStream x, int length) throws SQLException; @@ -1570,7 +1435,6 @@ public interface ResultSet { * he number of bytes to be read from the the stream. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void updateBinaryStream(String columnName, InputStream x, int length) throws SQLException; @@ -1585,7 +1449,6 @@ public interface ResultSet { * the new value for the specified column. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void updateBlob(int columnIndex, Blob x) throws SQLException; @@ -1599,7 +1462,6 @@ public interface ResultSet { * the new value for the specified column. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void updateBlob(String columnName, Blob x) throws SQLException; @@ -1613,7 +1475,6 @@ public interface ResultSet { * the new value for the specified column. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void updateBoolean(int columnIndex, boolean x) throws SQLException; @@ -1626,7 +1487,6 @@ public interface ResultSet { * the new value for the specified column. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void updateBoolean(String columnName, boolean x) throws SQLException; @@ -1639,7 +1499,6 @@ public interface ResultSet { * the new value for the specified column. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void updateByte(int columnIndex, byte x) throws SQLException; @@ -1652,7 +1511,6 @@ public interface ResultSet { * the new value for the specified column. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void updateByte(String columnName, byte x) throws SQLException; @@ -1666,7 +1524,6 @@ public interface ResultSet { * the new value for the specified column. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void updateBytes(int columnIndex, byte[] x) throws SQLException; @@ -1679,7 +1536,6 @@ public interface ResultSet { * the new value for the specified column. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void updateBytes(String columnName, byte[] x) throws SQLException; @@ -1695,7 +1551,6 @@ public interface ResultSet { * the length of data to write from the stream * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void updateCharacterStream(int columnIndex, Reader x, int length) throws SQLException; @@ -1712,7 +1567,6 @@ public interface ResultSet { * the length of data to write from the Reader * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void updateCharacterStream(String columnName, Reader reader, int length) throws SQLException; @@ -1727,7 +1581,6 @@ public interface ResultSet { * the new value for the specified column. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void updateClob(int columnIndex, Clob x) throws SQLException; @@ -1741,7 +1594,6 @@ public interface ResultSet { * the new value for the specified column. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void updateClob(String columnName, Clob x) throws SQLException; @@ -1755,7 +1607,6 @@ public interface ResultSet { * the new value for the specified column. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void updateDate(int columnIndex, Date x) throws SQLException; @@ -1769,7 +1620,6 @@ public interface ResultSet { * the new value for the specified column. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void updateDate(String columnName, Date x) throws SQLException; @@ -1782,7 +1632,6 @@ public interface ResultSet { * the new value for the specified column. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void updateDouble(int columnIndex, double x) throws SQLException; @@ -1795,7 +1644,6 @@ public interface ResultSet { * the new value for the specified column. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void updateDouble(String columnName, double x) throws SQLException; @@ -1808,7 +1656,6 @@ public interface ResultSet { * the new value for the specified column. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void updateFloat(int columnIndex, float x) throws SQLException; @@ -1821,7 +1668,6 @@ public interface ResultSet { * the new value for the specified column. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void updateFloat(String columnName, float x) throws SQLException; @@ -1834,7 +1680,6 @@ public interface ResultSet { * the new value for the specified column. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void updateInt(int columnIndex, int x) throws SQLException; @@ -1847,7 +1692,6 @@ public interface ResultSet { * the new value for the specified column. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void updateInt(String columnName, int x) throws SQLException; @@ -1860,7 +1704,6 @@ public interface ResultSet { * the new value for the specified column.. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void updateLong(int columnIndex, long x) throws SQLException; @@ -1873,7 +1716,6 @@ public interface ResultSet { * the new value for the specified column. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void updateLong(String columnName, long x) throws SQLException; @@ -1884,7 +1726,6 @@ public interface ResultSet { * the index of the column to update. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void updateNull(int columnIndex) throws SQLException; @@ -1895,7 +1736,6 @@ public interface ResultSet { * the name of the column to update. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void updateNull(String columnName) throws SQLException; @@ -1909,7 +1749,6 @@ public interface ResultSet { * the new value for the specified column. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void updateObject(int columnIndex, Object x) throws SQLException; @@ -1927,7 +1766,6 @@ public interface ResultSet { * after the decimal point. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void updateObject(int columnIndex, Object x, int scale) throws SQLException; @@ -1941,7 +1779,6 @@ public interface ResultSet { * the new value for the specified column. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void updateObject(String columnName, Object x) throws SQLException; @@ -1958,7 +1795,6 @@ public interface ResultSet { * after the decimal point. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void updateObject(String columnName, Object x, int scale) throws SQLException; @@ -1973,7 +1809,6 @@ public interface ResultSet { * the new value for the specified column. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void updateRef(int columnIndex, Ref x) throws SQLException; @@ -1987,7 +1822,6 @@ public interface ResultSet { * the new value for the specified column. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void updateRef(String columnName, Ref x) throws SQLException; @@ -1997,7 +1831,6 @@ public interface ResultSet { * * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void updateRow() throws SQLException; @@ -2010,7 +1843,6 @@ public interface ResultSet { * the new value for the specified column. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void updateShort(int columnIndex, short x) throws SQLException; @@ -2023,7 +1855,6 @@ public interface ResultSet { * the new value for the specified column. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void updateShort(String columnName, short x) throws SQLException; @@ -2036,7 +1867,6 @@ public interface ResultSet { * the new value for the specified column. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void updateString(int columnIndex, String x) throws SQLException; @@ -2049,7 +1879,6 @@ public interface ResultSet { * the new value for the specified column. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void updateString(String columnName, String x) throws SQLException; @@ -2062,7 +1891,6 @@ public interface ResultSet { * the new value for the specified column. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void updateTime(int columnIndex, Time x) throws SQLException; @@ -2075,7 +1903,6 @@ public interface ResultSet { * the new value for the specified column. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void updateTime(String columnName, Time x) throws SQLException; @@ -2089,7 +1916,6 @@ public interface ResultSet { * the new timestamp value for the specified column. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void updateTimestamp(int columnIndex, Timestamp x) throws SQLException; @@ -2103,7 +1929,6 @@ public interface ResultSet { * the new timestamp value for the specified column. * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public void updateTimestamp(String columnName, Timestamp x) throws SQLException; @@ -2116,7 +1941,6 @@ public interface ResultSet { * NULL}, {@code false} otherwise * @throws SQLException * if a database error happens. - * @since Android 1.0 */ public boolean wasNull() throws SQLException; } diff --git a/libcore/sql/src/main/java/java/sql/ResultSetMetaData.java b/libcore/sql/src/main/java/java/sql/ResultSetMetaData.java index 95c515d62..097093ffb 100644 --- a/libcore/sql/src/main/java/java/sql/ResultSetMetaData.java +++ b/libcore/sql/src/main/java/java/sql/ResultSetMetaData.java @@ -19,29 +19,21 @@ package java.sql; /** * Provides information about the columns returned in a {@code ResultSet}. - * - * @since Android 1.0 */ public interface ResultSetMetaData { /** * Indicates that a column cannot contain {@code NULL} values. - * - * @since Android 1.0 */ public static final int columnNoNulls = 0; /** * Indicates that a column can contain {@code NULL} values. - * - * @since Android 1.0 */ public static final int columnNullable = 1; /** * Indicates that it is unknown whether a column can contain {@code NULL}s or not. - * - * @since Android 1.0 */ public static final int columnNullableUnknown = 2; @@ -53,7 +45,6 @@ public interface ResultSetMetaData { * @return the catalog title. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public String getCatalogName(int column) throws SQLException; @@ -67,7 +58,6 @@ public interface ResultSetMetaData { * @throws SQLException * if there is a database error. * @see ResultSet#getObject - * @since Android 1.0 */ public String getColumnClassName(int column) throws SQLException; @@ -77,7 +67,6 @@ public interface ResultSetMetaData { * @return the column count. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public int getColumnCount() throws SQLException; @@ -90,7 +79,6 @@ public interface ResultSetMetaData { * @return the column's max width. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public int getColumnDisplaySize(int column) throws SQLException; @@ -103,7 +91,6 @@ public interface ResultSetMetaData { * @return the column's title. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public String getColumnLabel(int column) throws SQLException; @@ -115,7 +102,6 @@ public interface ResultSetMetaData { * @return the column title. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public String getColumnName(int column) throws SQLException; @@ -128,7 +114,6 @@ public interface ResultSetMetaData { * @throws SQLException * if there is a database error. * @see Types - * @since Android 1.0 */ public int getColumnType(int column) throws SQLException; @@ -140,7 +125,6 @@ public interface ResultSetMetaData { * @return the type name. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public String getColumnTypeName(int column) throws SQLException; @@ -152,7 +136,6 @@ public interface ResultSetMetaData { * @return the precision. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public int getPrecision(int column) throws SQLException; @@ -165,7 +148,6 @@ public interface ResultSetMetaData { * @return number of decimal places. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public int getScale(int column) throws SQLException; @@ -177,7 +159,6 @@ public interface ResultSetMetaData { * @return the name of the columns schema. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public String getSchemaName(int column) throws SQLException; @@ -189,7 +170,6 @@ public interface ResultSetMetaData { * @return the table title. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public String getTableName(int column) throws SQLException; @@ -203,7 +183,6 @@ public interface ResultSetMetaData { * otherwise. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public boolean isAutoIncrement(int column) throws SQLException; @@ -216,7 +195,6 @@ public interface ResultSetMetaData { * @return {@code true} if case matters, {@code false} otherwise. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public boolean isCaseSensitive(int column) throws SQLException; @@ -228,7 +206,6 @@ public interface ResultSetMetaData { * @return {@code true} if it is a monetary value, {@code false} otherwise. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public boolean isCurrency(int column) throws SQLException; @@ -241,7 +218,6 @@ public interface ResultSetMetaData { * @return {@code true} if the write is guaranteed, {@code false} otherwise. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public boolean isDefinitelyWritable(int column) throws SQLException; @@ -253,7 +229,6 @@ public interface ResultSetMetaData { * @return {@code true} if it is nullable, {@code false} otherwise. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public int isNullable(int column) throws SQLException; @@ -266,7 +241,6 @@ public interface ResultSetMetaData { * @return {@code true} if the column is read-only, {@code false} otherwise. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public boolean isReadOnly(int column) throws SQLException; @@ -279,7 +253,6 @@ public interface ResultSetMetaData { * otherwise. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public boolean isSearchable(int column) throws SQLException; @@ -292,7 +265,6 @@ public interface ResultSetMetaData { * @return {@code true} if they are signed, {@code false} otherwise. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public boolean isSigned(int column) throws SQLException; @@ -305,7 +277,6 @@ public interface ResultSetMetaData { * @return {@code true} if it is possible to write, {@code false} otherwise. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public boolean isWritable(int column) throws SQLException; } diff --git a/libcore/sql/src/main/java/java/sql/SQLData.java b/libcore/sql/src/main/java/java/sql/SQLData.java index cae9d15d0..f7dba05f6 100644 --- a/libcore/sql/src/main/java/java/sql/SQLData.java +++ b/libcore/sql/src/main/java/java/sql/SQLData.java @@ -31,7 +31,7 @@ package java.sql; * and can store changes back into the database using the * {@link PreparedStatement#setObject} method which performs the reverse mapping * into the SQL {@code UDT}. - *

          + *

          * Normally the implementation of a custom mapping is generated by * a tool requiring the name of the SQL {@code UDT}, the name * of the class which it is going to be mapped to, and the field names to which @@ -44,9 +44,6 @@ package java.sql; * Ordinarily an application would not call {@code SQLData} methods directly. * Similarly {@code SQLInput} and {@code SQLOutput} methods are not usually * called directly. - *

          - * - * @since Android 1.0 */ public interface SQLData { @@ -59,7 +56,6 @@ public interface SQLData { * {@code readSQL} when the object was created. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public String getSQLTypeName() throws SQLException; @@ -78,12 +74,10 @@ public interface SQLData { * distinct, then read its only data entry. For structured types, read every * entry. * - *

          *

          * The supplied input stream is typically initialized by the calling JDBC * driver with the type map before {@code readSQL} is called. - *

          - * + * * @param stream * the {@code SQLInput} stream from which the type map data is * read for the custom mapping. @@ -92,7 +86,6 @@ public interface SQLData { * @throws SQLException * if a database error occurs. * @see SQLInput - * @since Android 1.0 */ public void readSQL(SQLInput stream, String typeName) throws SQLException; @@ -109,7 +102,6 @@ public interface SQLData { * Write a single data element for a distinct type. For a structured type, * write a value for each attribute of the the SQL type. * - *

          * * @param stream * the {@code SQLOutput} stream to use to write out the data for @@ -117,7 +109,6 @@ public interface SQLData { * @throws SQLException * if a database error occurs. * @see SQLOutput - * @since Android 1.0 */ public void writeSQL(SQLOutput stream) throws SQLException; } diff --git a/libcore/sql/src/main/java/java/sql/SQLException.java b/libcore/sql/src/main/java/java/sql/SQLException.java index 2cea13999..5022eb0af 100644 --- a/libcore/sql/src/main/java/java/sql/SQLException.java +++ b/libcore/sql/src/main/java/java/sql/SQLException.java @@ -38,11 +38,8 @@ import java.io.Serializable; *
        • A chain to a next {@code Exception}, if relevant, which can give access * to additional error information.
        • * - *

          * * @see DatabaseMetaData - * - * @since Android 1.0 */ public class SQLException extends Exception implements Serializable { @@ -84,7 +81,6 @@ public class SQLException extends Exception implements Serializable { * the string to use as the reason string. * @param theSQLState * the string to use as the {@code SQLState} string. - * @since Android 1.0 */ public SQLException(String theReason, String theSQLState) { this(theReason, theSQLState, 0); @@ -102,7 +98,6 @@ public class SQLException extends Exception implements Serializable { * the string to use as the {@code SQLState} string. * @param theErrorCode * the integer value for the error code. - * @since Android 1.0 */ public SQLException(String theReason, String theSQLState, int theErrorCode) { super(theReason); @@ -115,7 +110,6 @@ public class SQLException extends Exception implements Serializable { * * @return The integer error code for this {@code SQLException}. The meaning * of the code is specific to the vendor of the database. - * @since Android 1.0 */ public int getErrorCode() { return vendorCode; @@ -156,7 +150,6 @@ public class SQLException extends Exception implements Serializable { * @param ex * the new {@code SQLException} to be added to the end of the * chain. - * @since Android 1.0 */ public void setNextException(SQLException ex) { if (next != null) { diff --git a/libcore/sql/src/main/java/java/sql/SQLInput.java b/libcore/sql/src/main/java/java/sql/SQLInput.java index b72c8398d..cd77a9161 100644 --- a/libcore/sql/src/main/java/java/sql/SQLInput.java +++ b/libcore/sql/src/main/java/java/sql/SQLInput.java @@ -32,7 +32,7 @@ import java.net.URL; * application programmers do not normally use the {@code SQLInput} methods * directly. Reader methods such as {@code readLong} and {@code readBytes} * provide means to read values from an {@code SQLInput} stream. - *

          + *

          * When the {@code getObject} method is called with an object which implements * the {@code SQLData} interface, the JDBC driver determines the SQL type of the * UDT being mapped by calling the {@code SQLData.getSQLType} method. The driver @@ -40,11 +40,8 @@ import java.net.URL; * the attributes of the UDT. The {@code SQLInput} stream is passed to the * {@code SQLData.readSQL} method which then calls the {@code SQLInput} reader * methods to read the attributes. - *

          - * + * * @see SQLData - * - * @since Android 1.0 */ public interface SQLInput { @@ -55,7 +52,6 @@ public interface SQLInput { * * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public String readString() throws SQLException; @@ -67,7 +63,6 @@ public interface SQLInput { * value is SQL {@code NULL}. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public boolean readBoolean() throws SQLException; @@ -78,7 +73,6 @@ public interface SQLInput { * {@code NULL}. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public byte readByte() throws SQLException; @@ -89,7 +83,6 @@ public interface SQLInput { * {@code NULL}. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public short readShort() throws SQLException; @@ -100,7 +93,6 @@ public interface SQLInput { * {@code NULL}. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public int readInt() throws SQLException; @@ -111,7 +103,6 @@ public interface SQLInput { * {@code NULL}. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public long readLong() throws SQLException; @@ -122,7 +113,6 @@ public interface SQLInput { * {@code NULL}. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public float readFloat() throws SQLException; @@ -133,7 +123,6 @@ public interface SQLInput { * {@code NULL}. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public double readDouble() throws SQLException; @@ -146,7 +135,6 @@ public interface SQLInput { * @throws SQLException * if there is a database error. * @see java.math.BigDecimal - * @since Android 1.0 */ public BigDecimal readBigDecimal() throws SQLException; @@ -157,7 +145,6 @@ public interface SQLInput { * SQL {@code NULL}. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public byte[] readBytes() throws SQLException; @@ -170,7 +157,6 @@ public interface SQLInput { * @throws SQLException * if there is a database error. * @see Date - * @since Android 1.0 */ public Date readDate() throws SQLException; @@ -183,7 +169,6 @@ public interface SQLInput { * @throws SQLException * if there is a database error. * @see Time - * @since Android 1.0 */ public Time readTime() throws SQLException; @@ -196,7 +181,6 @@ public interface SQLInput { * @throws SQLException * if there is a database error. * @see Timestamp - * @since Android 1.0 */ public Timestamp readTimestamp() throws SQLException; @@ -209,7 +193,6 @@ public interface SQLInput { * @throws SQLException * if there is a database error. * @see java.io.Reader - * @since Android 1.0 */ public Reader readCharacterStream() throws SQLException; @@ -222,7 +205,6 @@ public interface SQLInput { * @throws SQLException * if there is a database error. * @see java.io.InputStream - * @since Android 1.0 */ public InputStream readAsciiStream() throws SQLException; @@ -235,7 +217,6 @@ public interface SQLInput { * @throws SQLException * if there is a database error. * @see java.io.InputStream - * @since Android 1.0 */ public InputStream readBinaryStream() throws SQLException; @@ -247,20 +228,17 @@ public interface SQLInput { * for this JDBC driver, including any customized mappings, if present. A * type map is given to the {@code SQLInput} by the JDBC driver before the * {@code SQLInput} is given to the application. - *

          *

          * If the attribute is an SQL structured or distinct type, its SQL type is * determined. If the stream's type map contains an element for that SQL * type, the driver creates an object for the relevant type and invokes the * method {@code SQLData.readSQL} on it, which reads supplementary data from * the stream using whichever protocol is defined for that method. - *

          * * @return the next attribute as an Object. {@code null} if the value is SQL * {@code NULL}. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public Object readObject() throws SQLException; @@ -273,7 +251,6 @@ public interface SQLInput { * @throws SQLException * if there is a database error. * @see Ref - * @since Android 1.0 */ public Ref readRef() throws SQLException; @@ -285,7 +262,6 @@ public interface SQLInput { * the value is SQL {@code NULL}. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public Blob readBlob() throws SQLException; @@ -298,7 +274,6 @@ public interface SQLInput { * @throws SQLException * if there is a database error. * @see Clob - * @since Android 1.0 */ public Clob readClob() throws SQLException; @@ -311,7 +286,6 @@ public interface SQLInput { * @throws SQLException * if there is a database error. * @see Array - * @since Android 1.0 */ public Array readArray() throws SQLException; @@ -322,7 +296,6 @@ public interface SQLInput { * false} otherwise. * @throws SQLException * if there is a database error. - * @since Android 1.0 */ public boolean wasNull() throws SQLException; @@ -335,7 +308,6 @@ public interface SQLInput { * @throws SQLException * if there is a database error. * @see java.net.URL - * @since Android 1.0 */ public URL readURL() throws SQLException; } diff --git a/libcore/sql/src/main/java/java/sql/SQLOutput.java b/libcore/sql/src/main/java/java/sql/SQLOutput.java index 9fded5ea3..6710d4fbf 100644 --- a/libcore/sql/src/main/java/java/sql/SQLOutput.java +++ b/libcore/sql/src/main/java/java/sql/SQLOutput.java @@ -35,11 +35,8 @@ import java.net.URL; * the {@code SQLData.writeSQL} method, which in turn uses the appropriate * {@code SQLOutput} writer methods to write the data from the {@code SQLData} * object into the stream according to the defined mapping. - *

          - * + * * @see SQLData - * - * @since Android 1.0 */ public interface SQLOutput { @@ -50,7 +47,6 @@ public interface SQLOutput { * the {@code String} to write. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public void writeString(String theString) throws SQLException; @@ -61,7 +57,6 @@ public interface SQLOutput { * the {@code boolean} value to write. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public void writeBoolean(boolean theFlag) throws SQLException; @@ -72,7 +67,6 @@ public interface SQLOutput { * the {@code byte} value to write. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public void writeByte(byte theByte) throws SQLException; @@ -83,7 +77,6 @@ public interface SQLOutput { * the {@code short} value to write. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public void writeShort(short theShort) throws SQLException; @@ -94,7 +87,6 @@ public interface SQLOutput { * the {@code int} value to write. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public void writeInt(int theInt) throws SQLException; @@ -105,7 +97,6 @@ public interface SQLOutput { * the {@code long} value to write. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public void writeLong(long theLong) throws SQLException; @@ -116,7 +107,6 @@ public interface SQLOutput { * the {@code float} value to write. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public void writeFloat(float theFloat) throws SQLException; @@ -127,7 +117,6 @@ public interface SQLOutput { * the {@code double} value to write. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public void writeDouble(double theDouble) throws SQLException; @@ -138,7 +127,6 @@ public interface SQLOutput { * the {@code BigDecimal} value to write. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public void writeBigDecimal(BigDecimal theBigDecimal) throws SQLException; @@ -149,7 +137,6 @@ public interface SQLOutput { * the array of bytes to write. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public void writeBytes(byte[] theBytes) throws SQLException; @@ -161,7 +148,6 @@ public interface SQLOutput { * @throws SQLException * if a database error occurs. * @see Date - * @since Android 1.0 */ public void writeDate(Date theDate) throws SQLException; @@ -173,7 +159,6 @@ public interface SQLOutput { * @throws SQLException * if a database error occurs. * @see Time - * @since Android 1.0 */ public void writeTime(Time theTime) throws SQLException; @@ -185,7 +170,6 @@ public interface SQLOutput { * @throws SQLException * if a database error occurs. * @see Timestamp - * @since Android 1.0 */ public void writeTimestamp(Timestamp theTimestamp) throws SQLException; @@ -197,7 +181,6 @@ public interface SQLOutput { * java.io.Reader} object. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public void writeCharacterStream(Reader theStream) throws SQLException; @@ -209,7 +192,6 @@ public interface SQLOutput { * java.io.InputStream} object * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public void writeAsciiStream(InputStream theStream) throws SQLException; @@ -221,7 +203,6 @@ public interface SQLOutput { * object * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public void writeBinaryStream(InputStream theStream) throws SQLException; @@ -229,21 +210,18 @@ public interface SQLOutput { * Write an {@code SQLData} object into the output stream. *

          * If the {@code SQLData} object is null, writes {@code NULL} to the stream. - *

          *

          * Otherwise, calls the {@code SQLData.writeSQL} method of the object, which * writes the object's attributes to the stream by calling the appropriate * SQLOutput writer methods for each attribute, in order. The order of the * attributes is the order they are listed in the SQL definition of the User * Defined Type. - *

          * * @param theObject * the {@code SQLData} object to write. * @throws SQLException * if a database error occurs. * @see SQLData - * @since Android 1.0 */ public void writeObject(SQLData theObject) throws SQLException; @@ -255,7 +233,6 @@ public interface SQLOutput { * @throws SQLException * if a database error occurs. * @see Ref - * @since Android 1.0 */ public void writeRef(Ref theRef) throws SQLException; @@ -267,7 +244,6 @@ public interface SQLOutput { * @throws SQLException * if a database error occurs. * @see Blob - * @since Android 1.0 */ public void writeBlob(Blob theBlob) throws SQLException; @@ -279,7 +255,6 @@ public interface SQLOutput { * @throws SQLException * if a database error occurs. * @see Clob - * @since Android 1.0 */ public void writeClob(Clob theClob) throws SQLException; @@ -291,7 +266,6 @@ public interface SQLOutput { * @throws SQLException * if a database error occurs. * @see Struct - * @since Android 1.0 */ public void writeStruct(Struct theStruct) throws SQLException; @@ -303,7 +277,6 @@ public interface SQLOutput { * @throws SQLException * if a database error occurs. * @see Array - * @since Android 1.0 */ public void writeArray(Array theArray) throws SQLException; @@ -315,7 +288,6 @@ public interface SQLOutput { * @throws SQLException * if a database error occurs. * @see java.net.URL - * @since Android 1.0 */ public void writeURL(URL theURL) throws SQLException; } diff --git a/libcore/sql/src/main/java/java/sql/SQLPermission.java b/libcore/sql/src/main/java/java/sql/SQLPermission.java index a9f82d116..85dff7220 100644 --- a/libcore/sql/src/main/java/java/sql/SQLPermission.java +++ b/libcore/sql/src/main/java/java/sql/SQLPermission.java @@ -31,11 +31,8 @@ import java.security.Guard; * the {@code DriverManager.setLogStream} method. This is a potentially * dangerous operation since the logging stream can contain sensitive * information such as usernames and passwords. - *

          * * @see DriverManager - * - * @since Android 1.0 */ public final class SQLPermission extends BasicPermission implements Guard, Serializable { diff --git a/libcore/sql/src/main/java/java/sql/SQLWarning.java b/libcore/sql/src/main/java/java/sql/SQLWarning.java index de94da5e6..3ef67f584 100644 --- a/libcore/sql/src/main/java/java/sql/SQLWarning.java +++ b/libcore/sql/src/main/java/java/sql/SQLWarning.java @@ -23,8 +23,6 @@ import org.apache.harmony.sql.internal.nls.Messages; /** * An exception class that holds information about Database access warnings. - * - * @since Android 1.0 */ public class SQLWarning extends SQLException implements Serializable { @@ -34,8 +32,6 @@ public class SQLWarning extends SQLException implements Serializable { * Creates an {@code SQLWarning} object. The reason string is set to {@code * null}, the {@code SQLState} string is set to {@code null} and the error * code is set to 0. - * - * @since Android 1.0 */ public SQLWarning() { super(); @@ -48,7 +44,6 @@ public class SQLWarning extends SQLException implements Serializable { * * @param theReason * the reason why this warning is issued. - * @since Android 1.0 */ public SQLWarning(String theReason) { super(theReason); @@ -80,7 +75,6 @@ public class SQLWarning extends SQLException implements Serializable { * the X/Open standard specifc error code. * @param theErrorCode * a vendor specific error code. - * @since Android 1.0 */ public SQLWarning(String theReason, String theSQLState, int theErrorCode) { super(theReason, theSQLState, theErrorCode); @@ -92,7 +86,6 @@ public class SQLWarning extends SQLException implements Serializable { * @return the {@code SQLWarning} chained to this {@code SQLWarning}. * {@code null} if no {@code SQLWarning} is chained to this {@code * SQLWarning}. - * @since Android 1.0 */ public SQLWarning getNextWarning() { SQLException next = super.getNextException(); @@ -110,7 +103,6 @@ public class SQLWarning extends SQLException implements Serializable { * * @param w * the {@code SQLWarning} linked to this {@code SQLWarning}. - * @since Android 1.0 */ public void setNextWarning(SQLWarning w) { super.setNextException(w); diff --git a/libcore/sql/src/main/java/java/sql/Savepoint.java b/libcore/sql/src/main/java/java/sql/Savepoint.java index 42b4a17c6..0c6551596 100644 --- a/libcore/sql/src/main/java/java/sql/Savepoint.java +++ b/libcore/sql/src/main/java/java/sql/Savepoint.java @@ -22,8 +22,6 @@ package java.sql; * by a rollback via the {@link Connection#rollback} command. Rolling back to a * particular savepoint means that all changes that occurred after that * savepoint are undone. - * - * @since Android 1.0 */ public interface Savepoint { @@ -33,7 +31,6 @@ public interface Savepoint { * @return the ID for this savepoint. * @throws SQLException * if an error occurrs accessing the database. - * @since Android 1.0 */ public int getSavepointId() throws SQLException; @@ -43,7 +40,6 @@ public interface Savepoint { * @return the name of this savepoint. * @throws SQLException * if an error occurrs accessing the database. - * @since Android 1.0 */ public String getSavepointName() throws SQLException; } diff --git a/libcore/sql/src/main/java/java/sql/Statement.java b/libcore/sql/src/main/java/java/sql/Statement.java index 4985a9e83..6f2a1ed47 100644 --- a/libcore/sql/src/main/java/java/sql/Statement.java +++ b/libcore/sql/src/main/java/java/sql/Statement.java @@ -27,70 +27,52 @@ package java.sql; *

          * To have multiple {@code ResultSet} objects opened concurrently, multiple * {@code Statement} objects must be created and then executed. - *

          *

          * To obtain such an executable statement one needs to invoke {@code * Connection#createStatement}. - *

          - * + * * @see ResultSet * @see Connection#createStatement - * - * @since Android 1.0 */ public interface Statement { /** * Passing this constant to {@link #getMoreResults} implies that all {@code * ResultSet} objects previously kept open should be closed. - * - * @since Android 1.0 */ public static final int CLOSE_ALL_RESULTS = 3; /** * Passing this constant to {@link #getMoreResults} implies that the current * {@code ResultSet} object should be closed. - * - * @since Android 1.0 */ public static final int CLOSE_CURRENT_RESULT = 1; /** * Indicates that an error was encountered during execution of a batch * statement. - * - * @since Android 1.0 */ public static final int EXECUTE_FAILED = -3; /** * Passing this constant to getMoreResults implies that the current * {@code ResultSet} object should not be closed. - * - * @since Android 1.0 */ public static final int KEEP_CURRENT_RESULT = 2; /** * Indicates that generated keys should not be accessible for retrieval. - * - * @since Android 1.0 */ public static final int NO_GENERATED_KEYS = 2; /** * Indicates that generated keys should be accessible for retrieval. - * - * @since Android 1.0 */ public static final int RETURN_GENERATED_KEYS = 1; /** * Indicates that a batch statement was executed with a successful result, * but a count of the number of rows it affected is unavailable. - * - * @since Android 1.0 */ public static final int SUCCESS_NO_INFO = -2; @@ -100,15 +82,13 @@ public interface Statement { *

          * The list of commands is executed by invoking the {@code executeBatch} * method. - *

          - * + * * @param sql * the SQL command as a String. Typically an {@code INSERT} or * {@code UPDATE} statement. * @throws SQLException * if an error occurs accessing the database or the database * does not support batch updates. - * @since Android 1.0 */ public void addBatch(String sql) throws SQLException; @@ -120,7 +100,6 @@ public interface Statement { * * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public void cancel() throws SQLException; @@ -130,7 +109,6 @@ public interface Statement { * @throws SQLException * if an error occurs accessing the database or the database * does not support batch updates. - * @since Android 1.0 */ public void clearBatch() throws SQLException; @@ -139,7 +117,6 @@ public interface Statement { * * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public void clearWarnings() throws SQLException; @@ -148,13 +125,11 @@ public interface Statement { *

          * Using this method to release these resources as soon as possible is * strongly recommended. - *

          *

          * One should not rely on the resources being automatically released when * finalized during garbage collection. Doing so can result in unpredictable * behavior for the application. - *

          - * + * * @throws SQLException * if an error occurs accessing the database. */ @@ -166,8 +141,7 @@ public interface Statement { *

          * Use the {@code getResultSet} or {@code getUpdateCount} methods to get the * first result and {@code getMoreResults} to get any subsequent results. - *

          - * + * * @param sql * the SQL statement to execute * @return {@code true} if the first result is a {@code ResultSet}, {@code @@ -175,7 +149,6 @@ public interface Statement { * result. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public boolean execute(String sql) throws SQLException; @@ -187,8 +160,7 @@ public interface Statement { *

          * Use the {@code getResultSet} or {@code getUpdateCount} methods to get the * first result and {@code getMoreResults} to get any subsequent results. - *

          - * + * * @param sql * the SQL statement to execute. * @param autoGeneratedKeys @@ -201,7 +173,6 @@ public interface Statement { * or if there is no result. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public boolean execute(String sql, int autoGeneratedKeys) throws SQLException; @@ -214,8 +185,7 @@ public interface Statement { *

          * Use the {@code getResultSet} or {@code getUpdateCount} methods to get the * first result and {@code getMoreResults} to get any subsequent results. - *

          - * + * * @param sql * the SQL statement to execute. * @param columnIndexes @@ -227,7 +197,6 @@ public interface Statement { * result. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public boolean execute(String sql, int[] columnIndexes) throws SQLException; @@ -239,8 +208,7 @@ public interface Statement { *

          * Use the {@code getResultSet} or {@code getUpdateCount} methods to get the * first result and {@code getMoreResults} to get any subsequent results. - *

          - * + * * @param sql * the SQL statement to execute. * @param columnNames @@ -252,7 +220,6 @@ public interface Statement { * result * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public boolean execute(String sql, String[] columnNames) throws SQLException; @@ -300,7 +267,6 @@ public interface Statement { * @throws SQLException * if an error occurs accessing the database or if the statement * produces anything other than a single {@code ResultSet}. - * @since Android 1.0 */ public ResultSet executeQuery(String sql) throws SQLException; @@ -317,7 +283,6 @@ public interface Statement { * @throws SQLException * if an error occurs accessing the database or if the statement * produces a {@code ResultSet}. - * @since Android 1.0 */ public int executeUpdate(String sql) throws SQLException; @@ -339,7 +304,6 @@ public interface Statement { * @throws SQLException * if an error occurs accessing the database or if the statement * produces a {@code ResultSet}. - * @since Android 1.0 */ public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException; @@ -360,7 +324,6 @@ public interface Statement { * @throws SQLException * if an error occurs accessing the database or if the statement * produces a {@code ResultSet}. - * @since Android 1.0 */ public int executeUpdate(String sql, int[] columnIndexes) throws SQLException; @@ -381,7 +344,6 @@ public interface Statement { * @throws SQLException * if an error occurs accessing the database or if the statement * produces a {@code ResultSet}. - * @since Android 1.0 */ public int executeUpdate(String sql, String[] columnNames) throws SQLException; @@ -393,7 +355,6 @@ public interface Statement { * transmitted to the database. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public Connection getConnection() throws SQLException; @@ -408,7 +369,6 @@ public interface Statement { * * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public int getFetchDirection() throws SQLException; @@ -420,7 +380,6 @@ public interface Statement { * statement. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public int getFetchSize() throws SQLException; @@ -431,7 +390,6 @@ public interface Statement { * no keys are generated by this statement. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public ResultSet getGeneratedKeys() throws SQLException; @@ -447,7 +405,6 @@ public interface Statement { * limit. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public int getMaxFieldSize() throws SQLException; @@ -460,7 +417,6 @@ public interface Statement { * limit. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public int getMaxRows() throws SQLException; @@ -476,7 +432,6 @@ public interface Statement { * return -1. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public boolean getMoreResults() throws SQLException; @@ -499,7 +454,6 @@ public interface Statement { * will return -1. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public boolean getMoreResults(int current) throws SQLException; @@ -512,7 +466,6 @@ public interface Statement { * there is no current timeout. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public int getQueryTimeout() throws SQLException; @@ -523,7 +476,6 @@ public interface Statement { * result is an update count or if there are no more results. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public ResultSet getResultSet() throws SQLException; @@ -535,7 +487,6 @@ public interface Statement { * ResultSet.CONCUR_UPDATABLE}. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public int getResultSetConcurrency() throws SQLException; @@ -547,7 +498,6 @@ public interface Statement { * ResultSet.CLOSE_CURSORS_AT_COMMIT} * @throws SQLException * if there is an error while accessing the database. - * @since Android 1.0 */ public int getResultSetHoldability() throws SQLException; @@ -563,7 +513,6 @@ public interface Statement { * which is scrollable but is sensitive to changes made by others. * @throws SQLException * if there is an error accessing the database. - * @since Android 1.0 */ public int getResultSetType() throws SQLException; @@ -575,7 +524,6 @@ public interface Statement { * result is a {@code ResultSet} or if there are no more results. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public int getUpdateCount() throws SQLException; @@ -588,12 +536,10 @@ public interface Statement { * Warnings associated with reads from the {@code ResultSet} returned from * executing the statement will be attached to the {@code ResultSet}, not the * statement object. - *

          - * + * * @return an SQLWarning, null if there are no warnings * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public SQLWarning getWarnings() throws SQLException; @@ -602,19 +548,16 @@ public interface Statement { * execute methods. *

          * Cursor names must be unique within one Connection. - *

          *

          * With the cursor name set, it can then be used in SQL positioned * update or delete statements to determine the current row in a {@code * ResultSet} generated from this statement. The positioned update or delete * must be done with a different statement than this one. - *

          - * + * * @param name * the Cursor name as a string, * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public void setCursorName(String name) throws SQLException; @@ -625,14 +568,12 @@ public interface Statement { * on an SQL statement before sending it for execution. This does not apply * to {@link PreparedStatement}s since they are processed when created, * before this method can be called. - *

          - * + * * @param enable * {@code true} to set escape processing mode on, {@code * false} to turn it off. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public void setEscapeProcessing(boolean enable) throws SQLException; @@ -666,7 +607,6 @@ public interface Statement { * @throws SQLException * if an error occurs accessing the database, or if the rows * parameter is out of range. - * @since Android 1.0 */ public void setFetchSize(int rows) throws SQLException; @@ -682,7 +622,6 @@ public interface Statement { * @throws SQLException * if an error occurs accessing the database or the {@code max} * value is < {@code 0}. - * @since Android 1.0 */ public void setMaxFieldSize(int max) throws SQLException; @@ -696,7 +635,6 @@ public interface Statement { * @throws SQLException * if an error occurs accessing the database or if max < {@code * 0}. - * @since Android 1.0 */ public void setMaxRows(int max) throws SQLException; @@ -710,7 +648,6 @@ public interface Statement { * @throws SQLException * if an error occurs accessing the database or if seconds < * {@code 0}. - * @since Android 1.0 */ public void setQueryTimeout(int seconds) throws SQLException; } diff --git a/libcore/sql/src/main/java/java/sql/Struct.java b/libcore/sql/src/main/java/java/sql/Struct.java index 1167cdf8a..4d94d4974 100644 --- a/libcore/sql/src/main/java/java/sql/Struct.java +++ b/libcore/sql/src/main/java/java/sql/Struct.java @@ -23,8 +23,6 @@ import java.util.Map; * An interface which provides facilities for manipulating an SQL structured type * as a Java object. The {@code Struct} object has a value for each attribute of the SQL structured * type. - * - * @since Android 1.0 */ public interface Struct { @@ -35,7 +33,6 @@ public interface Struct { * @return the fully qualified name of SQL structured type. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public String getSQLTypeName() throws SQLException; @@ -49,7 +46,6 @@ public interface Struct { * @return an {@code Object} array containing the ordered attributes. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public Object[] getAttributes() throws SQLException; @@ -66,7 +62,6 @@ public interface Struct { * @return an Object array containing the ordered attributes,. * @throws SQLException * if a database error occurs. - * @since Android 1.0 */ public Object[] getAttributes(Map> theMap) throws SQLException; diff --git a/libcore/sql/src/main/java/java/sql/Time.java b/libcore/sql/src/main/java/java/sql/Time.java index 7fb28e1d5..d522ef89c 100644 --- a/libcore/sql/src/main/java/java/sql/Time.java +++ b/libcore/sql/src/main/java/java/sql/Time.java @@ -17,14 +17,11 @@ package java.sql; -import java.text.SimpleDateFormat; import java.util.Date; /** * Java representation of an SQL {@code TIME} value. Provides utilities to * format and parse the time's representation as a String in JDBC escape format. - * - * @since Android 1.0 */ public class Time extends Date { @@ -33,24 +30,22 @@ public class Time extends Date { /** * Constructs a {@code Time} object using the supplied values for Hour, * Minute and Second. The Year, Month and - * Day elements of the {@code Time} object are set to the date + * Day elements of the {@code Time} object are set to the date * of the Epoch (January 1, 1970). *

          * Any attempt to access the Year, Month or Day * elements of a {@code Time} object will result in an {@code * IllegalArgumentException}. - *

          + *

          * The result is undefined if any argument is out of bounds. - *

          - * - * @deprecated Please use the constructor {@link #Time(long)}. + * + * @deprecated Use the constructor {@link #Time(long)}. * @param theHour * a value in the range {@code [0,23]}. * @param theMinute * a value in the range {@code [0,59]}. * @param theSecond * a value in the range {@code [0,59]}. - * @since Android 1.0 */ @SuppressWarnings("deprecation") @Deprecated @@ -65,7 +60,6 @@ public class Time extends Date { * @param theTime * a {@code Time} specified in milliseconds since the * Epoch (January 1st 1970, 00:00:00.000). - * @since Android 1.0 */ public Time(long theTime) { super(theTime); @@ -77,7 +71,6 @@ public class Time extends Date { * @return does not return anything. * @throws IllegalArgumentException * if this method is called. - * @since Android 1.0 */ @SuppressWarnings("deprecation") @Deprecated @@ -92,7 +85,6 @@ public class Time extends Date { * @return does not return anything. * @throws IllegalArgumentException * if this method is called. - * @since Android 1.0 */ @SuppressWarnings("deprecation") @Deprecated @@ -107,7 +99,6 @@ public class Time extends Date { * @return does not return anything. * @throws IllegalArgumentException * if this method is called. - * @since Android 1.0 */ @SuppressWarnings("deprecation") @Deprecated @@ -122,7 +113,6 @@ public class Time extends Date { * @return does not return anything. * @throws IllegalArgumentException * if this method is called. - * @since Android 1.0 */ @SuppressWarnings("deprecation") @Deprecated @@ -136,7 +126,6 @@ public class Time extends Date { * {@code Time} object does not have a {@code Date} component. * @throws IllegalArgumentException * if this method is called. - * @since Android 1.0 */ @SuppressWarnings("deprecation") @Deprecated @@ -150,7 +139,6 @@ public class Time extends Date { * {@code Time} object does not have a Month component. * @throws IllegalArgumentException * if this method is called. - * @since Android 1.0 */ @SuppressWarnings("deprecation") @Deprecated @@ -180,7 +168,6 @@ public class Time extends Date { * A time value expressed as milliseconds since the Epoch. * Negative values are milliseconds before the Epoch. The Epoch * is January 1 1970, 00:00:00.000. - * @since Android 1.0 */ @Override public void setTime(long time) { @@ -193,12 +180,31 @@ public class Time extends Date { * * @return A String representing the {@code Time} value in JDBC escape * format: {@code HH:mm:ss} - * @since Android 1.0 */ @Override public String toString() { - SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss"); //$NON-NLS-1$ - return dateFormat.format(this); + StringBuilder sb = new StringBuilder(8); + + format(getHours(), 2, sb); + sb.append(':'); + format(getMinutes(), 2, sb); + sb.append(':'); + format(getSeconds(), 2, sb); + + return sb.toString(); + } + + private static final String PADDING = "00"; //$NON-NLS-1$ + + /* + * Private method to format the time + */ + private void format(int date, int digits, StringBuilder sb) { + String str = String.valueOf(date); + if (digits - str.length() > 0) { + sb.append(PADDING.substring(0, digits - str.length())); + } + sb.append(str); } /** @@ -206,8 +212,7 @@ public class Time extends Date { * JDBC escape format: {@code hh:mm:ss}. *

          * An exception occurs if the input string does not comply with this format. - *

          - * + * * @param timeString * A String representing the time value in JDBC escape format: * {@code hh:mm:ss}. @@ -215,7 +220,6 @@ public class Time extends Date { * time. * @throws IllegalArgumentException * if the supplied time string is not in JDBC escape format. - * @since Android 1.0 */ public static Time valueOf(String timeString) { if (timeString == null) { @@ -223,18 +227,21 @@ public class Time extends Date { } int firstIndex = timeString.indexOf(':'); int secondIndex = timeString.indexOf(':', firstIndex + 1); - // secondIndex == -1 means none or only one separator '-' has been found. + // secondIndex == -1 means none or only one separator '-' has been + // found. // The string is separated into three parts by two separator characters, // if the first or the third part is null string, we should throw // IllegalArgumentException to follow RI - if (secondIndex == -1|| firstIndex == 0 || secondIndex + 1 == timeString.length()) { + if (secondIndex == -1 || firstIndex == 0 + || secondIndex + 1 == timeString.length()) { throw new IllegalArgumentException(); } // parse each part of the string int hour = Integer.parseInt(timeString.substring(0, firstIndex)); - int minute = Integer.parseInt(timeString.substring(firstIndex + 1, secondIndex)); - int second = Integer.parseInt(timeString.substring(secondIndex + 1, timeString - .length())); + int minute = Integer.parseInt(timeString.substring(firstIndex + 1, + secondIndex)); + int second = Integer.parseInt(timeString.substring(secondIndex + 1, + timeString.length())); return new Time(hour, minute, second); } } diff --git a/libcore/sql/src/main/java/java/sql/Timestamp.java b/libcore/sql/src/main/java/java/sql/Timestamp.java index b526fb331..f16d93a4a 100644 --- a/libcore/sql/src/main/java/java/sql/Timestamp.java +++ b/libcore/sql/src/main/java/java/sql/Timestamp.java @@ -17,10 +17,10 @@ package java.sql; -import java.text.DecimalFormat; import java.text.ParsePosition; import java.text.SimpleDateFormat; import java.util.Date; +import java.util.regex.Pattern; import org.apache.harmony.sql.internal.nls.Messages; @@ -32,18 +32,16 @@ import org.apache.harmony.sql.internal.nls.Messages; * The {@code Timestamp} class consists of a regular date/time value, where only * the integral seconds value is stored, plus a nanoseconds value where the * fractional seconds are stored. - *

          + *

          * The addition of the nanosecond value field to the {@code Timestamp} object * makes it significantly different from the {@code java.util.Date} object which * it extends. Users should be aware that {@code Timestamp} objects are not * interchangable with {@code java.util.Date} objects when used outside the * confines of the {@code java.sql} package. - *

          - * + * * @see Date * @see Time * @see java.util.Date - * @since Android 1.0 */ public class Timestamp extends Date { @@ -52,12 +50,15 @@ public class Timestamp extends Date { // The nanoseconds time value of the Timestamp private int nanos; + // The regex pattern of yyyy-mm-dd hh:mm:ss + private static final String TIME_FORMAT_REGEX = "[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}.*"; //$NON-NLS-1$ + /** * Returns a {@code Timestamp} corresponding to the time specified by the * supplied values for Year, Month, Date, Hour, * Minutes, Seconds and Nanoseconds. - * - * @deprecated Please use the constructor {@link #Timestamp(long)}. + * + * @deprecated Use the constructor {@link #Timestamp(long)}. * @param theYear * specified as the year minus 1900. * @param theMonth @@ -75,7 +76,6 @@ public class Timestamp extends Date { * as an integer in the range [0,999'999'999] * @throws IllegalArgumentException * if any of the parameters is out of range. - * @since Android 1.0 */ @SuppressWarnings("deprecation") @Deprecated @@ -96,7 +96,6 @@ public class Timestamp extends Date { * @param theTime * a time value in the format of milliseconds since the Epoch * (January 1 1970 00:00:00.000 GMT). - * @since Android 1.0 */ public Timestamp(long theTime) { super(theTime); @@ -104,7 +103,7 @@ public class Timestamp extends Date { * Now set the time for this Timestamp object - which deals with the * nanosecond value as well as the base time */ - this.setTime(theTime); + setTimeImpl(theTime); } /** @@ -115,7 +114,6 @@ public class Timestamp extends Date { * the timestamp to compare with this timestamp object. * @return {@code true} if this {@code Timestamp} object is later than the * supplied timestamp, {@code false} otherwise. - * @since Android 1.0 */ public boolean after(Timestamp theTimestamp) { long thisTime = this.getTime(); @@ -148,7 +146,6 @@ public class Timestamp extends Date { * the timestamp to compare with this {@code Timestamp} object. * @return {@code true} if this {@code Timestamp} object is earlier than the * supplied timestamp, {@code false} otherwise. - * @since Android 1.0 */ public boolean before(Timestamp theTimestamp) { long thisTime = this.getTime(); @@ -195,7 +192,6 @@ public class Timestamp extends Date { * * @throws ClassCastException * if the supplied object is not a {@code Timestamp} object. - * @since Android 1.0 */ @Override public int compareTo(Date theObject) throws ClassCastException { @@ -218,7 +214,6 @@ public class Timestamp extends Date { *
        • {@code > 0}, if this {@code Timestamp} object is after the * supplied {@code Timestamp}
        • * - * @since Android 1.0 */ public int compareTo(Timestamp theTimestamp) { int result = super.compareTo(theTimestamp); @@ -245,7 +240,6 @@ public class Timestamp extends Date { * supplied {@code Timestamp} object
          {@code false} if the object * is not a {@code Timestamp} object or if the object is a {@code * Timestamp} but represents a different instant in time. - * @since Android 1.0 */ @Override public boolean equals(Object theObject) { @@ -263,7 +257,6 @@ public class Timestamp extends Date { * passed as an {@code Object}. * @return {@code true} if this {@code Timestamp} object is equal to the * supplied {@code Timestamp} object, {@code false} otherwise. - * @since Android 1.0 */ public boolean equals(Timestamp theTimestamp) { if (theTimestamp == null) { @@ -278,7 +271,6 @@ public class Timestamp extends Date { * * @return The timestamp's nanosecond value, an integer between 0 and * 999,999,999. - * @since Android 1.0 */ public int getNanos() { return nanos; @@ -288,10 +280,9 @@ public class Timestamp extends Date { * Returns the time represented by this {@code Timestamp} object, as a long * value containing the number of milliseconds since the Epoch (January 1 * 1970, 00:00:00.000 GMT). - * + * * @return the number of milliseconds that have passed since January 1 1970, * 00:00:00.000 GMT. - * @since Android 1.0 */ @Override public long getTime() { @@ -302,13 +293,12 @@ public class Timestamp extends Date { /** * Sets the nanosecond value for this {@code Timestamp}. - * + * * @param n * number of nanoseconds. * @throws IllegalArgumentException * if number of nanoseconds smaller than 0 or greater than * 999,999,999. - * @since Android 1.0 */ public void setNanos(int n) throws IllegalArgumentException { if ((n < 0) || (n > 999999999)) { @@ -322,14 +312,17 @@ public class Timestamp extends Date { * Sets the time represented by this {@code Timestamp} object to the * supplied time, defined as the number of milliseconds since the Epoch * (January 1 1970, 00:00:00.000 GMT). - * + * * @param theTime * number of milliseconds since the Epoch (January 1 1970, * 00:00:00.000 GMT). - * @since Android 1.0 */ @Override public void setTime(long theTime) { + setTimeImpl(theTime); + } + + private void setTimeImpl(long theTime) { /* * Deal with the nanoseconds value. The supplied time is in milliseconds - * so we must extract the milliseconds value and multiply by 1000000 to @@ -356,67 +349,47 @@ public class Timestamp extends Date { * * @return A string representing the instant defined by the {@code * Timestamp}, in JDBC Timestamp escape format. - * @since Android 1.0 */ @SuppressWarnings("deprecation") @Override public String toString() { - /* - * Use a DecimalFormat to lay out the nanosecond value as a simple - * string of 9 integers, with leading Zeros - */ - DecimalFormat decimalFormat = new DecimalFormat("0"); //$NON-NLS-1$ - decimalFormat.setMinimumIntegerDigits(9); - decimalFormat.setMaximumIntegerDigits(9); - String theNanos = decimalFormat.format(nanos); - theNanos = stripTrailingZeros(theNanos); - - String year = format((getYear() + 1900), 4); - String month = format((getMonth() + 1), 2); - String date = format(getDate(), 2); - String hours = format(getHours(), 2); - String minutes = format(getMinutes(), 2); - String seconds = format(getSeconds(), 2); - - return year + '-' + month + '-' + date + ' ' + hours + ':' + minutes - + ':' + seconds + '.' + theNanos; - } - - /* - * Private method to format the time - */ - private String format(int date, int digits) { - StringBuilder dateStringBuffer = new StringBuilder(String.valueOf(date)); - while (dateStringBuffer.length() < digits) { - dateStringBuffer = dateStringBuffer.insert(0,'0'); + StringBuilder sb = new StringBuilder(29); + + format((getYear() + 1900), 4, sb); + sb.append('-'); + format((getMonth() + 1), 2, sb); + sb.append('-'); + format(getDate(), 2, sb); + sb.append(' '); + format(getHours(), 2, sb); + sb.append(':'); + format(getMinutes(), 2, sb); + sb.append(':'); + format(getSeconds(), 2, sb); + sb.append('.'); + if (nanos == 0) { + sb.append('0'); + } else { + format(nanos, 9, sb); + while (sb.charAt(sb.length() - 1) == '0') { + sb.setLength(sb.length() - 1); + } } - return dateStringBuffer.toString(); + + return sb.toString(); } - - /* - * Private method to strip trailing '0' characters from a string. @param - * inputString the starting string @return a string with the trailing zeros - * stripped - will leave a single 0 at the beginning of the string - */ - private String stripTrailingZeros(String inputString) { - String finalString; - int i; - for (i = inputString.length(); i > 0; i--) { - if (inputString.charAt(i - 1) != '0') { - break; - } - /* - * If the string has a 0 as its first character, return a string - * with a single '0' - */ - if (i == 1) { - return "0"; //$NON-NLS-1$ - } - } + private static final String PADDING = "000000000"; //$NON-NLS-1$ - finalString = inputString.substring(0, i); - return finalString; + /* + * Private method to format the time + */ + private void format(int date, int digits, StringBuilder sb) { + String str = String.valueOf(date); + if (digits - str.length() > 0) { + sb.append(PADDING.substring(0, digits - str.length())); + } + sb.append(str); } /** @@ -431,7 +404,6 @@ public class Timestamp extends Date { * supplied {@code String}. * @throws IllegalArgumentException * if the provided string is {@code null}. - * @since Android 1.0 */ public static Timestamp valueOf(String s) throws IllegalArgumentException { if (s == null) { @@ -439,6 +411,12 @@ public class Timestamp extends Date { throw new IllegalArgumentException(Messages.getString("sql.3")); //$NON-NLS-1$ } + // omit trailing whitespaces + s = s.trim(); + if (!Pattern.matches(TIME_FORMAT_REGEX, s)) { + throw new IllegalArgumentException(Messages.getString("sql.2")); //$NON-NLS-1$ + } + SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //$NON-NLS-1$ ParsePosition pp = new ParsePosition(0); @@ -495,7 +473,8 @@ public class Timestamp extends Date { // Require the next character to be a "." if (s.charAt(position) != '.') { // sql.4=Bad input string format: expected '.' not {0} - throw new NumberFormatException(Messages.getString("sql.4", s.charAt(position))); //$NON-NLS-1$ + throw new NumberFormatException(Messages.getString( + "sql.4", s.charAt(position))); //$NON-NLS-1$ } // Get the length of the number string - need to account for the '.' int nanoLength = s.length() - position - 1; diff --git a/libcore/sql/src/main/java/java/sql/Types.java b/libcore/sql/src/main/java/java/sql/Types.java index 8ce042179..fed867175 100644 --- a/libcore/sql/src/main/java/java/sql/Types.java +++ b/libcore/sql/src/main/java/java/sql/Types.java @@ -21,8 +21,6 @@ package java.sql; * A class which defines constants used to identify generic SQL types, also * called JDBC types. The type constant values are equivalent to those defined * by X/OPEN. - * - * @since Android 1.0 */ public class Types { @@ -35,141 +33,101 @@ public class Types { /** * The type code that identifies the SQL type {@code ARRAY}. - * - * @since Android 1.0 */ public static final int ARRAY = 2003; /** * The type code that identifies the SQL type {@code BIGINT}. - * - * @since Android 1.0 */ public static final int BIGINT = -5; /** * The type code that identifies the SQL type {@code BINARY}. - * - * @since Android 1.0 */ public static final int BINARY = -2; /** * The type code that identifies the SQL type {@code BIT}. - * - * @since Android 1.0 */ public static final int BIT = -7; /** * The type code that identifies the SQL type {@code BLOB}. - * - * @since Android 1.0 */ public static final int BLOB = 2004; /** * The type code that identifies the SQL type {@code BOOLEAN}. - * - * @since Android 1.0 */ public static final int BOOLEAN = 16; /** * The type code that identifies the SQL type {@code CHAR}. - * - * @since Android 1.0 */ public static final int CHAR = 1; /** * The type code that identifies the SQL type {@code CLOB}. - * - * @since Android 1.0 */ public static final int CLOB = 2005; /** * The type code that identifies the SQL type {@code DATALINK}. - * - * @since Android 1.0 */ public static final int DATALINK = 70; /** * The type code that identifies the SQL type {@code DATE}. - * - * @since Android 1.0 */ public static final int DATE = 91; /** * The type code that identifies the SQL type {@code DECIMAL}. - * - * @since Android 1.0 */ public static final int DECIMAL = 3; /** * The type code that identifies the SQL type {@code DISTINCT}. - * - * @since Android 1.0 */ public static final int DISTINCT = 2001; /** * The type code that identifies the SQL type {@code DOUBLE}. - * - * @since Android 1.0 */ public static final int DOUBLE = 8; /** * The type code that identifies the SQL type {@code FLOAT}. - * - * @since Android 1.0 */ public static final int FLOAT = 6; /** * The type code that identifies the SQL type {@code INTEGER}. - * - * @since Android 1.0 */ public static final int INTEGER = 4; /** * The type code that identifies the SQL type {@code JAVA_OBJECT}. - * - * @since Android 1.0 */ public static final int JAVA_OBJECT = 2000; /** * The type code that identifies the SQL type {@code LONGVARBINARY}. - * - * @since Android 1.0 */ public static final int LONGVARBINARY = -4; /** * The type code that identifies the SQL type {@code LONGVARCHAR}. - * - * @since Android 1.0 */ public static final int LONGVARCHAR = -1; /** * The type code that identifies the SQL type {@code NULL}. - * - * @since Android 1.0 */ public static final int NULL = 0; /** * The type code that identifies the SQL type {@code NUMERIC}. - * - * @since Android 1.0 */ public static final int NUMERIC = 2; @@ -177,71 +135,51 @@ public class Types { * The type code that identifies that the SQL type is database specific and * is mapped to a Java object, accessed via the methods * {@code getObject} and {@code setObject}. - * - * @since Android 1.0 */ public static final int OTHER = 1111; /** * The type code that identifies the SQL type {@code REAL}. - * - * @since Android 1.0 */ public static final int REAL = 7; /** * The type code that identifies the SQL type {@code REF}. - * - * @since Android 1.0 */ public static final int REF = 2006; /** * The type code that identifies the SQL type {@code SMALLINT}. - * - * @since Android 1.0 */ public static final int SMALLINT = 5; /** * The type code that identifies the SQL type {@code STRUCT}. - * - * @since Android 1.0 */ public static final int STRUCT = 2002; /** * The type code that identifies the SQL type {@code TIME}. - * - * @since Android 1.0 */ public static final int TIME = 92; /** * The type code that identifies the SQL type {@code TIMESTAMP}. - * - * @since Android 1.0 */ public static final int TIMESTAMP = 93; /** * The type code that identifies the SQL type {@code TINYINT}. - * - * @since Android 1.0 */ public static final int TINYINT = -6; /** * The type code that identifies the SQL type {@code VARBINARY}. - * - * @since Android 1.0 */ public static final int VARBINARY = -3; /** * The type code that identifies the SQL type {@code VARCHAR}. - * - * @since Android 1.0 */ public static final int VARCHAR = 12; } diff --git a/libcore/sql/src/main/java/javax/sql/ConnectionEvent.java b/libcore/sql/src/main/java/javax/sql/ConnectionEvent.java index e07e7c15e..cce8a78c7 100644 --- a/libcore/sql/src/main/java/javax/sql/ConnectionEvent.java +++ b/libcore/sql/src/main/java/javax/sql/ConnectionEvent.java @@ -25,23 +25,20 @@ import java.io.Serializable; * Sent when specific events happen on a {@link PooledConnection} object. These * events are a facility to report when an application closes the pooled * connection or when an error occurs in the pooled connection. - * - * @since Android 1.0 */ public class ConnectionEvent extends EventObject implements Serializable { private static final long serialVersionUID = -4843217645290030002L; - private SQLException theSQLException; + private SQLException ex; /** * Creates a connection event initialized with the supplied {@code * PooledConnection} reporting that the application has closed the * connection. - * + * * @param theConnection * the connection for which this event is created. - * @since Android 1.0 */ public ConnectionEvent(PooledConnection theConnection) { super(theConnection); @@ -51,29 +48,27 @@ public class ConnectionEvent extends EventObject implements Serializable { * Creates a {@code ConnectionEvent} initialized with the supplied {@code * PooledConnection} and with the supplied {@code SQLException} indicating * that an error has occurred within the {@code PooledConnection}. - * + * * @param theConnection * the connection for which this event is created. * @param theException * information about the state of error that has occurred on the * application side. - * @since Android 1.0 */ public ConnectionEvent(PooledConnection theConnection, SQLException theException) { super(theConnection); - theSQLException = theException; + ex = theException; } /** * Gets the {@code SQLException} which holds information about the error * which occurred in the {@code PooledConnection}. - * + * * @return a {@code SQLException} containing information about the error. * May be {@code null} if no error has occurred. - * @since Android 1.0 */ public SQLException getSQLException() { - return theSQLException; + return ex; } } diff --git a/libcore/sql/src/main/java/javax/sql/ConnectionEventListener.java b/libcore/sql/src/main/java/javax/sql/ConnectionEventListener.java index 13338147c..eec0d5b98 100644 --- a/libcore/sql/src/main/java/javax/sql/ConnectionEventListener.java +++ b/libcore/sql/src/main/java/javax/sql/ConnectionEventListener.java @@ -27,13 +27,9 @@ import java.util.EventListener; * to a {@code ConnectionEventListener} either when the application closes a * connection it has been using or when a significant error occurs while the * connection is being used. - *

          *

          * The connection pool manager can return closed connections to the pool for * later reuse. Connections experiencing an error should be discarded. - *

          - * - * @since Android 1.0 */ public interface ConnectionEventListener extends EventListener { @@ -44,7 +40,6 @@ public interface ConnectionEventListener extends EventListener { * @param theEvent * a {@code ConnectionEvent} containing details about the source * of the event. - * @since Android 1.0 */ public void connectionClosed(ConnectionEvent theEvent); @@ -57,7 +52,6 @@ public interface ConnectionEventListener extends EventListener { * @param theEvent * a {@code ConnectionEvent} containing details about the source * of the event and the {@code SQLException} that has occurred. - * @since Android 1.0 */ public void connectionErrorOccurred(ConnectionEvent theEvent); } diff --git a/libcore/sql/src/main/java/javax/sql/ConnectionPoolDataSource.java b/libcore/sql/src/main/java/javax/sql/ConnectionPoolDataSource.java index d73128bc9..379fde059 100644 --- a/libcore/sql/src/main/java/javax/sql/ConnectionPoolDataSource.java +++ b/libcore/sql/src/main/java/javax/sql/ConnectionPoolDataSource.java @@ -27,9 +27,6 @@ import java.io.PrintWriter; * A class which implements the {@code ConnectionPoolDataSource} interface is * typically registered with a JNDI naming service directory and is retrieved * from there by name. - *

          - * - * @since Android 1.0 */ public interface ConnectionPoolDataSource { @@ -44,7 +41,6 @@ public interface ConnectionPoolDataSource { * @return the login timeout value in seconds. * @throws SQLException * if there is a problem accessing the database. - * @since Android 1.0 */ public int getLoginTimeout() throws SQLException; @@ -58,14 +54,12 @@ public interface ConnectionPoolDataSource { * created is {@code null}. Note that the log writer for an {@code * ConnectionPoolDataSource} is not the same as the log writer used by a * {@code DriverManager}. - *

          - * + * * @return a {@code PrintWriter} which is the log writer for this {@code * ConnectionPoolDataSource}. Can be {@code null}, in which case log * writing is disabled for this {@code ConnectionPoolDataSource}. * @throws SQLException * if there is a problem accessing the database. - * @since Android 1.0 */ public PrintWriter getLogWriter() throws SQLException; @@ -77,7 +71,6 @@ public interface ConnectionPoolDataSource { * database. * @throws SQLException * if there is a problem accessing the database. - * @since Android 1.0 */ public PooledConnection getPooledConnection() throws SQLException; @@ -94,7 +87,6 @@ public interface ConnectionPoolDataSource { * to the database. * @throws SQLException * if there is a problem accessing the database. - * @since Android 1.0 */ public PooledConnection getPooledConnection(String theUser, String thePassword) throws SQLException; @@ -111,7 +103,6 @@ public interface ConnectionPoolDataSource { * the new login timeout value in seconds. * @throws SQLException * if there is a problem accessing the database. - * @since Android 1.0 */ public void setLoginTimeout(int theTimeout) throws SQLException; @@ -125,13 +116,11 @@ public interface ConnectionPoolDataSource { * is {@code null}. Note that the log writer for a {@code * ConnectionPoolDataSource} is not the same as the log writer used by a * {@code DriverManager}. - *

          * * @param theWriter * is the log writer for this {@code ConnectionPoolDataSource}. * @throws SQLException * if there is a problem accessing the database. - * @since Android 1.0 */ public void setLogWriter(PrintWriter theWriter) throws SQLException; } diff --git a/libcore/sql/src/main/java/javax/sql/DataSource.java b/libcore/sql/src/main/java/javax/sql/DataSource.java index 98be7618d..7f739479e 100644 --- a/libcore/sql/src/main/java/javax/sql/DataSource.java +++ b/libcore/sql/src/main/java/javax/sql/DataSource.java @@ -29,12 +29,10 @@ import java.io.PrintWriter; * A class which implements the {@code DataSource} interface is typically * registered with a JNDI naming service directory and is retrieved from there * by name. - *

          *

          * The {@code DataSource} interface is typically implemented by the writer of a * JDBC driver. There are three variants of the {@code DataSource} interface, * which produce connections with different characteristics: - *

          *
            *
          1. Standard {@code DataSource}: produces standard {@code Connection} * objects with no special features.
          2. @@ -51,9 +49,6 @@ import java.io.PrintWriter; * Note that a JDBC driver which is accessed via the {@code DataSource} * interface is loaded via a JNDI lookup process. A driver loaded in this way * does not register itself with the {@code DriverManager}. - *

            - * - * @since Android 1.0 */ public interface DataSource { @@ -65,7 +60,6 @@ public interface DataSource { * database. * @throws SQLException * if there is a problem accessing the database. - * @since Android 1.0 */ public Connection getConnection() throws SQLException; @@ -82,7 +76,6 @@ public interface DataSource { * database. * @throws SQLException * if there is a problem accessing the database. - * @since Android 1.0 */ public Connection getConnection(String theUsername, String thePassword) throws SQLException; @@ -97,7 +90,6 @@ public interface DataSource { * @return the login timeout value in seconds. * @throws SQLException * if there is a problem accessing the database. - * @since Android 1.0 */ public int getLoginTimeout() throws SQLException; @@ -110,14 +102,12 @@ public interface DataSource { * log writer when an {@code DataSource} is created is {@code null}. Note * that the log writer for a {@code DataSource} is not the same as the log * writer used by a {@code DriverManager}. - *

            - * + * * @return a {@code PrintWriter} which is the log writer for this {@code * DataSource}. Can be {@code null}, in which case log writing is * disabled for this {@code DataSource}. * @throws SQLException * if there is a problem accessing the database. - * @since Android 1.0 */ public PrintWriter getLogWriter() throws SQLException; @@ -132,7 +122,6 @@ public interface DataSource { * the new login timeout value in seconds. * @throws SQLException * if there is a problem accessing the database. - * @since Android 1.0 */ public void setLoginTimeout(int theTimeout) throws SQLException; @@ -145,14 +134,12 @@ public interface DataSource { * log writer when a {@code DataSource} is created is {@code null}. Note * that the log writer for a {@code DataSource} is not the same as the log * writer used by a {@code DriverManager}. - *

            * * @param theWriter * a {@code PrintWriter} to use as the log writer for this * {@code DataSource}. * @throws SQLException * if there is a problem accessing the database. - * @since Android 1.0 */ public void setLogWriter(PrintWriter theWriter) throws SQLException; } diff --git a/libcore/sql/src/main/java/javax/sql/PooledConnection.java b/libcore/sql/src/main/java/javax/sql/PooledConnection.java index b4c561649..b268e0c07 100644 --- a/libcore/sql/src/main/java/javax/sql/PooledConnection.java +++ b/libcore/sql/src/main/java/javax/sql/PooledConnection.java @@ -30,13 +30,11 @@ import java.sql.Connection; * process both to establish a connection to a database and to destroy the * connection. Reusing connections through a pool is a way of improving system * performance and reducing overhead. - *

            *

            * It is not intended that an application uses the {@code PooledConnection} * interface directly. The {@code PooledConnection} interface is intended for * use by a component called a connection pool manager, typically part of the * infrastructure that supports use of the database by applications. - *

            *

            * Applications obtain connections to the database by calling the * {@link DataSource#getConnection} method. Behind the scenes, the connection @@ -44,7 +42,6 @@ import java.sql.Connection; * pool and passes back a connection object that wraps or references the {@code * PooledConnection} object. A new {@code PooledConnection} object will only be * created if the pool is empty. - *

            *

            * When the application is finished using a {@code PooledConnection}, the * application calls the {@link Connection#close} method. The connection pool @@ -54,7 +51,6 @@ import java.sql.Connection; * the underlying {@code PooledConnection} object from the connection and * returns it to the pool for reuse - the {@code PooledConnection} is thus * recycled rather than being destroyed. - *

            *

            * The connection to the database represented by the {@code PooledConnection} is * kept open until the {@code PooledConnection} object itself is deactivated by @@ -62,9 +58,6 @@ import java.sql.Connection; * This is typically done if there are too many inactive connections in the * pool, if the {@code PooledConnection} encounters a problem that makes it * unusable or if the whole system is being shut down. - *

            - * - * @since Android 1.0 */ public interface PooledConnection { @@ -77,7 +70,6 @@ public interface PooledConnection { * @param theListener * an object which implements the {@code ConnectionEventListener} * interface. - * @since Android 1.0 */ public void addConnectionEventListener(ConnectionEventListener theListener); @@ -89,7 +81,6 @@ public interface PooledConnection { * * @throws SQLException * if there is a problem accessing the database. - * @since Android 1.0 */ public void close() throws SQLException; @@ -102,7 +93,6 @@ public interface PooledConnection { * @return a {@code Connection} object. * @throws SQLException * if there is a problem accessing the database. - * @since Android 1.0 */ public Connection getConnection() throws SQLException; @@ -116,7 +106,6 @@ public interface PooledConnection { * interface. This object should have previously been registered * with the {@code PooledConnection} using the {@code * addConnectionEventListener} method. - * @since Android 1.0 */ public void removeConnectionEventListener( ConnectionEventListener theListener); diff --git a/libcore/sql/src/main/java/javax/sql/RowSet.java b/libcore/sql/src/main/java/javax/sql/RowSet.java index 4edc3d37a..77321a26f 100644 --- a/libcore/sql/src/main/java/javax/sql/RowSet.java +++ b/libcore/sql/src/main/java/javax/sql/RowSet.java @@ -43,14 +43,12 @@ import java.math.BigDecimal; * distinct data values which constitute the row set. The {@code RowSet} class * supports JavaBean events so that other components in an application can be * informed when changes happen such as changes in data values. - *

            *

            * {@code RowSet} is a facility implemented on top of the remainder of the JDBC * API. It may be connected, maintaining a connection to the database * throughout its lifecycle. The changes made on a disconnected {@code * RowSet} on the other hand can be persisted only establishing a new connection * with the database each time. - *

            *

            * Disconnected {@code RowSets} make use of {@code RowSetReaders} to populate * the {@code RowSet} with data, possibly from a non-relational database source. @@ -58,11 +56,9 @@ import java.math.BigDecimal; * data store. There is considerable freedom in the way that {@code * RowSetReaders} and {@code RowSetWriters} may be implemented to retrieve and * store data. - *

            - * + * * @see RowSetReader * @see RowSetWriter - * @since Android 1.0 */ public interface RowSet extends ResultSet { @@ -74,7 +70,6 @@ public interface RowSet extends ResultSet { * @param theListener * an object which implements the {@code rowSetListener} * interface. - * @since Android 1.0 */ public void addRowSetListener(RowSetListener theListener); @@ -85,11 +80,9 @@ public interface RowSet extends ResultSet { * a parameter is set or its value is actively reset. {@code * clearParameters} provides a facility to clear the values for all * parameters with one method call. - *

            - * + * * @throws SQLException * if a problem occurs accessing the database. - * @since Android 1.0 */ public void clearParameters() throws SQLException; @@ -104,20 +97,17 @@ public interface RowSet extends ResultSet { * transaction isolation, type map; plus some or all of the properties: * command, read only, maximum field size, maximum rows, escape processing, * and query timeout. - *

            *

            * The {@code RowSet} may use a {@code RowSetReader} to access the database * it will then invoke the {@link RowSetReader#readData} method on the * reader to fetch the data. When the new data is fetched all the listeners * are notified to take appropriate measures. - *

            - * + * * @throws SQLException * if a problem occurs accessing the database or if the * properties needed to access the database have not been set. * @see RowSetMetaData * @see RowSetReader - * @since Android 1.0 */ public void execute() throws SQLException; @@ -127,7 +117,6 @@ public interface RowSet extends ResultSet { * @return a string containing the {@code RowSet}'s command property. A * command is a SQL statement which is executed to fetch required * data into the {@code RowSet}. - * @since Android 1.0 */ public String getCommand(); @@ -138,7 +127,6 @@ public interface RowSet extends ResultSet { * can then be used to create a connection to the database. * * @return the name of the database. - * @since Android 1.0 */ public String getDataSourceName(); @@ -152,7 +140,6 @@ public interface RowSet extends ResultSet { * false} otherwise. * @throws SQLException * if a problem occurs accessing the database. - * @since Android 1.0 */ public boolean getEscapeProcessing() throws SQLException; @@ -166,7 +153,6 @@ public interface RowSet extends ResultSet { * @return the current maximum size in bytes. 0 implies no size limit. * @throws SQLException * if a problem occurs accessing the database. - * @since Android 1.0 */ public int getMaxFieldSize() throws SQLException; @@ -177,7 +163,6 @@ public interface RowSet extends ResultSet { * @return the previous maximum number of rows. 0 implies no row limit. * @throws SQLException * if a problem occurs accessing the database. - * @since Android 1.0 */ public int getMaxRows() throws SQLException; @@ -187,7 +172,6 @@ public interface RowSet extends ResultSet { * Therefore it should be set prior to invoking the {@link #execute} method. * * @return the value of the password property. - * @since Android 1.0 */ public String getPassword(); @@ -199,7 +183,6 @@ public interface RowSet extends ResultSet { * @return the timeout value in seconds. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public int getQueryTimeout() throws SQLException; @@ -217,7 +200,6 @@ public interface RowSet extends ResultSet { *
          3. {@code Connection.TRANSACTION_SERIALIZABLE}
          4. * * @see java.sql.Connection - * @since Android 1.0 */ public int getTransactionIsolation(); @@ -228,7 +210,6 @@ public interface RowSet extends ResultSet { * @return the custom mappings of SQL types to Java classes. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public Map> getTypeMap() throws SQLException; @@ -241,7 +222,6 @@ public interface RowSet extends ResultSet { * @return a String holding the value of the URL property. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public String getUrl() throws SQLException; @@ -252,7 +232,6 @@ public interface RowSet extends ResultSet { * * @return a {@code String} holding the value of the {@code username} * property. - * @since Android 1.0 */ public String getUsername(); @@ -261,7 +240,6 @@ public interface RowSet extends ResultSet { * * @return {@code true} if this {@code RowSet} is read-only, {@code false} * if it is updatable. - * @since Android 1.0 */ public boolean isReadOnly(); @@ -272,7 +250,6 @@ public interface RowSet extends ResultSet { * @param theListener * the {@link RowSetListener} to remove from the set of listeners * for this {@code RowSet}. - * @since Android 1.0 */ public void removeRowSetListener(RowSetListener theListener); @@ -287,7 +264,6 @@ public interface RowSet extends ResultSet { * the {@code Array} data value to which the parameter is set. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public void setArray(int parameterIndex, Array theArray) throws SQLException; @@ -306,7 +282,6 @@ public interface RowSet extends ResultSet { * the length of the data in bytes. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public void setAsciiStream(int parameterIndex, InputStream theInputStream, int length) throws SQLException; @@ -323,7 +298,6 @@ public interface RowSet extends ResultSet { * the big decimal value to which the parameter is set. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public void setBigDecimal(int parameterIndex, BigDecimal theBigDecimal) throws SQLException; @@ -342,7 +316,6 @@ public interface RowSet extends ResultSet { * the length of the data in bytes. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public void setBinaryStream(int parameterIndex, InputStream theInputStream, int length) throws SQLException; @@ -358,7 +331,6 @@ public interface RowSet extends ResultSet { * the {@code Blob} value to which the parameter is set. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public void setBlob(int parameterIndex, Blob theBlob) throws SQLException; @@ -373,7 +345,6 @@ public interface RowSet extends ResultSet { * the {@code boolean} value to which the parameter is set. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public void setBoolean(int parameterIndex, boolean theBoolean) throws SQLException; @@ -389,7 +360,6 @@ public interface RowSet extends ResultSet { * the {@code byte} value to which the parameter is set. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public void setByte(int parameterIndex, byte theByte) throws SQLException; @@ -404,7 +374,6 @@ public interface RowSet extends ResultSet { * the {@code Array} of {@code bytes} to which the parameter is set. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public void setBytes(int parameterIndex, byte[] theByteArray) throws SQLException; @@ -424,7 +393,6 @@ public interface RowSet extends ResultSet { * the length of the data in the {@code Reader} in characters. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public void setCharacterStream(int parameterIndex, Reader theReader, int length) throws SQLException; @@ -440,7 +408,6 @@ public interface RowSet extends ResultSet { * the {@code Clob} value to which the parameter is set. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public void setClob(int parameterIndex, Clob theClob) throws SQLException; @@ -453,7 +420,6 @@ public interface RowSet extends ResultSet { * the SQL query. Can be {@code null}. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public void setCommand(String cmd) throws SQLException; @@ -470,7 +436,6 @@ public interface RowSet extends ResultSet { * @throws SQLException * if an error occurs accessing the database. * @see java.sql.ResultSet - * @since Android 1.0 */ public void setConcurrency(int concurrency) throws SQLException; @@ -480,13 +445,11 @@ public interface RowSet extends ResultSet { * The database name can be used to find a {@link DataSource} which has been * registered with a naming service - the {@link DataSource} can then be * used to create a connection to the database. - *

            - * + * * @param name * the database name. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public void setDataSourceName(String name) throws SQLException; @@ -501,7 +464,6 @@ public interface RowSet extends ResultSet { * the date value to which the parameter is set. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public void setDate(int parameterIndex, Date theDate) throws SQLException; @@ -521,7 +483,6 @@ public interface RowSet extends ResultSet { * {@code DATE} value. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public void setDate(int parameterIndex, Date theDate, Calendar theCalendar) throws SQLException; @@ -537,7 +498,6 @@ public interface RowSet extends ResultSet { * the {@code double} value to which the parameter is set. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public void setDouble(int parameterIndex, double theDouble) throws SQLException; @@ -553,7 +513,6 @@ public interface RowSet extends ResultSet { * turn it off. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public void setEscapeProcessing(boolean enable) throws SQLException; @@ -568,7 +527,6 @@ public interface RowSet extends ResultSet { * the {@code float} value to which the parameter is set. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public void setFloat(int parameterIndex, float theFloat) throws SQLException; @@ -584,7 +542,6 @@ public interface RowSet extends ResultSet { * the {@code integer} value to which the parameter is set. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public void setInt(int parameterIndex, int theInteger) throws SQLException; @@ -599,7 +556,6 @@ public interface RowSet extends ResultSet { * the {@code long} value value to which the parameter is set. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public void setLong(int parameterIndex, long theLong) throws SQLException; @@ -615,7 +571,6 @@ public interface RowSet extends ResultSet { * implies no size limit. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public void setMaxFieldSize(int max) throws SQLException; @@ -628,7 +583,6 @@ public interface RowSet extends ResultSet { * RowSet}. 0 means no limit. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public void setMaxRows(int max) throws SQLException; @@ -644,7 +598,6 @@ public interface RowSet extends ResultSet { * java.sql.Types}. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public void setNull(int parameterIndex, int sqlType) throws SQLException; @@ -665,7 +618,6 @@ public interface RowSet extends ResultSet { * type. Ignored if the sqlType is not a UDT or REF type. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public void setNull(int parameterIndex, int sqlType, String typeName) throws SQLException; @@ -677,8 +629,7 @@ public interface RowSet extends ResultSet { * The JDBC specification provides a standard mapping for Java objects to * SQL data types. Database specific types can be mapped by JDBC driver * specific Java types. - *

            - * + * * @param parameterIndex * the index of the parameter to set; the first parameter's index * is 1. @@ -687,7 +638,6 @@ public interface RowSet extends ResultSet { * parameter is set. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public void setObject(int parameterIndex, Object theObject) throws SQLException; @@ -706,7 +656,6 @@ public interface RowSet extends ResultSet { * java.sql.Types}. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public void setObject(int parameterIndex, Object theObject, int targetSqlType) throws SQLException; @@ -729,7 +678,6 @@ public interface RowSet extends ResultSet { * types. Ignored for all other types. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public void setObject(int parameterIndex, Object theObject, int targetSqlType, int scale) throws SQLException; @@ -743,7 +691,6 @@ public interface RowSet extends ResultSet { * a {@code String} holding the password. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public void setPassword(String password) throws SQLException; @@ -755,7 +702,6 @@ public interface RowSet extends ResultSet { * the number of seconds for the timeout. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public void setQueryTimeout(int seconds) throws SQLException; @@ -767,7 +713,6 @@ public interface RowSet extends ResultSet { * {@code false} to allow updates. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public void setReadOnly(boolean readOnly) throws SQLException; @@ -784,7 +729,6 @@ public interface RowSet extends ResultSet { * @throws SQLException * if an error occurs accessing the database. * @see java.sql.Ref - * @since Android 1.0 */ public void setRef(int parameterIndex, Ref theRef) throws SQLException; @@ -799,7 +743,6 @@ public interface RowSet extends ResultSet { * the value to which the parameter is set. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public void setShort(int parameterIndex, short theShort) throws SQLException; @@ -817,7 +760,6 @@ public interface RowSet extends ResultSet { * the value to which the parameter is set. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public void setString(int parameterIndex, String theString) throws SQLException; @@ -836,7 +778,6 @@ public interface RowSet extends ResultSet { * if an error occurs accessing the database. * @see java.util.Calendar * @see java.sql.Time - * @since Android 1.0 */ public void setTime(int parameterIndex, Time theTime) throws SQLException; @@ -856,7 +797,6 @@ public interface RowSet extends ResultSet { * if an error occurs accessing the database. * @see java.util.Calendar * @see java.sql.Time - * @since Android 1.0 */ public void setTime(int parameterIndex, Time theTime, Calendar theCalendar) throws SQLException; @@ -875,7 +815,6 @@ public interface RowSet extends ResultSet { * if an error occurs accessing the database. * @see java.util.Calendar * @see java.sql.Timestamp - * @since Android 1.0 */ public void setTimestamp(int parameterIndex, Timestamp theTimestamp) throws SQLException; @@ -896,7 +835,6 @@ public interface RowSet extends ResultSet { * if an error occurs accessing the database. * @see java.util.Calendar * @see java.sql.Timestamp - * @since Android 1.0 */ public void setTimestamp(int parameterIndex, Timestamp theTimestamp, Calendar theCalendar) throws SQLException; @@ -909,8 +847,7 @@ public interface RowSet extends ResultSet { *

            * Keep in mind that setting a transaction isolation level has no effect * unless your driver and DBMS support it. - *

            - * + * * @param level * the transaction isolation level. One of: *
              @@ -922,7 +859,6 @@ public interface RowSet extends ResultSet { * @throws SQLException * if an error occurs accessing the database. * @see java.sql.Connection - * @since Android 1.0 */ public void setTransactionIsolation(int level) throws SQLException; @@ -939,7 +875,6 @@ public interface RowSet extends ResultSet { *
            * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public void setType(int type) throws SQLException; @@ -953,7 +888,6 @@ public interface RowSet extends ResultSet { * mapped. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public void setTypeMap(Map> theTypeMap) throws SQLException; @@ -967,7 +901,6 @@ public interface RowSet extends ResultSet { * the URL for the database. Can be {@code null}. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public void setUrl(String theURL) throws SQLException; @@ -979,7 +912,6 @@ public interface RowSet extends ResultSet { * the new user name for this row set. * @throws SQLException * if an error occurs accessing the database. - * @since Android 1.0 */ public void setUsername(String theUsername) throws SQLException; } diff --git a/libcore/sql/src/main/java/javax/sql/RowSetEvent.java b/libcore/sql/src/main/java/javax/sql/RowSetEvent.java index 9d4c98c19..d18e9ceac 100644 --- a/libcore/sql/src/main/java/javax/sql/RowSetEvent.java +++ b/libcore/sql/src/main/java/javax/sql/RowSetEvent.java @@ -33,9 +33,6 @@ import java.io.Serializable; * The event contains a reference to the {@code RowSet} object which generated * the message so that the listeners can extract whatever information they need * from that reference. - *

            - * - * @since Android 1.0 */ public class RowSetEvent extends EventObject implements Serializable { @@ -50,7 +47,6 @@ public class RowSetEvent extends EventObject implements Serializable { * * @param theSource * the {@code RowSet} which generated the event. - * @since Android 1.0 */ public RowSetEvent(RowSet theSource) { super(theSource); diff --git a/libcore/sql/src/main/java/javax/sql/RowSetInternal.java b/libcore/sql/src/main/java/javax/sql/RowSetInternal.java index baa261d8a..78de4b0b5 100644 --- a/libcore/sql/src/main/java/javax/sql/RowSetInternal.java +++ b/libcore/sql/src/main/java/javax/sql/RowSetInternal.java @@ -22,9 +22,9 @@ import java.sql.Connection; import java.sql.ResultSet; /** - * An interface provided by a {@code RowSet} object to let either a {@code RowSetReader} or a - * {@code RowSetWriter} access its internal state, thereby providing facilities to read and update the state of - * the {@code RowSet}. + * An interface provided by a {@code RowSet} object to let either a {@code + * RowSetReader} or a {@code RowSetWriter} access its internal state, thereby + * providing facilities to read and update the state of the {@code RowSet}. */ public interface RowSetInternal { @@ -34,7 +34,6 @@ public interface RowSetInternal { * @return the connection or {@code null}. * @throws SQLException * if there is a problem accessing the database. - * @since Android 1.0 */ public Connection getConnection() throws SQLException; @@ -44,13 +43,11 @@ public interface RowSetInternal { *

            * The {@code ResultSet}'s cursor is positioned before the first row of * data. - *

            * * @return the {@code ResultSet} that contained the original data value of * the {@code RowSet}. * @throws SQLException * if there is a problem accessing the database. - * @since Android 1.0 */ public ResultSet getOriginal() throws SQLException; @@ -63,7 +60,6 @@ public interface RowSetInternal { * if there is a problem accessing the database, or if the * cursor is not on a valid row (before the first row, after the * last one or pointing to the insert row). - * @since Android 1.0 */ public ResultSet getOriginalRow() throws SQLException; @@ -74,7 +70,6 @@ public interface RowSetInternal { * @return the values of parameters that have been set. * @throws SQLException * if there is a problem accessing the database. - * @since Android 1.0 */ public Object[] getParams() throws SQLException; @@ -87,7 +82,6 @@ public interface RowSetInternal { * holds the metadata about the {@code RowSet}'s columns. * @throws SQLException * if there is a problem accessing the database. - * @since Android 1.0 */ public void setMetaData(RowSetMetaData theMetaData) throws SQLException; } diff --git a/libcore/sql/src/main/java/javax/sql/RowSetListener.java b/libcore/sql/src/main/java/javax/sql/RowSetListener.java index 06a7253e1..ede019786 100644 --- a/libcore/sql/src/main/java/javax/sql/RowSetListener.java +++ b/libcore/sql/src/main/java/javax/sql/RowSetListener.java @@ -25,8 +25,6 @@ import java.util.EventListener; * implement the {@code RowSetListener} interface and then register itself with * the {@code RowSet} of interest using the * {@link RowSet#addRowSetListener(RowSetListener)} method. - * - * @since Android 1.0 */ public interface RowSetListener extends EventListener { @@ -39,7 +37,6 @@ public interface RowSetListener extends EventListener { * {@code RowSet} involved. This information can be used to * retrieve information about the change, such as the updated * data values. - * @since Android 1.0 */ public void cursorMoved(RowSetEvent theEvent); @@ -52,7 +49,6 @@ public interface RowSetListener extends EventListener { * {@code RowSet} involved. This information can be used to * retrieve information about the change, such as the new cursor * position. - * @since Android 1.0 */ public void rowChanged(RowSetEvent theEvent); @@ -66,7 +62,6 @@ public interface RowSetListener extends EventListener { * {@code RowSet} involved. This information can be used to * retrieve information about the change, such as the updated * rows of data. - * @since Android 1.0 */ public void rowSetChanged(RowSetEvent theEvent); } diff --git a/libcore/sql/src/main/java/javax/sql/RowSetMetaData.java b/libcore/sql/src/main/java/javax/sql/RowSetMetaData.java index 3051876db..ad07199a6 100644 --- a/libcore/sql/src/main/java/javax/sql/RowSetMetaData.java +++ b/libcore/sql/src/main/java/javax/sql/RowSetMetaData.java @@ -26,14 +26,11 @@ import java.sql.SQLException; *

            * {@code RowSetMetaData} extends {@link java.sql.ResultSetMetaData}, adding new * operations for carrying out value sets. - *

            *

            * Application code would not normally call this interface directly. It would be * called internally when {@code RowSet.execute} is called. - *

            - * + * * @see RowSetInternal#setMetaData(RowSetMetaData) - * @since Android 1.0 */ public interface RowSetMetaData extends ResultSetMetaData { @@ -50,7 +47,6 @@ public interface RowSetMetaData extends ResultSetMetaData { * turn it off (default). * @throws SQLException * if a problem occurs accessing the database. - * @since Android 1.0 */ public void setAutoIncrement(int columnIndex, boolean autoIncrement) throws SQLException; @@ -67,7 +63,6 @@ public interface RowSetMetaData extends ResultSetMetaData { * to make it case insensitive (default). * @throws SQLException * if a problem occurs accessing the database. - * @since Android 1.0 */ public void setCaseSensitive(int columnIndex, boolean caseSensitive) throws SQLException; @@ -82,7 +77,6 @@ public interface RowSetMetaData extends ResultSetMetaData { * the new catalog's name. * @throws SQLException * if a problem occurs accessing the database. - * @since Android 1.0 */ public void setCatalogName(int columnIndex, String catalogName) throws SQLException; @@ -94,7 +88,6 @@ public interface RowSetMetaData extends ResultSetMetaData { * the number of columns contained in the {@code RowSet}. * @throws SQLException * if a problem occurs accessing the database. - * @since Android 1.0 */ public void setColumnCount(int columnCount) throws SQLException; @@ -109,7 +102,6 @@ public interface RowSetMetaData extends ResultSetMetaData { * the normal maximum column width in characters. * @throws SQLException * if a problem occurs accessing the database. - * @since Android 1.0 */ public void setColumnDisplaySize(int columnIndex, int displaySize) throws SQLException; @@ -125,7 +117,6 @@ public interface RowSetMetaData extends ResultSetMetaData { * the alias name for the column. * @throws SQLException * if a problem occurs accessing the database. - * @since Android 1.0 */ public void setColumnLabel(int columnIndex, String theLabel) throws SQLException; @@ -140,7 +131,6 @@ public interface RowSetMetaData extends ResultSetMetaData { * the column's label. * @throws SQLException * if a problem occurs accessing the database. - * @since Android 1.0 */ public void setColumnName(int columnIndex, String theColumnName) throws SQLException; @@ -155,7 +145,6 @@ public interface RowSetMetaData extends ResultSetMetaData { * the SQL Type, as defined by {@code java.sql.Types}. * @throws SQLException * if a problem occurs accessing the database. - * @since Android 1.0 */ public void setColumnType(int columnIndex, int theSQLType) throws SQLException; @@ -171,7 +160,6 @@ public interface RowSetMetaData extends ResultSetMetaData { * the SQL type name for the column. * @throws SQLException * if a problem occurs accessing the database. - * @since Android 1.0 */ public void setColumnTypeName(int columnIndex, String theTypeName) throws SQLException; @@ -189,7 +177,6 @@ public interface RowSetMetaData extends ResultSetMetaData { * value (default). * @throws SQLException * if a problem occurs accessing the database. - * @since Android 1.0 */ public void setCurrency(int columnIndex, boolean isCurrency) throws SQLException; @@ -210,10 +197,8 @@ public interface RowSetMetaData extends ResultSetMetaData { *

            * The default value is {@code * ResultSetMetaData.columnNullableUnknown}. - *

            * @throws SQLException * if a problem occurs accessing the database. - * @since Android 1.0 */ public void setNullable(int columnIndex, int nullability) throws SQLException; @@ -229,7 +214,6 @@ public interface RowSetMetaData extends ResultSetMetaData { * the number of decimal digits. * @throws SQLException * if a problem occurs accessing the database. - * @since Android 1.0 */ public void setPrecision(int columnIndex, int thePrecision) throws SQLException; @@ -245,7 +229,6 @@ public interface RowSetMetaData extends ResultSetMetaData { * the number of digits after the decimal point. * @throws SQLException * if a problem occurs accessing the database. - * @since Android 1.0 */ public void setScale(int columnIndex, int theScale) throws SQLException; @@ -259,7 +242,6 @@ public interface RowSetMetaData extends ResultSetMetaData { * a {@code String} containing the schema name. * @throws SQLException * if a problem occurs accessing the database. - * @since Android 1.0 */ public void setSchemaName(int columnIndex, String theSchemaName) throws SQLException; @@ -276,7 +258,6 @@ public interface RowSetMetaData extends ResultSetMetaData { * clause search, {@code false} otherwise. * @throws SQLException * if a problem occurs accessing the database. - * @since Android 1.0 */ public void setSearchable(int columnIndex, boolean isSearchable) throws SQLException; @@ -292,7 +273,6 @@ public interface RowSetMetaData extends ResultSetMetaData { * false} otherwise. * @throws SQLException * if a problem occurs accessing the database. - * @since Android 1.0 */ public void setSigned(int columnIndex, boolean isSigned) throws SQLException; @@ -307,7 +287,6 @@ public interface RowSetMetaData extends ResultSetMetaData { * the table name for the column. * @throws SQLException * if a problem occurs accessing the database. - * @since Android 1.0 */ public void setTableName(int columnIndex, String theTableName) throws SQLException; diff --git a/libcore/sql/src/main/java/javax/sql/RowSetReader.java b/libcore/sql/src/main/java/javax/sql/RowSetReader.java index d4a902fed..0ff5c90d6 100644 --- a/libcore/sql/src/main/java/javax/sql/RowSetReader.java +++ b/libcore/sql/src/main/java/javax/sql/RowSetReader.java @@ -27,7 +27,6 @@ import java.sql.SQLException; * RowSet} for this to work. * * @see RowSet - * @since Android 1.0 */ public interface RowSetReader { @@ -44,7 +43,6 @@ public interface RowSetReader { * events are sent to listeners - any listeners are informed by the calling * {@code RowSet}'s {@code execute} method once the reader returns from the * {@code readData} method. - *

            * * @param theCaller * must be the calling {@code RowSet} object, which must have @@ -53,7 +51,6 @@ public interface RowSetReader { * if a problem occurs accessing the database or if the reader * calls the {@link RowSet#execute()} method. * @see RowSetInternal - * @since Android 1.0 */ public void readData(RowSetInternal theCaller) throws SQLException; } diff --git a/libcore/sql/src/main/java/javax/sql/RowSetWriter.java b/libcore/sql/src/main/java/javax/sql/RowSetWriter.java index 34473b207..8e56d5002 100644 --- a/libcore/sql/src/main/java/javax/sql/RowSetWriter.java +++ b/libcore/sql/src/main/java/javax/sql/RowSetWriter.java @@ -28,16 +28,13 @@ import java.sql.SQLException; * The writer must establish a connection to the {@code RowSet}'s database * before writing the data. The {@code RowSet} calling this interface must * implement the {@code RowSetInternal} interface. - *

            *

            * The writer may encounter a situation where the updated data needs to be * written back to the database, but has already been updated there in the mean * time. How a conflict of this kind is handled is determined by the * implementation of this writer. - *

            - * + * * @see RowSetInternal - * @since Android 1.0 */ public interface RowSetWriter { @@ -56,7 +53,6 @@ public interface RowSetWriter { * otherwise (which typically implies some form of conflict). * @throws SQLException * if a problem occurs accessing the database. - * @since Android 1.0 */ public boolean writeData(RowSetInternal theRowSet) throws SQLException; } diff --git a/libcore/sql/src/main/java/org/apache/harmony/sql/internal/nls/Messages.java b/libcore/sql/src/main/java/org/apache/harmony/sql/internal/nls/Messages.java index b3dbd32db..234bdc9a6 100644 --- a/libcore/sql/src/main/java/org/apache/harmony/sql/internal/nls/Messages.java +++ b/libcore/sql/src/main/java/org/apache/harmony/sql/internal/nls/Messages.java @@ -27,7 +27,6 @@ package org.apache.harmony.sql.internal.nls; - import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Locale; diff --git a/libcore/sql/src/main/java/org/apache/harmony/sql/internal/nls/messages.properties b/libcore/sql/src/main/java/org/apache/harmony/sql/internal/nls/messages.properties index 3e6ff1d5f..6927cf2c6 100644 --- a/libcore/sql/src/main/java/org/apache/harmony/sql/internal/nls/messages.properties +++ b/libcore/sql/src/main/java/org/apache/harmony/sql/internal/nls/messages.properties @@ -40,3 +40,22 @@ sql.22=Invalid position and substring length sql.23=Buffer is not sufficient to hold the value sql.24=Invalid length for truncate sql.25=Unsupported operation. SerialClob is not instantiated with a fully implemented Clob object. +sql.26=Invalid column count. Cannot be less or equal to zero +sql.27=Invalid column index :{0} +sql.28=Invalid SQL type for column +sql.29=Invalid nullable constant set. Must be either columnNoNulls, columnNullable or columnNullableUnknown +sql.30=Invalid column display size. Cannot be less than zero +sql.31=Invalid precision value. Cannot be less than zero +sql.32=Invalid scale size. Cannot be less than zero +sql.33=Cannot instantiate a SQLOutputImpl instance with null parameters +sql.34=Cannot instantiate a SQLInputImpl instance with null parameters +sql.35=SQLInputImpl exception: Invalid read position +sql.36=No more attributes +sql.37=Operation not supported +sql.38=Object is invalid +sql.39=Cannot instantiate a SerialArray object with a null Array object +sql.40=ClassNotFoundException: {0} +sql.41=Invalid JNDI context supplied +sql.42=Illegal Argument +sql.43=The object is not serializable +sql.44=No logger has been set diff --git a/libcore/sql/src/test/java/org/apache/harmony/sql/tests/java/sql/DateTest.java b/libcore/sql/src/test/java/org/apache/harmony/sql/tests/java/sql/DateTest.java index 3b833c482..39f507445 100644 --- a/libcore/sql/src/test/java/org/apache/harmony/sql/tests/java/sql/DateTest.java +++ b/libcore/sql/src/test/java/org/apache/harmony/sql/tests/java/sql/DateTest.java @@ -293,17 +293,27 @@ public class DateTest extends TestCase { args = {} ) public void testToString() { - // This test is set up for GMT time zone, so need to set the time zone - // to GMT first - TimeZone.setDefault(TimeZone.getTimeZone("GMT")); - - for (int i = 0; i < TIME_ARRAY.length; i++) { - Date theDate = new Date(TIME_ARRAY[i]); - assertEquals(SQL_DATEARRAY[i], theDate.toString()); + // Loop through the timezones testing the String conversion for each + for (int i = 0; i < TIMEZONES.length; i++) { + testToString(TIMEZONES[i], TIME_ARRAY, SQL_TZ_DATEARRAYS[i]); } // end for } // end method testToString() + private void testToString(String timeZone, long[] theDates, String[] theDateStrings) { + // Set the timezone + TimeZone.setDefault(TimeZone.getTimeZone(timeZone)); + + for (int i = 0; i < theDates.length; i++) { + // Create the Date object + Date theDate = new Date(theDates[i]); + // Convert to a date string ... and compare + String JDBCString = theDate.toString(); + assertEquals(theDateStrings[i], JDBCString); + } // end for + + } // end testToString( String, long[], String[] ) + /* * Test of the void setTime(int) method This does depend on the Time Zone * settings and sets up the time zone to one of a group of specific time @@ -372,7 +382,7 @@ public class DateTest extends TestCase { theDate = Date.valueOf(element); fail("Should throw IllegalArgumentException."); } catch (IllegalArgumentException e) { - //expected + // expected } // end try } // end for @@ -388,70 +398,77 @@ public class DateTest extends TestCase { args = {java.lang.String.class} ) public void test_valueOf_IllegalArgumentException() { - try{ + try { Date.valueOf("1996-10-07-01"); fail("should throw NumberFormatException"); } catch (NumberFormatException e) { - //expected + // expected } - try{ + try { Date.valueOf("-10-07-01"); fail("should throw IllegalArgumentException"); } catch (IllegalArgumentException e) { - //expected + // expected } - try{ + try { Date.valueOf("--01"); fail("should throw IllegalArgumentException"); } catch (IllegalArgumentException e) { - //expected + // expected } - try{ + try { Date.valueOf("1991--"); fail("should throw IllegalArgumentException"); } catch (IllegalArgumentException e) { - //expected + // expected } - try{ + try { Date.valueOf("-01-"); fail("should throw IllegalArgumentException"); } catch (IllegalArgumentException e) { - //expected + // expected } - try{ + try { Date.valueOf("-10-w2-01"); fail("should throw IllegalArgumentException"); } catch (IllegalArgumentException e) { - //expected + // expected } - try{ + try { Date.valueOf("07-w2-"); fail("should throw IllegalArgumentException"); } catch (IllegalArgumentException e) { - //expected + // expected } - try{ + try { Date.valueOf("1997-w2-w2"); fail("should throw NumberFormatException"); } catch (NumberFormatException e) { - //expected + // expected } - try{ + try { Date.valueOf("1996--01"); fail("should throw NumberFormatException"); } catch (NumberFormatException e) { - //expected + // expected } } + // Reset defualt timezone + static TimeZone defaultTimeZone = TimeZone.getDefault(); + + protected void tearDown(){ + TimeZone.setDefault(defaultTimeZone); + } + } // end class DateTest diff --git a/libcore/sql/src/test/java/org/apache/harmony/sql/tests/java/sql/DriverManagerTest.java b/libcore/sql/src/test/java/org/apache/harmony/sql/tests/java/sql/DriverManagerTest.java index 7e08db9d9..5552f4fcb 100644 --- a/libcore/sql/src/test/java/org/apache/harmony/sql/tests/java/sql/DriverManagerTest.java +++ b/libcore/sql/src/test/java/org/apache/harmony/sql/tests/java/sql/DriverManagerTest.java @@ -24,6 +24,7 @@ import dalvik.annotation.TestLevel; import dalvik.annotation.TestTargetNew; import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.io.PrintStream; import java.io.PrintWriter; import java.lang.reflect.Method; @@ -31,10 +32,12 @@ import java.security.Permission; import java.sql.Connection; import java.sql.Driver; import java.sql.DriverManager; +import java.sql.DriverPropertyInfo; import java.sql.SQLException; import java.sql.SQLPermission; import java.util.Enumeration; import java.util.Properties; +import tests.support.Support_Exec; import junit.framework.TestCase; @TestTargetClass(DriverManager.class) @@ -684,6 +687,77 @@ public class DriverManagerTest extends TestCase { } // end class TestSecurityManager + /** + * @tests {@link java.sql.DriverManager#registerDriver(Driver)} + * + * Registers a driver for multiple times and deregisters it only once. + * + * Regression for HARMONY-4205 + */ + public void test_registerDriver_MultiTimes() throws SQLException { + int register_count = 10; + int deregister_count = 1; + + Driver dummy = new DummyDriver(); + DriverManager.registerDriver(new BadDummyDriver()); + for (int i = 0; i < register_count; i++) { + DriverManager.registerDriver(dummy); + } + DriverManager.registerDriver(new BadDummyDriver()); + for (int i = 0; i < deregister_count; i++) { + DriverManager.deregisterDriver(dummy); + } + Driver d = DriverManager.getDriver("jdbc:dummy_protocol:dummy_subname"); + assertNotNull(d); + } + + /** + * Regression for HARMONY-4303 + */ + @KnownFailure(value="bug 2002061") + public void test_initClass() throws Exception { + String[] arg = new String[1]; + arg[0] = "org/apache/harmony/sql/tests/java/sql/TestMainForDriver"; + String result = Support_Exec.execJava(arg, null, true); + assertEquals("", result); + } + + private static class BadDummyDriver extends DummyDriver { + public boolean acceptsURL(String url) { + return false; + } + } + + private static class DummyDriver implements Driver { + + String goodurl = "jdbc:dummy_protocol:dummy_subname"; + + public boolean acceptsURL(String url) { + return url.equals(goodurl); + } + + public Connection connect(String url, Properties info) { + return null; + } + + public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) { + return null; + } + + public int getMajorVersion() { + return 0; + } + + public int getMinorVersion() { + return 0; + } + + public boolean jdbcCompliant() { + return true; + } + + } + } // end class DriverManagerTest diff --git a/libcore/sql/src/test/java/org/apache/harmony/sql/tests/java/sql/TestHelper_Connection1.java b/libcore/sql/src/test/java/org/apache/harmony/sql/tests/java/sql/TestHelper_Connection1.java index 8b1fd6035..d78244449 100644 --- a/libcore/sql/src/test/java/org/apache/harmony/sql/tests/java/sql/TestHelper_Connection1.java +++ b/libcore/sql/src/test/java/org/apache/harmony/sql/tests/java/sql/TestHelper_Connection1.java @@ -46,8 +46,9 @@ public class TestHelper_Connection1 implements Connection { return null; } - public Statement createStatement(int resultSetType, int resultSetConcurrency, - int resultSetHoldability) throws SQLException { + public Statement createStatement(int resultSetType, + int resultSetConcurrency, int resultSetHoldability) + throws SQLException { return null; } @@ -97,12 +98,13 @@ public class TestHelper_Connection1 implements Connection { } public CallableStatement prepareCall(String sql, int resultSetType, - int resultSetConcurrency, int resultSetHoldability) throws SQLException { + int resultSetConcurrency, int resultSetHoldability) + throws SQLException { return null; } - public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) - throws SQLException { + public CallableStatement prepareCall(String sql, int resultSetType, + int resultSetConcurrency) throws SQLException { return null; } @@ -111,7 +113,8 @@ public class TestHelper_Connection1 implements Connection { } public PreparedStatement prepareStatement(String sql, int resultSetType, - int resultSetConcurrency, int resultSetHoldability) throws SQLException { + int resultSetConcurrency, int resultSetHoldability) + throws SQLException { return null; } diff --git a/libcore/sql/src/test/java/org/apache/harmony/sql/tests/java/sql/TestHelper_Driver1.java b/libcore/sql/src/test/java/org/apache/harmony/sql/tests/java/sql/TestHelper_Driver1.java index ae09f9485..8638d2ba1 100644 --- a/libcore/sql/src/test/java/org/apache/harmony/sql/tests/java/sql/TestHelper_Driver1.java +++ b/libcore/sql/src/test/java/org/apache/harmony/sql/tests/java/sql/TestHelper_Driver1.java @@ -104,7 +104,8 @@ public class TestHelper_Driver1 implements Driver { public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException { - DriverPropertyInfo[] theInfos = { new DriverPropertyInfo(userProperty, "*"), + DriverPropertyInfo[] theInfos = { + new DriverPropertyInfo(userProperty, "*"), new DriverPropertyInfo(passwordProperty, "*"), }; return theInfos; } diff --git a/libcore/sql/src/test/java/org/apache/harmony/sql/tests/java/sql/TestHelper_Driver3.java b/libcore/sql/src/test/java/org/apache/harmony/sql/tests/java/sql/TestHelper_Driver3.java index f02bdc3e7..f78bb312a 100644 --- a/libcore/sql/src/test/java/org/apache/harmony/sql/tests/java/sql/TestHelper_Driver3.java +++ b/libcore/sql/src/test/java/org/apache/harmony/sql/tests/java/sql/TestHelper_Driver3.java @@ -17,7 +17,6 @@ package org.apache.harmony.sql.tests.java.sql; - /** * TODO Type description * diff --git a/libcore/sql/src/test/java/org/apache/harmony/sql/tests/java/sql/TestHelper_Driver4.java b/libcore/sql/src/test/java/org/apache/harmony/sql/tests/java/sql/TestHelper_Driver4.java index 655436dfc..3e7000c06 100644 --- a/libcore/sql/src/test/java/org/apache/harmony/sql/tests/java/sql/TestHelper_Driver4.java +++ b/libcore/sql/src/test/java/org/apache/harmony/sql/tests/java/sql/TestHelper_Driver4.java @@ -92,10 +92,13 @@ public class TestHelper_Driver4 implements Driver { String user = (String) info.get(userProperty); String password = (String) info.get(passwordProperty); if (user == null || password == null) { - throw new SQLException("Userid and/or password not supplied"); + throw new SQLException( + "Userid and/or password not supplied"); } - if (!user.equals(validuser) || !password.equals(validpassword)) { - throw new SQLException("Userid and/or password not valid"); + if (!user.equals(validuser) + || !password.equals(validpassword)) { + throw new SQLException( + "Userid and/or password not valid"); } // end if } // end if // It all checks out - so return a connection @@ -117,7 +120,8 @@ public class TestHelper_Driver4 implements Driver { public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException { - DriverPropertyInfo[] theInfos = { new DriverPropertyInfo(userProperty, "*"), + DriverPropertyInfo[] theInfos = { + new DriverPropertyInfo(userProperty, "*"), new DriverPropertyInfo(passwordProperty, "*"), }; return theInfos; } diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/platform/OSComponent.java b/libcore/sql/src/test/java/org/apache/harmony/sql/tests/java/sql/TestMainForDriver.java similarity index 50% rename from libcore/luni/src/main/java/org/apache/harmony/luni/platform/OSComponent.java rename to libcore/sql/src/test/java/org/apache/harmony/sql/tests/java/sql/TestMainForDriver.java index b5f14f29c..0fe7f67fc 100644 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/platform/OSComponent.java +++ b/libcore/sql/src/test/java/org/apache/harmony/sql/tests/java/sql/TestMainForDriver.java @@ -1,12 +1,13 @@ -/* Licensed to the Apache Software Foundation (ASF) under one or more +/* + * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -14,38 +15,22 @@ * limitations under the License. */ -package org.apache.harmony.luni.platform; +package org.apache.harmony.sql.tests.java.sql; - -//BEGIN android-changed -import dalvik.system.VMStack; -// END android-changed - -/** - * OSComponent - * +/* + * Load DriverManager class and initialize the class with SecurityManager + * Regression for HARMONY-4303 */ -class OSComponent implements IAdaptable { - - /** - * - */ - public OSComponent() { - super(); - // BEGIN android-changed - if (VMStack.getCallingClassLoader() != null) { - throw new SecurityException(); +public class TestMainForDriver { + public static void main(String[] args) throws Throwable { + // Install SecurityManager + System.setSecurityManager(new SecurityManager()); + // Load java.sql.DriverManager and it will invoke its method + try { + Class.forName("java.sql.DriverManager"); + } catch (ExceptionInInitializerError e) { + // ExceptionInInitializerError is caused by AccessControlException + throw e.getException(); } - // END android-changed } - - /* - * (non-Javadoc) - * - * @see org.apache.harmony.luni.platform.IAdaptable#getAdapter(java.lang.Class) - */ - public Object getAdapter(Class adapter) { - return Platform.getAdapterManager().getAdapter(this, adapter); - } - } diff --git a/libcore/sql/src/test/java/org/apache/harmony/sql/tests/java/sql/TimestampTest.java b/libcore/sql/src/test/java/org/apache/harmony/sql/tests/java/sql/TimestampTest.java index bc1a9d5e8..c1c09a066 100644 --- a/libcore/sql/src/test/java/org/apache/harmony/sql/tests/java/sql/TimestampTest.java +++ b/libcore/sql/src/test/java/org/apache/harmony/sql/tests/java/sql/TimestampTest.java @@ -37,6 +37,22 @@ import junit.framework.TestCase; public class TimestampTest extends TestCase { + static class MockTimestamp extends Timestamp{ + private String holiday; + + public MockTimestamp(long theTime) { + super(theTime); + holiday = "Christmas"; + } + + // Constructor should not call this public API, + // since it may be overrided to use variables uninitialized. + public void setTime(long theTime){ + super.setTime(theTime); + holiday.hashCode(); + } + } + static long TIME_TEST1 = 38720231; // 10:45:20.231 GMT static long TIME_TEST2 = 80279000; // 22:17:59.000 GMT @@ -126,6 +142,9 @@ public class TimestampTest extends TestCase { // The Timestamp should have been created assertNotNull(theTimestamp); + + Timestamp mockTimestamp = new MockTimestamp(TIME_TEST1); + assertNotNull(mockTimestamp); } // end method testTimestamplong /* @@ -277,6 +296,7 @@ public class TimestampTest extends TestCase { ) @SuppressWarnings("deprecation") public void testGetDate() { + TimeZone.setDefault(TimeZone.getTimeZone("GMT")); for (int i = 0; i < TIME_ARRAY.length; i++) { Timestamp theTimestamp = new Timestamp(TIME_ARRAY[i]); assertEquals(DATE_ARRAY[i], theTimestamp.getDate()); @@ -295,6 +315,7 @@ public class TimestampTest extends TestCase { ) @SuppressWarnings("deprecation") public void testGetHours() { + TimeZone.setDefault(TimeZone.getTimeZone("GMT")); for (int i = 0; i < TIME_ARRAY.length; i++) { Timestamp theTimestamp = new Timestamp(TIME_ARRAY[i]); assertEquals(HOURS_ARRAY[i], theTimestamp.getHours()); @@ -350,6 +371,7 @@ public class TimestampTest extends TestCase { args = {java.lang.String.class} ) public void testValueOfString() { + TimeZone.setDefault(TimeZone.getTimeZone("GMT")); for (int i = 0; i < TIME_ARRAY.length; i++) { Timestamp theTimestamp = new Timestamp(TIME_ARRAY[i]); Timestamp theTimestamp2 = Timestamp.valueOf(STRING_GMT_ARRAY[i]); @@ -370,7 +392,7 @@ public class TimestampTest extends TestCase { Timestamp.valueOf(element); fail("Should throw IllegalArgumentException."); } catch (IllegalArgumentException e) { - //expected + // expected } // end try } // end for @@ -387,6 +409,7 @@ public class TimestampTest extends TestCase { args = {java.lang.String.class} ) public void testValueOfString1() { + TimeZone.setDefault(TimeZone.getTimeZone("GMT")); Timestamp theReturn; @@ -439,8 +462,37 @@ public class TimestampTest extends TestCase { } } + // Regression test for HARMONY-5506 + String date = "1970-01-01 22:17:59.0 "; + Timestamp t = Timestamp.valueOf(date); + assertEquals(80279000,t.getTime()); + } // end method testValueOfString + public void testValueOf_IAE() { + try { + java.sql.Timestamp.valueOf("2008-12-22 15:00:01."); + fail("should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // Expected + } + + try { + // bug of RI 5, passed on RI 6 + java.sql.Timestamp.valueOf("178548938-12-22 15:00:01.000000001"); + fail("should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // Expected + } + + try { + java.sql.Timestamp.valueOf("2008-12-22 15:00:01.0000000011"); + fail("should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // Expected + } + } + /* * Method test for toString */ @@ -451,14 +503,36 @@ public class TimestampTest extends TestCase { args = {} ) public void testToString() { + TimeZone.setDefault(TimeZone.getTimeZone("GMT")); + for (int i = 0; i < TIME_ARRAY.length; i++) { Timestamp theTimestamp = new Timestamp(TIME_ARRAY[i]); assertEquals("Wrong conversion for test " + i, STRING_GMT_ARRAY[i], theTimestamp.toString()); } // end for + Timestamp t1 = new Timestamp(Long.MIN_VALUE); + assertEquals("292278994-08-17 07:12:55.192", t1.toString()); //$NON-NLS-1$ + + Timestamp t2 = new Timestamp(Long.MIN_VALUE + 1); + assertEquals("292278994-08-17 07:12:55.193", t2.toString()); //$NON-NLS-1$ + + Timestamp t3 = new Timestamp(Long.MIN_VALUE + 807); + assertEquals("292278994-08-17 07:12:55.999", t3.toString()); //$NON-NLS-1$ + + Timestamp t4 = new Timestamp(Long.MIN_VALUE + 808); + assertEquals("292269055-12-02 16:47:05.0", t4.toString()); //$NON-NLS-1$ } // end method testtoString + private void testToString(String timeZone, long[] theTimeStamps, String[] theTimeStampStrings) { + TimeZone.setDefault(TimeZone.getTimeZone(timeZone)); + for (int i = 0; i < TIME_ARRAY.length; i++) { + Timestamp theTimestamp = new Timestamp(theTimeStamps[i]); + assertEquals(theTimeStampStrings[i], theTimestamp.toString()); + } // end for + + } + /* * Method test for getNanos */ @@ -487,6 +561,8 @@ public class TimestampTest extends TestCase { args = {int.class} ) public void testSetNanosint() { + TimeZone.setDefault(TimeZone.getTimeZone("GMT")); + int[] NANOS_INVALID = { -137891990, 1635665198, -1 }; for (int i = 0; i < TIME_ARRAY.length; i++) { Timestamp theTimestamp = new Timestamp(TIME_ARRAY[i]); @@ -787,5 +863,11 @@ public class TimestampTest extends TestCase { Timestamp t4 = new Timestamp(Long.MIN_VALUE + 808); assertEquals("292269055-12-02 16:47:05.0", t4.toString()); //$NON-NLS-1$ } - + + // Reset defualt timezone + TimeZone defaultTimeZone = TimeZone.getDefault(); + + protected void tearDown() { + TimeZone.setDefault(defaultTimeZone); + } } // end class TimestampTest diff --git a/libcore/sql/src/test/java/org/apache/harmony/sql/tests/javax/sql/ConnectionEventTest.java b/libcore/sql/src/test/java/org/apache/harmony/sql/tests/javax/sql/ConnectionEventTest.java index 376b17336..12d436484 100644 --- a/libcore/sql/src/test/java/org/apache/harmony/sql/tests/javax/sql/ConnectionEventTest.java +++ b/libcore/sql/src/test/java/org/apache/harmony/sql/tests/javax/sql/ConnectionEventTest.java @@ -25,10 +25,14 @@ import dalvik.annotation.TestTargetClass; import junit.framework.TestCase; import java.sql.SQLException; +import java.io.Serializable; import javax.sql.ConnectionEvent; import javax.sql.PooledConnection; +import org.apache.harmony.testframework.serialization.SerializationTest; +import org.apache.harmony.testframework.serialization.SerializationTest.SerializableAssert; + @TestTargetClass(ConnectionEvent.class) public class ConnectionEventTest extends TestCase { @@ -112,5 +116,63 @@ public class ConnectionEventTest extends TestCase { assertNotSame(ce3.getSQLException(), ce2.getSQLException()); } -} + @TestTargetNew( + level = TestLevel.SUFFICIENT, + notes = "", + method = "!SerializationSelf", + args = {} + ) + public void testSerializationSelf() throws Exception { + Impl_PooledConnection ipc = new Impl_PooledConnection(); + SQLException e = new SQLException(); + ConnectionEvent ce = new ConnectionEvent(ipc, e); + SerializationTest.verifySelf(ce, CONNECTIONEVENT_COMPARATOR); + } + + @TestTargetNew( + level = TestLevel.SUFFICIENT, + notes = "", + method = "!Serialization", + args = {} + ) + public void testSerializationCompatibility() throws Exception { + Impl_PooledConnection ipc = new Impl_PooledConnection(); + SQLException nextSQLException = new SQLException("nextReason", + "nextSQLState", 33); + + int vendorCode = 10; + SQLException sqlException = new SQLException("reason", "SQLState", + vendorCode); + + sqlException.setNextException(nextSQLException); + + ConnectionEvent ce = new ConnectionEvent(ipc, sqlException); + + SerializationTest.verifyGolden(this, ce, CONNECTIONEVENT_COMPARATOR); + } + + private static final SerializableAssert CONNECTIONEVENT_COMPARATOR = new SerializableAssert() { + + public void assertDeserialized(Serializable initial, + Serializable deserialized) { + ConnectionEvent ceInitial = (ConnectionEvent) initial; + ConnectionEvent ceDeser = (ConnectionEvent) deserialized; + + SQLException initThr = ceInitial.getSQLException(); + SQLException dserThr = ceDeser.getSQLException(); + + // verify SQLState + assertEquals(initThr.getSQLState(), dserThr.getSQLState()); + + // verify vendorCode + assertEquals(initThr.getErrorCode(), dserThr.getErrorCode()); + + // verify next + if (initThr.getNextException() == null) { + assertNull(dserThr.getNextException()); + } + } + + }; +} diff --git a/libcore/sql/src/test/java/org/apache/harmony/sql/tests/javax/sql/Impl_PooledConnection.java b/libcore/sql/src/test/java/org/apache/harmony/sql/tests/javax/sql/Impl_PooledConnection.java index d135ced03..0eca2d28b 100644 --- a/libcore/sql/src/test/java/org/apache/harmony/sql/tests/javax/sql/Impl_PooledConnection.java +++ b/libcore/sql/src/test/java/org/apache/harmony/sql/tests/javax/sql/Impl_PooledConnection.java @@ -33,6 +33,7 @@ class Impl_PooledConnection implements PooledConnection { return null; } - public void removeConnectionEventListener(ConnectionEventListener theListener) { + public void removeConnectionEventListener( + ConnectionEventListener theListener) { } } diff --git a/libcore/sql/src/test/java/org/apache/harmony/sql/tests/javax/sql/Impl_RowSet.java b/libcore/sql/src/test/java/org/apache/harmony/sql/tests/javax/sql/Impl_RowSet.java index ace6b9a0b..946c40df5 100644 --- a/libcore/sql/src/test/java/org/apache/harmony/sql/tests/javax/sql/Impl_RowSet.java +++ b/libcore/sql/src/test/java/org/apache/harmony/sql/tests/javax/sql/Impl_RowSet.java @@ -99,34 +99,38 @@ class Impl_RowSet implements RowSet { public void removeRowSetListener(RowSetListener theListener) { } - public void setArray(int parameterIndex, Array theArray) throws SQLException { - } - - public void setAsciiStream(int parameterIndex, InputStream theInputStream, int length) + public void setArray(int parameterIndex, Array theArray) throws SQLException { } - public void setBigDecimal(int parameterIndex, BigDecimal theBigDecimal) throws SQLException { + public void setAsciiStream(int parameterIndex, InputStream theInputStream, + int length) throws SQLException { } - public void setBinaryStream(int parameterIndex, InputStream theInputStream, int length) + public void setBigDecimal(int parameterIndex, BigDecimal theBigDecimal) throws SQLException { } + public void setBinaryStream(int parameterIndex, InputStream theInputStream, + int length) throws SQLException { + } + public void setBlob(int parameterIndex, Blob theBlob) throws SQLException { } - public void setBoolean(int parameterIndex, boolean theBoolean) throws SQLException { + public void setBoolean(int parameterIndex, boolean theBoolean) + throws SQLException { } public void setByte(int parameterIndex, byte theByte) throws SQLException { } - public void setBytes(int parameterIndex, byte[] theByteArray) throws SQLException { + public void setBytes(int parameterIndex, byte[] theByteArray) + throws SQLException { } - public void setCharacterStream(int parameterIndex, Reader theReader, int length) - throws SQLException { + public void setCharacterStream(int parameterIndex, Reader theReader, + int length) throws SQLException { } public void setClob(int parameterIndex, Clob theClob) throws SQLException { @@ -148,13 +152,15 @@ class Impl_RowSet implements RowSet { public void setDate(int parameterIndex, Date theDate) throws SQLException { } - public void setDouble(int parameterIndex, double theDouble) throws SQLException { + public void setDouble(int parameterIndex, double theDouble) + throws SQLException { } public void setEscapeProcessing(boolean enable) throws SQLException { } - public void setFloat(int parameterIndex, float theFloat) throws SQLException { + public void setFloat(int parameterIndex, float theFloat) + throws SQLException { } public void setInt(int parameterIndex, int theInteger) throws SQLException { @@ -169,21 +175,23 @@ class Impl_RowSet implements RowSet { public void setMaxRows(int max) throws SQLException { } - public void setNull(int parameterIndex, int sqlType, String typeName) throws SQLException { + public void setNull(int parameterIndex, int sqlType, String typeName) + throws SQLException { } public void setNull(int parameterIndex, int sqlType) throws SQLException { } - public void setObject(int parameterIndex, Object theObject, int targetSqlType, int scale) - throws SQLException { + public void setObject(int parameterIndex, Object theObject, + int targetSqlType, int scale) throws SQLException { } - public void setObject(int parameterIndex, Object theObject, int targetSqlType) - throws SQLException { + public void setObject(int parameterIndex, Object theObject, + int targetSqlType) throws SQLException { } - public void setObject(int parameterIndex, Object theObject) throws SQLException { + public void setObject(int parameterIndex, Object theObject) + throws SQLException { } public void setPassword(String password) throws SQLException { @@ -198,10 +206,12 @@ class Impl_RowSet implements RowSet { public void setRef(int parameterIndex, Ref theRef) throws SQLException { } - public void setShort(int parameterIndex, short theShort) throws SQLException { + public void setShort(int parameterIndex, short theShort) + throws SQLException { } - public void setString(int parameterIndex, String theString) throws SQLException { + public void setString(int parameterIndex, String theString) + throws SQLException { } public void setTime(int parameterIndex, Time theTime, Calendar theCalendar) @@ -211,11 +221,12 @@ class Impl_RowSet implements RowSet { public void setTime(int parameterIndex, Time theTime) throws SQLException { } - public void setTimestamp(int parameterIndex, Timestamp theTimestamp, Calendar theCalendar) - throws SQLException { + public void setTimestamp(int parameterIndex, Timestamp theTimestamp, + Calendar theCalendar) throws SQLException { } - public void setTimestamp(int parameterIndex, Timestamp theTimestamp) throws SQLException { + public void setTimestamp(int parameterIndex, Timestamp theTimestamp) + throws SQLException { } public void setTransactionIsolation(int level) throws SQLException { @@ -224,7 +235,8 @@ class Impl_RowSet implements RowSet { public void setType(int type) throws SQLException { } - public void setTypeMap(Map> theTypeMap) throws SQLException { + public void setTypeMap(Map> theTypeMap) + throws SQLException { } public void setUrl(String theURL) throws SQLException { @@ -279,7 +291,8 @@ class Impl_RowSet implements RowSet { return null; } - public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException { + public BigDecimal getBigDecimal(int columnIndex, int scale) + throws SQLException { return null; } @@ -287,7 +300,8 @@ class Impl_RowSet implements RowSet { return null; } - public BigDecimal getBigDecimal(String columnName, int scale) throws SQLException { + public BigDecimal getBigDecimal(String columnName, int scale) + throws SQLException { return null; } @@ -419,7 +433,8 @@ class Impl_RowSet implements RowSet { return null; } - public Object getObject(int columnIndex, Map> map) throws SQLException { + public Object getObject(int columnIndex, Map> map) + throws SQLException { return null; } @@ -427,7 +442,8 @@ class Impl_RowSet implements RowSet { return null; } - public Object getObject(String columnName, Map> map) throws SQLException { + public Object getObject(String columnName, Map> map) + throws SQLException { return null; } @@ -483,7 +499,8 @@ class Impl_RowSet implements RowSet { return null; } - public Timestamp getTimestamp(int columnIndex, Calendar cal) throws SQLException { + public Timestamp getTimestamp(int columnIndex, Calendar cal) + throws SQLException { return null; } @@ -491,7 +508,8 @@ class Impl_RowSet implements RowSet { return null; } - public Timestamp getTimestamp(String columnName, Calendar cal) throws SQLException { + public Timestamp getTimestamp(String columnName, Calendar cal) + throws SQLException { return null; } @@ -599,10 +617,12 @@ class Impl_RowSet implements RowSet { throws SQLException { } - public void updateBigDecimal(int columnIndex, BigDecimal x) throws SQLException { + public void updateBigDecimal(int columnIndex, BigDecimal x) + throws SQLException { } - public void updateBigDecimal(String columnName, BigDecimal x) throws SQLException { + public void updateBigDecimal(String columnName, BigDecimal x) + throws SQLException { } public void updateBinaryStream(int columnIndex, InputStream x, int length) @@ -641,8 +661,8 @@ class Impl_RowSet implements RowSet { throws SQLException { } - public void updateCharacterStream(String columnName, Reader reader, int length) - throws SQLException { + public void updateCharacterStream(String columnName, Reader reader, + int length) throws SQLException { } public void updateClob(int columnIndex, Clob x) throws SQLException { @@ -687,13 +707,15 @@ class Impl_RowSet implements RowSet { public void updateNull(String columnName) throws SQLException { } - public void updateObject(int columnIndex, Object x, int scale) throws SQLException { + public void updateObject(int columnIndex, Object x, int scale) + throws SQLException { } public void updateObject(int columnIndex, Object x) throws SQLException { } - public void updateObject(String columnName, Object x, int scale) throws SQLException { + public void updateObject(String columnName, Object x, int scale) + throws SQLException { } public void updateObject(String columnName, Object x) throws SQLException { @@ -726,10 +748,12 @@ class Impl_RowSet implements RowSet { public void updateTime(String columnName, Time x) throws SQLException { } - public void updateTimestamp(int columnIndex, Timestamp x) throws SQLException { + public void updateTimestamp(int columnIndex, Timestamp x) + throws SQLException { } - public void updateTimestamp(String columnName, Timestamp x) throws SQLException { + public void updateTimestamp(String columnName, Timestamp x) + throws SQLException { } public boolean wasNull() throws SQLException { diff --git a/libcore/sql/src/test/java/org/apache/harmony/sql/tests/javax/sql/RowSetEventTest.java b/libcore/sql/src/test/java/org/apache/harmony/sql/tests/javax/sql/RowSetEventTest.java index bf30fbcdd..9f048f7af 100644 --- a/libcore/sql/src/test/java/org/apache/harmony/sql/tests/javax/sql/RowSetEventTest.java +++ b/libcore/sql/src/test/java/org/apache/harmony/sql/tests/javax/sql/RowSetEventTest.java @@ -45,7 +45,7 @@ public class RowSetEventTest extends TestCase { fail("illegal argument exception expected"); } catch (IllegalArgumentException e) { } - + Impl_RowSet irs = new Impl_RowSet(); RowSetEvent rse = new RowSetEvent(irs); assertSame(irs, rse.getSource()); diff --git a/libcore/suncompat/src/main/java/sun/misc/Unsafe.java b/libcore/suncompat/src/main/java/sun/misc/Unsafe.java index e880af292..6ae572f72 100644 --- a/libcore/suncompat/src/main/java/sun/misc/Unsafe.java +++ b/libcore/suncompat/src/main/java/sun/misc/Unsafe.java @@ -52,9 +52,7 @@ public final class Unsafe { * Only code on the bootclasspath is allowed to get at the * Unsafe instance. */ - ClassLoader calling = VMStack.getCallingClassLoader2(); - ClassLoader current = Unsafe.class.getClassLoader(); - + ClassLoader calling = VMStack.getCallingClassLoader(); if ((calling != null) && (calling != Unsafe.class.getClassLoader())) { throw new SecurityException("Unsafe access denied"); } diff --git a/libcore/suncompat/src/test/java/sun/misc/AllTests.java b/libcore/suncompat/src/test/java/sun/misc/AllTests.java new file mode 100644 index 000000000..8ea3bcfce --- /dev/null +++ b/libcore/suncompat/src/test/java/sun/misc/AllTests.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package sun.misc; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class AllTests { + + public static void main(String[] args) { + junit.textui.TestRunner.run(AllTests.suite()); + } + + public static Test suite() { + TestSuite suite = tests.TestSuiteFactory.createTestSuite("Test for sun.misc"); + + // $JUnit-BEGIN$ + + suite.addTestSuite(UnsafeTest.class); + + // $JUnit-END$ + + return suite; + } +} diff --git a/libcore/suncompat/src/test/java/sun/misc/UnsafeTest.java b/libcore/suncompat/src/test/java/sun/misc/UnsafeTest.java new file mode 100644 index 000000000..338055b63 --- /dev/null +++ b/libcore/suncompat/src/test/java/sun/misc/UnsafeTest.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package sun.misc; + +import dalvik.annotation.TestLevel; +import dalvik.annotation.TestTargetClass; +import dalvik.annotation.TestTargetNew; +import junit.framework.TestCase; + +import java.util.concurrent.Callable; +import java.util.concurrent.Executors; + +@TestTargetClass(Unsafe.class) +public class UnsafeTest extends TestCase { + + @TestTargetNew( + level = TestLevel.PARTIAL, + notes = "", + method = "getUnsafe", + args = {} + ) + public void test_getUnsafeForbidden() { + try { + Unsafe.getUnsafe(); + fail(); + } catch (SecurityException expected) { + } + } + + /** + * Regression for 2053217. We used to look one level higher than necessary + * on the stack. + */ + @TestTargetNew( + level = TestLevel.PARTIAL, + notes = "", + method = "getUnsafe", + args = {} + ) + public void test_getUnsafeForbiddenWithSystemCaller() throws Exception { + Callable callable = Executors.callable(new Runnable() { + public void run() { + Unsafe.getUnsafe(); + } + }); + + try { + callable.call(); + fail(); + } catch (SecurityException expected) { + } + } +} diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/platform/IAdapterManager.java b/libcore/suncompat/src/test/java/tests/suncompat/AllTests.java similarity index 59% rename from libcore/luni/src/main/java/org/apache/harmony/luni/platform/IAdapterManager.java rename to libcore/suncompat/src/test/java/tests/suncompat/AllTests.java index 886dda7d2..088451705 100644 --- a/libcore/luni/src/main/java/org/apache/harmony/luni/platform/IAdapterManager.java +++ b/libcore/suncompat/src/test/java/tests/suncompat/AllTests.java @@ -4,9 +4,9 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -14,21 +14,25 @@ * limitations under the License. */ -package org.apache.harmony.luni.platform; +package tests.suncompat; +import junit.framework.Test; +import junit.framework.TestSuite; /** - * IAdapterManager - * + * Test suite that includes all tests for the suncompat project. */ -public interface IAdapterManager { - public Object getAdapter(IAdaptable adaptable, Class adapterType); +public class AllTests { - public boolean hasAdapter(IAdaptable adaptable, Class adapterType); + public static void main(String[] args) { + junit.textui.TestRunner.run(AllTests.suite()); + } - public void registerAdapters(IAdapterFactory factory, Class adaptable); - - public void unregisterAdapters(IAdapterFactory factory); - - public void unregisterAdapters(IAdapterFactory factory, Class adaptable); + public static Test suite() { + TestSuite suite = tests.TestSuiteFactory.createTestSuite("All suncompat test suites"); + // $JUnit-BEGIN$ + suite.addTest(sun.misc.AllTests.suite()); + // $JUnit-END$ + return suite; + } } diff --git a/libcore/support/src/test/java/tests/resources/EmptyEntries_signed.jar b/libcore/support/src/test/java/tests/resources/EmptyEntries_signed.jar new file mode 100644 index 0000000000000000000000000000000000000000..237d244d6987d00ece0ac757b8d569c727d7a07e GIT binary patch literal 1927 zcmWIWW@Zs#;9%fj@a|N%WSm=<&X74Ox%;PmQE&aT<;&Z(ix>Bm7fknBtF=Mz#DNIY7aaws z46Gz4@SReaX`keOvpfH!SA}m-=!}Est~utP3>7cw6?7}V>Fn|*V)s*5>F$X;j@VeN zu6o#!Gi#q^iM6ttS$d^KS^tll_YS2SZ9TsJPWIROcb~Ifco{6b#Md)z?uVVM$A7#( zZXV#x&SC7jhjAVQ1A{mt0|Sx%3vu-gi1&7l)C+dY?K_*-O8zu=aWtAuvm=t=)bz0}`wJDDx zk9AB{m?|iEuIv869S=2CBi4y}h_9{GdKCNZnr|)t9?NA5B}9w1@tyvcy!$D)0@n(| zoPA4~h3g}1OD1RZDQy#(nzN*7txNDell=AKv0J+4UFzX~AZs4V@b!tno@=S?=Vtx= z6qB)i-bt|-75=9``;F|MwjOtF|8--#QVgp~j7+>tSO3`xi2%7%Z-lPTi9NV@)8nJR z6(ZNr>wo4+>Wa=L+sYf!MGaYj%++A>tn~}+Y>!AV$U?KOoH+}we`g4MkjD*q~38febr8^QzX9@(FSqtNsy%kK% zO-whKZ0eZwz}(2d*wDb(q=DBQFCLS#GF*BnJZ|&c`fti|{L4Zm6_+1bGmW!#?YZZP7V0-o1qxeVuG@K@ zXDZY2yK^iLUKG8^E%St>b^40<`=5^4Tix8y8OSwb-jN2*^9x$`ePs1L`Yl3A zS|)t0w~mHzt;y3l8yNJX4_wO3?>R5>W{$nFPlnT@Z%NmmHbtNQQ pathToRequest + = new ConcurrentHashMap(); + /* List of all active worker threads */ Vector activeThreads = new Vector(); @@ -206,7 +210,7 @@ public class Support_TestWebServer implements Support_HttpConstants { * a redirect code with the Location response header set to the value * specified. * @param redirect The location to be redirected to - * @param redirectCode The code to send when redirecting + * @param code The code to send when redirecting */ public void setRedirect(String redirect, int code) { redirectHost = redirect; @@ -214,6 +218,14 @@ public class Support_TestWebServer implements Support_HttpConstants { log("Server will redirect output to "+redirect+" code "+code); } + /** + * Returns a map from recently-requested paths (like "/index.html") to a + * snapshot of the request data. + */ + public Map pathToRequest() { + return pathToRequest; + } + /** * Cause the thread accepting connections on the server socket to close */ @@ -327,6 +339,28 @@ public class Support_TestWebServer implements Support_HttpConstants { /* End of line byte sequence */ static final byte[] EOL = {(byte)'\r', (byte)'\n' }; + /** + * An immutable snapshot of an HTTP request. + */ + public static class Request { + private final String path; + private final Map headers; + // TODO: include posted content? + + public Request(String path, Map headers) { + this.path = path; + this.headers = new LinkedHashMap(headers); + } + + public String getPath() { + return path; + } + + public Map getHeaders() { + return headers; + } + } + /** * The worker thread handles all interactions with a current open * connection. If pipelining is turned on, this will allow this @@ -347,6 +381,9 @@ public class Support_TestWebServer implements Support_HttpConstants { /* Reference to current requests test file/data */ private String testID; + /* The requested path, such as "/test1" */ + private String path; + /* Reference to test number from testID */ private int testNum; @@ -359,7 +396,7 @@ public class Support_TestWebServer implements Support_HttpConstants { boolean running = false; /* Request headers are stored here */ - private Hashtable headers = new Hashtable(); + private Map headers = new LinkedHashMap(); /* Create a new worker thread */ Worker() { @@ -559,10 +596,8 @@ public class Support_TestWebServer implements Support_HttpConstants { i++; } - testID = new String(buf, 0, index, i-index); - if (testID.startsWith("/")) { - testID = testID.substring(1); - } + path = new String(buf, 0, index, i-index); + testID = path.substring(1); return nread; } @@ -601,7 +636,7 @@ public class Support_TestWebServer implements Support_HttpConstants { while (buf[i] == ' ') { i++; } - String headerValue = new String(buf, i, nread-1); + String headerValue = new String(buf, i, nread-i); headers.put(headerName, headerValue); return nread; @@ -666,6 +701,8 @@ public class Support_TestWebServer implements Support_HttpConstants { // If status line found, read any headers nread = readHeaders(is); + pathToRequest().put(path, new Request(path, headers)); + // Then read content (if any) // TODO handle chunked encoding from the client if (headers.get(requestHeaders[REQ_CONTENT_LENGTH]) != null) { diff --git a/libcore/support/src/test/java/tests/util/PrefsTester.java b/libcore/support/src/test/java/tests/util/PrefsTester.java new file mode 100644 index 000000000..047b3579f --- /dev/null +++ b/libcore/support/src/test/java/tests/util/PrefsTester.java @@ -0,0 +1,73 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tests.util; + +import java.util.prefs.BackingStoreException; +import java.util.prefs.Preferences; +import java.util.Arrays; + +/** + * Prepares the shared preferences store for a test by wiping preference data + * before and after the test. Sample usage: + *
            + * public void MyPreferencesTest extends TestCase {
            + *     private final PrefsTester prefsTester = new PrefsTester();
            + *
            + *     public void setUp() throws BackingStoreException {
            + *         super.setUp();
            + *         prefsTester.setUp();
            + *     }
            + *
            + *     public void tearDown() throws BackingStoreException {
            + *         prefsTester.tearDown();
            + *         super.tearDown();
            + *     }
            + *
            + *     ...
            + * }
            + * + *

            Once the preferences classes have been initialized, the path where their + * data is stored is fixed. For that reason, every test that reads or writes + * preferences must first prepare preferences for testing by using this class. + */ +public final class PrefsTester { + + static { + String tmp = System.getProperty("java.io.tmpdir"); + System.setProperty("user.home", tmp); + System.setProperty("java.home", tmp); + } + + public void setUp() throws BackingStoreException { + clear(); + } + + public void tearDown() throws BackingStoreException { + clear(); + } + + private void clear() throws BackingStoreException { + for (Preferences root : Arrays .asList( + Preferences.systemRoot(), Preferences.userRoot())) { + for (String child : root.childrenNames()) { + root.node(child).removeNode(); + } + root.clear(); + root.flush(); + } + } +} diff --git a/libcore/text/src/main/java/java/text/Annotation.java b/libcore/text/src/main/java/java/text/Annotation.java index a45ccaae8..40234495b 100644 --- a/libcore/text/src/main/java/java/text/Annotation.java +++ b/libcore/text/src/main/java/java/text/Annotation.java @@ -30,13 +30,9 @@ package java.text; * By wrapping text attribute values into an {@code Annotation}, these aspects * will be taken into account when handling annotation text and the * corresponding main text. - *

            *

            * Note: There is no semantic connection between this annotation class and the * {@code java.lang.annotation} package. - *

            - * - * @since Android 1.0 */ public class Annotation { @@ -44,10 +40,9 @@ public class Annotation { /** * Constructs a new {@code Annotation}. - * + * * @param attribute the attribute attached to this annotation. This may be * {@code null}. - * @since Android 1.0 */ public Annotation(Object attribute) { value = attribute; @@ -55,9 +50,8 @@ public class Annotation { /** * Returns the value of this annotation. The value may be {@code null}. - * + * * @return the value of this annotation or {@code null}. - * @since Android 1.0 */ public Object getValue() { return value; @@ -65,9 +59,8 @@ public class Annotation { /** * Returns this annotation in string representation. - * + * * @return the string representation of this annotation. - * @since Android 1.0 */ @Override public String toString() { diff --git a/libcore/text/src/main/java/java/text/AttributedCharacterIterator.java b/libcore/text/src/main/java/java/text/AttributedCharacterIterator.java index 07bbdec44..4f260ecbb 100644 --- a/libcore/text/src/main/java/java/text/AttributedCharacterIterator.java +++ b/libcore/text/src/main/java/java/text/AttributedCharacterIterator.java @@ -31,15 +31,11 @@ import org.apache.harmony.text.internal.nls.Messages; * {@code AttributedCharacterIterator} also allows the user to find runs and * their limits. Runs are defined as ranges of characters that all have the same * attributes with the same values. - * - * @since Android 1.0 */ public interface AttributedCharacterIterator extends CharacterIterator { /** * Defines keys for text attributes. - * - * @since Android 1.0 */ public static class Attribute implements Serializable { @@ -48,11 +44,9 @@ public interface AttributedCharacterIterator extends CharacterIterator { /** * This attribute marks segments from an input method. Most input * methods create these segments for words. - * + * * The value objects are of the type {@code Annotation} which contain * {@code null}. - * - * @since Android 1.0 */ public static final Attribute INPUT_METHOD_SEGMENT = new Attribute( "input_method_segment"); //$NON-NLS-1$ @@ -60,8 +54,6 @@ public interface AttributedCharacterIterator extends CharacterIterator { /** * The attribute describing the language of a character. The value * objects are of type {@code Locale} or a subtype of it. - * - * @since Android 1.0 */ public static final Attribute LANGUAGE = new Attribute("language"); //$NON-NLS-1$ @@ -70,8 +62,6 @@ public interface AttributedCharacterIterator extends CharacterIterator { * Japanese), this attribute allows to define which reading should be * used. The value objects are of type {@code Annotation} which * contain a {@code String}. - * - * @since Android 1.0 */ public static final Attribute READING = new Attribute("reading"); //$NON-NLS-1$ @@ -79,10 +69,9 @@ public interface AttributedCharacterIterator extends CharacterIterator { /** * The constructor for an {@code Attribute} with the name passed. - * + * * @param name * the name of the new {@code Attribute}. - * @since Android 1.0 */ protected Attribute(String name) { this.name = name; @@ -92,23 +81,21 @@ public interface AttributedCharacterIterator extends CharacterIterator { * Compares this attribute with the specified object. Checks if both * objects are the same instance. It is defined final so all subclasses * have the same behavior for this method. - * + * * @param object * the object to compare against. * @return {@code true} if the object passed is equal to this instance; * {@code false} otherwise. - * @since Android 1.0 */ @Override public final boolean equals(Object object) { - return super.equals(object); + return this == object; } /** * Returns the name of this attribute. - * + * * @return the name of this attribute. - * @since Android 1.0 */ protected String getName() { return name; @@ -118,9 +105,8 @@ public interface AttributedCharacterIterator extends CharacterIterator { * Calculates the hash code for objects of type {@code Attribute}. It * is defined final so all sub types calculate their hash code * identically. - * + * * @return the hash code for this instance of {@code Attribute}. - * @since Android 1.0 */ @Override public final int hashCode() { @@ -129,27 +115,30 @@ public interface AttributedCharacterIterator extends CharacterIterator { /** * Resolves a deserialized instance to the correct constant attribute. - * + * * @return the {@code Attribute} this instance represents. * @throws InvalidObjectException * if this instance is not of type {@code Attribute.class} * or if it is not a known {@code Attribute}. - * @since Android 1.0 */ protected Object readResolve() throws InvalidObjectException { if (this.getClass() != Attribute.class) { // text.0C=cannot resolve subclasses throw new InvalidObjectException(Messages.getString("text.0C")); //$NON-NLS-1$ } - if (this.name.equals(INPUT_METHOD_SEGMENT.name)) { + // BEGIN android-changed + // call getName() only once + String name = this.getName(); + if (name.equals(INPUT_METHOD_SEGMENT.getName())) { return INPUT_METHOD_SEGMENT; } - if (this.name.equals(LANGUAGE.name)) { + if (name.equals(LANGUAGE.getName())) { return LANGUAGE; } - if (this.name.equals(READING.name)) { + if (name.equals(READING.getName())) { return READING; } + // END android-changed // text.02=Unknown attribute throw new InvalidObjectException(Messages.getString("text.02")); //$NON-NLS-1$ } @@ -157,9 +146,8 @@ public interface AttributedCharacterIterator extends CharacterIterator { /** * Returns the name of the class followed by a "(", the name of the * attribute, and a ")". - * + * * @return the string representing this instance. - * @since Android 1.0 */ @Override public String toString() { @@ -171,20 +159,18 @@ public interface AttributedCharacterIterator extends CharacterIterator { * Returns a set of attributes present in the {@code * AttributedCharacterIterator}. An empty set is returned if no attributes * were defined. - * + * * @return a set of attribute keys; may be empty. - * @since Android 1.0 */ public Set getAllAttributeKeys(); /** * Returns the value stored in the attribute for the current character. If * the attribute was not defined then {@code null} is returned. - * + * * @param attribute the attribute for which the value should be returned. * @return the value of the requested attribute for the current character or * {@code null} if it was not defined. - * @since Android 1.0 */ public Object getAttribute(Attribute attribute); @@ -192,72 +178,65 @@ public interface AttributedCharacterIterator extends CharacterIterator { * Returns a map of all attributes of the current character. If no * attributes were defined for the current character then an empty map is * returned. - * + * * @return a map of all attributes for the current character or an empty * map. - * @since Android 1.0 */ public Map getAttributes(); /** * Returns the index of the last character in the run having the same * attributes as the current character. - * + * * @return the index of the last character of the current run. - * @since Android 1.0 */ public int getRunLimit(); /** * Returns the index of the last character in the run that has the same * attribute value for the given attribute as the current character. - * + * * @param attribute * the attribute which the run is based on. * @return the index of the last character of the current run. - * @since Android 1.0 */ public int getRunLimit(Attribute attribute); /** * Returns the index of the last character in the run that has the same * attribute values for the attributes in the set as the current character. - * + * * @param attributes * the set of attributes which the run is based on. * @return the index of the last character of the current run. - * @since Android 1.0 */ public int getRunLimit(Set attributes); /** * Returns the index of the first character in the run that has the same * attributes as the current character. - * + * * @return the index of the last character of the current run. - * @since Android 1.0 */ public int getRunStart(); /** * Returns the index of the first character in the run that has the same * attribute value for the given attribute as the current character. - * + * * @param attribute * the attribute which the run is based on. * @return the index of the last character of the current run. - * @since Android 1.0 */ public int getRunStart(Attribute attribute); /** * Returns the index of the first character in the run that has the same * attribute values for the attributes in the set as the current character. - * + * * @param attributes * the set of attributes which the run is based on. * @return the index of the last character of the current run. - * @since Android 1.0 */ public int getRunStart(Set attributes); } diff --git a/libcore/text/src/main/java/java/text/AttributedString.java b/libcore/text/src/main/java/java/text/AttributedString.java index 540e6716b..77bbf78f4 100644 --- a/libcore/text/src/main/java/java/text/AttributedString.java +++ b/libcore/text/src/main/java/java/text/AttributedString.java @@ -33,8 +33,6 @@ import org.apache.harmony.text.internal.nls.Messages; /** * Holds a string with attributes describing the characters of * this string. - * - * @since Android 1.0 */ public class AttributedString { @@ -180,8 +178,8 @@ public class AttributedString { /** * Returns a set of attributes present in the {@code AttributedString}. - * An empty set returned indicates that no attributes where defined - * + * An empty set returned indicates that no attributes where defined. + * * @return a set of attribute keys that may be empty. */ public Set getAllAttributeKeys() { @@ -384,18 +382,17 @@ public class AttributedString { /** * Constructs an {@code AttributedString} from an {@code * AttributedCharacterIterator}, which represents attributed text. - * + * * @param iterator * the {@code AttributedCharacterIterator} that contains the text * for this attributed string. - * @since Android 1.0 */ public AttributedString(AttributedCharacterIterator iterator) { if (iterator.getBeginIndex() > iterator.getEndIndex()) { // text.0A=Invalid substring range throw new IllegalArgumentException(Messages.getString("text.0A")); //$NON-NLS-1$ } - StringBuffer buffer = new StringBuffer(); + StringBuilder buffer = new StringBuilder(); for (int i = iterator.getBeginIndex(); i < iterator.getEndIndex(); i++) { buffer.append(iterator.current()); iterator.next(); @@ -436,7 +433,7 @@ public class AttributedString { return; } - StringBuffer buffer = new StringBuffer(); + StringBuilder buffer = new StringBuilder(); iterator.setIndex(start); while (iterator.getIndex() < end) { buffer.append(iterator.current()); @@ -470,7 +467,7 @@ public class AttributedString { * in the specified {@code AttributedCharacterIterator}, starting at {@code * start} and ending at {@code end}. All attributes will be copied to this * attributed string. - * + * * @param iterator * the {@code AttributedCharacterIterator} that contains the text * for this attributed string. @@ -479,10 +476,9 @@ public class AttributedString { * @param end * the end index of the range of the copied text. * @throws IllegalArgumentException - * if {@code start} is less than first index of + * if {@code start} is less than first index of * {@code iterator}, {@code end} is greater than the last * index + 1 in {@code iterator} or if {@code start > end}. - * @since Android 1.0 */ public AttributedString(AttributedCharacterIterator iterator, int start, int end) { @@ -495,7 +491,7 @@ public class AttributedString { * start}, ending at {@code end} and it will copy the attributes defined in * the specified set. If the set is {@code null} then all attributes are * copied. - * + * * @param iterator * the {@code AttributedCharacterIterator} that contains the text * for this attributed string. @@ -507,10 +503,9 @@ public class AttributedString { * the set of attributes that will be copied, or all if it is * {@code null}. * @throws IllegalArgumentException - * if {@code start} is less than first index of - * {@code iterator}, {@code end} is greater than the last index + + * if {@code start} is less than first index of + * {@code iterator}, {@code end} is greater than the last index + * 1 in {@code iterator} or if {@code start > end}. - * @since Android 1.0 */ public AttributedString(AttributedCharacterIterator iterator, int start, int end, AttributedCharacterIterator.Attribute[] attributes) { @@ -527,10 +522,9 @@ public class AttributedString { /** * Creates an {@code AttributedString} from the given text. - * + * * @param value * the text to take as base for this attributed string. - * @since Android 1.0 */ public AttributedString(String value) { if (value == null) { @@ -543,7 +537,7 @@ public class AttributedString { /** * Creates an {@code AttributedString} from the given text and the * attributes. The whole text has the given attributes applied. - * + * * @param value * the text to take as base for this attributed string. * @param attributes @@ -553,7 +547,6 @@ public class AttributedString { * attributes} is greater than 0. * @throws NullPointerException * if {@code value} is {@code null}. - * @since Android 1.0 */ public AttributedString(String value, Map attributes) { @@ -579,7 +572,7 @@ public class AttributedString { /** * Applies a given attribute to this string. - * + * * @param attribute * the attribute that will be applied to this string. * @param value @@ -589,7 +582,6 @@ public class AttributedString { * if the length of this attributed string is 0. * @throws NullPointerException * if {@code attribute} is {@code null}. - * @since Android 1.0 */ public void addAttribute(AttributedCharacterIterator.Attribute attribute, Object value) { @@ -612,7 +604,7 @@ public class AttributedString { /** * Applies a given attribute to the given range of this string. - * + * * @param attribute * the attribute that will be applied to this string. * @param value @@ -627,7 +619,6 @@ public class AttributedString { * of this string, or if {@code start >= end}. * @throws NullPointerException * if {@code attribute} is {@code null}. - * @since Android 1.0 */ public void addAttribute(AttributedCharacterIterator.Attribute attribute, Object value, int start, int end) { @@ -650,9 +641,6 @@ public class AttributedString { return; } ListIterator it = ranges.listIterator(); - // BEGIN android-changed - // copied from a newer version of harmony - // value can't be null while (it.hasNext()) { Range range = it.next(); if (end <= range.start) { @@ -710,13 +698,12 @@ public class AttributedString { return; } } - // END android-changed it.add(new Range(start, end, value)); } /** * Applies a given set of attributes to the given range of the string. - * + * * @param attributes * the set of attributes that will be applied to this string. * @param start @@ -726,7 +713,6 @@ public class AttributedString { * @throws IllegalArgumentException * if {@code start < 0}, {@code end} is greater than the length * of this string, or if {@code start >= end}. - * @since Android 1.0 */ public void addAttributes( Map attributes, @@ -743,9 +729,8 @@ public class AttributedString { /** * Returns an {@code AttributedCharacterIterator} that gives access to the * complete content of this attributed string. - * + * * @return the newly created {@code AttributedCharacterIterator}. - * @since Android 1.0 */ public AttributedCharacterIterator getIterator() { return new AttributedIterator(this); @@ -756,12 +741,11 @@ public class AttributedString { * complete content of this attributed string. Only attributes contained in * {@code attributes} are available from this iterator if they are defined * for this text. - * + * * @param attributes * the array containing attributes that will be in the new * iterator if they are defined for this text. * @return the newly created {@code AttributedCharacterIterator}. - * @since Android 1.0 */ public AttributedCharacterIterator getIterator( AttributedCharacterIterator.Attribute[] attributes) { @@ -773,7 +757,7 @@ public class AttributedString { * contents of this attributed string starting at index {@code start} up to * index {@code end}. Only attributes contained in {@code attributes} are * available from this iterator if they are defined for this text. - * + * * @param attributes * the array containing attributes that will be in the new * iterator if they are defined for this text. @@ -782,7 +766,6 @@ public class AttributedString { * @param end * the end index of the iterator on the underlying text. * @return the newly created {@code AttributedCharacterIterator}. - * @since Android 1.0 */ public AttributedCharacterIterator getIterator( AttributedCharacterIterator.Attribute[] attributes, int start, diff --git a/libcore/text/src/main/java/java/text/Bidi.java b/libcore/text/src/main/java/java/text/Bidi.java index 228dab3fb..7939deaf2 100644 --- a/libcore/text/src/main/java/java/text/Bidi.java +++ b/libcore/text/src/main/java/java/text/Bidi.java @@ -15,8 +15,13 @@ * limitations under the License. */ +// BEGIN android-note +// changed from icu.text.Bidi to BidiWrapper +// END android-note + package java.text; +// BEGIN android-added import java.awt.font.NumericShaper; import java.awt.font.TextAttribute; import java.util.Arrays; @@ -24,6 +29,7 @@ import java.util.LinkedList; import org.apache.harmony.text.BidiRun; import org.apache.harmony.text.BidiWrapper; +// END android-added import org.apache.harmony.text.internal.nls.Messages; /** @@ -40,40 +46,51 @@ import org.apache.harmony.text.internal.nls.Messages; * obtained from the run index. The level of any particular run indicates the * direction of the text as well as the nesting level. Left-to-right runs have * even levels while right-to-left runs have odd levels. - * - * @since Android 1.0 */ public final class Bidi { /** * Constant that indicates the default base level. If there is no strong * character, then set the paragraph level to 0 (left-to-right). - * - * @since Android 1.0 */ public static final int DIRECTION_DEFAULT_LEFT_TO_RIGHT = -2; /** * Constant that indicates the default base level. If there is no strong * character, then set the paragraph level to 1 (right-to-left). - * - * @since Android 1.0 */ public static final int DIRECTION_DEFAULT_RIGHT_TO_LEFT = -1; /** * Constant that specifies the default base level as 0 (left-to-right). - * - * @since Android 1.0 */ public static final int DIRECTION_LEFT_TO_RIGHT = 0; /** * Constant that specifies the default base level as 1 (right-to-left). - * - * @since Android 1.0 */ public static final int DIRECTION_RIGHT_TO_LEFT = 1; + // BEGIN android-removed + // /* + // * Converts the constant from the value specified in the Java spec, to the + // * value required by the ICU implementation. + // */ + // private final static int convertDirectionConstant(int javaConst) { + // switch (javaConst) { + // case DIRECTION_DEFAULT_LEFT_TO_RIGHT : return com.ibm.icu.text.Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT; + // case DIRECTION_DEFAULT_RIGHT_TO_LEFT : return com.ibm.icu.text.Bidi.DIRECTION_DEFAULT_RIGHT_TO_LEFT; + // case DIRECTION_LEFT_TO_RIGHT : return com.ibm.icu.text.Bidi.DIRECTION_LEFT_TO_RIGHT; + // case DIRECTION_RIGHT_TO_LEFT : return com.ibm.icu.text.Bidi.DIRECTION_RIGHT_TO_LEFT; + // default : return com.ibm.icu.text.Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT; + // } + // } + // + // /* + // * Use an embedded ICU4J Bidi object to do all the work + // */ + // private com.ibm.icu.text.Bidi icuBidi; + // END android-removed + /** * Creates a {@code Bidi} object from the {@code * AttributedCharacterIterator} of a paragraph text. The RUN_DIRECTION @@ -89,16 +106,15 @@ public final class Bidi { * attribute specifies the instance of NumericShaper used to convert * European digits to other decimal digits before performing the bidi * algorithm. - * + * * @param paragraph * the String containing the paragraph text to perform the * algorithm. * @throws IllegalArgumentException * if {@code paragraph} is {@code null}. - * @see TextAttribute#BIDI_EMBEDDING - * @see TextAttribute#NUMERIC_SHAPING - * @see TextAttribute#RUN_DIRECTION - * @since Android 1.0 + * @see java.awt.font.TextAttribute#BIDI_EMBEDDING + * @see java.awt.font.TextAttribute#NUMERIC_SHAPING + * @see java.awt.font.TextAttribute#RUN_DIRECTION */ public Bidi(AttributedCharacterIterator paragraph) { if (paragraph == null) { @@ -106,6 +122,7 @@ public final class Bidi { throw new IllegalArgumentException(Messages.getString("text.14")); //$NON-NLS-1$ } + // BEGIN android-added int begin = paragraph.getBeginIndex(); int end = paragraph.getEndIndex(); int length = end - begin; @@ -164,6 +181,7 @@ public final class Bidi { long pBidi = createUBiDi(text, 0, embeddings, 0, length, flags); readBidiInfo(pBidi); BidiWrapper.ubidi_close(pBidi); + // END android-added } /** @@ -191,17 +209,27 @@ public final class Bidi { * DIRECTION_DEFAULT_LEFT_TO_RIGHT. * @throws IllegalArgumentException * if {@code textStart}, {@code embStart}, or {@code - * paragraphLength} is negative; if - * {@code text.length < textStart + paragraphLength} or + * paragraphLength} is negative; if + * {@code text.length < textStart + paragraphLength} or * {@code embeddings.length < embStart + paragraphLength}. * @see #DIRECTION_LEFT_TO_RIGHT * @see #DIRECTION_RIGHT_TO_LEFT * @see #DIRECTION_DEFAULT_RIGHT_TO_LEFT * @see #DIRECTION_DEFAULT_LEFT_TO_RIGHT - * @since Android 1.0 */ public Bidi(char[] text, int textStart, byte[] embeddings, int embStart, int paragraphLength, int flags) { + + if (text == null || text.length - textStart < paragraphLength) { + throw new IllegalArgumentException(); + } + + if (embeddings != null) { + if (embeddings.length - embStart < paragraphLength) { + throw new IllegalArgumentException(); + } + } + if (textStart < 0) { // text.0D=Negative textStart value {0} throw new IllegalArgumentException(Messages.getString( @@ -217,10 +245,13 @@ public final class Bidi { throw new IllegalArgumentException(Messages.getString( "text.11", paragraphLength)); //$NON-NLS-1$ } + + // BEGIN android-changed long pBidi = createUBiDi(text, textStart, embeddings, embStart, paragraphLength, flags); readBidiInfo(pBidi); BidiWrapper.ubidi_close(pBidi); + // END android-changed } /** @@ -238,13 +269,13 @@ public final class Bidi { * @see #DIRECTION_RIGHT_TO_LEFT * @see #DIRECTION_DEFAULT_RIGHT_TO_LEFT * @see #DIRECTION_DEFAULT_LEFT_TO_RIGHT - * @since Android 1.0 */ public Bidi(String paragraph, int flags) { this((paragraph == null ? null : paragraph.toCharArray()), 0, null, 0, (paragraph == null ? 0 : paragraph.length()), flags); } + // BEGIN android-added // create the native UBiDi struct, need to be closed with ubidi_close(). private static long createUBiDi(char[] text, int textStart, byte[] embeddings, int embStart, int paragraphLength, int flags) { @@ -291,7 +322,7 @@ public final class Bidi { return bidi; } - // private constructor, used by createLineBidi() + /* private constructor used by createLineBidi() */ private Bidi(long pBidi) { readBidiInfo(pBidi); } @@ -335,15 +366,17 @@ public final class Bidi { private int direction; private boolean unidirectional; + // END android-added /** * Returns whether the base level is from left to right. * * @return true if the base level is from left to right. - * @since Android 1.0 */ public boolean baseIsLeftToRight() { + // BEGIN android-changed return baseLevel % 2 == 0 ? true : false; + // END android-changed } /** @@ -360,15 +393,19 @@ public final class Bidi { * if {@code lineStart < 0}, {@code lineLimit < 0}, {@code * lineStart > lineLimit} or if {@code lineStart} is greater * than the length of this object's paragraph text. - * @since Android 1.0 */ public Bidi createLineBidi(int lineStart, int lineLimit) { + // BEGIN android-removed + // int length = icuBidi.getLength(); + // END android-removed if (lineStart < 0 || lineLimit < 0 || lineLimit > length || lineStart > lineLimit) { // text.12=Invalid ranges (start={0}, limit={1}, length={2}) throw new IllegalArgumentException(Messages.getString( "text.12", new Object[] { lineStart, lineLimit, length })); //$NON-NLS-1$ } + + // BEGIN android-changed char[] text = new char[this.length]; Arrays.fill(text, 'a'); byte[] embeddings = new byte[this.length]; @@ -386,26 +423,29 @@ public final class Bidi { BidiWrapper.ubidi_close(line); BidiWrapper.ubidi_close(parent); return result; + // END android-changed } /** * Returns the base level. * * @return the base level. - * @since Android 1.0 */ public int getBaseLevel() { + // BEGIN android-changed return baseLevel; + // END android-changed } /** * Returns the length of the text in the {@code Bidi} object. * * @return the length. - * @since Android 1.0 */ public int getLength() { + // BEGIN android-changed return length; + // END android-changed } /** @@ -414,24 +454,26 @@ public final class Bidi { * @param offset * the offset of the character. * @return the level. - * @since Android 1.0 */ public int getLevelAt(int offset) { + // BEGIN android-changed try { return offsetLevel[offset] & ~BidiWrapper.UBIDI_LEVEL_OVERRIDE; } catch (RuntimeException e) { return baseLevel; } + // END android-changed } /** * Returns the number of runs in the bidirectional text. * * @return the number of runs, at least 1. - * @since Android 1.0 */ public int getRunCount() { + // BEGIN android-changed return unidirectional ? 1 : runs.length; + // END android-changed } /** @@ -440,10 +482,11 @@ public final class Bidi { * @param run * the index of the run. * @return the level of the run. - * @since Android 1.0 */ public int getRunLevel(int run) { + // BEGIN android-changed return unidirectional ? baseLevel : runs[run].getLevel(); + // END android-changed } /** @@ -452,10 +495,11 @@ public final class Bidi { * @param run * the index of the run. * @return the limit offset of the run. - * @since Android 1.0 */ public int getRunLimit(int run) { + // BEGIN android-changed return unidirectional ? length : runs[run].getLimit(); + // END android-changed } /** @@ -464,10 +508,11 @@ public final class Bidi { * @param run * the index of the run. * @return the start offset of the run. - * @since Android 1.0 */ public int getRunStart(int run) { + // BEGIN android-changed return unidirectional ? 0 : runs[run].getStart(); + // END android-changed } /** @@ -476,10 +521,11 @@ public final class Bidi { * * @return {@code true} if the text is from left to right; {@code false} * otherwise. - * @since Android 1.0 */ public boolean isLeftToRight() { + // BEGIN android-changed return direction == BidiWrapper.UBiDiDirection_UBIDI_LTR; + // END android-changed } /** @@ -487,10 +533,11 @@ public final class Bidi { * * @return {@code true} if the text direction is mixed; {@code false} * otherwise. - * @since Android 1.0 */ public boolean isMixed() { + // BEGIN android-changed return direction == BidiWrapper.UBiDiDirection_UBIDI_MIXED; + // END android-changed } /** @@ -499,10 +546,11 @@ public final class Bidi { * * @return {@code true} if the text is from right to left; {@code false} * otherwise. - * @since Android 1.0 */ public boolean isRightToLeft() { + // BEGIN android-changed return direction == BidiWrapper.UBiDiDirection_UBIDI_RTL; + // END android-changed } /** @@ -526,7 +574,6 @@ public final class Bidi { * if {@code count}, {@code levelStart} or {@code objectStart} * is negative; if {@code count > levels.length - levelStart} or * if {@code count > objects.length - objectStart}. - * @since Android 1.0 */ public static void reorderVisually(byte[] levels, int levelStart, Object[] objects, int objectStart, int count) { @@ -539,6 +586,8 @@ public final class Bidi { new Object[] { levels.length, levelStart, objects.length, objectStart, count })); } + + // BEGIN android-changed byte[] realLevels = new byte[count]; System.arraycopy(levels, levelStart, realLevels, 0, count); @@ -550,6 +599,7 @@ public final class Bidi { } System.arraycopy(result.toArray(), 0, objects, objectStart, count); + // END android-changed } /** @@ -568,15 +618,17 @@ public final class Bidi { * if {@code start} or {@code limit} is negative; {@code start > * limit} or {@code limit} is greater than the length of this * object's paragraph text. - * @since Android 1.0 */ public static boolean requiresBidi(char[] text, int start, int limit) { - int length = text.length; - if (limit < 0 || start < 0 || start > limit || limit > length) { + //int length = text.length; + if (limit < 0 || start < 0 || start > limit || limit > text.length) { throw new IllegalArgumentException(); } + + // BEGIN android-changed Bidi bidi = new Bidi(text, start, null, 0, limit - start, 0); return !bidi.isLeftToRight(); + // END android-changed } /** @@ -584,12 +636,13 @@ public final class Bidi { * debugging. * * @return a string containing the internal message. - * @since Android 1.0 */ @Override public String toString() { + // BEGIN android-changed return super.toString() + "[direction: " + direction + " baselevel: " + baseLevel //$NON-NLS-1$ //$NON-NLS-2$ + " length: " + length + " runs: " + (unidirectional ? "null" : runs.toString()) + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + // END android-changed } } diff --git a/libcore/text/src/main/java/java/text/BreakIterator.java b/libcore/text/src/main/java/java/text/BreakIterator.java index 76b848eb0..7d1917936 100644 --- a/libcore/text/src/main/java/java/text/BreakIterator.java +++ b/libcore/text/src/main/java/java/text/BreakIterator.java @@ -14,17 +14,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/** -******************************************************************************* -* Copyright (C) 1996-2007, International Business Machines Corporation and * -* others. All Rights Reserved. * -******************************************************************************* -*/ // BEGIN android-note -// The class javadoc and some of the method descriptions are copied from ICU4J -// source files. Changes have been made to the copied descriptions. -// The icu license header was added to this file. // The icu implementation used was changed from icu4j to icu4jni. // END android-note @@ -93,7 +84,6 @@ import java.util.Locale; * {@link CharacterIterator}, which makes it possible to use {@code * BreakIterator} to analyze text in any text-storage vehicle that provides a * {@code CharacterIterator} interface. - *

            *

            * Note: Some types of {@code BreakIterator} can take a long time to * create, and instances of {@code BreakIterator} are not currently cached by @@ -104,12 +94,10 @@ import java.util.Locale; * wrapping) and use it to do the whole job of wrapping the text. *

            * Examples: - *

            *

            * Creating and using text boundaries: - *

            *
            - * + * *
              * public static void main(String args[]) {
              *     if (args.length == 1) {
            @@ -127,13 +115,12 @@ import java.util.Locale;
              *     }
              * }
              * 
            - * + * *
            *

            * Print each element in order: - *

            *
            - * + * *
              * public static void printEachForward(BreakIterator boundary, String source) {
              *     int start = boundary.first();
            @@ -142,13 +129,12 @@ import java.util.Locale;
              *     }
              * }
              * 
            - * + * *
            *

            * Print each element in reverse order: - *

            *
            - * + * *
              * public static void printEachBackward(BreakIterator boundary, String source) {
              *     int end = boundary.last();
            @@ -158,13 +144,12 @@ import java.util.Locale;
              *     }
              * }
              * 
            - * + * *
            *

            * Print the first element: - *

            *
            - * + * *
              * public static void printFirst(BreakIterator boundary, String source) {
              *     int start = boundary.first();
            @@ -172,13 +157,12 @@ import java.util.Locale;
              *     System.out.println(source.substring(start, end));
              * }
              * 
            - * + * *
            *

            * Print the last element: - *

            *
            - * + * *
              * public static void printLast(BreakIterator boundary, String source) {
              *     int end = boundary.last();
            @@ -186,13 +170,12 @@ import java.util.Locale;
              *     System.out.println(source.substring(start, end));
              * }
              * 
            - * + * *
            *

            * Print the element at a specified position: - *

            *
            - * + * *
              * public static void printAt(BreakIterator boundary, int pos, String source) {
              *     int end = boundary.following(pos);
            @@ -200,13 +183,12 @@ import java.util.Locale;
              *     System.out.println(source.substring(start, end));
              * }
              * 
            - * + * *
            *

            * Find the next word: - *

            *
            - * + * *
              * public static int nextWordStartAfter(int pos, String text) {
              *     BreakIterator wb = BreakIterator.getWordInstance();
            @@ -224,7 +206,7 @@ import java.util.Locale;
              *     return BreakIterator.DONE;
              * }
              * 
            - * + * *
            *

            * The iterator returned by {@code BreakIterator.getWordInstance()} is unique in @@ -239,23 +221,14 @@ import java.util.Locale; * CJK ideograph, a Hangul syllable, a Kana character, etc.), then the text * between this boundary and the next is a word; otherwise, it's the material * between words.) - *

            - * + * * @see CharacterIterator - * @since Android 1.0 */ public abstract class BreakIterator implements Cloneable { - /* - * ----------------------------------------------------------------------- - * constants - * ----------------------------------------------------------------------- - */ /** * This constant is returned by iterate methods like {@code previous()} or * {@code next()} if they have returned all valid boundaries. - * - * @since Android 1.0 */ public static final int DONE = -1; @@ -265,23 +238,11 @@ public abstract class BreakIterator implements Cloneable { private static final int SHORT_LENGTH = 2; - /* - * ----------------------------------------------------------------------- - * variables - * ----------------------------------------------------------------------- - */ // the wrapped ICU implementation com.ibm.icu4jni.text.BreakIterator wrapped; - /* - * ----------------------------------------------------------------------- - * constructors - * ----------------------------------------------------------------------- - */ /** * Default constructor, just for invocation by a subclass. - * - * @since Android 1.0 */ protected BreakIterator() { super(); @@ -294,16 +255,10 @@ public abstract class BreakIterator implements Cloneable { wrapped = iterator; } - /* - * ----------------------------------------------------------------------- - * methods - * ----------------------------------------------------------------------- - */ /** * Returns all supported locales in an array. * * @return all supported locales. - * @since Android 1.0 */ public static Locale[] getAvailableLocales() { return com.ibm.icu4jni.text.BreakIterator.getAvailableLocales(); @@ -314,7 +269,6 @@ public abstract class BreakIterator implements Cloneable { * characters using the default locale. * * @return a new instance of {@code BreakIterator} using the default locale. - * @since Android 1.0 */ public static BreakIterator getCharacterInstance() { return new RuleBasedBreakIterator(com.ibm.icu4jni.text.BreakIterator @@ -328,7 +282,6 @@ public abstract class BreakIterator implements Cloneable { * @param where * the given locale. * @return a new instance of {@code BreakIterator} using the given locale. - * @since Android 1.0 */ public static BreakIterator getCharacterInstance(Locale where) { if (where == null) { @@ -344,7 +297,6 @@ public abstract class BreakIterator implements Cloneable { * line breaks using the default locale. * * @return a new instance of {@code BreakIterator} using the default locale. - * @since Android 1.0 */ public static BreakIterator getLineInstance() { return new RuleBasedBreakIterator(com.ibm.icu4jni.text.BreakIterator @@ -359,7 +311,6 @@ public abstract class BreakIterator implements Cloneable { * the given locale. * @return a new instance of {@code BreakIterator} using the given locale. * @throws NullPointerException if {@code where} is {@code null}. - * @since Android 1.0 */ public static BreakIterator getLineInstance(Locale where) { if (where == null) { @@ -375,7 +326,6 @@ public abstract class BreakIterator implements Cloneable { * sentence-breaks using the default locale. * * @return a new instance of {@code BreakIterator} using the default locale. - * @since Android 1.0 */ public static BreakIterator getSentenceInstance() { return new RuleBasedBreakIterator(com.ibm.icu4jni.text.BreakIterator @@ -390,7 +340,6 @@ public abstract class BreakIterator implements Cloneable { * the given locale. * @return a new instance of {@code BreakIterator} using the given locale. * @throws NullPointerException if {@code where} is {@code null}. - * @since Android 1.0 */ public static BreakIterator getSentenceInstance(Locale where) { if (where == null) { @@ -406,7 +355,6 @@ public abstract class BreakIterator implements Cloneable { * word-breaks using the default locale. * * @return a new instance of {@code BreakIterator} using the default locale. - * @since Android 1.0 */ public static BreakIterator getWordInstance() { return new RuleBasedBreakIterator(com.ibm.icu4jni.text.BreakIterator @@ -421,7 +369,6 @@ public abstract class BreakIterator implements Cloneable { * the given locale. * @return a new instance of {@code BreakIterator} using the given locale. * @throws NullPointerException if {@code where} is {@code null}. - * @since Android 1.0 */ public static BreakIterator getWordInstance(Locale where) { if (where == null) { @@ -442,7 +389,6 @@ public abstract class BreakIterator implements Cloneable { * the given offset to check. * @return {@code true} if the given offset is a boundary position; {@code * false} otherwise. - * @since Android 1.0 */ public boolean isBoundary(int offset) { return wrapped.isBoundary(offset); @@ -452,11 +398,12 @@ public abstract class BreakIterator implements Cloneable { * Returns the position of last boundary preceding the given offset, and * sets the current position to the returned value, or {@code DONE} if the * given offset specifies the starting position. - * + * * @param offset * the given start position to be searched for. * @return the position of the last boundary preceding the given offset. - * @since Android 1.0 + * @throws IllegalArgumentException + * if the offset is invalid. */ public int preceding(int offset) { return wrapped.preceding(offset); @@ -469,22 +416,15 @@ public abstract class BreakIterator implements Cloneable { * * @param newText * the new text string to be analyzed. - * @since Android 1.0 */ public void setText(String newText) { wrapped.setText(newText); } - /* - * ----------------------------------------------------------------------- - * abstract methods - * ----------------------------------------------------------------------- - */ /** * Returns this iterator's current position. * * @return this iterator's current position. - * @since Android 1.0 */ public abstract int current(); @@ -493,7 +433,6 @@ public abstract class BreakIterator implements Cloneable { * that position. * * @return the position of the first boundary. - * @since Android 1.0 */ public abstract int first(); @@ -501,11 +440,12 @@ public abstract class BreakIterator implements Cloneable { * Sets the position of the first boundary to the one following the given * offset and returns this position. Returns {@code DONE} if there is no * boundary after the given offset. - * + * * @param offset * the given position to be searched for. * @return the position of the first boundary following the given offset. - * @since Android 1.0 + * @throws IllegalArgumentException + * if the offset is invalid. */ public abstract int following(int offset); @@ -518,7 +458,6 @@ public abstract class BreakIterator implements Cloneable { * * @return a {@code CharacterIterator} which represents the text being * analyzed. - * @since Android 1.0 */ public abstract CharacterIterator getText(); @@ -527,7 +466,6 @@ public abstract class BreakIterator implements Cloneable { * that position. * * @return the position of last boundary. - * @since Android 1.0 */ public abstract int last(); @@ -537,7 +475,6 @@ public abstract class BreakIterator implements Cloneable { * boundary was found after the current position. * * @return the position of last boundary. - * @since Android 1.0 */ public abstract int next(); @@ -549,7 +486,6 @@ public abstract class BreakIterator implements Cloneable { * @param n * the given position. * @return the position of last boundary. - * @since Android 1.0 */ public abstract int next(int n); @@ -559,7 +495,6 @@ public abstract class BreakIterator implements Cloneable { * no boundary was found before the current position. * * @return the position of last boundary. - * @since Android 1.0 */ public abstract int previous(); @@ -571,21 +506,14 @@ public abstract class BreakIterator implements Cloneable { * @param newText * the {@code CharacterIterator} referring to the text to be * analyzed. - * @since Android 1.0 */ public abstract void setText(CharacterIterator newText); - /* - * ----------------------------------------------------------------------- - * methods override Object - * ----------------------------------------------------------------------- - */ /** * Creates a copy of this iterator, all status information including the * current position are kept the same. * * @return a copy of this iterator. - * @since Android 1.0 */ @Override public Object clone() { @@ -612,7 +540,6 @@ public abstract class BreakIterator implements Cloneable { * @throws ArrayIndexOutOfBoundsException * if {@code offset < 0} or {@code offset + LONG_LENGTH} is * greater than the length of {@code buf}. - * @since Android 1.0 */ protected static long getLong(byte[] buf, int offset) { if (null == buf) { @@ -642,7 +569,6 @@ public abstract class BreakIterator implements Cloneable { * @throws ArrayIndexOutOfBoundsException * if {@code offset < 0} or {@code offset + INT_LENGTH} is * greater than the length of {@code buf}. - * @since Android 1.0 */ protected static int getInt(byte[] buf, int offset) { if (null == buf) { @@ -672,7 +598,6 @@ public abstract class BreakIterator implements Cloneable { * @throws ArrayIndexOutOfBoundsException * if {@code offset < 0} or {@code offset + SHORT_LENGTH} is * greater than the length of {@code buf}. - * @since Android 1.0 */ protected static short getShort(byte[] buf, int offset) { if (null == buf) { diff --git a/libcore/text/src/main/java/java/text/CharacterIterator.java b/libcore/text/src/main/java/java/text/CharacterIterator.java index dfcd21dfd..81c1fa44d 100644 --- a/libcore/text/src/main/java/java/text/CharacterIterator.java +++ b/libcore/text/src/main/java/java/text/CharacterIterator.java @@ -21,16 +21,12 @@ package java.text; * An interface for the bidirectional iteration over a group of characters. The * iteration starts at the begin index in the group of characters and continues * to one index before the end index. - * - * @since Android 1.0 */ public interface CharacterIterator extends Cloneable { /** * A constant which indicates that there is no character at the current * index. - * - * @since Android 1.0 */ public static final char DONE = '\uffff'; @@ -40,7 +36,6 @@ public interface CharacterIterator extends Cloneable { * @return a shallow copy of this character iterator. * * @see java.lang.Cloneable - * @since Android 1.0 */ public Object clone(); @@ -49,7 +44,6 @@ public interface CharacterIterator extends Cloneable { * * @return the current character, or {@code DONE} if the current index is * past the beginning or end of the sequence. - * @since Android 1.0 */ public char current(); @@ -58,7 +52,6 @@ public interface CharacterIterator extends Cloneable { * the new position. * * @return the character at the begin index. - * @since Android 1.0 */ public char first(); @@ -66,7 +59,6 @@ public interface CharacterIterator extends Cloneable { * Returns the begin index. * * @return the index of the first character of the iteration. - * @since Android 1.0 */ public int getBeginIndex(); @@ -74,7 +66,6 @@ public interface CharacterIterator extends Cloneable { * Returns the end index. * * @return the index one past the last character of the iteration. - * @since Android 1.0 */ public int getEndIndex(); @@ -82,7 +73,6 @@ public interface CharacterIterator extends Cloneable { * Returns the current index. * * @return the current index. - * @since Android 1.0 */ public int getIndex(); @@ -91,7 +81,6 @@ public interface CharacterIterator extends Cloneable { * at the new position. * * @return the character before the end index. - * @since Android 1.0 */ public char last(); @@ -100,7 +89,6 @@ public interface CharacterIterator extends Cloneable { * * @return the character at the next index, or {@code DONE} if the next * index would be past the end. - * @since Android 1.0 */ public char next(); @@ -109,7 +97,6 @@ public interface CharacterIterator extends Cloneable { * * @return the character at the previous index, or {@code DONE} if the * previous index would be past the beginning. - * @since Android 1.0 */ public char previous(); @@ -121,10 +108,9 @@ public interface CharacterIterator extends Cloneable { * the new index that this character iterator is set to. * @return the character at the new index, or {@code DONE} if the index is * past the end. - * @exception IllegalArgumentException - * if {@code location} is less than the begin index or - * greater than the end index. - * @since Android 1.0 + * @throws IllegalArgumentException + * if {@code location} is less than the begin index or greater than + * the end index. */ public char setIndex(int location); } diff --git a/libcore/text/src/main/java/java/text/ChoiceFormat.java b/libcore/text/src/main/java/java/text/ChoiceFormat.java index 41dacedff..1a61781dc 100644 --- a/libcore/text/src/main/java/java/text/ChoiceFormat.java +++ b/libcore/text/src/main/java/java/text/ChoiceFormat.java @@ -14,18 +14,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/** -******************************************************************************* -* Copyright (C) 1996-2007, International Business Machines Corporation and * -* others. All Rights Reserved. * -******************************************************************************* -*/ - -// BEGIN android-note -// The class javadoc description is copied from ICU UserGuide. -// Changes have been made to the copied descriptions. -// The icu license header was added to this file. -// END android-note package java.text; @@ -46,44 +34,38 @@ import java.util.Locale; * or last index is used depending on whether the number is too low or too high. * The length of the format array must be the same as the length of the limits * array. - *

            *
            Examples:
            *
            - * + * *
              * double[] limits = {1, 2, 3, 4, 5, 6, 7};
              * String[] fmts = {"Sun", "Mon", "Tue", "Wed", "Thur", "Fri", "Sat"};
            - * 
            + *
              * double[] limits2 = {0, 1, ChoiceFormat.nextDouble(1)};
              * String[] fmts2 = {"no files", "one file", "many files"};
              * 
            *
            - *

            + *

            * ChoiceFormat.nextDouble(double) allows to get the double following the one * passed to the method. This is used to create half open intervals. - *

            *

            * {@code ChoiceFormat} objects also may be converted to and from patterns. * The conversion can be done programmatically, as in the example above, or * by using a pattern like the following: - *

            *
            - * + * *
              * "1#Sun|2#Mon|3#Tue|4#Wed|5#Thur|6#Fri|7#Sat"
              * "0#are no files|1#is one file|1<are many files"
              * 
            - * + * *
            *

            * where: - *

            *
              *
            • "#" specifies an inclusive limit value;
            • *
            • "<" specifies an exclusive limit value.
            • *
            - * - * @since Android 1.0 */ public class ChoiceFormat extends NumberFormat { @@ -102,8 +84,7 @@ public class ChoiceFormat extends NumberFormat { *

            * The length of the {@code limits} and {@code formats} arrays must be the * same. - *

            - * + * * @param limits * an array of doubles in ascending order. The lowest and highest * possible values are negative and positive infinity. @@ -111,7 +92,6 @@ public class ChoiceFormat extends NumberFormat { * the strings associated with the ranges defined through {@code * limits}. The lower bound of the associated range is at the * same index as the string. - * @since Android 1.0 */ public ChoiceFormat(double[] limits, String[] formats) { setChoices(limits, formats); @@ -123,10 +103,8 @@ public class ChoiceFormat extends NumberFormat { * * @param template * the pattern of strings and ranges. - * - * @exception IllegalArgumentException - * if an error occurs while parsing the pattern. - * @since Android 1.0 + * @throws IllegalArgumentException + * if an error occurs while parsing the pattern. */ public ChoiceFormat(String template) { applyPattern(template); @@ -138,10 +116,8 @@ public class ChoiceFormat extends NumberFormat { * * @param template * the pattern of strings and ranges. - * - * @exception IllegalArgumentException - * if an error occurs while parsing the pattern. - * @since Android 1.0 + * @throws IllegalArgumentException + * if an error occurs while parsing the pattern. */ public void applyPattern(String template) { double[] limits = new double[5]; @@ -212,7 +188,6 @@ public class ChoiceFormat extends NumberFormat { * @return a shallow copy of this {@code ChoiceFormat}. * * @see java.lang.Cloneable - * @since Android 1.0 */ @Override public Object clone() { @@ -232,7 +207,6 @@ public class ChoiceFormat extends NumberFormat { * @return {@code true} if the specified object is equal to this instance; * {@code false} otherwise. * @see #hashCode - * @since Android 1.0 */ @Override public boolean equals(Object object) { @@ -258,7 +232,6 @@ public class ChoiceFormat extends NumberFormat { * @param field * a {@code FieldPosition} which is ignored. * @return the string buffer. - * @since Android 1.0 */ @Override public StringBuffer format(double value, StringBuffer buffer, @@ -283,7 +256,6 @@ public class ChoiceFormat extends NumberFormat { * @param field * a {@code FieldPosition} which is ignored. * @return the string buffer. - * @since Android 1.0 */ @Override public StringBuffer format(long value, StringBuffer buffer, @@ -296,7 +268,6 @@ public class ChoiceFormat extends NumberFormat { * ChoiceFormat}. * * @return an array of format strings. - * @since Android 1.0 */ public Object[] getFormats() { return choiceFormats; @@ -307,7 +278,6 @@ public class ChoiceFormat extends NumberFormat { * * @return the array of doubles which make up the limits of this {@code * ChoiceFormat}. - * @since Android 1.0 */ public double[] getLimits() { return choiceLimits; @@ -320,7 +290,6 @@ public class ChoiceFormat extends NumberFormat { * @return the receiver's hash. * * @see #equals - * @since Android 1.0 */ @Override public int hashCode() { @@ -339,7 +308,6 @@ public class ChoiceFormat extends NumberFormat { * @param value * a double value. * @return the next larger double value. - * @since Android 1.0 */ public static final double nextDouble(double value) { if (value == Double.POSITIVE_INFINITY) { @@ -365,7 +333,6 @@ public class ChoiceFormat extends NumberFormat { * {@code true} to get the next larger value, {@code false} to * get the previous smaller value. * @return the next larger or smaller double value. - * @since Android 1.0 */ public static double nextDouble(double value, boolean increment) { return increment ? nextDouble(value) : previousDouble(value); @@ -378,26 +345,30 @@ public class ChoiceFormat extends NumberFormat { * corresponding range in the limits array is returned. If the string is * successfully parsed then the index of the {@code ParsePosition} passed to * this method is updated to the index following the parsed text. - * - * @param string - * the source string to parse. - * @param position - * input/output parameter, specifies the start index in {@code string} - * from where to start parsing. See the Returns section for - * a description of the output values. - * @return if one of the format strings of this {@code ChoiceFormat} instance - * is found in {@code string} starting at the index specified by {@code position.getIndex()} then + *

            + * If one of the format strings of this {@code ChoiceFormat} instance is + * found in {@code string} starting at {@code position.getIndex()} then *

              - *
            • the index in {@code position} is set to the index following the parsed text; - *
            • the {@link java.lang.Double Double} corresponding to the format string is returned.
            • + *
            • the index in {@code position} is set to the index following the + * parsed text; + *
            • the {@link java.lang.Double Double} corresponding to the format + * string is returned.
            • *
            *

            * If none of the format strings is found in {@code string} then - *

              - *
            • the error index in {@code position} is set to the current index in {@code position};
            • + *
                + *
              • the error index in {@code position} is set to the current index in + * {@code position};
              • *
              • {@link java.lang.Double#NaN Double.NaN} is returned. *
              - * @since Android 1.0 + * @param string + * the source string to parse. + * @param position + * input/output parameter, specifies the start index in {@code + * string} from where to start parsing. See the Returns + * section for a description of the output values. + * @return a Double resulting from the parse, or Double.NaN if there is an + * error */ @Override public Number parse(String string, ParsePosition position) { @@ -419,7 +390,6 @@ public class ChoiceFormat extends NumberFormat { * @param value * a double value. * @return the next smaller double value. - * @since Android 1.0 */ public static final double previousDouble(double value) { if (value == Double.NEGATIVE_INFINITY) { @@ -439,13 +409,12 @@ public class ChoiceFormat extends NumberFormat { * Sets the double values and associated strings of this ChoiceFormat. When * calling {@link #format(double, StringBuffer, FieldPosition) format} with * a double value {@code d}, then the element {@code i} in {@code formats} - * is selected where {@code i} fulfills + * is selected where {@code i} fulfills * {@code limits[i] <= d < limits[i+1]}. *

              * The length of the {@code limits} and {@code formats} arrays must be the * same. - *

              - * + * * @param limits * an array of doubles in ascending order. The lowest and highest * possible values are negative and positive infinity. @@ -453,7 +422,6 @@ public class ChoiceFormat extends NumberFormat { * the strings associated with the ranges defined through {@code * limits}. The lower bound of the associated range is at the * same index as the string. - * @since Android 1.0 */ public void setChoices(double[] limits, String[] formats) { if (limits.length != formats.length) { @@ -476,10 +444,9 @@ public class ChoiceFormat extends NumberFormat { * ranges and their associated strings. * * @return the pattern. - * @since Android 1.0 */ public String toPattern() { - StringBuffer buffer = new StringBuffer(); + StringBuilder buffer = new StringBuilder(); for (int i = 0; i < choiceLimits.length; i++) { if (i != 0) { buffer.append('|'); diff --git a/libcore/text/src/main/java/java/text/CollationElementIterator.java b/libcore/text/src/main/java/java/text/CollationElementIterator.java index 66c007956..fb562d8a1 100644 --- a/libcore/text/src/main/java/java/text/CollationElementIterator.java +++ b/libcore/text/src/main/java/java/text/CollationElementIterator.java @@ -28,31 +28,23 @@ package java.text; * source string. *

              * For illustration, consider the following in Spanish: - *

              *

              * "ca": the first collation element is collation_element('c') and second * collation element is collation_element('a'). - *

              *

              * Since "ch" in Spanish sorts as one entity, the example below returns one * collation element for the two characters 'c' and 'h': - *

              *

              * "cha": the first collation element is collation_element('ch') and the second * one is collation_element('a'). - *

              *

              * In German, since the character '\u0086' is a composed character of 'a' * and 'e', the iterator returns two collation elements for the single character * '\u0086': - *

              *

              * "\u0086b": the first collation element is collation_element('a'), the * second one is collation_element('e'), and the third collation element is * collation_element('b'). - *

              - * - * @since Android 1.0 */ public final class CollationElementIterator { @@ -61,8 +53,6 @@ public final class CollationElementIterator { * {@code next()} and {@code previous()} when the end or the * beginning of the source string has been reached, and there are no more * valid collation elements to return. - * - * @since Android 1.0 */ public static final int NULLORDER = -1; @@ -83,7 +73,6 @@ public final class CollationElementIterator { * method. * @return the maximum length of any expansion sequence ending with the * specified collation element. - * @since Android 1.0 */ public int getMaxExpansion(int order) { return this.icuIterator.getMaxExpansion(order); @@ -111,7 +100,6 @@ public final class CollationElementIterator { * @return The position of the collation element in the source string that * will be returned by the next invocation of the {@link #next()} * method. - * @since Android 1.0 */ public int getOffset() { return this.icuIterator.getOffset(); @@ -122,7 +110,6 @@ public final class CollationElementIterator { * * @return the next collation element or {@code NULLORDER} if the end * of the iteration has been reached. - * @since Android 1.0 */ public int next() { return this.icuIterator.next(); @@ -133,7 +120,6 @@ public final class CollationElementIterator { * * @return the previous collation element, or {@code NULLORDER} when * the start of the iteration has been reached. - * @since Android 1.0 */ public int previous() { return this.icuIterator.previous(); @@ -146,7 +132,6 @@ public final class CollationElementIterator { * @param order * the element of the collation. * @return the element's 16 bit primary order. - * @since Android 1.0 */ public static final int primaryOrder(int order) { return com.ibm.icu4jni.text.CollationElementIterator.primaryOrder(order); @@ -160,9 +145,6 @@ public final class CollationElementIterator { * If the {@code RuleBasedCollator} used by this iterator has had its * attributes changed, calling {@code reset()} reinitializes the iterator to * use the new attributes. - *

              - * - * @since Android 1.0 */ public void reset() { this.icuIterator.reset(); @@ -175,7 +157,6 @@ public final class CollationElementIterator { * @param order * the element of the collator. * @return the 8 bit secondary order of the element. - * @since Android 1.0 */ public static final short secondaryOrder(int order) { return (short) com.ibm.icu4jni.text.CollationElementIterator @@ -193,19 +174,16 @@ public final class CollationElementIterator { * adjusted to the start of that sequence. As a result of this, any * subsequent call made to {@code getOffset()} may not return the same value * set by this method. - *

              *

              * If the decomposition mode is on, and offset is in the middle of a * decomposable range of source text, the iterator may not return a correct * result for the next forwards or backwards iteration. The user must ensure * that the offset is not in the middle of a decomposable range. - *

              * * @param newOffset * the character offset into the original source string to set. * Note that this is not an offset into the corresponding * sequence of collation elements. - * @since Android 1.0 */ public void setOffset(int newOffset) { this.icuIterator.setOffset(newOffset); @@ -217,7 +195,6 @@ public final class CollationElementIterator { * * @param source * the new source string iterator for iteration. - * @since Android 1.0 */ public void setText(CharacterIterator source) { this.icuIterator.setText(source); @@ -229,7 +206,6 @@ public final class CollationElementIterator { * * @param source * the new source string for iteration. - * @since Android 1.0 */ public void setText(String source) { this.icuIterator.setText(source); @@ -242,7 +218,6 @@ public final class CollationElementIterator { * @param order * the element of the collation. * @return the 8 bit tertiary order of the element. - * @since Android 1.0 */ public static final short tertiaryOrder(int order) { return (short) com.ibm.icu4jni.text.CollationElementIterator diff --git a/libcore/text/src/main/java/java/text/CollationKey.java b/libcore/text/src/main/java/java/text/CollationKey.java index f43bfd115..a994fe254 100644 --- a/libcore/text/src/main/java/java/text/CollationKey.java +++ b/libcore/text/src/main/java/java/text/CollationKey.java @@ -14,17 +14,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/** -******************************************************************************* -* Copyright (C) 1996-2006, International Business Machines Corporation and * -* others. All Rights Reserved. * -******************************************************************************* -*/ // BEGIN android-note -// The class javadoc and some of the method descriptions are copied from ICU4J -// source files. Changes have been made to the copied descriptions. -// The icu license header was added to this file. // The icu implementation used was changed from icu4j to icu4jni. // END android-note @@ -38,13 +29,11 @@ package java.text; * string under two different {@code Collator} instances might differ. Hence * comparing collation keys generated from different {@code Collator} instances * can give incorrect results. - *

              *

              * Both the method {@code CollationKey.compareTo(CollationKey)} and the method * {@code Collator.compare(String, String)} compares two strings and returns * their relative order. The performance characteristics of these two approaches * can differ. - *

              *

              * During the construction of a {@code CollationKey}, the entire source string * is examined and processed into a series of bits terminated by a null, that @@ -54,19 +43,16 @@ package java.text; * the {@code CollationKey}, but once the key is created, binary comparisons * are fast. This approach is recommended when the same strings are to be * compared over and over again. - *

              *

              * On the other hand, implementations of * {@code Collator.compare(String, String)} can examine and process the strings * only until the first characters differ in order. This approach is * recommended if the strings are to be compared only once. - *

              *

              * The following example shows how collation keys can be used to sort a * list of strings: - *

              *
              - * + * *
                * // Create an array of CollationKeys for the Strings to be sorted.
                * Collator myCollator = Collator.getInstance();
              @@ -89,12 +75,11 @@ package java.text;
                * System.out.println(keys[1].getSourceString());
                * System.out.println(keys[2].getSourceString());
                * 
              - * + * *
              - * + * * @see Collator * @see RuleBasedCollator - * @since Android 1.0 */ public final class CollationKey implements Comparable { @@ -116,7 +101,6 @@ public final class CollationKey implements Comparable { * @return a negative value if this {@code CollationKey} is less than the * specified {@code CollationKey}, 0 if they are equal and a * positive value if this {@code CollationKey} is greater. - * @since Android 1.0 */ public int compareTo(CollationKey value) { return icuKey.compareTo(value.icuKey); @@ -133,7 +117,6 @@ public final class CollationKey implements Comparable { * @return {@code true} if {@code object} is equal to this collation key; * {@code false} otherwise. * @see #hashCode - * @since Android 1.0 */ @Override public boolean equals(Object object) { @@ -148,7 +131,6 @@ public final class CollationKey implements Comparable { * Returns the string from which this collation key was created. * * @return the source string of this collation key. - * @since Android 1.0 */ public String getSourceString() { return this.source; @@ -161,7 +143,6 @@ public final class CollationKey implements Comparable { * @return the receiver's hash. * * @see #equals - * @since Android 1.0 */ @Override public int hashCode() { @@ -172,7 +153,6 @@ public final class CollationKey implements Comparable { * Returns the collation key as a byte array. * * @return an array of bytes. - * @since Android 1.0 */ public byte[] toByteArray() { return icuKey.toByteArray(); diff --git a/libcore/text/src/main/java/java/text/Collator.java b/libcore/text/src/main/java/java/text/Collator.java index a34b412fc..aaa3e12e5 100644 --- a/libcore/text/src/main/java/java/text/Collator.java +++ b/libcore/text/src/main/java/java/text/Collator.java @@ -14,17 +14,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/** -******************************************************************************* -* Copyright (C) 1996-2007, International Business Machines Corporation and * -* others. All Rights Reserved. * -******************************************************************************* -*/ // BEGIN android-note -// The class javadoc and some of the method descriptions are copied from ICU4J -// source files. Changes have been made to the copied descriptions. -// The icu license header was added to this file. // The icu implementation used was changed from icu4j to icu4jni. // END android-note @@ -47,7 +38,6 @@ import org.apache.harmony.luni.util.PriviAction; * href="http://www.unicode.org/unicode/reports/tr10/"> Unicode Collation * Algorithm (UCA), there are 4 different levels of strength used in * comparisons: - *

              *
                *
              • PRIMARY strength: Typically, this is used to denote differences between * base characters (for example, "a" < "b"). It is the strongest difference. @@ -83,12 +73,10 @@ import org.apache.harmony.luni.util.PriviAction; * NFD. If canonical decomposition is turned off, it is the user's * responsibility to ensure that all text is already in the appropriate form * before performing a comparison or before getting a {@link CollationKey}. - *

                *

                * Examples: - *

                *
                - * + * *
                  * // Get the Collator for US English and set its strength to PRIMARY
                  * Collator usCollator = Collator.getInstance(Locale.US);
                @@ -97,14 +85,13 @@ import org.apache.harmony.luni.util.PriviAction;
                  *     System.out.println("Strings are equivalent");
                  * }
                  * 
                - * + * *
                *

                * The following example shows how to compare two strings using the collator for * the default locale. - *

                *
                - * + * *
                  * // Compare two strings in the default locale
                  * Collator myCollator = Collator.getInstance();
                @@ -121,12 +108,11 @@ import org.apache.harmony.luni.util.PriviAction;
                  *     System.out.println("Error: \u00e0\u0325 should be not equal to a\u0325\u0300 without decomposition");
                  * }
                  * 
                - * + * *
                - * + * * @see RuleBasedCollator * @see CollationKey - * @since Android 1.0 */ public abstract class Collator implements Comparator, Cloneable { @@ -138,51 +124,37 @@ public abstract class Collator implements Comparator, Cloneable { /** * Constant used to specify the decomposition rule. - * - * @since Android 1.0 */ public static final int NO_DECOMPOSITION = 0; /** * Constant used to specify the decomposition rule. - * - * @since Android 1.0 */ public static final int CANONICAL_DECOMPOSITION = 1; /** * Constant used to specify the decomposition rule. This value for * decomposition is not supported. - * - * @since Android 1.0 */ public static final int FULL_DECOMPOSITION = 2; /** * Constant used to specify the collation strength. - * - * @since Android 1.0 */ public static final int PRIMARY = 0; /** * Constant used to specify the collation strength. - * - * @since Android 1.0 */ public static final int SECONDARY = 1; /** * Constant used to specify the collation strength. - * - * @since Android 1.0 */ public static final int TERTIARY = 2; /** * Constant used to specify the collation strength. - * - * @since Android 1.0 */ public static final int IDENTICAL = 3; @@ -214,8 +186,6 @@ public abstract class Collator implements Comparator, Cloneable { /** * Constructs a new {@code Collator} instance. - * - * @since Android 1.0 */ protected Collator() { super(); @@ -230,7 +200,6 @@ public abstract class Collator implements Comparator, Cloneable { * * @return a shallow copy of this collator. * @see java.lang.Cloneable - * @since Android 1.0 */ @Override public Object clone() { @@ -254,10 +223,8 @@ public abstract class Collator implements Comparator, Cloneable { * @return a negative value if {@code object1} is less than {@code object2}, * 0 if they are equal, and a positive value if {@code object1} is * greater than {@code object2}. - * @exception ClassCastException - * if {@code object1} or {@code object2} is not a - * {@code String}. - * @since Android 1.0 + * @throws ClassCastException + * if {@code object1} or {@code object2} is not a {@code String}. */ public int compare(Object object1, Object object2) { return compare((String) object1, (String) object2); @@ -273,7 +240,6 @@ public abstract class Collator implements Comparator, Cloneable { * @return a negative value if {@code string1} is less than {@code string2}, * 0 if they are equal and a positive value if {@code string1} is * greater than {@code string2}. - * @since Android 1.0 */ public abstract int compare(String string1, String string2); @@ -287,7 +253,6 @@ public abstract class Collator implements Comparator, Cloneable { * it has the same strength and decomposition values as this * collator; {@code false} otherwise. * @see #hashCode - * @since Android 1.0 */ @Override public boolean equals(Object object) { @@ -309,7 +274,6 @@ public abstract class Collator implements Comparator, Cloneable { * the second string to compare. * @return {@code true} if {@code string1} and {@code string2} are equal * using the collation rules, false otherwise. - * @since Android 1.0 */ public boolean equals(String string1, String string2) { return compare(string1, string2) == 0; @@ -320,7 +284,6 @@ public abstract class Collator implements Comparator, Cloneable { * {@code Collator}. * * @return an array of {@code Locale}. - * @since Android 1.0 */ public static Locale[] getAvailableLocales() { return com.ibm.icu4jni.text.Collator.getAvailableLocales(); @@ -333,7 +296,6 @@ public abstract class Collator implements Comparator, Cloneable { * @param string * the source string that is converted into a collation key. * @return the collation key for {@code string}. - * @since Android 1.0 */ public abstract CollationKey getCollationKey(String string); @@ -343,7 +305,6 @@ public abstract class Collator implements Comparator, Cloneable { * @return the decomposition rule, either {@code NO_DECOMPOSITION} or * {@code CANONICAL_DECOMPOSITION}. {@code FULL_DECOMPOSITION} is * not supported. - * @since Android 1.0 */ public int getDecomposition() { return decompositionMode_ICU_Java(this.icuColl.getDecomposition()); @@ -354,7 +315,6 @@ public abstract class Collator implements Comparator, Cloneable { * {@code Locale}. * * @return the collator for the default locale. - * @since Android 1.0 */ public static Collator getInstance() { return getInstance(Locale.getDefault()); @@ -367,7 +327,6 @@ public abstract class Collator implements Comparator, Cloneable { * @param locale * the locale. * @return the collator for {@code locale}. - * @since Android 1.0 */ public static Collator getInstance(Locale locale) { String key = locale.toString(); @@ -386,7 +345,6 @@ public abstract class Collator implements Comparator, Cloneable { * * @return the strength value, either PRIMARY, SECONDARY, TERTIARY or * IDENTICAL. - * @since Android 1.0 */ public int getStrength() { return strength_ICU_Java(this.icuColl.getStrength()); @@ -399,7 +357,6 @@ public abstract class Collator implements Comparator, Cloneable { * * @see #equals(Object) * @see #equals(String, String) - * @since Android 1.0 */ @Override public abstract int hashCode(); @@ -411,10 +368,9 @@ public abstract class Collator implements Comparator, Cloneable { * the decomposition rule, either {@code NO_DECOMPOSITION} or * {@code CANONICAL_DECOMPOSITION}. {@code FULL_DECOMPOSITION} * is not supported. - * @exception IllegalArgumentException - * if the provided decomposition rule is not valid. This - * includes {@code FULL_DECOMPOSITION}. - * @since Android 1.0 + * @throws IllegalArgumentException + * if the provided decomposition rule is not valid. This includes + * {@code FULL_DECOMPOSITION}. */ public void setDecomposition(int value) { this.icuColl.setDecomposition(decompositionMode_Java_ICU(value)); @@ -426,10 +382,8 @@ public abstract class Collator implements Comparator, Cloneable { * @param value * the strength value, either PRIMARY, SECONDARY, TERTIARY, or * IDENTICAL. - * - * @exception IllegalArgumentException - * if the provided strength value is not valid. - * @since Android 1.0 + * @throws IllegalArgumentException + * if the provided strength value is not valid. */ public void setStrength(int value) { this.icuColl.setStrength(strength_Java_ICU(value)); diff --git a/libcore/text/src/main/java/java/text/DateFormat.java b/libcore/text/src/main/java/java/text/DateFormat.java index 38759aea8..f39965a52 100644 --- a/libcore/text/src/main/java/java/text/DateFormat.java +++ b/libcore/text/src/main/java/java/text/DateFormat.java @@ -14,17 +14,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/** -******************************************************************************* -* Copyright (C) 1996-2007, International Business Machines Corporation and * -* others. All Rights Reserved. * -******************************************************************************* -*/ // BEGIN android-note -// The class javadoc and some of the method descriptions are copied from ICU4J -// source files. Changes have been made to the copied descriptions. -// The icu license header was added to this file. +// changed from ICU to resource bundles // END android-note package java.text; @@ -34,7 +26,9 @@ import java.util.Calendar; import java.util.Date; import java.util.Hashtable; import java.util.Locale; +// BEGIN android-added import java.util.ResourceBundle; +// BEGIN android-added import java.util.TimeZone; import org.apache.harmony.text.internal.nls.Messages; @@ -52,58 +46,52 @@ import org.apache.harmony.text.internal.nls.Messages; * styles. The formatting styles include FULL, LONG, MEDIUM, and SHORT. More * details and examples for using these styles are provided in the method * descriptions. - *

                *

                * {@code DateFormat} helps you to format and parse dates for any locale. Your * code can be completely independent of the locale conventions for months, days * of the week, or even the calendar format: lunar vs. solar. - *

                *

                * To format a date for the current Locale, use one of the static factory * methods: - *

                *
                - * + * *
                  * myString = DateFormat.getDateInstance().format(myDate);
                  * 
                - * + * *
                *

                * If you are formatting multiple dates, it is more efficient to get the format * and use it multiple times so that the system doesn't have to fetch the * information about the local language and country conventions multiple times. - *

                *
                - * + * *
                  * DateFormat df = DateFormat.getDateInstance();
                  * for (int i = 0; i < a.length; ++i) {
                  *     output.println(df.format(myDate[i]) + "; ");
                  * }
                  * 
                - * + * *
                *

                * To format a number for a different locale, specify it in the call to * {@code getDateInstance}: - *

                *
                - * + * *
                  * DateFormat df = DateFormat.getDateInstance(DateFormat.LONG, Locale.FRANCE);
                  * 
                - * + * *
                *

                * {@code DateFormat} can also be used to parse strings: - *

                *
                - * + * *
                  * myDate = df.parse(myString);
                  * 
                - * + * *
                *

                * Use {@code getDateInstance} to get the normal date format for a country. @@ -112,7 +100,6 @@ import org.apache.harmony.text.internal.nls.Messages; * date and time format. You can pass in different options to these factory * methods to control the length of the result; from SHORT to MEDIUM to LONG to * FULL. The exact result depends on the locale, but generally: - *

                *
                  *
                • SHORT is completely numeric, such as 12.13.52 or 3:30pm *
                • MEDIUM is longer, such as Jan 12, 1952 @@ -126,7 +113,6 @@ import org.apache.harmony.text.internal.nls.Messages; * from the factory methods to a {@code SimpleDateFormat}. This will work for * the majority of countries; just remember to put it in a try block in case you * encounter an unusual one. - *

                  *

                  * There are versions of the parse and format methods which use * {@code ParsePosition} and {@code FieldPosition} to allow you to @@ -139,13 +125,11 @@ import org.apache.harmony.text.internal.nls.Messages; * Date formats are not synchronized. It is recommended to create separate * format instances for each thread. If multiple threads access a format * concurrently, it must be synchronized externally. - *

                  - * + * * @see NumberFormat * @see SimpleDateFormat * @see Calendar * @see TimeZone - * @since Android 1.0 */ public abstract class DateFormat extends Format { @@ -154,83 +138,61 @@ public abstract class DateFormat extends Format { /** * The calendar that this {@code DateFormat} uses to format a number * representing a date. - * - * @since Android 1.0 */ protected Calendar calendar; /** * The number format used to format a number. - * - * @since Android 1.0 */ protected NumberFormat numberFormat; /** * The format style constant defining the default format style. The default * is MEDIUM. - * - * @since Android 1.0 */ public final static int DEFAULT = 2; /** * The format style constant defining the full style. - * - * @since Android 1.0 */ public final static int FULL = 0; /** * The format style constant defining the long style. - * - * @since Android 1.0 */ public final static int LONG = 1; /** * The format style constant defining the medium style. - * - * @since Android 1.0 */ public final static int MEDIUM = 2; /** * The format style constant defining the short style. - * - * @since Android 1.0 */ public final static int SHORT = 3; /** * The {@code FieldPosition} selector for 'G' field alignment, corresponds * to the {@link Calendar#ERA} field. - * - * @since Android 1.0 */ public final static int ERA_FIELD = 0; /** * The {@code FieldPosition} selector for 'y' field alignment, corresponds * to the {@link Calendar#YEAR} field. - * - * @since Android 1.0 */ public final static int YEAR_FIELD = 1; /** * The {@code FieldPosition} selector for 'M' field alignment, corresponds * to the {@link Calendar#MONTH} field. - * - * @since Android 1.0 */ public final static int MONTH_FIELD = 2; /** * The {@code FieldPosition} selector for 'd' field alignment, corresponds * to the {@link Calendar#DATE} field. - * - * @since Android 1.0 */ public final static int DATE_FIELD = 3; @@ -239,8 +201,6 @@ public abstract class DateFormat extends Format { * to the {@link Calendar#HOUR_OF_DAY} field. {@code HOUR_OF_DAY1_FIELD} is * used for the one-based 24-hour clock. For example, 23:59 + 01:00 results * in 24:59. - * - * @since Android 1.0 */ public final static int HOUR_OF_DAY1_FIELD = 4; @@ -249,80 +209,60 @@ public abstract class DateFormat extends Format { * to the {@link Calendar#HOUR_OF_DAY} field. {@code HOUR_OF_DAY0_FIELD} is * used for the zero-based 24-hour clock. For example, 23:59 + 01:00 results * in 00:59. - * - * @since Android 1.0 */ public final static int HOUR_OF_DAY0_FIELD = 5; /** * FieldPosition selector for 'm' field alignment, corresponds to the * {@link Calendar#MINUTE} field. - * - * @since Android 1.0 */ public final static int MINUTE_FIELD = 6; /** * FieldPosition selector for 's' field alignment, corresponds to the * {@link Calendar#SECOND} field. - * - * @since Android 1.0 */ public final static int SECOND_FIELD = 7; /** * FieldPosition selector for 'S' field alignment, corresponds to the * {@link Calendar#MILLISECOND} field. - * - * @since Android 1.0 */ public final static int MILLISECOND_FIELD = 8; /** * FieldPosition selector for 'E' field alignment, corresponds to the * {@link Calendar#DAY_OF_WEEK} field. - * - * @since Android 1.0 */ public final static int DAY_OF_WEEK_FIELD = 9; /** * FieldPosition selector for 'D' field alignment, corresponds to the * {@link Calendar#DAY_OF_YEAR} field. - * - * @since Android 1.0 */ public final static int DAY_OF_YEAR_FIELD = 10; /** * FieldPosition selector for 'F' field alignment, corresponds to the * {@link Calendar#DAY_OF_WEEK_IN_MONTH} field. - * - * @since Android 1.0 */ public final static int DAY_OF_WEEK_IN_MONTH_FIELD = 11; /** * FieldPosition selector for 'w' field alignment, corresponds to the * {@link Calendar#WEEK_OF_YEAR} field. - * - * @since Android 1.0 */ public final static int WEEK_OF_YEAR_FIELD = 12; /** * FieldPosition selector for 'W' field alignment, corresponds to the * {@link Calendar#WEEK_OF_MONTH} field. - * - * @since Android 1.0 */ public final static int WEEK_OF_MONTH_FIELD = 13; /** * FieldPosition selector for 'a' field alignment, corresponds to the * {@link Calendar#AM_PM} field. - * - * @since Android 1.0 */ public final static int AM_PM_FIELD = 14; @@ -331,8 +271,6 @@ public abstract class DateFormat extends Format { * {@link Calendar#HOUR} field. {@code HOUR1_FIELD} is used for the * one-based 12-hour clock. For example, 11:30 PM + 1 hour results in 12:30 * AM. - * - * @since Android 1.0 */ public final static int HOUR1_FIELD = 15; @@ -340,8 +278,6 @@ public abstract class DateFormat extends Format { * The {@code FieldPosition} selector for 'z' field alignment, corresponds * to the {@link Calendar#ZONE_OFFSET} and {@link Calendar#DST_OFFSET} * fields. - * - * @since Android 1.0 */ public final static int HOUR0_FIELD = 16; @@ -349,15 +285,11 @@ public abstract class DateFormat extends Format { * The {@code FieldPosition} selector for 'z' field alignment, corresponds * to the {@link Calendar#ZONE_OFFSET} and {@link Calendar#DST_OFFSET} * fields. - * - * @since Android 1.0 */ public final static int TIMEZONE_FIELD = 17; /** * Constructs a new instance of {@code DateFormat}. - * - * @since Android 1.0 */ protected DateFormat() { } @@ -368,7 +300,6 @@ public abstract class DateFormat extends Format { * @return a shallow copy of this {@code DateFormat}. * * @see java.lang.Cloneable - * @since Android 1.0 */ @Override public Object clone() { @@ -388,7 +319,6 @@ public abstract class DateFormat extends Format { * it has the same properties as this date format; {@code false} * otherwise. * @see #hashCode - * @since Android 1.0 */ @Override public boolean equals(Object object) { @@ -417,8 +347,7 @@ public abstract class DateFormat extends Format { * a format field, then its {@code beginIndex} and {@code endIndex} members * will be updated with the position of the first occurrence of this field * in the formatted text. - *

                  - * + * * @param object * the source object to format, must be a {@code Date} or a * {@code Number}. If {@code object} is a number then a date is @@ -429,10 +358,9 @@ public abstract class DateFormat extends Format { * on input: an optional alignment field; on output: the offsets * of the alignment field in the formatted text. * @return the string buffer. - * @exception IllegalArgumentException - * if {@code object} is neither a {@code Date} nor a - * {@code Number} instance. - * @since Android 1.0 + * @throws IllegalArgumentException + * if {@code object} is neither a {@code Date} nor a + * {@code Number} instance. */ @Override public final StringBuffer format(Object object, StringBuffer buffer, @@ -453,7 +381,6 @@ public abstract class DateFormat extends Format { * @param date * the date to format. * @return the formatted string. - * @since Android 1.0 */ public final String format(Date date) { return format(date, new StringBuffer(), new FieldPosition(0)) @@ -468,8 +395,7 @@ public abstract class DateFormat extends Format { * a format field, then its {@code beginIndex} and {@code endIndex} members * will be updated with the position of the first occurrence of this field * in the formatted text. - *

                  - * + * * @param date * the date to format. * @param buffer @@ -478,7 +404,6 @@ public abstract class DateFormat extends Format { * on input: an optional alignment field; on output: the offsets * of the alignment field in the formatted text. * @return the string buffer. - * @since Android 1.0 */ public abstract StringBuffer format(Date date, StringBuffer buffer, FieldPosition field); @@ -487,7 +412,6 @@ public abstract class DateFormat extends Format { * Gets the list of installed locales which support {@code DateFormat}. * * @return an array of locales. - * @since Android 1.0 */ public static Locale[] getAvailableLocales() { return Locale.getAvailableLocales(); @@ -497,7 +421,6 @@ public abstract class DateFormat extends Format { * Returns the calendar used by this {@code DateFormat}. * * @return the calendar used by this date format. - * @since Android 1.0 */ public Calendar getCalendar() { return calendar; @@ -508,7 +431,6 @@ public abstract class DateFormat extends Format { * the DEFAULT style for the default locale. * * @return the {@code DateFormat} instance for the default style and locale. - * @since Android 1.0 */ public final static DateFormat getDateInstance() { return getDateInstance(DEFAULT); @@ -525,7 +447,6 @@ public abstract class DateFormat extends Format { * @throws IllegalArgumentException * if {@code style} is not one of SHORT, MEDIUM, LONG, FULL, or * DEFAULT. - * @since Android 1.0 */ public final static DateFormat getDateInstance(int style) { checkDateStyle(style); @@ -545,13 +466,14 @@ public abstract class DateFormat extends Format { * DEFAULT. * @return the {@code DateFormat} instance for {@code style} and * {@code locale}. - * @since Android 1.0 */ public final static DateFormat getDateInstance(int style, Locale locale) { checkDateStyle(style); + // BEGIN android-changed ResourceBundle bundle = getBundle(locale); String pattern = bundle.getString("Date_" + getStyleName(style)); //$NON-NLS-1$ return new SimpleDateFormat(pattern, locale); + // END android-changed } /** @@ -559,7 +481,6 @@ public abstract class DateFormat extends Format { * and time values in the DEFAULT style for the default locale. * * @return the {@code DateFormat} instance for the default style and locale. - * @since Android 1.0 */ public final static DateFormat getDateTimeInstance() { return getDateTimeInstance(DEFAULT, DEFAULT); @@ -578,7 +499,6 @@ public abstract class DateFormat extends Format { * @throws IllegalArgumentException * if {@code dateStyle} or {@code timeStyle} is not one of * SHORT, MEDIUM, LONG, FULL, or DEFAULT. - * @since Android 1.0 */ public final static DateFormat getDateTimeInstance(int dateStyle, int timeStyle) { @@ -602,16 +522,17 @@ public abstract class DateFormat extends Format { * @throws IllegalArgumentException * if {@code dateStyle} or {@code timeStyle} is not one of * SHORT, MEDIUM, LONG, FULL, or DEFAULT. - * @since Android 1.0 */ public final static DateFormat getDateTimeInstance(int dateStyle, int timeStyle, Locale locale) { checkTimeStyle(timeStyle); checkDateStyle(dateStyle); + // BEGIN android-changed ResourceBundle bundle = getBundle(locale); String pattern = bundle.getString("Date_" + getStyleName(dateStyle)) //$NON-NLS-1$ + " " + bundle.getString("Time_" + getStyleName(timeStyle)); //$NON-NLS-1$ //$NON-NLS-2$ return new SimpleDateFormat(pattern, locale); + // END android-changed } /** @@ -620,7 +541,6 @@ public abstract class DateFormat extends Format { * * @return the {@code DateFormat} instance for the SHORT style and default * locale. - * @since Android 1.0 */ public final static DateFormat getInstance() { return getDateTimeInstance(SHORT, SHORT); @@ -630,7 +550,6 @@ public abstract class DateFormat extends Format { * Returns the {@code NumberFormat} used by this {@code DateFormat}. * * @return the {@code NumberFormat} used by this date format. - * @since Android 1.0 */ public NumberFormat getNumberFormat() { return numberFormat; @@ -662,7 +581,6 @@ public abstract class DateFormat extends Format { * values in the DEFAULT style for the default locale. * * @return the {@code DateFormat} instance for the default style and locale. - * @since Android 1.0 */ public final static DateFormat getTimeInstance() { return getTimeInstance(DEFAULT); @@ -679,7 +597,6 @@ public abstract class DateFormat extends Format { * @throws IllegalArgumentException * if {@code style} is not one of SHORT, MEDIUM, LONG, FULL, or * DEFAULT. - * @since Android 1.0 */ public final static DateFormat getTimeInstance(int style) { checkTimeStyle(style); @@ -699,20 +616,20 @@ public abstract class DateFormat extends Format { * DEFAULT. * @return the {@code DateFormat} instance for {@code style} and * {@code locale}. - * @since Android 1.0 */ public final static DateFormat getTimeInstance(int style, Locale locale) { checkTimeStyle(style); + // BEGIN android-changed ResourceBundle bundle = getBundle(locale); String pattern = bundle.getString("Time_" + getStyleName(style)); //$NON-NLS-1$ return new SimpleDateFormat(pattern, locale); + // END android-changed } /** * Returns the time zone of this date format's calendar. * * @return the time zone of the calendar used by this date format. - * @since Android 1.0 */ public TimeZone getTimeZone() { return calendar.getTimeZone(); @@ -731,7 +648,6 @@ public abstract class DateFormat extends Format { * Indicates whether the calendar used by this date format is lenient. * * @return {@code true} if the calendar is lenient; {@code false} otherwise. - * @since Android 1.0 */ public boolean isLenient() { return calendar.isLenient(); @@ -744,9 +660,8 @@ public abstract class DateFormat extends Format { * @param string * the string to parse. * @return the {@code Date} resulting from the parsing. - * @exception ParseException - * if an error occurs during parsing. - * @since Android 1.0 + * @throws ParseException + * if an error occurs during parsing. */ public Date parse(String string) throws ParseException { ParsePosition position = new ParsePosition(0); @@ -770,8 +685,7 @@ public abstract class DateFormat extends Format { * this object's format method but can still be parsed as a date, then the * parse succeeds. Clients may insist on strict adherence to the format by * calling {@code setLenient(false)}. - *

                  - * + * * @param string * the string to parse. * @param position @@ -782,7 +696,6 @@ public abstract class DateFormat extends Format { * the index where the error occurred. * @return the date resulting from the parse, or {@code null} if there is an * error. - * @since Android 1.0 */ public abstract Date parse(String string, ParsePosition position); @@ -797,8 +710,7 @@ public abstract class DateFormat extends Format { * this object's format method but can still be parsed as a date, then the * parse succeeds. Clients may insist on strict adherence to the format by * calling {@code setLenient(false)}. - *

                  - * + * * @param string * the string to parse. * @param position @@ -809,7 +721,6 @@ public abstract class DateFormat extends Format { * is set to the index where the error occurred. * @return the date resulting from the parsing, or {@code null} if there is * an error. - * @since Android 1.0 */ @Override public Object parseObject(String string, ParsePosition position) { @@ -821,7 +732,6 @@ public abstract class DateFormat extends Format { * * @param cal * the new calendar. - * @since Android 1.0 */ public void setCalendar(Calendar cal) { calendar = cal; @@ -836,7 +746,6 @@ public abstract class DateFormat extends Format { * @param value * {@code true} to set the calendar to be lenient, {@code false} * otherwise. - * @since Android 1.0 */ public void setLenient(boolean value) { calendar.setLenient(value); @@ -847,7 +756,6 @@ public abstract class DateFormat extends Format { * * @param format * the new number format. - * @since Android 1.0 */ public void setNumberFormat(NumberFormat format) { numberFormat = format; @@ -858,7 +766,6 @@ public abstract class DateFormat extends Format { * * @param timezone * the new time zone. - * @since Android 1.0 */ public void setTimeZone(TimeZone timezone) { calendar.setTimeZone(timezone); @@ -871,8 +778,6 @@ public abstract class DateFormat extends Format { *

                  * There is no public constructor in this class, the only instances are the * constants defined here. - *

                  - * @since Android 1.0 */ public static class Field extends Format.Field { @@ -882,135 +787,99 @@ public abstract class DateFormat extends Format { /** * Marks the era part of a date. - * - * @since Android 1.0 */ public final static Field ERA = new Field("era", Calendar.ERA); //$NON-NLS-1$ /** * Marks the year part of a date. - * - * @since Android 1.0 */ public final static Field YEAR = new Field("year", Calendar.YEAR); //$NON-NLS-1$ /** * Marks the month part of a date. - * - * @since Android 1.0 */ public final static Field MONTH = new Field("month", Calendar.MONTH); //$NON-NLS-1$ /** * Marks the hour of the day part of a date (0-11). - * - * @since Android 1.0 */ public final static Field HOUR_OF_DAY0 = new Field("hour of day", //$NON-NLS-1$ Calendar.HOUR_OF_DAY); /** * Marks the hour of the day part of a date (1-12). - * - * @since Android 1.0 */ public final static Field HOUR_OF_DAY1 = new Field("hour of day 1", -1); //$NON-NLS-1$ /** * Marks the minute part of a time. - * - * @since Android 1.0 */ public final static Field MINUTE = new Field("minute", Calendar.MINUTE); //$NON-NLS-1$ /** * Marks the second part of a time. - * - * @since Android 1.0 */ public final static Field SECOND = new Field("second", Calendar.SECOND); //$NON-NLS-1$ /** * Marks the millisecond part of a time. - * - * @since Android 1.0 */ public final static Field MILLISECOND = new Field("millisecond", //$NON-NLS-1$ Calendar.MILLISECOND); /** * Marks the day of the week part of a date. - * - * @since Android 1.0 */ public final static Field DAY_OF_WEEK = new Field("day of week", //$NON-NLS-1$ Calendar.DAY_OF_WEEK); /** * Marks the day of the month part of a date. - * - * @since Android 1.0 */ public final static Field DAY_OF_MONTH = new Field("day of month", //$NON-NLS-1$ Calendar.DAY_OF_MONTH); /** * Marks the day of the year part of a date. - * - * @since Android 1.0 */ public final static Field DAY_OF_YEAR = new Field("day of year", //$NON-NLS-1$ Calendar.DAY_OF_YEAR); /** * Marks the day of the week in the month part of a date. - * - * @since Android 1.0 */ public final static Field DAY_OF_WEEK_IN_MONTH = new Field( "day of week in month", Calendar.DAY_OF_WEEK_IN_MONTH); //$NON-NLS-1$ /** * Marks the week of the year part of a date. - * - * @since Android 1.0 */ public final static Field WEEK_OF_YEAR = new Field("week of year", //$NON-NLS-1$ Calendar.WEEK_OF_YEAR); /** * Marks the week of the month part of a date. - * - * @since Android 1.0 */ public final static Field WEEK_OF_MONTH = new Field("week of month", //$NON-NLS-1$ Calendar.WEEK_OF_MONTH); /** * Marks the time indicator part of a date. - * - * @since Android 1.0 */ public final static Field AM_PM = new Field("am pm", Calendar.AM_PM); //$NON-NLS-1$ /** * Marks the hour part of a date (0-11). - * - * @since Android 1.0 */ public final static Field HOUR0 = new Field("hour", Calendar.HOUR); //$NON-NLS-1$ /** * Marks the hour part of a date (1-12). - * - * @since Android 1.0 */ public final static Field HOUR1 = new Field("hour 1", -1); //$NON-NLS-1$ /** * Marks the time zone part of a date. - * - * @since Android 1.0 */ public final static Field TIME_ZONE = new Field("time zone", -1); //$NON-NLS-1$ @@ -1022,12 +891,11 @@ public abstract class DateFormat extends Format { /** * Constructs a new instance of {@code DateFormat.Field} with the given * fieldName and calendar field. - * + * * @param fieldName * the field name. * @param calendarField * the calendar field type of the field. - * @since Android 1.0 */ protected Field(String fieldName, int calendarField) { super(fieldName); @@ -1042,7 +910,6 @@ public abstract class DateFormat extends Format { * Returns the Calendar field that this field represents. * * @return the calendar field. - * @since Android 1.0 */ public int getCalendarField() { return calendarField; @@ -1059,7 +926,6 @@ public abstract class DateFormat extends Format { * @throws IllegalArgumentException * if {@code calendarField} is negative or greater than the * field count of {@code Calendar}. - * @since Android 1.0 */ public static Field ofCalendarField(int calendarField) { if (calendarField < 0 || calendarField >= Calendar.FIELD_COUNT) { @@ -1072,18 +938,23 @@ public abstract class DateFormat extends Format { /** * Resolves instances that are deserialized to the constant * {@code DateFormat.Field} values. - * + * * @return the resolved field object. * @throws InvalidObjectException * if an error occurs while resolving the field object. - * @since Android 1.0 */ @Override protected Object readResolve() throws InvalidObjectException { + if (this.getClass() != Field.class) { + // text.0C=cannot resolve subclasses + throw new InvalidObjectException(Messages.getString("text.0C")); //$NON-NLS-1$ + } + if (calendarField != -1) { try { Field result = ofCalendarField(calendarField); - if (result != null && this.equals(result)) { + + if (result != null && this.getName().equals(result.getName())) { return result; } } catch (IllegalArgumentException e) { diff --git a/libcore/text/src/main/java/java/text/DateFormatSymbols.java b/libcore/text/src/main/java/java/text/DateFormatSymbols.java index 22c74e90e..ad25bd806 100644 --- a/libcore/text/src/main/java/java/text/DateFormatSymbols.java +++ b/libcore/text/src/main/java/java/text/DateFormatSymbols.java @@ -14,29 +14,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/** -******************************************************************************* -* Copyright (C) 1996-2007, International Business Machines Corporation and * -* others. All Rights Reserved. * -******************************************************************************* -*/ // BEGIN android-note -// The class javadoc and some of the method descriptions are copied from ICU4J -// source files. Changes have been made to the copied descriptions. -// The icu license header was added to this file. +// The icu implementation used was changed from icu4j to icu4jni. // END android-note package java.text; -import java.io.Serializable; -// BEGIN android-added import java.io.IOException; import java.io.ObjectOutputStream; -// END android-added +import java.io.Serializable; import java.util.Arrays; import java.util.Locale; +// BEGIN android-added import java.util.ResourceBundle; +// END android-added // BEGIN android-added import com.ibm.icu4jni.util.Resources; @@ -55,17 +47,15 @@ import com.ibm.icu4jni.util.Resources; * the formatter is created, you may modify its format pattern using the * {@code setPattern} method. For more information about creating formatters * using {@code DateFormat}'s factory methods, see {@link DateFormat}. - *

                  *

                  * If you decide to create a date/time formatter with a specific format pattern * for a specific locale, you can do so with: - *

                  *
                  - * + * *
                    * new SimpleDateFormat(aPattern, new DateFormatSymbols(aLocale)).
                    * 
                  - * + * *
                  *

                  * {@code DateFormatSymbols} objects can be cloned. When you obtain a @@ -73,15 +63,12 @@ import com.ibm.icu4jni.util.Resources; * formatting data. For instance, you can replace the localized date/time format * pattern characters with the ones that you feel easy to remember or you can * change the representative cities to your favorite ones. - *

                  *

                  * New {@code DateFormatSymbols} subclasses may be added to support * {@code SimpleDateFormat} for date/time formatting for additional locales. - *

                  - * + * * @see DateFormat * @see SimpleDateFormat - * @since Android 1.0 */ public class DateFormatSymbols implements Serializable, Cloneable { @@ -93,6 +80,10 @@ public class DateFormatSymbols implements Serializable, Cloneable { String[][] zoneStrings; + // BEGIN android-removed + // transient private com.ibm.icu4jni.text.DateFormatSymbols icuSymbols; + // END android-removed + // BEGIN android-added /** * Locale, necessary to lazily load time zone strings. We force the time @@ -110,15 +101,13 @@ public class DateFormatSymbols implements Serializable, Cloneable { if (zoneStrings == null) { zoneStrings = Resources.getDisplayTimeZones(locale.toString()); } - return zoneStrings; + return zoneStrings; } // END android-added /** * Constructs a new {@code DateFormatSymbols} instance containing the * symbols for the default locale. - * - * @since Android 1.0 */ public DateFormatSymbols() { this(Locale.getDefault()); @@ -130,9 +119,9 @@ public class DateFormatSymbols implements Serializable, Cloneable { * * @param locale * the locale. - * @since Android 1.0 */ public DateFormatSymbols(Locale locale) { + // BEGIN android-changed ResourceBundle bundle = Format.getBundle(locale); localPatternChars = bundle.getString("LocalPatternChars"); //$NON-NLS-1$ ampms = bundle.getStringArray("ampm"); //$NON-NLS-1$ @@ -141,12 +130,33 @@ public class DateFormatSymbols implements Serializable, Cloneable { shortMonths = bundle.getStringArray("shortMonths"); //$NON-NLS-1$ shortWeekdays = bundle.getStringArray("shortWeekdays"); //$NON-NLS-1$ weekdays = bundle.getStringArray("weekdays"); //$NON-NLS-1$ - // BEGIN android-changed - // zoneStrings = (String[][]) bundle.getObject("timezones"); //$NON-NLS-1$ this.locale = locale; // END android-changed } + + private void writeObject(ObjectOutputStream oos) throws IOException { + // BEGIN android-changed + internalZoneStrings(); + // END android-changed + oos.defaultWriteObject(); + } + + // BEGIN android-removed + // DateFormatSymbols(Locale locale, + // com.ibm.icu4jni.text.DateFormatSymbols icuSymbols) { + // + // this.icuSymbols = icuSymbols; + // localPatternChars = icuSymbols.getLocalPatternChars(); + // ampms = icuSymbols.getAmPmStrings(); + // eras = icuSymbols.getEras(); + // months = icuSymbols.getMonths(); + // shortMonths = icuSymbols.getShortMonths(); + // shortWeekdays = icuSymbols.getShortWeekdays(); + // weekdays = icuSymbols.getWeekdays(); + // } + // END android-removed + @Override public Object clone() { // BEGIN android-changed @@ -168,7 +178,6 @@ public class DateFormatSymbols implements Serializable, Cloneable { * {@code DateFormatSymbols} and has the same symbols as this * object, {@code false} otherwise. * @see #hashCode - * @since Android 1.0 */ @Override public boolean equals(Object object) { @@ -178,7 +187,18 @@ public class DateFormatSymbols implements Serializable, Cloneable { if (!(object instanceof DateFormatSymbols)) { return false; } + + // BEGIN android-removed + // if (zoneStrings == null) { + // zoneStrings = icuSymbols.getZoneStrings(); + // } + // END android-removed DateFormatSymbols obj = (DateFormatSymbols) object; + // BEGIN android-removed + // if (obj.zoneStrings == null) { + // obj.zoneStrings = obj.icuSymbols.getZoneStrings(); + // } + // END android-removed if (!localPatternChars.equals(obj.localPatternChars)) { return false; } @@ -233,7 +253,6 @@ public class DateFormatSymbols implements Serializable, Cloneable { * {@code Calendar.PM} as indices for the array. * * @return an array of strings. - * @since Android 1.0 */ public String[] getAmPmStrings() { return ampms.clone(); @@ -245,7 +264,6 @@ public class DateFormatSymbols implements Serializable, Cloneable { * {@code GregorianCalendar.AD} as indices for the array. * * @return an array of strings. - * @since Android 1.0 */ public String[] getEras() { return eras.clone(); @@ -256,7 +274,6 @@ public class DateFormatSymbols implements Serializable, Cloneable { * specify date and time fields. * * @return a string containing the pattern characters. - * @since Android 1.0 */ public String getLocalPatternChars() { return localPatternChars; @@ -268,7 +285,6 @@ public class DateFormatSymbols implements Serializable, Cloneable { * indices for the array. * * @return an array of strings. - * @since Android 1.0 */ public String[] getMonths() { return months.clone(); @@ -280,7 +296,6 @@ public class DateFormatSymbols implements Serializable, Cloneable { * {@code Calendar.JANUARY} etc. as indices for the array. * * @return an array of strings. - * @since Android 1.0 */ public String[] getShortMonths() { return shortMonths.clone(); @@ -292,7 +307,6 @@ public class DateFormatSymbols implements Serializable, Cloneable { * {@code Calendar.SUNDAY} etc. as indices for the array. * * @return an array of strings. - * @since Android 1.0 */ public String[] getShortWeekdays() { return shortWeekdays.clone(); @@ -304,7 +318,6 @@ public class DateFormatSymbols implements Serializable, Cloneable { * {@code Calendar.SUNDAY} etc. as indices for the array. * * @return an array of strings. - * @since Android 1.0 */ public String[] getWeekdays() { return weekdays.clone(); @@ -318,12 +331,11 @@ public class DateFormatSymbols implements Serializable, Cloneable { * and abbreviated names for daylight time. * * @return a two-dimensional array of strings. - * @since Android 1.0 */ public String[][] getZoneStrings() { - // BEGIN android-added + // BEGIN android-changed String[][] zoneStrings = internalZoneStrings(); - // END android-added + // END android-changed String[][] clone = new String[zoneStrings.length][]; for (int i = zoneStrings.length; --i >= 0;) { clone[i] = zoneStrings[i].clone(); @@ -333,6 +345,9 @@ public class DateFormatSymbols implements Serializable, Cloneable { @Override public int hashCode() { + // BEGIN android-changed + String[][] zoneStrings = internalZoneStrings(); + // END android-changed int hashCode; hashCode = localPatternChars.hashCode(); for (String element : ampms) { @@ -353,12 +368,11 @@ public class DateFormatSymbols implements Serializable, Cloneable { for (String element : weekdays) { hashCode += element.hashCode(); } - // BEGIN android-added - String[][] zoneStrings = internalZoneStrings(); - // END android-added for (String[] element : zoneStrings) { for (int j = 0; j < element.length; j++) { - hashCode += element[j].hashCode(); + if (element[j] != null) { + hashCode += element[j].hashCode(); + } } } return hashCode; @@ -371,7 +385,6 @@ public class DateFormatSymbols implements Serializable, Cloneable { * * @param data * the array of strings for AM and PM. - * @since Android 1.0 */ public void setAmPmStrings(String[] data) { ampms = data.clone(); @@ -384,7 +397,6 @@ public class DateFormatSymbols implements Serializable, Cloneable { * * @param data * the array of strings for BC and AD. - * @since Android 1.0 */ public void setEras(String[] data) { eras = data.clone(); @@ -396,7 +408,8 @@ public class DateFormatSymbols implements Serializable, Cloneable { * * @param data * the string containing the pattern characters. - * @since Android 1.0 + * @throws NullPointerException + * if {@code data} is null */ public void setLocalPatternChars(String data) { if (data == null) { @@ -412,7 +425,6 @@ public class DateFormatSymbols implements Serializable, Cloneable { * * @param data * the array of strings. - * @since Android 1.0 */ public void setMonths(String[] data) { months = data.clone(); @@ -425,7 +437,6 @@ public class DateFormatSymbols implements Serializable, Cloneable { * * @param data * the array of strings. - * @since Android 1.0 */ public void setShortMonths(String[] data) { shortMonths = data.clone(); @@ -438,7 +449,6 @@ public class DateFormatSymbols implements Serializable, Cloneable { * * @param data * the array of strings. - * @since Android 1.0 */ public void setShortWeekdays(String[] data) { shortWeekdays = data.clone(); @@ -451,7 +461,6 @@ public class DateFormatSymbols implements Serializable, Cloneable { * * @param data * the array of strings. - * @since Android 1.0 */ public void setWeekdays(String[] data) { weekdays = data.clone(); @@ -466,20 +475,8 @@ public class DateFormatSymbols implements Serializable, Cloneable { * * @param data * the two-dimensional array of strings. - * @since Android 1.0 */ public void setZoneStrings(String[][] data) { zoneStrings = data.clone(); } - - // BEGIN android-added - private void writeObject(ObjectOutputStream out) - throws IOException { - // Ensure internal zone strings are initialized to ensure backward - // compatibility. - internalZoneStrings(); - - out.defaultWriteObject(); - } - // END android-added } diff --git a/libcore/text/src/main/java/java/text/DecimalFormat.java b/libcore/text/src/main/java/java/text/DecimalFormat.java index 393a2e530..7a0f52950 100644 --- a/libcore/text/src/main/java/java/text/DecimalFormat.java +++ b/libcore/text/src/main/java/java/text/DecimalFormat.java @@ -14,24 +14,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/** -******************************************************************************* -* Copyright (C) 1996-2007, International Business Machines Corporation and * -* others. All Rights Reserved. * -******************************************************************************* -*/ // BEGIN android-note -// The class javadoc and some of the method descriptions are copied from ICU4J -// source files. Changes have been made to the copied descriptions. -// The icu license header was added to this file. // The icu implementation used was changed from icu4j to icu4jni. // END android-note package java.text; import java.io.IOException; -import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectStreamField; @@ -42,8 +32,6 @@ import java.security.PrivilegedAction; import java.util.Currency; import java.util.Locale; -import org.apache.harmony.text.internal.nls.Messages; - /** * A concrete subclass of {@link NumberFormat} that formats decimal numbers. It * has a variety of features designed to make it possible to parse and format @@ -56,7 +44,6 @@ import org.apache.harmony.text.internal.nls.Messages; * This is an enhanced version of {@code DecimalFormat} that is based on * the standard version in the RI. New or changed functionality is labeled * NEW. - *

                  *

                  * To obtain a {@link NumberFormat} for a specific locale (including the default * locale), call one of {@code NumberFormat}'s factory methods such as @@ -65,18 +52,18 @@ import org.apache.harmony.text.internal.nls.Messages; * {@link NumberFormat} factory methods may return subclasses other than * {@code DecimalFormat}. If you need to customize the format object, do * something like this:

                  - * + * *
                    * NumberFormat f = NumberFormat.getInstance(loc);
                    * if (f instanceof DecimalFormat) {
                    *     ((DecimalFormat)f).setDecimalSeparatorAlwaysShown(true);
                    * }
                    * 
                  - * + * *
                  *
                  Example:
                  *
                  - * + * *
                    * // Print out a number using the localized number, currency,
                    * // and percent format for each locale
                  @@ -115,7 +102,7 @@ import org.apache.harmony.text.internal.nls.Messages;
                    *     }
                    * }
                    * 
                  - * + * *
                  *

                  Patterns

                  *

                  @@ -126,7 +113,6 @@ import org.apache.harmony.text.internal.nls.Messages; * digits. The symbols are stored in a {@link DecimalFormatSymbols} object. When * using the {@link NumberFormat} factory methods, the pattern and symbols are * read from ICU's locale data. - *

                  *

                  Special Pattern Characters

                  *

                  * Many characters in a pattern are taken literally; they are matched during @@ -139,19 +125,16 @@ import org.apache.harmony.text.internal.nls.Messages; * character changes. Some special characters affect the behavior of the * formatter by their presence; for example, if the percent character is seen, * then the value is multiplied by 100 before being displayed. - *

                  *

                  * To insert a special character in a pattern as a literal, that is, without any * special meaning, the character must be quoted. There are some exceptions to * this which are noted below. - *

                  *

                  * The characters listed here are used in non-localized patterns. Localized * patterns use the corresponding characters taken from this formatter's * {@link DecimalFormatSymbols} object instead, and these characters lose their * special status. Two exceptions are the currency sign and quote, which are not * localized. - *

                  *
                  * @@ -297,7 +280,7 @@ import org.apache.harmony.text.internal.nls.Messages; * {@code DecimalFormat} to throw an {@link IllegalArgumentException} with a * message that describes the problem. *

                  Pattern BNF

                  - * + * *
                    * pattern    := subpattern (';' subpattern)?
                    * subpattern := prefix? number exponent? suffix?
                  @@ -310,7 +293,7 @@ import org.apache.harmony.text.internal.nls.Messages;
                    * exponent   := 'E' '+'? '0'* '0'
                    * padSpec    := '*' padChar
                    * padChar    := '\\u0000'..'\\uFFFD' - quote
                  - *  
                  + *
                    * Notation:
                    *   X*       0 or more instances of X
                    *   X?       0 or 1 instances of X
                  @@ -318,7 +301,7 @@ import org.apache.harmony.text.internal.nls.Messages;
                    *   C..D     any character from C up to D, inclusive
                    *   S-T      characters in S, except those in T
                    * 
                  - * + * * The first subpattern is for positive numbers. The second (optional) * subpattern is for negative numbers. *

                  @@ -446,7 +429,6 @@ import org.apache.harmony.text.internal.nls.Messages; * specified directly, and the formatter settings for these counts are ignored. * Instead, the formatter uses as many integer and fraction digits as required * to display the specified number of significant digits. - *

                  *
                  Examples:
                  *
                  * @@ -554,10 +536,9 @@ import org.apache.harmony.text.internal.nls.Messages; *

                  * {@code DecimalFormat} objects are not synchronized. Multiple threads should * not access one formatter concurrently. - * + * * @see Format * @see NumberFormat - * @since Android 1.0 */ public class DecimalFormat extends NumberFormat { @@ -578,11 +559,20 @@ public class DecimalFormat extends NumberFormat { /** * Constructs a new {@code DecimalFormat} for formatting and parsing numbers * for the default locale. - * - * @since Android 1.0 */ public DecimalFormat() { - this(getPattern(Locale.getDefault(), "Number")); //$NON-NLS-1$ + Locale locale = Locale.getDefault(); + icuSymbols = new com.ibm.icu4jni.text.DecimalFormatSymbols(locale); + symbols = new DecimalFormatSymbols(locale); + // BEGIN android-changed + dform = new com.ibm.icu4jni.text.DecimalFormat( + getPattern(Locale.getDefault(), "Number"), icuSymbols); + // END android-changed + + super.setMaximumFractionDigits(dform.getMaximumFractionDigits()); + super.setMaximumIntegerDigits(dform.getMaximumIntegerDigits()); + super.setMinimumFractionDigits(dform.getMinimumFractionDigits()); + super.setMinimumIntegerDigits(dform.getMinimumIntegerDigits()); } /** @@ -591,12 +581,19 @@ public class DecimalFormat extends NumberFormat { * * @param pattern * the non-localized pattern. - * @exception IllegalArgumentException - * if the pattern cannot be parsed. - * @since Android 1.0 + * @throws IllegalArgumentException + * if the pattern cannot be parsed. */ public DecimalFormat(String pattern) { - this(pattern, new DecimalFormatSymbols()); + Locale locale = Locale.getDefault(); + icuSymbols = new com.ibm.icu4jni.text.DecimalFormatSymbols(locale); + symbols = new DecimalFormatSymbols(locale); + dform = new com.ibm.icu4jni.text.DecimalFormat(pattern, icuSymbols); + + super.setMaximumFractionDigits(dform.getMaximumFractionDigits()); + super.setMaximumIntegerDigits(dform.getMaximumIntegerDigits()); + super.setMinimumFractionDigits(dform.getMinimumFractionDigits()); + super.setMinimumIntegerDigits(dform.getMinimumIntegerDigits()); } /** @@ -607,13 +604,12 @@ public class DecimalFormat extends NumberFormat { * the non-localized pattern. * @param value * the DecimalFormatSymbols. - * @exception IllegalArgumentException - * if the pattern cannot be parsed. - * @since Android 1.0 + * @throws IllegalArgumentException + * if the pattern cannot be parsed. */ public DecimalFormat(String pattern, DecimalFormatSymbols value) { symbols = (DecimalFormatSymbols) value.clone(); - Locale locale = (Locale) this.getInternalField("locale", symbols); //$NON-NLS-1$ + Locale locale = symbols.getLocale(); icuSymbols = new com.ibm.icu4jni.text.DecimalFormatSymbols(locale); copySymbols(icuSymbols, symbols); @@ -625,15 +621,27 @@ public class DecimalFormat extends NumberFormat { super.setMinimumIntegerDigits(dform.getMinimumIntegerDigits()); } + // BEGIN android-removed + // DecimalFormat(String pattern, DecimalFormatSymbols value, com.ibm.icu4jni.text.DecimalFormat icuFormat) { + // symbols = value; + // icuSymbols = value.getIcuSymbols(); + // dform = icuFormat; + // + // super.setMaximumFractionDigits(dform.getMaximumFractionDigits()); + // super.setMaximumIntegerDigits(dform.getMaximumIntegerDigits()); + // super.setMinimumFractionDigits(dform.getMinimumFractionDigits()); + // super.setMinimumIntegerDigits(dform.getMinimumIntegerDigits()); + // } + // END android-removed + /** * Changes the pattern of this decimal format to the specified pattern which * uses localized pattern characters. * * @param pattern * the localized pattern. - * @exception IllegalArgumentException - * if the pattern cannot be parsed. - * @since Android 1.0 + * @throws IllegalArgumentException + * if the pattern cannot be parsed. */ public void applyLocalizedPattern(String pattern) { dform.applyLocalizedPattern(pattern); @@ -645,9 +653,8 @@ public class DecimalFormat extends NumberFormat { * * @param pattern * the non-localized pattern. - * @exception IllegalArgumentException - * if the pattern cannot be parsed. - * @since Android 1.0 + * @throws IllegalArgumentException + * if the pattern cannot be parsed. */ public void applyPattern(String pattern) { @@ -660,7 +667,6 @@ public class DecimalFormat extends NumberFormat { * * @return a shallow copy of this decimal format. * @see java.lang.Cloneable - * @since Android 1.0 */ @Override public Object clone() { @@ -680,7 +686,6 @@ public class DecimalFormat extends NumberFormat { * @return {@code true} if the specified object is equal to this decimal * format; {@code false} otherwise. * @see #hashCode - * @since Android 1.0 */ @Override public boolean equals(Object object) { @@ -708,7 +713,6 @@ public class DecimalFormat extends NumberFormat { * if {@code object} cannot be formatted by this format. * @throws NullPointerException * if {@code object} is {@code null}. - * @since Android 1.0 */ @Override public AttributedCharacterIterator formatToCharacterIterator(Object object) { @@ -726,8 +730,7 @@ public class DecimalFormat extends NumberFormat { * specifying a format field, then its {@code beginIndex} and * {@code endIndex} members will be updated with the position of the first * occurrence of this field in the formatted text. - *

                  - * + * * @param value * the double to format. * @param buffer @@ -737,7 +740,6 @@ public class DecimalFormat extends NumberFormat { * on input: an optional alignment field; on output: the offsets * of the alignment field in the formatted text. * @return the string buffer. - * @since Android 1.0 */ @Override public StringBuffer format(double value, StringBuffer buffer, @@ -753,8 +755,7 @@ public class DecimalFormat extends NumberFormat { * specifying a format field, then its {@code beginIndex} and * {@code endIndex} members will be updated with the position of the first * occurrence of this field in the formatted text. - *

                  - * + * * @param value * the long to format. * @param buffer @@ -764,7 +765,6 @@ public class DecimalFormat extends NumberFormat { * on input: an optional alignment field; on output: the offsets * of the alignment field in the formatted text. * @return the string buffer. - * @since Android 1.0 */ @Override public StringBuffer format(long value, StringBuffer buffer, @@ -780,8 +780,7 @@ public class DecimalFormat extends NumberFormat { * specifying a format field, then its {@code beginIndex} and * {@code endIndex} members will be updated with the position of the first * occurrence of this field in the formatted text. - *

                  - * + * * @param number * the object to format. * @param toAppendTo @@ -794,7 +793,6 @@ public class DecimalFormat extends NumberFormat { * if {@code number} is not an instance of {@code Number}. * @throws NullPointerException * if {@code toAppendTo} or {@code pos} is {@code null}. - * @since Android 1.0 */ @Override public final StringBuffer format(Object number, StringBuffer toAppendTo, @@ -816,7 +814,6 @@ public class DecimalFormat extends NumberFormat { * * @return a copy of the {@code DecimalFormatSymbols} used by this decimal * format. - * @since Android 1.0 */ public DecimalFormatSymbols getDecimalFormatSymbols() { return (DecimalFormatSymbols) symbols.clone(); @@ -827,7 +824,6 @@ public class DecimalFormat extends NumberFormat { * * @return the currency used by this decimal format. * @see DecimalFormatSymbols#getCurrency() - * @since Android 1.0 */ @Override public Currency getCurrency() { @@ -841,9 +837,8 @@ public class DecimalFormat extends NumberFormat { * Returns the number of digits grouped together by the grouping separator. * This only allows to get the primary grouping size. There is no API to get * the secondary grouping size. - * + * * @return the number of digits grouped together. - * @since Android 1.0 */ public int getGroupingSize() { return dform.getGroupingSize(); @@ -854,7 +849,6 @@ public class DecimalFormat extends NumberFormat { * or after parsing. * * @return the multiplier. - * @since Android 1.0 */ public int getMultiplier() { return dform.getMultiplier(); @@ -864,7 +858,6 @@ public class DecimalFormat extends NumberFormat { * Returns the prefix which is formatted or parsed before a negative number. * * @return the negative prefix. - * @since Android 1.0 */ public String getNegativePrefix() { return dform.getNegativePrefix(); @@ -874,7 +867,6 @@ public class DecimalFormat extends NumberFormat { * Returns the suffix which is formatted or parsed after a negative number. * * @return the negative suffix. - * @since Android 1.0 */ public String getNegativeSuffix() { return dform.getNegativeSuffix(); @@ -884,7 +876,6 @@ public class DecimalFormat extends NumberFormat { * Returns the prefix which is formatted or parsed before a positive number. * * @return the positive prefix. - * @since Android 1.0 */ public String getPositivePrefix() { return dform.getPositivePrefix(); @@ -894,7 +885,6 @@ public class DecimalFormat extends NumberFormat { * Returns the suffix which is formatted or parsed after a positive number. * * @return the positive suffix. - * @since Android 1.0 */ public String getPositiveSuffix() { return dform.getPositiveSuffix(); @@ -911,7 +901,6 @@ public class DecimalFormat extends NumberFormat { * * @return {@code true} if the decimal separator should always be formatted; * {@code false} otherwise. - * @since Android 1.0 */ public boolean isDecimalSeparatorAlwaysShown() { return dform.isDecimalSeparatorAlwaysShown(); @@ -924,7 +913,6 @@ public class DecimalFormat extends NumberFormat { * @return {@code true} if parse always returns {@code BigDecimals}, * {@code false} if the type of the result is {@code Long} or * {@code Double}. - * @since Android 1.0 */ public boolean isParseBigDecimal() { return this.parseBigDecimal; @@ -937,14 +925,18 @@ public class DecimalFormat extends NumberFormat { * {@code java.lang.Integer}. Special cases are NaN, positive and negative * infinity, which are still returned as {@code java.lang.Double}. * + * * @param value * {@code true} that the resulting numbers of parse operations * will be of type {@code java.lang.Integer} except for the * special cases described above. - * @since Android 1.0 */ @Override public void setParseIntegerOnly(boolean value) { + // In this implementation, com.ibm.icu.text.DecimalFormat is wrapped to + // fulfill most of the format and parse feature. And this method is + // delegated to the wrapped instance of com.ibm.icu.text.DecimalFormat. + dform.setParseIntegerOnly(value); } @@ -954,7 +946,6 @@ public class DecimalFormat extends NumberFormat { * * @return {@code true} if this {@code DecimalFormat}'s parse method only * returns {@code java.lang.Integer}; {@code false} otherwise. - * @since Android 1.0 */ @Override public boolean isParseIntegerOnly() { @@ -985,7 +976,6 @@ public class DecimalFormat extends NumberFormat { * long, otherwise the result is a {@code Double}. If * {@code isParseBigDecimal} is {@code true} then it returns the * result as a {@code BigDecimal}. - * @since Android 1.0 */ @Override public Number parse(String string, ParsePosition position) { @@ -1048,7 +1038,6 @@ public class DecimalFormat extends NumberFormat { * * @param value * the {@code DecimalFormatSymbols} to set. - * @since Android 1.0 */ public void setDecimalFormatSymbols(DecimalFormatSymbols value) { if (value != null) { @@ -1066,7 +1055,6 @@ public class DecimalFormat extends NumberFormat { * @param currency * the currency this {@code DecimalFormat} should use. * @see DecimalFormatSymbols#setCurrency(Currency) - * @since Android 1.0 */ @Override public void setCurrency(Currency currency) { @@ -1084,7 +1072,6 @@ public class DecimalFormat extends NumberFormat { * @param value * {@code true} if the decimal separator should always be * formatted; {@code false} otherwise. - * @since Android 1.0 */ public void setDecimalSeparatorAlwaysShown(boolean value) { dform.setDecimalSeparatorAlwaysShown(value); @@ -1094,10 +1081,9 @@ public class DecimalFormat extends NumberFormat { * Sets the number of digits grouped together by the grouping separator. * This only allows to set the primary grouping size; the secondary grouping * size can only be set with a pattern. - * + * * @param value * the number of digits grouped together. - * @since Android 1.0 */ public void setGroupingSize(int value) { dform.setGroupingSize(value); @@ -1109,7 +1095,6 @@ public class DecimalFormat extends NumberFormat { * * @param value * {@code true} if grouping is used; {@code false} otherwise. - * @since Android 1.0 */ @Override public void setGroupingUsed(boolean value) { @@ -1120,7 +1105,6 @@ public class DecimalFormat extends NumberFormat { * Indicates whether grouping will be used in this format. * * @return {@code true} if grouping is used; {@code false} otherwise. - * @since Android 1.0 */ @Override public boolean isGroupingUsed() { @@ -1137,7 +1121,6 @@ public class DecimalFormat extends NumberFormat { * * @param value * the maximum number of fraction digits. - * @since Android 1.0 */ @Override public void setMaximumFractionDigits(int value) { @@ -1155,7 +1138,6 @@ public class DecimalFormat extends NumberFormat { * * @param value * the maximum number of integer digits. - * @since Android 1.0 */ @Override public void setMaximumIntegerDigits(int value) { @@ -1171,7 +1153,6 @@ public class DecimalFormat extends NumberFormat { * * @param value * the minimum number of fraction digits. - * @since Android 1.0 */ @Override public void setMinimumFractionDigits(int value) { @@ -1187,7 +1168,6 @@ public class DecimalFormat extends NumberFormat { * * @param value * the minimum number of integer digits. - * @since Android 1.0 */ @Override public void setMinimumIntegerDigits(int value) { @@ -1201,7 +1181,6 @@ public class DecimalFormat extends NumberFormat { * * @param value * the multiplier. - * @since Android 1.0 */ public void setMultiplier(int value) { dform.setMultiplier(value); @@ -1212,7 +1191,6 @@ public class DecimalFormat extends NumberFormat { * * @param value * the negative prefix. - * @since Android 1.0 */ public void setNegativePrefix(String value) { dform.setNegativePrefix(value); @@ -1223,7 +1201,6 @@ public class DecimalFormat extends NumberFormat { * * @param value * the negative suffix. - * @since Android 1.0 */ public void setNegativeSuffix(String value) { dform.setNegativeSuffix(value); @@ -1234,7 +1211,6 @@ public class DecimalFormat extends NumberFormat { * * @param value * the positive prefix. - * @since Android 1.0 */ public void setPositivePrefix(String value) { dform.setPositivePrefix(value); @@ -1245,7 +1221,6 @@ public class DecimalFormat extends NumberFormat { * * @param value * the positive suffix. - * @since Android 1.0 */ public void setPositiveSuffix(String value) { dform.setPositiveSuffix(value); @@ -1258,7 +1233,6 @@ public class DecimalFormat extends NumberFormat { * @param newValue * {@code true} if all the returned objects should be of type * {@code BigDecimal}; {@code false} otherwise. - * @since Android 1.0 */ public void setParseBigDecimal(boolean newValue) { this.parseBigDecimal = newValue; @@ -1269,7 +1243,6 @@ public class DecimalFormat extends NumberFormat { * characters. * * @return the localized pattern. - * @since Android 1.0 */ public String toLocalizedPattern() { return dform.toLocalizedPattern(); @@ -1280,7 +1253,6 @@ public class DecimalFormat extends NumberFormat { * characters. * * @return the non-localized pattern. - * @since Android 1.0 */ public String toPattern() { return dform.toPattern(); @@ -1322,47 +1294,47 @@ public class DecimalFormat extends NumberFormat { * if some I/O error occurs * @throws ClassNotFoundException */ + @SuppressWarnings("nls") private void writeObject(ObjectOutputStream stream) throws IOException, ClassNotFoundException { ObjectOutputStream.PutField fields = stream.putFields(); - fields.put("positivePrefix", dform.getPositivePrefix()); //$NON-NLS-1$ - fields.put("positiveSuffix", dform.getPositiveSuffix()); //$NON-NLS-1$ - fields.put("negativePrefix", dform.getNegativePrefix()); //$NON-NLS-1$ - fields.put("negativeSuffix", dform.getNegativeSuffix()); //$NON-NLS-1$ - String posPrefixPattern = (String) this.getInternalField( - "posPrefixPattern", dform); //$NON-NLS-1$ - fields.put("posPrefixPattern", posPrefixPattern); //$NON-NLS-1$ - String posSuffixPattern = (String) this.getInternalField( - "posSuffixPattern", dform); //$NON-NLS-1$ - fields.put("posSuffixPattern", posSuffixPattern); //$NON-NLS-1$ - String negPrefixPattern = (String) this.getInternalField( - "negPrefixPattern", dform); //$NON-NLS-1$ - fields.put("negPrefixPattern", negPrefixPattern); //$NON-NLS-1$ - String negSuffixPattern = (String) this.getInternalField( - "negSuffixPattern", dform); //$NON-NLS-1$ - fields.put("negSuffixPattern", negSuffixPattern); //$NON-NLS-1$ - fields.put("multiplier", dform.getMultiplier()); //$NON-NLS-1$ - fields.put("groupingSize", (byte) dform.getGroupingSize()); //$NON-NLS-1$ + fields.put("positivePrefix", dform.getPositivePrefix()); + fields.put("positiveSuffix", dform.getPositiveSuffix()); + fields.put("negativePrefix", dform.getNegativePrefix()); + fields.put("negativeSuffix", dform.getNegativeSuffix()); + String posPrefixPattern = (String) Format.getInternalField( + "posPrefixPattern", dform); + fields.put("posPrefixPattern", posPrefixPattern); + String posSuffixPattern = (String) Format.getInternalField( + "posSuffixPattern", dform); + fields.put("posSuffixPattern", posSuffixPattern); + String negPrefixPattern = (String) Format.getInternalField( + "negPrefixPattern", dform); + fields.put("negPrefixPattern", negPrefixPattern); + String negSuffixPattern = (String) Format.getInternalField( + "negSuffixPattern", dform); + fields.put("negSuffixPattern", negSuffixPattern); + fields.put("multiplier", dform.getMultiplier()); + fields.put("groupingSize", (byte) dform.getGroupingSize()); // BEGIN android-added - fields.put("groupingUsed", dform.isGroupingUsed()); //$NON-NLS-1$ + fields.put("groupingUsed", dform.isGroupingUsed()); // END android-added - fields.put("decimalSeparatorAlwaysShown", dform //$NON-NLS-1$ + fields.put("decimalSeparatorAlwaysShown", dform .isDecimalSeparatorAlwaysShown()); - fields.put("parseBigDecimal", parseBigDecimal); //$NON-NLS-1$ - fields.put("symbols", symbols); //$NON-NLS-1$ - boolean useExponentialNotation = ((Boolean) this.getInternalField( - "useExponentialNotation", dform)).booleanValue(); //$NON-NLS-1$ - fields.put("useExponentialNotation", useExponentialNotation); //$NON-NLS-1$ - byte minExponentDigits = ((Byte) this.getInternalField( - "minExponentDigits", dform)).byteValue(); //$NON-NLS-1$ - fields.put("minExponentDigits", minExponentDigits); //$NON-NLS-1$ - fields.put("maximumIntegerDigits", dform.getMaximumIntegerDigits()); //$NON-NLS-1$ - fields.put("minimumIntegerDigits", dform.getMinimumIntegerDigits()); //$NON-NLS-1$ - fields.put("maximumFractionDigits", dform.getMaximumFractionDigits()); //$NON-NLS-1$ - fields.put("minimumFractionDigits", dform.getMinimumFractionDigits()); //$NON-NLS-1$ - fields.put("serialVersionOnStream", CURRENT_SERIAL_VERTION); //$NON-NLS-1$ + fields.put("parseBigDecimal", parseBigDecimal); + fields.put("symbols", symbols); + boolean useExponentialNotation = ((Boolean) Format.getInternalField( + "useExponentialNotation", dform)).booleanValue(); + fields.put("useExponentialNotation", useExponentialNotation); + byte minExponentDigits = ((Byte) Format.getInternalField( + "minExponentDigits", dform)).byteValue(); + fields.put("minExponentDigits", minExponentDigits); + fields.put("maximumIntegerDigits", dform.getMaximumIntegerDigits()); + fields.put("minimumIntegerDigits", dform.getMinimumIntegerDigits()); + fields.put("maximumFractionDigits", dform.getMaximumFractionDigits()); + fields.put("minimumFractionDigits", dform.getMinimumFractionDigits()); + fields.put("serialVersionOnStream", CURRENT_SERIAL_VERTION); stream.writeFields(); - } /** @@ -1376,41 +1348,42 @@ public class DecimalFormat extends NumberFormat { * @throws ClassNotFoundException * if some class of serialized objects or fields cannot be found */ + @SuppressWarnings("nls") private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { ObjectInputStream.GetField fields = stream.readFields(); - String positivePrefix = (String) fields.get("positivePrefix", ""); //$NON-NLS-1$ //$NON-NLS-2$ - String positiveSuffix = (String) fields.get("positiveSuffix", ""); //$NON-NLS-1$ //$NON-NLS-2$ - String negativePrefix = (String) fields.get("negativePrefix", "-"); //$NON-NLS-1$ //$NON-NLS-2$ - String negativeSuffix = (String) fields.get("negativeSuffix", ""); //$NON-NLS-1$ //$NON-NLS-2$ - - String posPrefixPattern = (String) fields.get("posPrefixPattern", ""); //$NON-NLS-1$ //$NON-NLS-2$ - String posSuffixPattern = (String) fields.get("posSuffixPattern", ""); //$NON-NLS-1$ //$NON-NLS-2$ - String negPrefixPattern = (String) fields.get("negPrefixPattern", "-"); //$NON-NLS-1$ //$NON-NLS-2$ - String negSuffixPattern = (String) fields.get("negSuffixPattern", ""); //$NON-NLS-1$ //$NON-NLS-2$ - - int multiplier = fields.get("multiplier", 1); //$NON-NLS-1$ - byte groupingSize = fields.get("groupingSize", (byte) 3); //$NON-NLS-1$ + String positivePrefix = (String) fields.get("positivePrefix", ""); + String positiveSuffix = (String) fields.get("positiveSuffix", ""); + String negativePrefix = (String) fields.get("negativePrefix", "-"); + String negativeSuffix = (String) fields.get("negativeSuffix", ""); + + String posPrefixPattern = (String) fields.get("posPrefixPattern", ""); + String posSuffixPattern = (String) fields.get("posSuffixPattern", ""); + String negPrefixPattern = (String) fields.get("negPrefixPattern", "-"); + String negSuffixPattern = (String) fields.get("negSuffixPattern", ""); + + int multiplier = fields.get("multiplier", 1); + byte groupingSize = fields.get("groupingSize", (byte) 3); // BEGIN android-added - boolean groupingUsed = fields.get("groupingUsed", true); //$NON-NLS-1$ + boolean groupingUsed = fields.get("groupingUsed", true); // END android-added boolean decimalSeparatorAlwaysShown = fields.get( - "decimalSeparatorAlwaysShown", false); //$NON-NLS-1$ - boolean parseBigDecimal = fields.get("parseBigDecimal", false); //$NON-NLS-1$ - symbols = (DecimalFormatSymbols) fields.get("symbols", null); //$NON-NLS-1$ + "decimalSeparatorAlwaysShown", false); + boolean parseBigDecimal = fields.get("parseBigDecimal", false); + symbols = (DecimalFormatSymbols) fields.get("symbols", null); - boolean useExponentialNotation = fields.get("useExponentialNotation", //$NON-NLS-1$ + boolean useExponentialNotation = fields.get("useExponentialNotation", false); - byte minExponentDigits = fields.get("minExponentDigits", (byte) 0); //$NON-NLS-1$ + byte minExponentDigits = fields.get("minExponentDigits", (byte) 0); - int maximumIntegerDigits = fields.get("maximumIntegerDigits", 309); //$NON-NLS-1$ - int minimumIntegerDigits = fields.get("minimumIntegerDigits", 309); //$NON-NLS-1$ - int maximumFractionDigits = fields.get("maximumFractionDigits", 340); //$NON-NLS-1$ - int minimumFractionDigits = fields.get("minimumFractionDigits", 340); //$NON-NLS-1$ - this.serialVersionOnStream = fields.get("serialVersionOnStream", 0); //$NON-NLS-1$ + int maximumIntegerDigits = fields.get("maximumIntegerDigits", 309); + int minimumIntegerDigits = fields.get("minimumIntegerDigits", 309); + int maximumFractionDigits = fields.get("maximumFractionDigits", 340); + int minimumFractionDigits = fields.get("minimumFractionDigits", 340); + this.serialVersionOnStream = fields.get("serialVersionOnStream", 0); - Locale locale = (Locale) getInternalField("locale", symbols); //$NON-NLS-1$ + Locale locale = (Locale) Format.getInternalField("locale", symbols); // BEGIN android-removed // dform = new com.ibm.icu4jni.text.DecimalFormat("", //$NON-NLS-1$ // new com.ibm.icu4jni.text.DecimalFormatSymbols(locale)); @@ -1421,18 +1394,18 @@ public class DecimalFormat extends NumberFormat { dform = new com.ibm.icu4jni.text.DecimalFormat("", //$NON-NLS-1$ icuSymbols); // END android-added - setInternalField("useExponentialNotation", dform, new Boolean( //$NON-NLS-1$ - useExponentialNotation)); - setInternalField("minExponentDigits", dform, //$NON-NLS-1$ + setInternalField("useExponentialNotation", dform, Boolean + .valueOf(useExponentialNotation)); + setInternalField("minExponentDigits", dform, new Byte(minExponentDigits)); dform.setPositivePrefix(positivePrefix); dform.setPositiveSuffix(positiveSuffix); dform.setNegativePrefix(negativePrefix); dform.setNegativeSuffix(negativeSuffix); - setInternalField("posPrefixPattern", dform, posPrefixPattern); //$NON-NLS-1$ - setInternalField("posSuffixPattern", dform, posSuffixPattern); //$NON-NLS-1$ - setInternalField("negPrefixPattern", dform, negPrefixPattern); //$NON-NLS-1$ - setInternalField("negSuffixPattern", dform, negSuffixPattern); //$NON-NLS-1$ + setInternalField("posPrefixPattern", dform, posPrefixPattern); + setInternalField("posSuffixPattern", dform, posSuffixPattern); + setInternalField("negPrefixPattern", dform, negPrefixPattern); + setInternalField("negSuffixPattern", dform, negSuffixPattern); dform.setMultiplier(multiplier); dform.setGroupingSize(groupingSize); // BEGIN android-added @@ -1445,21 +1418,14 @@ public class DecimalFormat extends NumberFormat { dform.setMaximumFractionDigits(maximumFractionDigits); this.setParseBigDecimal(parseBigDecimal); - if (super.getMaximumIntegerDigits() > Integer.MAX_VALUE - || super.getMinimumIntegerDigits() > Integer.MAX_VALUE - || super.getMaximumFractionDigits() > Integer.MAX_VALUE - || super.getMinimumIntegerDigits() > Integer.MAX_VALUE) { - // text.09=The deserialized date is invalid - throw new InvalidObjectException(Messages.getString("text.09")); //$NON-NLS-1$ - } if (serialVersionOnStream < 3) { - setMaximumIntegerDigits(super.getMinimumIntegerDigits()); + setMaximumIntegerDigits(super.getMaximumIntegerDigits()); setMinimumIntegerDigits(super.getMinimumIntegerDigits()); setMaximumFractionDigits(super.getMaximumFractionDigits()); setMinimumFractionDigits(super.getMinimumFractionDigits()); } if (serialVersionOnStream < 1) { - this.setInternalField("useExponentialNotation", dform, //$NON-NLS-1$ + this.setInternalField("useExponentialNotation", dform, Boolean.FALSE); } serialVersionOnStream = 3; @@ -1473,10 +1439,16 @@ public class DecimalFormat extends NumberFormat { */ private void copySymbols(final com.ibm.icu4jni.text.DecimalFormatSymbols icu, final DecimalFormatSymbols dfs) { + Currency currency = dfs.getCurrency(); // BEGIN android-changed - icu.setCurrency(Currency.getInstance(dfs.getCurrency() - .getCurrencyCode())); + if (currency == null) { + icu.setCurrency(Currency.getInstance("XXX")); //$NON-NLS-1$ + } else { + icu.setCurrency(Currency.getInstance(dfs.getCurrency() + .getCurrencyCode())); + } // END android-changed + icu.setCurrencySymbol(dfs.getCurrencySymbol()); icu.setDecimalSeparator(dfs.getDecimalSeparator()); icu.setDigit(dfs.getDigit()); @@ -1518,31 +1490,4 @@ public class DecimalFormat extends NumberFormat { } }); } - - /* - * Gets private field value by reflection. - * - * @param fieldName the field name to be set @param target the object which - * field to be gotten - */ - private Object getInternalField(final String fieldName, final Object target) { - Object value = AccessController - .doPrivileged(new PrivilegedAction() { - public Object run() { - Object result = null; - java.lang.reflect.Field field = null; - try { - field = target.getClass().getDeclaredField( - fieldName); - field.setAccessible(true); - result = field.get(target); - } catch (Exception e1) { - return null; - } - return result; - } - }); - return value; - } - } diff --git a/libcore/text/src/main/java/java/text/DecimalFormatSymbols.java b/libcore/text/src/main/java/java/text/DecimalFormatSymbols.java index 3415ee87c..a71a4c649 100644 --- a/libcore/text/src/main/java/java/text/DecimalFormatSymbols.java +++ b/libcore/text/src/main/java/java/text/DecimalFormatSymbols.java @@ -14,17 +14,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/** -******************************************************************************* -* Copyright (C) 1996-2007, International Business Machines Corporation and * -* others. All Rights Reserved. * -******************************************************************************* -*/ // BEGIN android-note -// The class javadoc and some of the method descriptions are copied from ICU4J -// source files. Changes have been made to the copied descriptions. -// The icu license header was added to this file. +// changed from ICU to resource bundles // END android-note package java.text; @@ -37,7 +29,9 @@ import java.io.Serializable; import java.util.Arrays; import java.util.Currency; import java.util.Locale; +// BEGIN android-added import java.util.ResourceBundle; +// END android-added /** * Encapsulates the set of symbols (such as the decimal separator, the grouping @@ -46,10 +40,9 @@ import java.util.ResourceBundle; * {@code DecimalFormatSymbols} from its locale data. If you need to change any * of these symbols, you can get the {@code DecimalFormatSymbols} object from * your {@code DecimalFormat} and modify it. - * + * * @see java.util.Locale * @see DecimalFormat - * @since Android 1.0 */ public final class DecimalFormatSymbols implements Cloneable, Serializable { @@ -73,8 +66,6 @@ public final class DecimalFormatSymbols implements Cloneable, Serializable { * the default locale. Best practice is to create a {@code DecimalFormat} * and then to get the {@code DecimalFormatSymbols} from that object by * calling {@link DecimalFormat#getDecimalFormatSymbols()}. - * - * @since Android 1.0 */ public DecimalFormatSymbols() { this(Locale.getDefault()); @@ -88,9 +79,9 @@ public final class DecimalFormatSymbols implements Cloneable, Serializable { * * @param locale * the locale. - * @since Android 1.0 */ public DecimalFormatSymbols(Locale locale) { + // BEGIN android-changed ResourceBundle bundle = Format.getBundle(locale); patternChars = bundle.getString("DecimalPatternChars").toCharArray(); //$NON-NLS-1$ infinity = bundle.getString("Infinity"); //$NON-NLS-1$ @@ -105,8 +96,10 @@ public final class DecimalFormatSymbols implements Cloneable, Serializable { currencySymbol = bundle.getString("CurrencySymbol"); //$NON-NLS-1$ intlCurrencySymbol = bundle.getString("IntCurrencySymbol"); //$NON-NLS-1$ } + // END android-changed } + /** * Returns a new {@code DecimalFormatSymbols} with the same symbols as this * {@code DecimalFormatSymbols}. @@ -114,7 +107,6 @@ public final class DecimalFormatSymbols implements Cloneable, Serializable { * @return a shallow copy of this {@code DecimalFormatSymbols}. * * @see java.lang.Cloneable - * @since Android 1.0 */ @Override public Object clone() { @@ -137,7 +129,6 @@ public final class DecimalFormatSymbols implements Cloneable, Serializable { * @return {@code true} if the specified object is equal to this * {@code DecimalFormatSymbols}; {@code false} otherwise. * @see #hashCode - * @since Android 1.0 */ @Override public boolean equals(Object object) { @@ -161,13 +152,12 @@ public final class DecimalFormatSymbols implements Cloneable, Serializable { * been previously called with a value that is not a valid ISO 4217 currency * code. *

                  - * + * * @return the currency that was set in the constructor or by calling * {@code setCurrency()} or {@code setInternationalCurrencySymbol()}, * or {@code null} if an invalid currency was set. * @see #setCurrency(Currency) * @see #setInternationalCurrencySymbol(String) - * @since Android 1.0 */ public Currency getCurrency() { return currency; @@ -177,7 +167,6 @@ public final class DecimalFormatSymbols implements Cloneable, Serializable { * Returns the international currency symbol. * * @return the international currency symbol as string. - * @since Android 1.0 */ public String getInternationalCurrencySymbol() { return intlCurrencySymbol; @@ -187,7 +176,6 @@ public final class DecimalFormatSymbols implements Cloneable, Serializable { * Returns the currency symbol. * * @return the currency symbol as string. - * @since Android 1.0 */ public String getCurrencySymbol() { return currencySymbol; @@ -197,7 +185,6 @@ public final class DecimalFormatSymbols implements Cloneable, Serializable { * Returns the character which represents the decimal point in a number. * * @return the decimal separator character. - * @since Android 1.0 */ public char getDecimalSeparator() { return patternChars[DecimalSeparator]; @@ -208,7 +195,6 @@ public final class DecimalFormatSymbols implements Cloneable, Serializable { * pattern. * * @return the digit pattern character. - * @since Android 1.0 */ public char getDigit() { return patternChars[Digit]; @@ -218,7 +204,6 @@ public final class DecimalFormatSymbols implements Cloneable, Serializable { * Returns the character used as the thousands separator in a number. * * @return the thousands separator character. - * @since Android 1.0 */ public char getGroupingSeparator() { return patternChars[GroupingSeparator]; @@ -228,7 +213,6 @@ public final class DecimalFormatSymbols implements Cloneable, Serializable { * Returns the string which represents infinity. * * @return the infinity symbol as a string. - * @since Android 1.0 */ public String getInfinity() { return infinity; @@ -238,7 +222,6 @@ public final class DecimalFormatSymbols implements Cloneable, Serializable { * Returns the minus sign character. * * @return the minus sign as a character. - * @since Android 1.0 */ public char getMinusSign() { return patternChars[MinusSign]; @@ -249,7 +232,6 @@ public final class DecimalFormatSymbols implements Cloneable, Serializable { * value. * * @return the monetary decimal point as a character. - * @since Android 1.0 */ public char getMonetaryDecimalSeparator() { return patternChars[MonetaryDecimalSeparator]; @@ -259,7 +241,6 @@ public final class DecimalFormatSymbols implements Cloneable, Serializable { * Returns the string which represents NaN. * * @return the symbol NaN as a string. - * @since Android 1.0 */ public String getNaN() { return NaN; @@ -270,7 +251,6 @@ public final class DecimalFormatSymbols implements Cloneable, Serializable { * in a format pattern. * * @return the pattern separator character. - * @since Android 1.0 */ public char getPatternSeparator() { return patternChars[PatternSeparator]; @@ -280,7 +260,6 @@ public final class DecimalFormatSymbols implements Cloneable, Serializable { * Returns the percent character. * * @return the percent character. - * @since Android 1.0 */ public char getPercent() { return patternChars[Percent]; @@ -290,7 +269,6 @@ public final class DecimalFormatSymbols implements Cloneable, Serializable { * Returns the per mill sign character. * * @return the per mill sign character. - * @since Android 1.0 */ public char getPerMill() { return patternChars[PerMill]; @@ -300,7 +278,6 @@ public final class DecimalFormatSymbols implements Cloneable, Serializable { * Returns the character which represents zero. * * @return the zero character. - * @since Android 1.0 */ public char getZeroDigit() { return patternChars[ZeroDigit]; @@ -331,7 +308,6 @@ public final class DecimalFormatSymbols implements Cloneable, Serializable { * the new currency. * @throws NullPointerException * if {@code currency} is {@code null}. - * @since Android 1.0 */ public void setCurrency(Currency currency) { if (currency == null) { @@ -355,7 +331,6 @@ public final class DecimalFormatSymbols implements Cloneable, Serializable { * * @param value * the currency code. - * @since Android 1.0 */ public void setInternationalCurrencySymbol(String value) { if (value == null) { @@ -382,7 +357,6 @@ public final class DecimalFormatSymbols implements Cloneable, Serializable { * * @param value * the currency symbol. - * @since Android 1.0 */ public void setCurrencySymbol(String value) { currencySymbol = value; @@ -393,7 +367,6 @@ public final class DecimalFormatSymbols implements Cloneable, Serializable { * * @param value * the decimal separator character. - * @since Android 1.0 */ public void setDecimalSeparator(char value) { patternChars[DecimalSeparator] = value; @@ -404,7 +377,6 @@ public final class DecimalFormatSymbols implements Cloneable, Serializable { * * @param value * the digit character. - * @since Android 1.0 */ public void setDigit(char value) { patternChars[Digit] = value; @@ -415,7 +387,6 @@ public final class DecimalFormatSymbols implements Cloneable, Serializable { * * @param value * the grouping separator character. - * @since Android 1.0 */ public void setGroupingSeparator(char value) { patternChars[GroupingSeparator] = value; @@ -426,7 +397,6 @@ public final class DecimalFormatSymbols implements Cloneable, Serializable { * * @param value * the string representing infinity. - * @since Android 1.0 */ public void setInfinity(String value) { infinity = value; @@ -437,7 +407,6 @@ public final class DecimalFormatSymbols implements Cloneable, Serializable { * * @param value * the minus sign character. - * @since Android 1.0 */ public void setMinusSign(char value) { patternChars[MinusSign] = value; @@ -449,7 +418,6 @@ public final class DecimalFormatSymbols implements Cloneable, Serializable { * * @param value * the monetary decimal separator character. - * @since Android 1.0 */ public void setMonetaryDecimalSeparator(char value) { patternChars[MonetaryDecimalSeparator] = value; @@ -460,7 +428,6 @@ public final class DecimalFormatSymbols implements Cloneable, Serializable { * * @param value * the string representing NaN. - * @since Android 1.0 */ public void setNaN(String value) { NaN = value; @@ -472,7 +439,6 @@ public final class DecimalFormatSymbols implements Cloneable, Serializable { * * @param value * the pattern separator character. - * @since Android 1.0 */ public void setPatternSeparator(char value) { patternChars[PatternSeparator] = value; @@ -483,7 +449,6 @@ public final class DecimalFormatSymbols implements Cloneable, Serializable { * * @param value * the percent character. - * @since Android 1.0 */ public void setPercent(char value) { patternChars[Percent] = value; @@ -494,7 +459,6 @@ public final class DecimalFormatSymbols implements Cloneable, Serializable { * * @param value * the per mill character. - * @since Android 1.0 */ public void setPerMill(char value) { patternChars[PerMill] = value; @@ -505,7 +469,6 @@ public final class DecimalFormatSymbols implements Cloneable, Serializable { * * @param value * the zero digit character. - * @since Android 1.0 */ public void setZeroDigit(char value) { patternChars[ZeroDigit] = value; @@ -588,4 +551,14 @@ public final class DecimalFormatSymbols implements Cloneable, Serializable { currency = null; } } + + Locale getLocale(){ + return locale; + } + + // BEGIN android-removed + // com.ibm.icu4jni.text.DecimalFormatSymbols getIcuSymbols() { + // return icuSymbols; + // } + // END android-removed } diff --git a/libcore/text/src/main/java/java/text/FieldPosition.java b/libcore/text/src/main/java/java/text/FieldPosition.java index 4cf985d61..d2da2977a 100644 --- a/libcore/text/src/main/java/java/text/FieldPosition.java +++ b/libcore/text/src/main/java/java/text/FieldPosition.java @@ -25,13 +25,9 @@ package java.text; * A {@code FieldPosition} can be created by using the integer constants in the * various format classes (for example {@code NumberFormat.INTEGER_FIELD}) or * one of the fields of type {@code Format.Field}. - *

                  *

                  * If more than one field information is needed, the method * {@link NumberFormat#formatToCharacterIterator(Object)} should be used. - *

                  - * - * @since Android 1.0 */ public class FieldPosition { @@ -44,7 +40,6 @@ public class FieldPosition { * * @param field * the field to identify. - * @since Android 1.0 */ public FieldPosition(int field) { myField = field; @@ -56,7 +51,6 @@ public class FieldPosition { * * @param attribute * the field attribute to identify. - * @since Android 1.0 */ public FieldPosition(Format.Field attribute) { myAttribute = attribute; @@ -71,7 +65,6 @@ public class FieldPosition { * the field attribute to identify. * @param field * the field to identify. - * @since Android 1.0 */ public FieldPosition(Format.Field attribute, int field) { myAttribute = attribute; @@ -92,7 +85,6 @@ public class FieldPosition { * @return {@code true} if the specified object is equal to this field * position; {@code false} otherwise. * @see #hashCode - * @since Android 1.0 */ @Override public boolean equals(Object object) { @@ -108,7 +100,6 @@ public class FieldPosition { * Returns the index of the beginning of the field. * * @return the first index of the field. - * @since Android 1.0 */ public int getBeginIndex() { return beginIndex; @@ -118,7 +109,6 @@ public class FieldPosition { * Returns the index one past the end of the field. * * @return one past the index of the last character in the field. - * @since Android 1.0 */ public int getEndIndex() { return endIndex; @@ -128,7 +118,6 @@ public class FieldPosition { * Returns the field which is being identified. * * @return the field constant. - * @since Android 1.0 */ public int getField() { return myField; @@ -138,7 +127,6 @@ public class FieldPosition { * Returns the attribute which is being identified. * * @return the field. - * @since Android 1.0 */ public Format.Field getFieldAttribute() { return myAttribute; @@ -155,7 +143,6 @@ public class FieldPosition { * * @param index * the index of the first character in the field. - * @since Android 1.0 */ public void setBeginIndex(int index) { beginIndex = index; @@ -166,7 +153,6 @@ public class FieldPosition { * * @param index * one past the index of the last character in the field. - * @since Android 1.0 */ public void setEndIndex(int index) { endIndex = index; @@ -176,7 +162,6 @@ public class FieldPosition { * Returns the string representation of this field position. * * @return the string representation of this field position. - * @since Android 1.0 */ @Override public String toString() { diff --git a/libcore/text/src/main/java/java/text/Format.java b/libcore/text/src/main/java/java/text/Format.java index 5e31cd4ac..6ee1ba2c5 100644 --- a/libcore/text/src/main/java/java/text/Format.java +++ b/libcore/text/src/main/java/java/text/Format.java @@ -14,26 +14,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/** -******************************************************************************* -* Copyright (C) 1996-2007, International Business Machines Corporation and * -* others. All Rights Reserved. * -******************************************************************************* -*/ - -// BEGIN android-note -// The class javadoc and some of the method descriptions are copied from ICU4J -// source files. Changes have been made to the copied descriptions. -// The icu license header was added to this file. -// END android-note package java.text; import java.io.Serializable; import java.security.AccessController; import java.security.PrivilegedAction; +// BEGIN android-added import java.util.Locale; import java.util.ResourceBundle; +// END android-added import org.apache.harmony.text.internal.nls.Messages; @@ -49,7 +39,6 @@ import org.apache.harmony.text.internal.nls.Messages; * A date and time formatter like {@code SimpleDateFormat} may represent a * specific date, encoded numerically, as a string such as "Wednesday, February * 26, 1997 AD". - *

                  *

                  * Many of the concrete subclasses of {@code Format} employ the notion of a * pattern. A pattern is a string representation of the rules which govern the @@ -60,7 +49,6 @@ import org.apache.harmony.text.internal.nls.Messages; * of a pattern is defined by each subclass. Even though many subclasses use * patterns, the notion of a pattern is not inherent to {@code Format} classes * in general, and is not part of the explicit base class protocol. - *

                  *

                  * Two complex formatting classes are worth mentioning: {@code MessageFormat} * and {@code ChoiceFormat}. {@code ChoiceFormat} is a subclass of @@ -73,9 +61,6 @@ import org.apache.harmony.text.internal.nls.Messages; * 27, 1997." given the arguments 0, "MyDisk", and the date value of 2/27/97. * See the {@link ChoiceFormat} and {@link MessageFormat} descriptions for * further information. - *

                  - * - * @since Android 1.0 */ public abstract class Format implements Serializable, Cloneable { @@ -83,8 +68,6 @@ public abstract class Format implements Serializable, Cloneable { /** * Constructs a new {@code Format} instance. - * - * @since Android 1.0 */ public Format() { } @@ -95,7 +78,6 @@ public abstract class Format implements Serializable, Cloneable { * @return a shallow copy of this format. * * @see java.lang.Cloneable - * @since Android 1.0 */ @Override public Object clone() { @@ -106,6 +88,7 @@ public abstract class Format implements Serializable, Cloneable { } } + // BEGIN android-added static ResourceBundle getBundle(final Locale locale) { return AccessController .doPrivileged(new PrivilegedAction() { @@ -116,6 +99,7 @@ public abstract class Format implements Serializable, Cloneable { } }); } + // END android-added String convertPattern(String template, String fromChars, String toChars, boolean check) { @@ -156,9 +140,8 @@ public abstract class Format implements Serializable, Cloneable { * @param object * the object to format. * @return the formatted string. - * @exception IllegalArgumentException - * if the object cannot be formatted by this format. - * @since Android 1.0 + * @throws IllegalArgumentException + * if the object cannot be formatted by this format. */ public final String format(Object object) { return format(object, new StringBuffer(), new FieldPosition(0)) @@ -173,8 +156,7 @@ public abstract class Format implements Serializable, Cloneable { * member contains an enum value specifying a field on input, then its * {@code beginIndex} and {@code endIndex} members will be updated with the * text offset of the first occurrence of this field in the formatted text. - *

                  - * + * * @param object * the object to format. * @param buffer @@ -183,9 +165,8 @@ public abstract class Format implements Serializable, Cloneable { * on input: an optional alignment field; on output: the offsets * of the alignment field in the formatted text. * @return the string buffer. - * @exception IllegalArgumentException - * if the object cannot be formatted by this format. - * @since Android 1.0 + * @throws IllegalArgumentException + * if the object cannot be formatted by this format. */ public abstract StringBuffer format(Object object, StringBuffer buffer, FieldPosition field); @@ -197,15 +178,13 @@ public abstract class Format implements Serializable, Cloneable { *

                  * Subclasses should return an {@code AttributedCharacterIterator} with the * appropriate attributes. - *

                  - * + * * @param object * the object to format. * @return an {@code AttributedCharacterIterator} with the formatted object * and attributes. - * @exception IllegalArgumentException - * if the object cannot be formatted by this format. - * @since Android 1.0 + * @throws IllegalArgumentException + * if the object cannot be formatted by this format. */ public AttributedCharacterIterator formatToCharacterIterator(Object object) { return new AttributedString(format(object)).getIterator(); @@ -217,9 +196,8 @@ public abstract class Format implements Serializable, Cloneable { * @param string * the string to parse. * @return the object resulting from the parse. - * @exception ParseException - * if an error occurs during parsing. - * @since Android 1.0 + * @throws ParseException + * if an error occurs during parsing. */ public Object parseObject(String string) throws ParseException { ParsePosition position = new ParsePosition(0); @@ -247,10 +225,35 @@ public abstract class Format implements Serializable, Cloneable { * set to the index where the error occurred. * @return the object resulting from the parse or {@code null} if there is * an error. - * @since Android 1.0 */ public abstract Object parseObject(String string, ParsePosition position); + /* + * Gets private field value by reflection. + * + * @param fieldName the field name to be set @param target the object which + * field to be gotten + */ + static Object getInternalField(final String fieldName, final Object target) { + Object value = AccessController + .doPrivileged(new PrivilegedAction() { + public Object run() { + Object result = null; + java.lang.reflect.Field field = null; + try { + field = target.getClass().getDeclaredField( + fieldName); + field.setAccessible(true); + result = field.get(target); + } catch (Exception e1) { + return null; + } + return result; + } + }); + return value; + } + static boolean upTo(String string, ParsePosition position, StringBuffer buffer, char stop) { int index = position.getIndex(), length = string.length(); @@ -307,8 +310,6 @@ public abstract class Format implements Serializable, Cloneable { * {@code AttributedCharacterIterator} that the * {@code formatToCharacterIterator()} method returns in {@code Format} * subclasses. - * - * @since Android 1.0 */ public static class Field extends AttributedCharacterIterator.Attribute { @@ -316,10 +317,9 @@ public abstract class Format implements Serializable, Cloneable { /** * Constructs a new instance of {@code Field} with the given field name. - * + * * @param fieldName * the field name. - * @since Android 1.0 */ protected Field(String fieldName) { super(fieldName); diff --git a/libcore/text/src/main/java/java/text/MessageFormat.java b/libcore/text/src/main/java/java/text/MessageFormat.java index 6405b6cff..4ab1adefa 100644 --- a/libcore/text/src/main/java/java/text/MessageFormat.java +++ b/libcore/text/src/main/java/java/text/MessageFormat.java @@ -14,18 +14,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/** -******************************************************************************* -* Copyright (C) 1996-2007, International Business Machines Corporation and * -* others. All Rights Reserved. * -******************************************************************************* -*/ - -// BEGIN android-note -// The class javadoc and some of the method descriptions are copied from ICU4J -// source files. Changes have been made to the copied descriptions. -// The icu license header was added to this file. -// END android-note package java.text; @@ -44,12 +32,11 @@ import org.apache.harmony.text.internal.nls.Messages; /** * Produces concatenated - * messages in language-neutral way. Use this class to construct messages + * messages in language-neutral way. Use this class to construct messages * displayed for end users. *

                  * {@code MessageFormat} takes a set of objects, formats them and then * inserts the formatted strings into the pattern at the appropriate places. - *

                  *

                  * Note: {@code MessageFormat} differs from the other * {@code Format} classes in that you create a {@code MessageFormat} @@ -58,12 +45,12 @@ import org.apache.harmony.text.internal.nls.Messages; * {@code MessageFormat} itself doesn't implement locale specific * behavior. Any locale specific behavior is defined by the pattern that you * provide as well as the subformats used for inserted arguments. - * + * *

                  Patterns and their interpretation

                  - * + * * {@code MessageFormat} uses patterns of the following form: *
                  - * + * *
                    * MessageFormatPattern:
                    *         String
                  @@ -97,9 +84,9 @@ import org.apache.harmony.text.internal.nls.Messages;
                    *         ' QuotedPattern '
                    *         UnquotedPattern
                    * 
                  - * + * *
                  - * + * *

                  * Within a String, {@code "''"} represents a single quote. A * QuotedString can contain arbitrary characters except single quotes; @@ -118,7 +105,6 @@ import org.apache.harmony.text.internal.nls.Messages; * {@code "ab {0} de"} and {@code "ab '}' de"} are valid subformat * patterns, but {@code "ab {0'}' de"} and {@code "ab } de"} are * not. - *

                  *
                  *
                  Warning:
                  *
                  The rules for using quotes within message format patterns unfortunately @@ -233,11 +219,11 @@ import org.apache.harmony.text.internal.nls.Messages; *
                  * *
                  {@code new ChoiceFormat(subformatPattern)}
                  - * + * *

                  Usage Information

                  *

                  * Here are some examples of usage:

                  - * + * *
                    * Object[] arguments = {
                    *         new Integer(7), new Date(System.currentTimeMillis()),
                  @@ -250,15 +236,14 @@ import org.apache.harmony.text.internal.nls.Messages;
                    * 
                    * At 12:30 PM on Jul 3, 2053, there was a disturbance in the Force on planet 7.
                    * 
                  - * - *
                  + * + *
                  *

                  * Typically, the message format will come from resources, and the * arguments will be dynamically set at runtime. - *

                  *

                  * Example 2:

                  - * + * *
                    * Object[] testArgs = {new Long(3), "MyDisk"};
                    * MessageFormat form = new MessageFormat("The disk \"{1}\" contains {0} file(s).");
                  @@ -270,15 +255,14 @@ import org.apache.harmony.text.internal.nls.Messages;
                    * The disk "MyDisk" contains 1 file(s).
                    * The disk "MyDisk" contains 1,273 file(s).
                    * 
                  - * + * *
                  - * + * *

                  * For more sophisticated patterns, you can use a {@code ChoiceFormat} to - * get output such as: - *

                  + * get output such as: *
                  - * + * *
                    * MessageFormat form = new MessageFormat("The disk \"{1}\" contains {0}.");
                    * double[] filelimits = {0,1,2};
                  @@ -294,15 +278,15 @@ import org.apache.harmony.text.internal.nls.Messages;
                    * The disk "MyDisk" contains one file.
                    * The disk "MyDisk" contains 1,273 files.
                    * 
                  - * + * *
                  You can either do this programmatically, as in the above * example, or by using a pattern (see {@link ChoiceFormat} for more * information) as in:
                  - * + * *
                    * form.applyPattern("There {0,choice,0#are no files|1#is one file|1<are {0,number,integer} files}.");
                    * 
                  - * + * *
                  *

                  * Note: As we see above, the string produced by a @@ -312,11 +296,9 @@ import org.apache.harmony.text.internal.nls.Messages; * {@code ChoiceFormat} programmatically (instead of using the string * patterns), then be careful not to produce a format that recurses on itself, * which will cause an infinite loop. - *

                  *

                  * When a single argument is parsed more than once in the string, the last match * will be the final result of the parsing. For example: - *

                  *
                  *
                    * MessageFormat mf = new MessageFormat("{0,number,#.##}, {0,number,#.#}");
                  @@ -329,10 +311,9 @@ import org.apache.harmony.text.internal.nls.Messages;
                    * 
                  *
                  *

                  - * Likewise, parsing with a {@code MessageFormat} object using patterns - * containing multiple occurrences of the same argument would return the last + * Likewise, parsing with a {@code MessageFormat} object using patterns + * containing multiple occurrences of the same argument would return the last * match. For example: - *

                  *
                  *
                    * MessageFormat mf = new MessageFormat("{0}, {0}, {0}");
                  @@ -346,14 +327,12 @@ import org.apache.harmony.text.internal.nls.Messages;
                    * Message formats are not synchronized. It is recommended to create separate
                    * format instances for each thread. If multiple threads access a format
                    * concurrently, it must be synchronized externally.
                  - * 

                  - * + * * @see java.util.Locale * @see Format * @see NumberFormat * @see DecimalFormat * @see ChoiceFormat - * @since Android 1.0 */ public class MessageFormat extends Format { @@ -379,9 +358,8 @@ public class MessageFormat extends Format { * the pattern. * @param locale * the locale. - * @exception IllegalArgumentException - * if the pattern cannot be parsed. - * @since Android 1.0 + * @throws IllegalArgumentException + * if the pattern cannot be parsed. */ public MessageFormat(String template, Locale locale) { this.locale = locale; @@ -394,9 +372,8 @@ public class MessageFormat extends Format { * * @param template * the pattern. - * @exception IllegalArgumentException - * if the pattern cannot be parsed. - * @since Android 1.0 + * @throws IllegalArgumentException + * if the pattern cannot be parsed. */ public MessageFormat(String template) { applyPattern(template); @@ -407,9 +384,8 @@ public class MessageFormat extends Format { * * @param template * the new pattern. - * @exception IllegalArgumentException - * if the pattern cannot be parsed. - * @since Android 1.0 + * @throws IllegalArgumentException + * if the pattern cannot be parsed. */ public void applyPattern(String template) { int length = template.length(); @@ -422,15 +398,31 @@ public class MessageFormat extends Format { Vector localFormats = new Vector(); while (position.getIndex() < length) { if (Format.upTo(template, position, buffer, '{')) { - byte arg; + int arg = 0; int offset = position.getIndex(); - if (offset >= length - || (arg = (byte) Character.digit(template - .charAt(offset++), 10)) == -1) { + if (offset >= length) { // text.19=Invalid argument number throw new IllegalArgumentException(Messages .getString("text.19")); //$NON-NLS-1$ } + // Get argument number + char ch; + while ((ch = template.charAt(offset++)) != '}' && ch != ',') { + if (ch < '0' && ch > '9') { + // text.19=Invalid argument number + throw new IllegalArgumentException(Messages + .getString("text.19")); //$NON-NLS-1$ + } + + arg = arg * 10 + (ch - '0'); + + if (arg < 0 || offset >= length) { + // text.19=Invalid argument number + throw new IllegalArgumentException(Messages + .getString("text.19")); //$NON-NLS-1$ + } + } + offset--; position.setIndex(offset); localFormats.addElement(parseVariable(template, position)); if (argCount >= args.length) { @@ -465,7 +457,6 @@ public class MessageFormat extends Format { * * @return a shallow copy of this {@code MessageFormat}. * @see java.lang.Cloneable - * @since Android 1.0 */ @Override public Object clone() { @@ -490,7 +481,6 @@ public class MessageFormat extends Format { * @return {@code true} if the specified object is equal to this * {@code MessageFormat}; {@code false} otherwise. * @see #hashCode - * @since Android 1.0 */ @Override public boolean equals(Object object) { @@ -526,10 +516,9 @@ public class MessageFormat extends Format { * the object to format. * @return an {@code AttributedCharacterIterator} with the formatted message and * attributes. - * @exception IllegalArgumentException - * if the arguments in the object array cannot be formatted - * by this message format. - * @since Android 1.0 + * @throws IllegalArgumentException + * if the arguments in the object array cannot be formatted + * by this message format. */ @Override public AttributedCharacterIterator formatToCharacterIterator(Object object) { @@ -564,8 +553,7 @@ public class MessageFormat extends Format { * {@code MessageFormat.Field.ARGUMENT}, then the begin and end index of * this field position is set to the location of the first occurrence of a * message format argument. Otherwise, the {@code FieldPosition} is ignored. - *

                  - * + * * @param objects * the array of objects to format. * @param buffer @@ -574,7 +562,6 @@ public class MessageFormat extends Format { * on input: an optional alignment field; on output: the offsets * of the alignment field in the formatted text. * @return the string buffer. - * @since Android 1.0 */ public final StringBuffer format(Object[] objects, StringBuffer buffer, FieldPosition field) { @@ -723,18 +710,16 @@ public class MessageFormat extends Format { * {@code MessageFormat.Field.ARGUMENT}, then the begin and end index of * this field position is set to the location of the first occurrence of a * message format argument. Otherwise, the {@code FieldPosition} is ignored. - *

                  *

                  * Calling this method is equivalent to calling - *

                  *
                  * *
                        * format((Object[])object, buffer, field)
                        * 
                  - * + * *
                  - * + * * @param object * the object to format, must be an array of {@code Object}. * @param buffer @@ -745,7 +730,6 @@ public class MessageFormat extends Format { * @return the string buffer. * @throws ClassCastException * if {@code object} is not an array of {@code Object}. - * @since Android 1.0 */ @Override public final StringBuffer format(Object object, StringBuffer buffer, @@ -761,22 +745,26 @@ public class MessageFormat extends Format { * @param objects * the array of objects to format. * @return the formatted result. - * @exception IllegalArgumentException - * if the pattern cannot be parsed. - * @since Android 1.0 + * @throws IllegalArgumentException + * if the pattern cannot be parsed. */ public static String format(String template, Object... objects) { - // BEGIN android-note - // changed parameter type from array to varargs. - // END android-note + if (objects != null) { + for (int i = 0; i < objects.length; i++) { + if (objects[i] == null) { + objects[i] = "null"; + } + } + } + // BEGIN android-changed return new MessageFormat(template).format(objects); + // END android-changed } /** * Returns the {@code Format} instances used by this message format. * * @return an array of {@code Format} instances. - * @since Android 1.0 */ public Format[] getFormats() { return formats.clone(); @@ -788,7 +776,6 @@ public class MessageFormat extends Format { * of the last one. * * @return an array of formats, ordered by argument index. - * @since Android 1.0 */ public Format[] getFormatsByArgumentIndex() { Format[] answer = new Format[maxArgumentIndex + 1]; @@ -806,7 +793,6 @@ public class MessageFormat extends Format { * the index of the format to set. * @param format * the format that will be set at index {@code argIndex}. - * @since Android 1.0 */ public void setFormatByArgumentIndex(int argIndex, Format format) { for (int i = 0; i < maxOffset + 1; i++) { @@ -822,7 +808,6 @@ public class MessageFormat extends Format { * * @param formats * the formats in an array. - * @since Android 1.0 */ public void setFormatsByArgumentIndex(Format[] formats) { for (int j = 0; j < formats.length; j++) { @@ -838,7 +823,6 @@ public class MessageFormat extends Format { * Returns the locale used when creating formats. * * @return the locale used to create formats. - * @since Android 1.0 */ public Locale getLocale() { return locale; @@ -869,9 +853,8 @@ public class MessageFormat extends Format { * @param string * the string to parse. * @return the array of {@code Object} arguments resulting from the parse. - * @exception ParseException - * if an error occurs during parsing. - * @since Android 1.0 + * @throws ParseException + * if an error occurs during parsing. */ public Object[] parse(String string) throws ParseException { ParsePosition position = new ParsePosition(0); @@ -900,7 +883,6 @@ public class MessageFormat extends Format { * set to the index where the error occurred. * @return the array of objects resulting from the parse, or {@code null} if * there is an error. - * @since Android 1.0 */ public Object[] parse(String string, ParsePosition position) { if (string == null) { @@ -972,7 +954,6 @@ public class MessageFormat extends Format { * set to the index where the error occurred. * @return the array of objects resulting from the parse, or {@code null} if * there is an error. - * @since Android 1.0 */ @Override public Object parseObject(String string, ParsePosition position) { @@ -1096,7 +1077,6 @@ public class MessageFormat extends Format { * the index of the format to change. * @param format * the {@code Format} that replaces the old format. - * @since Android 1.0 */ public void setFormat(int offset, Format format) { formats[offset] = format; @@ -1107,7 +1087,6 @@ public class MessageFormat extends Format { * * @param formats * an array of {@code Format}. - * @since Android 1.0 */ public void setFormats(Format[] formats) { int min = this.formats.length; @@ -1126,7 +1105,6 @@ public class MessageFormat extends Format { * * @param locale * the new locale. - * @since Android 1.0 */ public void setLocale(Locale locale) { this.locale = locale; @@ -1216,7 +1194,6 @@ public class MessageFormat extends Format { * Returns the pattern of this message format. * * @return the pattern. - * @since Android 1.0 */ public String toPattern() { StringBuffer buffer = new StringBuffer(); @@ -1302,7 +1279,7 @@ public class MessageFormat extends Format { int offset = 0; int offsetsLength = maxOffset + 1; int[] offsets = new int[offsetsLength]; - StringBuffer pattern = new StringBuffer(); + StringBuilder pattern = new StringBuilder(); for (int i = 0; i <= maxOffset; i++) { offset += strings[i].length(); offsets[i] = offset; @@ -1351,32 +1328,22 @@ public class MessageFormat extends Format { *

                  * There is no public constructor in this class, the only instances are the * constants defined here. - *

                  - * - * @since Android 1.0 */ public static class Field extends Format.Field { private static final long serialVersionUID = 7899943957617360810L; - // BEGIN android-removed - // public static final Field ARGUMENT = new Field("message argument field"); //$NON-NLS-1$ - // END android-removed - /** * This constant stands for the message argument. - * - * @since Android 1.0 */ public static final Field ARGUMENT = new Field("message argument field"); //$NON-NLS-1$ /** * Constructs a new instance of {@code MessageFormat.Field} with the * given field name. - * + * * @param fieldName * the field name. - * @since Android 1.0 */ protected Field(String fieldName) { super(fieldName); @@ -1385,7 +1352,7 @@ public class MessageFormat extends Format { /** * Resolves instances that are deserialized to the constant * {@code MessageFormat.Field} values. - * + * * @return the resolved field object. * @throws InvalidObjectException * if an error occurs while resolving the field object. diff --git a/libcore/text/src/main/java/java/text/NumberFormat.java b/libcore/text/src/main/java/java/text/NumberFormat.java index 75102409c..5b8d88343 100644 --- a/libcore/text/src/main/java/java/text/NumberFormat.java +++ b/libcore/text/src/main/java/java/text/NumberFormat.java @@ -14,17 +14,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -/** -******************************************************************************* -* Copyright (C) 1996-2007, International Business Machines Corporation and * -* others. All Rights Reserved. * -******************************************************************************* -*/ // BEGIN android-note -// The class javadoc and some of the method descriptions are copied from ICU4J -// source files. Changes have been made to the copied descriptions. -// The icu license header was added to this file. +// changed from ICU to resource bundles // END android-note package java.text; @@ -36,7 +28,9 @@ import java.io.ObjectOutputStream; import java.io.ObjectStreamField; import java.util.Currency; import java.util.Locale; +// BEGIN android-added import java.util.ResourceBundle; +// END android-added import org.apache.harmony.text.internal.nls.Messages; @@ -50,53 +44,48 @@ import org.apache.harmony.text.internal.nls.Messages; * Your code can be completely independent of the locale conventions for decimal * points, thousands-separators, or even the particular decimal digits used, or * whether the number format is even decimal. - *

                  *

                  * To format a number for the current locale, use one of the factory class * methods: - *

                  *
                  - * + * *
                    * myString = NumberFormat.getInstance().format(myNumber);
                    * 
                  - * + * *
                  *

                  * If you are formatting multiple numbers, it is more efficient to get the * format and use it multiple times so that the system doesn't have to fetch the * information about the local language and country conventions multiple times. - *

                  *
                  - * + * *
                    * NumberFormat nf = NumberFormat.getInstance();
                    * for (int i = 0; i < a.length; ++i) {
                    *     output.println(nf.format(myNumber[i]) + "; ");
                    * }
                    * 
                  - * + * *
                  *

                  * To format a number for a different locale, specify it in the call to * {@code getInstance}. - *

                  *
                  - * + * *
                    * NumberFormat nf = NumberFormat.getInstance(Locale.FRENCH);
                    * 
                  - * + * *
                  *

                  * You can also use a {@code NumberFormat} to parse numbers: - *

                  *
                  - * + * *
                    * myNumber = nf.parse(myString);
                    * 
                  - * + * *
                  *

                  * Use {@code getInstance} or {@code getNumberInstance} to get the normal number @@ -104,7 +93,6 @@ import org.apache.harmony.text.internal.nls.Messages; * {@code getCurrencyInstance} to get the currency number format and use * {@code getPercentInstance} to get a format for displaying percentages. With * this format, a fraction like 0.53 is displayed as 53%. - *

                  *

                  * You can also control the display of numbers with methods such as * {@code setMinimumFractionDigits}. If you want even more control over the @@ -113,7 +101,6 @@ import org.apache.harmony.text.internal.nls.Messages; * {@code DecimalFormat}. This will work for the vast majority of locales; just * remember to put it in a {@code try} block in case you encounter an unusual * one. - *

                  *

                  * {@code NumberFormat} is designed such that some controls work for formatting * and others work for parsing. For example, {@code setParseIntegerOnly} only @@ -121,7 +108,6 @@ import org.apache.harmony.text.internal.nls.Messages; * leaves the parse position just after '6'); if set to {@code false}, * "3456.78" is parsed as 3456.78 (and leaves the parse position just after * '8'). This is independent of formatting. - *

                  *

                  * You can also use forms of the {@code parse} and {@code format} methods with * {@code ParsePosition} and {@code FieldPosition} to allow you to: @@ -155,11 +141,9 @@ import org.apache.harmony.text.internal.nls.Messages; * and the {@code NumberFormat} API is essentially an abstraction of * {@code DecimalFormat's} API. Refer to {@code DecimalFormat} for more * information about this API. - *

                  - * + * * @see DecimalFormat * @see java.text.ChoiceFormat - * @since Android 1.0 */ public abstract class NumberFormat extends Format { @@ -167,15 +151,11 @@ public abstract class NumberFormat extends Format { /** * Field constant identifying the integer part of a number. - * - * @since Android 1.0 */ public static final int INTEGER_FIELD = 0; /** * Field constant identifying the fractional part of a number. - * - * @since Android 1.0 */ public static final int FRACTION_FIELD = 1; @@ -186,8 +166,6 @@ public abstract class NumberFormat extends Format { /** * Constructs a new instance of {@code NumberFormat}. - * - * @since Android 1.0 */ public NumberFormat() { } @@ -198,7 +176,6 @@ public abstract class NumberFormat extends Format { * * @return a shallow copy of this {@code NumberFormat}. * @see java.lang.Cloneable - * @since Android 1.0 */ @Override public Object clone() { @@ -215,7 +192,6 @@ public abstract class NumberFormat extends Format { * @return {@code true} if the specified object is equal to this number * format; {@code false} otherwise. * @see #hashCode - * @since Android 1.0 */ @Override public boolean equals(Object object) { @@ -240,7 +216,6 @@ public abstract class NumberFormat extends Format { * @param value * the double to format. * @return the formatted string. - * @since Android 1.0 */ public final String format(double value) { return format(value, new StringBuffer(), new FieldPosition(0)) @@ -255,8 +230,7 @@ public abstract class NumberFormat extends Format { * specifying a format field, then its {@code beginIndex} and * {@code endIndex} members will be updated with the position of the first * occurrence of this field in the formatted text. - *

                  - * + * * @param value * the double to format. * @param buffer @@ -266,7 +240,6 @@ public abstract class NumberFormat extends Format { * on input: an optional alignment field; on output: the offsets * of the alignment field in the formatted text. * @return the string buffer. - * @since Android 1.0 */ public abstract StringBuffer format(double value, StringBuffer buffer, FieldPosition field); @@ -277,7 +250,6 @@ public abstract class NumberFormat extends Format { * @param value * the long to format. * @return the formatted string. - * @since Android 1.0 */ public final String format(long value) { return format(value, new StringBuffer(), new FieldPosition(0)) @@ -292,8 +264,7 @@ public abstract class NumberFormat extends Format { * specifying a format field, then its {@code beginIndex} and * {@code endIndex} members will be updated with the position of the first * occurrence of this field in the formatted text. - *

                  - * + * * @param value * the long to format. * @param buffer @@ -303,7 +274,6 @@ public abstract class NumberFormat extends Format { * on input: an optional alignment field; on output: the offsets * of the alignment field in the formatted text. * @return the string buffer. - * @since Android 1.0 */ public abstract StringBuffer format(long value, StringBuffer buffer, FieldPosition field); @@ -316,8 +286,7 @@ public abstract class NumberFormat extends Format { * a format field, then its {@code beginIndex} and {@code endIndex} members * will be updated with the position of the first occurrence of this field * in the formatted text. - *

                  - * + * * @param object * the object to format, must be a {@code Number}. * @param buffer @@ -328,7 +297,6 @@ public abstract class NumberFormat extends Format { * @return the string buffer. * @throws IllegalArgumentException * if {@code object} is not an instance of {@code Number}. - * @since Android 1.0 */ @Override public StringBuffer format(Object object, StringBuffer buffer, @@ -348,7 +316,6 @@ public abstract class NumberFormat extends Format { * Gets the list of installed locales which support {@code NumberFormat}. * * @return an array of locales. - * @since Android 1.0 */ public static Locale[] getAvailableLocales() { return Locale.getAvailableLocales(); @@ -365,7 +332,6 @@ public abstract class NumberFormat extends Format { * @return the currency that was set in getInstance() or in setCurrency(), * or {@code null}. * @throws UnsupportedOperationException - * @since Android 1.0 */ public Currency getCurrency() { throw new UnsupportedOperationException(); @@ -376,7 +342,6 @@ public abstract class NumberFormat extends Format { * for the default locale. * * @return a {@code NumberFormat} for handling currency values. - * @since Android 1.0 */ public final static NumberFormat getCurrencyInstance() { return getCurrencyInstance(Locale.getDefault()); @@ -389,10 +354,11 @@ public abstract class NumberFormat extends Format { * @param locale * the locale to use. * @return a {@code NumberFormat} for handling currency values. - * @since Android 1.0 */ public static NumberFormat getCurrencyInstance(Locale locale) { + // BEGIN android-changed return getInstance(locale, "Currency"); //$NON-NLS-1$ + // END android-changed } /** @@ -400,7 +366,6 @@ public abstract class NumberFormat extends Format { * default locale. * * @return a {@code NumberFormat} for handling integers. - * @since Android 1.0 */ public final static NumberFormat getIntegerInstance() { return getIntegerInstance(Locale.getDefault()); @@ -413,12 +378,13 @@ public abstract class NumberFormat extends Format { * @param locale * the locale to use. * @return a {@code NumberFormat} for handling integers. - * @since Android 1.0 */ public static NumberFormat getIntegerInstance(Locale locale) { + // BEGIN android-changed NumberFormat format = getInstance(locale, "Integer"); //$NON-NLS-1$ format.setParseIntegerOnly(true); return format; + // END android-changed } /** @@ -426,7 +392,6 @@ public abstract class NumberFormat extends Format { * default locale. * * @return a {@code NumberFormat} for handling {@code Number} objects. - * @since Android 1.0 */ public final static NumberFormat getInstance() { return getNumberInstance(); @@ -439,16 +404,17 @@ public abstract class NumberFormat extends Format { * @param locale * the locale to use. * @return a {@code NumberFormat} for handling {@code Number} objects. - * @since Android 1.0 */ public static NumberFormat getInstance(Locale locale) { return getNumberInstance(locale); } + // BEGIN android-added static NumberFormat getInstance(Locale locale, String type) { return new DecimalFormat(getPattern(locale, type), new DecimalFormatSymbols(locale)); } + // END android-added /** * Returns the maximum number of fraction digits that are printed when @@ -456,7 +422,6 @@ public abstract class NumberFormat extends Format { * the least significant digits are truncated. * * @return the maximum number of fraction digits. - * @since Android 1.0 */ public int getMaximumFractionDigits() { return maximumFractionDigits; @@ -468,7 +433,6 @@ public abstract class NumberFormat extends Format { * most significant digits are truncated. * * @return the maximum number of integer digits. - * @since Android 1.0 */ public int getMaximumIntegerDigits() { return maximumIntegerDigits; @@ -479,7 +443,6 @@ public abstract class NumberFormat extends Format { * formatting. * * @return the minimum number of fraction digits. - * @since Android 1.0 */ public int getMinimumFractionDigits() { return minimumFractionDigits; @@ -490,7 +453,6 @@ public abstract class NumberFormat extends Format { * formatting. * * @return the minimum number of integer digits. - * @since Android 1.0 */ public int getMinimumIntegerDigits() { return minimumIntegerDigits; @@ -501,7 +463,6 @@ public abstract class NumberFormat extends Format { * default locale. * * @return a {@code NumberFormat} for handling {@code Number} objects. - * @since Android 1.0 */ public final static NumberFormat getNumberInstance() { return getNumberInstance(Locale.getDefault()); @@ -514,23 +475,25 @@ public abstract class NumberFormat extends Format { * @param locale * the locale to use. * @return a {@code NumberFormat} for handling {@code Number} objects. - * @since Android 1.0 */ public static NumberFormat getNumberInstance(Locale locale) { + // BEGIN android-changed return getInstance(locale, "Number"); //$NON-NLS-1$ + // END android-changed } + // BEGIN android-added static String getPattern(Locale locale, String type) { ResourceBundle bundle = getBundle(locale); return bundle.getString(type); } + // END android-added /** * Returns a {@code NumberFormat} for formatting and parsing percentage * values for the default locale. * * @return a {@code NumberFormat} for handling percentage values. - * @since Android 1.0 */ public final static NumberFormat getPercentInstance() { return getPercentInstance(Locale.getDefault()); @@ -543,10 +506,11 @@ public abstract class NumberFormat extends Format { * @param locale * the locale to use. * @return a {@code NumberFormat} for handling percentage values. - * @since Android 1.0 */ public static NumberFormat getPercentInstance(Locale locale) { + // BEGIN android-changed return getInstance(locale, "Percent"); //$NON-NLS-1$ + // END android-changed } @Override @@ -562,7 +526,6 @@ public abstract class NumberFormat extends Format { * * @return {@code true} if a grouping separator is used; {@code false} * otherwise. - * @since Android 1.0 */ public boolean isGroupingUsed() { return groupingUsed; @@ -574,7 +537,6 @@ public abstract class NumberFormat extends Format { * * @return {@code true} if this number format only parses integers, * {@code false} if if parsese integers as well as fractions. - * @since Android 1.0 */ public boolean isParseIntegerOnly() { return parseIntegerOnly; @@ -587,9 +549,8 @@ public abstract class NumberFormat extends Format { * @param string * the string to parse. * @return the {@code Number} resulting from the parsing. - * @exception ParseException - * if an error occurs during parsing. - * @since Android 1.0 + * @throws ParseException + * if an error occurs during parsing. */ public Number parse(String string) throws ParseException { ParsePosition pos = new ParsePosition(0); @@ -617,7 +578,6 @@ public abstract class NumberFormat extends Format { * set to the index where the error occurred. * @return the {@code Number} resulting from the parse or {@code null} if * there is an error. - * @since Android 1.0 */ public abstract Number parse(String string, ParsePosition position); @@ -642,12 +602,10 @@ public abstract class NumberFormat extends Format { * This implementation throws {@code UnsupportedOperationException}, * concrete subclasses should override this method if they support currency * formatting. - *

                  - * + * * @param currency * the new currency. * @throws UnsupportedOperationException - * @since Android 1.0 */ public void setCurrency(Currency currency) { throw new UnsupportedOperationException(); @@ -660,7 +618,6 @@ public abstract class NumberFormat extends Format { * @param value * {@code true} if a grouping separator is used; {@code false} * otherwise. - * @since Android 1.0 */ public void setGroupingUsed(boolean value) { groupingUsed = value; @@ -673,7 +630,6 @@ public abstract class NumberFormat extends Format { * * @param value * the maximum number of fraction digits. - * @since Android 1.0 */ public void setMaximumFractionDigits(int value) { maximumFractionDigits = value < 0 ? 0 : value; @@ -689,7 +645,6 @@ public abstract class NumberFormat extends Format { * * @param value * the new maximum number of integer numerals for display. - * @since Android 1.0 */ public void setMaximumIntegerDigits(int value) { maximumIntegerDigits = value < 0 ? 0 : value; @@ -704,7 +659,6 @@ public abstract class NumberFormat extends Format { * * @param value * the minimum number of fraction digits. - * @since Android 1.0 */ public void setMinimumFractionDigits(int value) { minimumFractionDigits = value < 0 ? 0 : value; @@ -719,7 +673,6 @@ public abstract class NumberFormat extends Format { * * @param value * the minimum number of integer digits. - * @since Android 1.0 */ public void setMinimumIntegerDigits(int value) { minimumIntegerDigits = value < 0 ? 0 : value; @@ -737,7 +690,6 @@ public abstract class NumberFormat extends Format { * @param value * {@code true} to only parse integers, {@code false} to parse * integers as well as fractions. - * @since Android 1.0 */ public void setParseIntegerOnly(boolean value) { parseIntegerOnly = value; @@ -824,8 +776,6 @@ public abstract class NumberFormat extends Format { * There is no public constructor in this class, the only instances are the * constants defined here. *

                  - * - * @since Android 1.0 */ public static class Field extends Format.Field { @@ -833,90 +783,67 @@ public abstract class NumberFormat extends Format { /** * This constant stands for the number sign. - * - * @since Android 1.0 */ public static final Field SIGN = new Field("sign"); //$NON-NLS-1$ /** * This constant stands for the integer part of the number. - * - * @since Android 1.0 */ public static final Field INTEGER = new Field("integer"); //$NON-NLS-1$ /** * This constant stands for the fraction part of the number. - * - * @since Android 1.0 */ public static final Field FRACTION = new Field("fraction"); //$NON-NLS-1$ /** * This constant stands for the exponent part of the number. - * - * @since Android 1.0 */ public static final Field EXPONENT = new Field("exponent"); //$NON-NLS-1$ /** * This constant stands for the exponent sign symbol. - * - * @since Android 1.0 */ public static final Field EXPONENT_SIGN = new Field("exponent sign"); //$NON-NLS-1$ /** * This constant stands for the exponent symbol. - * - * @since Android 1.0 */ public static final Field EXPONENT_SYMBOL = new Field("exponent symbol"); //$NON-NLS-1$ /** * This constant stands for the decimal separator. - * - * @since Android 1.0 */ public static final Field DECIMAL_SEPARATOR = new Field( "decimal separator"); //$NON-NLS-1$ /** * This constant stands for the grouping separator. - * - * @since Android 1.0 */ public static final Field GROUPING_SEPARATOR = new Field( "grouping separator"); //$NON-NLS-1$ /** * This constant stands for the percent symbol. - * - * @since Android 1.0 */ public static final Field PERCENT = new Field("percent"); //$NON-NLS-1$ /** * This constant stands for the permille symbol. - * - * @since Android 1.0 */ public static final Field PERMILLE = new Field("per mille"); //$NON-NLS-1$ /** * This constant stands for the currency symbol. - * - * @since Android 1.0 */ public static final Field CURRENCY = new Field("currency"); //$NON-NLS-1$ /** * Constructs a new instance of {@code NumberFormat.Field} with the * given field name. - * + * * @param fieldName * the field name. - * @since Android 1.0 */ protected Field(String fieldName) { super(fieldName); @@ -925,7 +852,7 @@ public abstract class NumberFormat extends Format { /** * Resolves instances that are deserialized to the constant * {@code NumberFormat.Field} values. - * + * * @return the resolved field object. * @throws InvalidObjectException * if an error occurs while resolving the field object. diff --git a/libcore/text/src/main/java/java/text/ParseException.java b/libcore/text/src/main/java/java/text/ParseException.java index 52184624b..b5a9c62c7 100644 --- a/libcore/text/src/main/java/java/text/ParseException.java +++ b/libcore/text/src/main/java/java/text/ParseException.java @@ -19,8 +19,6 @@ package java.text; /** * Thrown when the string being parsed is not in the correct form. - * - * @since Android 1.0 */ public class ParseException extends Exception { @@ -36,7 +34,6 @@ public class ParseException extends Exception { * the detail message for this exception. * @param location * the index at which the parse exception occurred. - * @since Android 1.0 */ public ParseException(String detailMessage, int location) { super(detailMessage); @@ -47,7 +44,6 @@ public class ParseException extends Exception { * Returns the index at which this parse exception occurred. * * @return the location of this exception in the parsed string. - * @since Android 1.0 */ public int getErrorOffset() { return errorOffset; diff --git a/libcore/text/src/main/java/java/text/ParsePosition.java b/libcore/text/src/main/java/java/text/ParsePosition.java index c3d1001b1..c643df9c3 100644 --- a/libcore/text/src/main/java/java/text/ParsePosition.java +++ b/libcore/text/src/main/java/java/text/ParsePosition.java @@ -21,8 +21,6 @@ package java.text; * Tracks the current position in a parsed string. In case of an error the error * index can be set to the position where the error occurred without having to * change the parse position. - * - * @since Android 1.0 */ public class ParsePosition { @@ -33,7 +31,6 @@ public class ParsePosition { * * @param index * the index to begin parsing. - * @since Android 1.0 */ public ParsePosition(int index) { currentPosition = index; @@ -50,7 +47,6 @@ public class ParsePosition { * @return {@code true} if the specified object is equal to this * {@code ParsePosition}; {@code false} otherwise. * @see #hashCode - * @since Android 1.0 */ @Override public boolean equals(Object object) { @@ -66,7 +62,6 @@ public class ParsePosition { * Returns the index at which the parse could not continue. * * @return the index of the parse error or -1 if there is no error. - * @since Android 1.0 */ public int getErrorIndex() { return errorIndex; @@ -76,7 +71,6 @@ public class ParsePosition { * Returns the current parse position. * * @return the current position. - * @since Android 1.0 */ public int getIndex() { return currentPosition; @@ -92,7 +86,6 @@ public class ParsePosition { * * @param index * the index of the parse error. - * @since Android 1.0 */ public void setErrorIndex(int index) { errorIndex = index; @@ -103,7 +96,6 @@ public class ParsePosition { * * @param index * the current parse position. - * @since Android 1.0 */ public void setIndex(int index) { currentPosition = index; @@ -113,7 +105,6 @@ public class ParsePosition { * Returns the string representation of this parse position. * * @return the string representation of this parse position. - * @since Android 1.0 */ @Override public String toString() { diff --git a/libcore/text/src/main/java/java/text/RuleBasedCollator.java b/libcore/text/src/main/java/java/text/RuleBasedCollator.java index 41a51e2cc..1a3bab7b1 100644 --- a/libcore/text/src/main/java/java/text/RuleBasedCollator.java +++ b/libcore/text/src/main/java/java/text/RuleBasedCollator.java @@ -15,17 +15,7 @@ * limitations under the License. */ -/** -******************************************************************************* -* Copyright (C) 1996-2007, International Business Machines Corporation and * -* others. All Rights Reserved. * -******************************************************************************* -*/ - // BEGIN android-note -// The class javadoc and some of the method descriptions are copied from ICU4J -// source files. Changes have been made to the copied descriptions. -// The icu license header was added to this file. // The icu implementation used was changed from icu4j to icu4jni. // END android-note @@ -38,7 +28,6 @@ import org.apache.harmony.text.internal.nls.Messages; *

                  * {@code RuleBasedCollator} has the following restrictions for efficiency * (other subclasses may be used for more complex languages): - *

                  *
                    *
                  1. If a French secondary ordering is specified it applies to the whole * collator object.
                  2. @@ -51,19 +40,17 @@ import org.apache.harmony.text.internal.nls.Messages; *

                    * The collation table is composed of a list of collation rules, where each rule * is of three forms: - *

                    *
                    - * + * *
                      * 
                      *  
                      *  
                      * 
                    - * + * *
                    *

                    * The rule elements are defined as follows: - *

                    *
                      *
                    • Text-Argument: A text-argument is any sequence of * characters, excluding special characters (that is, common whitespace @@ -76,7 +63,6 @@ import org.apache.harmony.text.internal.nls.Messages; * specify that all accents (secondary differences) are backwards. *

                      * '@' : Indicates that accents are sorted backwards, as in French. - *

                      *
                    • *
                    • Relation: The relations are the following: *
                        @@ -92,33 +78,30 @@ import org.apache.harmony.text.internal.nls.Messages; *

                        * '&' : Indicates that the next rule follows the position to where the reset * text-argument would be sorted. - *

                        * *
                      *

                      * This sounds more complicated than it is in practice. For example, the * following are equivalent ways of expressing the same thing: - *

                      *
                      - * + * *
                        * a < b < c
                        * a < b & b < c
                        * a < c & a < b
                        * 
                      - * + * *
                      *

                      * Notice that the order is important, as the subsequent item goes immediately * after the text-argument. The following are not equivalent: - *

                      *
                      - * + * *
                        * a < b & a < c
                        * a < c & a < b
                        * 
                      - * + * *
                      *

                      * Either the text-argument must already be present in the sequence, or some @@ -132,7 +115,6 @@ import org.apache.harmony.text.internal.nls.Messages; * German a-umlaut is treated as if it expands to two characters (expressed as * {@code "a,A < b,B ... & ae;\u00e3 & AE;\u00c3"}, where \u00e3 and \u00c3 * are the escape sequences for a-umlaut). - *

                      *

                      Ignorable Characters

                      *

                      * For ignorable characters, the first rule must start with a relation (the @@ -141,7 +123,6 @@ import org.apache.harmony.text.internal.nls.Messages; * {@code "<"}, then all text-arguments up to the first {@code "<"} are * ignorable. For example, {@code ", - < a < b"} makes {@code "-"} an ignorable * character. - *

                      *

                      Normalization and Accents

                      *

                      * {@code RuleBasedCollator} automatically processes its rule table to include @@ -150,7 +131,6 @@ import org.apache.harmony.text.internal.nls.Messages; * combining accent characters, the pre-composed accented characters matching * all canonical combinations of characters from the rule string will be entered * in the table. - *

                      *

                      * This allows you to use a RuleBasedCollator to compare accented strings even * when the collator is set to NO_DECOMPOSITION. However, if the strings to be @@ -158,11 +138,9 @@ import org.apache.harmony.text.internal.nls.Messages; * should set the collator to CANONICAL_DECOMPOSITION to enable sorting of * combining sequences. For more information, see The Unicode Standard, Version 3.0. - *

                      *

                      Errors

                      *

                      * The following rules are not valid: - *

                      *
                        *
                      • A text-argument contains unquoted punctuation symbols, for example * {@code "a < b-c < d"}.
                      • @@ -175,7 +153,6 @@ import org.apache.harmony.text.internal.nls.Messages; *

                        * If you produce one of these errors, {@code RuleBasedCollator} throws a * {@code ParseException}. - *

                        *

                        Examples

                        *

                        * Normally, to create a rule-based collator object, you will use @@ -183,117 +160,109 @@ import org.apache.harmony.text.internal.nls.Messages; * rule-based collator object with specialized rules tailored to your needs, you * construct the {@code RuleBasedCollator} with the rules contained in a * {@code String} object. For example: - *

                        *
                        - * + * *
                          * String Simple = "< a < b < c < d";
                        - * 
                        + *
                          * RuleBasedCollator mySimple = new RuleBasedCollator(Simple);
                          * 
                        - * + * *
                        *

                        * Or: - *

                        *
                        - * + * *
                          * String Norwegian = "< a,A< b,B< c,C< d,D< e,E< f,F< g,G< h,H< i,I"
                        - *         + "< j,J< k,K< l,L< m,M< n,N< o,O< p,P< q,Q< r,R" 
                        + *         + "< j,J< k,K< l,L< m,M< n,N< o,O< p,P< q,Q< r,R"
                          *         + "< s,S< t,T< u,U< v,V< w,W< x,X< y,Y< z,Z"
                        - *         + "< \u00E5=a\u030A,\u00C5=A\u030A" 
                        + *         + "< \u00E5=a\u030A,\u00C5=A\u030A"
                          *         + ";aa,AA< \u00E6,\u00C6< \u00F8,\u00D8";
                        - * 
                        + *
                          * RuleBasedCollator myNorwegian = new RuleBasedCollator(Norwegian);
                          * 
                        - * + * *
                        *

                        * Combining {@code Collator}s is as simple as concatenating strings. Here is * an example that combines two {@code Collator}s from two different locales: - *

                        *
                        - * + * *
                          * // Create an en_US Collator object
                          * RuleBasedCollator en_USCollator = (RuleBasedCollator)Collator
                          *         .getInstance(new Locale("en", "US", ""));
                        - * 
                        + *
                          * // Create a da_DK Collator object
                          * RuleBasedCollator da_DKCollator = (RuleBasedCollator)Collator
                          *         .getInstance(new Locale("da", "DK", ""));
                        - * 
                        + *
                          * // Combine the two collators
                          * // First, get the collation rules from en_USCollator
                          * String en_USRules = en_USCollator.getRules();
                        - * 
                        + *
                          * // Second, get the collation rules from da_DKCollator
                          * String da_DKRules = da_DKCollator.getRules();
                        - * 
                        + *
                          * RuleBasedCollator newCollator = new RuleBasedCollator(en_USRules + da_DKRules);
                          * // newCollator has the combined rules
                          * 
                        - * + * *
                        *

                        * The next example shows to make changes on an existing table to create a new * {@code Collator} object. For example, add {@code "& C < ch, cH, Ch, CH"} to * the {@code en_USCollator} object to create your own: - *

                        *
                        - * + * *
                          * // Create a new Collator object with additional rules
                          * String addRules = "& C < ch, cH, Ch, CH";
                        - * 
                        + *
                          * RuleBasedCollator myCollator = new RuleBasedCollator(en_USCollator + addRules);
                          * // myCollator contains the new rules
                          * 
                        - * + * *
                        *

                        * The following example demonstrates how to change the order of non-spacing * accents: - *

                        *
                        - * + * *
                          * // old rule
                          * String oldRules = "= \u00a8 ; \u00af ; \u00bf" + "< a , A ; ae, AE ; \u00e6 , \u00c6"
                          *         + "< b , B < c, C < e, E & C < d, D";
                        - * 
                        + *
                          * // change the order of accent characters
                          * String addOn = "& \u00bf ; \u00af ; \u00a8;";
                        - * 
                        + *
                          * RuleBasedCollator myCollator = new RuleBasedCollator(oldRules + addOn);
                          * 
                        - * + * *
                        *

                        * The last example shows how to put new primary ordering in before the default * setting. For example, in the Japanese {@code Collator}, you can either sort * English characters before or after Japanese characters: - *

                        *
                        - * + * *
                          * // get en_US Collator rules
                          * RuleBasedCollator en_USCollator = (RuleBasedCollator)
                          *     Collator.getInstance(Locale.US);
                        - * 
                        + *
                          * // add a few Japanese character to sort before English characters
                          * // suppose the last character before the first base letter 'a' in
                          * // the English collation rule is \u30A2
                          * String jaString = "& \u30A2 , \u30FC < \u30C8";
                        - * 
                        - * RuleBasedCollator myJapaneseCollator = 
                        + *
                        + * RuleBasedCollator myJapaneseCollator =
                          *     new RuleBasedCollator(en_USCollator.getRules() + jaString);
                          * 
                        - * + * *
                        - * - * @since Android 1.0 */ public class RuleBasedCollator extends Collator { @@ -320,7 +289,6 @@ public class RuleBasedCollator extends Collator { * @throws ParseException * if {@code rules} contains rules with invalid collation rule * syntax. - * @since Android 1.0 */ public RuleBasedCollator(String rules) throws ParseException { if (rules == null) { @@ -359,7 +327,6 @@ public class RuleBasedCollator extends Collator { * @param source * the source character iterator. * @return a {@code CollationElementIterator} for {@code source}. - * @since Android 1.0 */ public CollationElementIterator getCollationElementIterator( CharacterIterator source) { @@ -377,7 +344,6 @@ public class RuleBasedCollator extends Collator { * @param source * the source string. * @return the {@code CollationElementIterator} for {@code source}. - * @since Android 1.0 */ public CollationElementIterator getCollationElementIterator(String source) { if (source == null) { @@ -390,7 +356,7 @@ public class RuleBasedCollator extends Collator { /** * Returns the collation rules of this collator. These {@code rules} can be - * fed into the {@link #RuleBasedCollator(String)} constructor. + * fed into the {@code RuleBasedCollator(String)} constructor. *

                        * Note that the {@code rules} are actually interpreted as a delta to the * standard Unicode Collation Algorithm (UCA). Hence, an empty {@code rules} @@ -399,7 +365,6 @@ public class RuleBasedCollator extends Collator { * specifications and may result in different behavior. * * @return the collation rules. - * @since Android 1.0 */ public String getRules() { return ((com.ibm.icu4jni.text.RuleBasedCollator) this.icuColl).getRules(); @@ -411,7 +376,6 @@ public class RuleBasedCollator extends Collator { * * @return a shallow copy of this collator. * @see java.lang.Cloneable - * @since Android 1.0 */ @Override public Object clone() { @@ -431,7 +395,6 @@ public class RuleBasedCollator extends Collator { * {@code CollationKey.compareTo(CollationKey)} for the comparisons. If each * string is compared to only once, using * {@code RuleBasedCollator.compare(String, String)} has better performance. - *

                        * * @param source * the source text. @@ -440,7 +403,6 @@ public class RuleBasedCollator extends Collator { * @return an integer which may be a negative value, zero, or else a * positive value depending on whether {@code source} is less than, * equivalent to, or greater than {@code target}. - * @since Android 1.0 */ @Override public int compare(String source, String target) { @@ -457,7 +419,6 @@ public class RuleBasedCollator extends Collator { * @param source * the specified source text. * @return the {@code CollationKey} for the given source text. - * @since Android 1.0 */ @Override public CollationKey getCollationKey(String source) { @@ -486,7 +447,6 @@ public class RuleBasedCollator extends Collator { * @return {@code true} if the specified object is equal to this * {@code RuleBasedCollator}; {@code false} otherwise. * @see #hashCode - * @since Android 1.0 */ @Override public boolean equals(Object obj) { diff --git a/libcore/text/src/main/java/java/text/SimpleDateFormat.java b/libcore/text/src/main/java/java/text/SimpleDateFormat.java index c78f29e73..52c5e1b57 100644 --- a/libcore/text/src/main/java/java/text/SimpleDateFormat.java +++ b/libcore/text/src/main/java/java/text/SimpleDateFormat.java @@ -15,17 +15,8 @@ * limitations under the License. */ -/** -******************************************************************************* -* Copyright (C) 1996-2007, International Business Machines Corporation and * -* others. All Rights Reserved. * -******************************************************************************* -*/ - // BEGIN android-note -// The class javadoc and some of the method descriptions are copied from ICU4J -// source files. Changes have been made to the copied descriptions. -// The icu license header was added to this file. +// changed from ICU to resource bundles and Java parsing/formatting // END android-note package java.text; @@ -38,9 +29,11 @@ import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.Locale; +// BEGIN android-added import java.util.ResourceBundle; import java.util.SimpleTimeZone; import java.util.TimeZone; +// END android-added import java.util.Vector; import org.apache.harmony.text.internal.nls.Messages; @@ -58,13 +51,11 @@ import org.apache.harmony.text.internal.nls.Messages; * default format pattern. You may modify the format pattern using the {@code * applyPattern} methods as desired. For more information on using these * methods, see {@link DateFormat}. - *

                        *

                        Time Format Syntax

                        *

                        * To specify the time format, use a time pattern string. In this * pattern, all ASCII letters are reserved as pattern letters, which are defined * as follows: - *

                        * * * @@ -217,7 +208,6 @@ import org.apache.harmony.text.internal.nls.Messages; * (Text): 4 or more pattern letters → use the full form, * less than 4 pattern letters → use a short or abbreviated form if one * exists. - *

                        *

                        * (Number): the minimum number of digits. Shorter numbers are * zero-padded to this amount. Year is handled specially; that is, if the count @@ -225,18 +215,15 @@ import org.apache.harmony.text.internal.nls.Messages; * "1997", "yy" produces "97".) Unlike other fields, fractional seconds are * padded on the right with zero. *

                        - *

                        * (Text & Number): 3 or over, use text, otherwise use number. *

                        * Any characters in the pattern that are not in the ranges of ['a'..'z'] and * ['A'..'Z'] will be treated as quoted text. For instance, characters like ':', * '.', ' ', '#' and '@' will appear in the resulting time text even they are * not embraced within single quotes. - *

                        *

                        * A pattern containing any invalid pattern letter will result in an exception * thrown during formatting or parsing. - *

                        *

                        Examples Using the US Locale

                        * *
                        @@ -275,7 +262,6 @@ import org.apache.harmony.text.internal.nls.Messages;
                          * am/pm marker 'a' is left out from the format pattern while the
                          * "hour in am/pm" pattern symbol is used. This information loss can happen when
                          * formatting the time in PM.
                        - * 

                        *

                        * When parsing a date string using the abbreviated year pattern ("yy"), {@code * SimpleDateFormat} must interpret the abbreviated year relative to some @@ -291,12 +277,10 @@ import org.apache.harmony.text.internal.nls.Messages; * example, "-1"), is interpreted literally. So "01/02/3" or "01/02/003" are * parsed, using the same pattern, as Jan 2, 3 AD. Likewise, "01/02/-3" is * parsed as Jan 2, 4 BC. - *

                        *

                        * If the year pattern does not have exactly two 'y' characters, the year is * interpreted literally, regardless of the number of digits. So using the * pattern "MM/dd/yyyy", "01/11/12" parses to Jan 11, 12 A.D. - *

                        *

                        * When numeric fields are adjacent directly, with no intervening delimiter * characters, they constitute a run of adjacent numeric fields. Such runs are @@ -308,11 +292,9 @@ import org.apache.harmony.text.internal.nls.Messages; * parsed again. This is repeated until either the parse succeeds or the * leftmost field is one character in length. If the parse still fails at that * point, the parse of the run fails. - *

                        *

                        * For time zones that have no names, use the strings "GMT+hours:minutes" or * "GMT-hours:minutes". - *

                        *

                        * The calendar defines the first day of the week, the first week of the year, * whether hours are zero based or not (0 vs. 12 or 24) and the time zone. There @@ -324,17 +306,19 @@ import org.apache.harmony.text.internal.nls.Messages; * * @see Calendar * @see GregorianCalendar - * @see TimeZone + * @see java.util.TimeZone * @see DateFormat * @see DateFormatSymbols * @see DecimalFormat - * @since Android 1.0 */ public class SimpleDateFormat extends DateFormat { private static final long serialVersionUID = 4774881970558875024L; + // BEGIN android-changed + // private static final String patternChars = "GyMdkHmsSEDFwWahKzYeugAZvcLQqV"; //$NON-NLS-1$ private static final String patternChars = "GyMdkHmsSEDFwWahKzZ"; //$NON-NLS-1$ + // END android-changed private String pattern; @@ -344,15 +328,21 @@ public class SimpleDateFormat extends DateFormat { private Date defaultCenturyStart; + // BEGIN android-removed + // private transient String tzId; + // + // private transient com.ibm.icu.text.SimpleDateFormat icuFormat; + // END android-removed + /** * Constructs a new {@code SimpleDateFormat} for formatting and parsing * dates and times in the {@code SHORT} style for the default locale. - * - * @since Android 1.0 */ public SimpleDateFormat() { this(Locale.getDefault()); + // BEGIN android-changed pattern = defaultPattern(); + // END android-changed formatData = new DateFormatSymbols(Locale.getDefault()); } @@ -363,35 +353,133 @@ public class SimpleDateFormat extends DateFormat { * * @param pattern * the pattern. - * @exception IllegalArgumentException - * if {@code pattern} is not considered to be usable by this - * formatter. - * @since Android 1.0 + * @throws NullPointerException + * if the pattern is {@code null}. + * @throws IllegalArgumentException + * if {@code pattern} is not considered to be usable by this + * formatter. */ public SimpleDateFormat(String pattern) { this(pattern, Locale.getDefault()); } + + /** + * Validates the format character. + * + * @param format + * the format character + * + * @throws IllegalArgumentException + * when the format character is invalid + */ + private void validateFormat(char format) { + int index = patternChars.indexOf(format); + if (index == -1) { + // text.03=Unknown pattern character - '{0}' + throw new IllegalArgumentException(Messages.getString( + "text.03", format)); //$NON-NLS-1$ + } + } + /** + * Validates the pattern. + * + * @param template + * the pattern to validate. + * + * @throws NullPointerException + * if the pattern is null + * @throws IllegalArgumentException + * if the pattern is invalid + */ + private void validatePattern(String template) { + boolean quote = false; + int next, last = -1, count = 0; + + final int patternLength = template.length(); + for (int i = 0; i < patternLength; i++) { + next = (template.charAt(i)); + if (next == '\'') { + if (count > 0) { + validateFormat((char) last); + count = 0; + } + if (last == next) { + last = -1; + } else { + last = next; + } + quote = !quote; + continue; + } + if (!quote + && (last == next || (next >= 'a' && next <= 'z') || (next >= 'A' && next <= 'Z'))) { + if (last == next) { + count++; + } else { + if (count > 0) { + validateFormat((char) last); + } + last = next; + count = 1; + } + } else { + if (count > 0) { + validateFormat((char) last); + count = 0; + } + last = -1; + } + } + if (count > 0) { + validateFormat((char) last); + } + + if (quote) { + // text.04=Unterminated quote {0} + throw new IllegalArgumentException(Messages.getString("text.04")); //$NON-NLS-1$ + } + + } + /** * Constructs a new {@code SimpleDateFormat} using the specified * non-localized pattern and {@code DateFormatSymbols} and the {@code * Calendar} for the default locale. - * + * * @param template * the pattern. * @param value * the DateFormatSymbols. - * @exception IllegalArgumentException - * if the pattern is invalid. - * @since Android 1.0 + * @throws NullPointerException + * if the pattern is {@code null}. + * @throws IllegalArgumentException + * if the pattern is invalid. */ public SimpleDateFormat(String template, DateFormatSymbols value) { this(Locale.getDefault()); validatePattern(template); + // BEGIN android-removed + // icuFormat = new com.ibm.icu.text.SimpleDateFormat(template, Locale.getDefault()); + // icuFormat.setTimeZone(com.ibm.icu.util.TimeZone.getTimeZone(tzId)); + // END android-removed pattern = template; formatData = (DateFormatSymbols) value.clone(); } + // BEGIN android-removed + // private void copySymbols(DateFormatSymbols value, com.ibm.icu.text.DateFormatSymbols icuSymbols) { + // icuSymbols.setAmPmStrings(value.getAmPmStrings()); + // icuSymbols.setEras(value.getEras()); + // icuSymbols.setLocalPatternChars(value.getLocalPatternChars()); + // icuSymbols.setMonths(value.getMonths()); + // icuSymbols.setShortMonths(value.getShortMonths()); + // icuSymbols.setShortWeekdays(value.getShortWeekdays()); + // icuSymbols.setWeekdays(value.getWeekdays()); + // icuSymbols.setZoneStrings(value.getZoneStrings()); + // } + // END android-removed + /** * Constructs a new {@code SimpleDateFormat} using the specified * non-localized pattern and the {@code DateFormatSymbols} and {@code @@ -401,228 +489,59 @@ public class SimpleDateFormat extends DateFormat { * the pattern. * @param locale * the locale. - * @exception IllegalArgumentException - * if the pattern is invalid. - * @since Android 1.0 + * @throws NullPointerException + * if the pattern is {@code null}. + * @throws IllegalArgumentException + * if the pattern is invalid. */ public SimpleDateFormat(String template, Locale locale) { this(locale); validatePattern(template); + // BEGIN android-removed + // icuFormat = new com.ibm.icu.text.SimpleDateFormat(template, locale); + // icuFormat.setTimeZone(com.ibm.icu.util.TimeZone.getTimeZone(tzId)); + // END android-removed pattern = template; + // BEGIN android-changed formatData = new DateFormatSymbols(locale); + // END android-changed } + // BEGIN android-removed + // SimpleDateFormat(Locale locale, com.ibm.icu.text.SimpleDateFormat icuFormat){ + // this(locale); + // this.icuFormat = icuFormat; + // this.icuFormat.setTimeZone(com.ibm.icu.util.TimeZone.getTimeZone(tzId)); + // pattern = (String)Format.getInternalField("pattern", icuFormat); //$NON-NLS-1$ + // formatData = new DateFormatSymbols(locale); + // } + // END android-removed + private SimpleDateFormat(Locale locale) { numberFormat = NumberFormat.getInstance(locale); numberFormat.setParseIntegerOnly(true); numberFormat.setGroupingUsed(false); calendar = new GregorianCalendar(locale); calendar.add(Calendar.YEAR, -80); + // BEGIN android-removed + // tzId = calendar.getTimeZone().getID(); + // END android-removed creationYear = calendar.get(Calendar.YEAR); defaultCenturyStart = calendar.getTime(); } - private void append(StringBuffer buffer, FieldPosition position, - Vector fields, char format, int count) { - int field = -1; - int index = patternChars.indexOf(format); - if (index == -1) { - // text.03=Unknown pattern character - '{0}' - throw new IllegalArgumentException(Messages.getString( - "text.03", format)); //$NON-NLS-1$ - } - - int beginPosition = buffer.length(); - Field dateFormatField = null; - - switch (index) { - case ERA_FIELD: - dateFormatField = Field.ERA; - buffer.append(formatData.eras[calendar.get(Calendar.ERA)]); - break; - case YEAR_FIELD: - dateFormatField = Field.YEAR; - int year = calendar.get(Calendar.YEAR); - if (count < 4) { - appendNumber(buffer, 2, year %= 100); - } else { - appendNumber(buffer, count, year); - } - break; - case MONTH_FIELD: - dateFormatField = Field.MONTH; - int month = calendar.get(Calendar.MONTH); - if (count <= 2) { - appendNumber(buffer, count, month + 1); - } else if (count == 3) { - buffer.append(formatData.shortMonths[month]); - } else { - buffer.append(formatData.months[month]); - } - break; - case DATE_FIELD: - dateFormatField = Field.DAY_OF_MONTH; - field = Calendar.DATE; - break; - case HOUR_OF_DAY1_FIELD: // k - dateFormatField = Field.HOUR_OF_DAY1; - int hour = calendar.get(Calendar.HOUR_OF_DAY); - appendNumber(buffer, count, hour == 0 ? 24 : hour); - break; - case HOUR_OF_DAY0_FIELD: // H - dateFormatField = Field.HOUR_OF_DAY0; - field = Calendar.HOUR_OF_DAY; - break; - case MINUTE_FIELD: - dateFormatField = Field.MINUTE; - field = Calendar.MINUTE; - break; - case SECOND_FIELD: - dateFormatField = Field.SECOND; - field = Calendar.SECOND; - break; - case MILLISECOND_FIELD: - dateFormatField = Field.MILLISECOND; - int value = calendar.get(Calendar.MILLISECOND); - appendNumber(buffer, count, value); - break; - case DAY_OF_WEEK_FIELD: - dateFormatField = Field.DAY_OF_WEEK; - int day = calendar.get(Calendar.DAY_OF_WEEK); - if (count < 4) { - buffer.append(formatData.shortWeekdays[day]); - } else { - buffer.append(formatData.weekdays[day]); - } - break; - case DAY_OF_YEAR_FIELD: - dateFormatField = Field.DAY_OF_YEAR; - field = Calendar.DAY_OF_YEAR; - break; - case DAY_OF_WEEK_IN_MONTH_FIELD: - dateFormatField = Field.DAY_OF_WEEK_IN_MONTH; - field = Calendar.DAY_OF_WEEK_IN_MONTH; - break; - case WEEK_OF_YEAR_FIELD: - dateFormatField = Field.WEEK_OF_YEAR; - field = Calendar.WEEK_OF_YEAR; - break; - case WEEK_OF_MONTH_FIELD: - dateFormatField = Field.WEEK_OF_MONTH; - field = Calendar.WEEK_OF_MONTH; - break; - case AM_PM_FIELD: - dateFormatField = Field.AM_PM; - buffer.append(formatData.ampms[calendar.get(Calendar.AM_PM)]); - break; - case HOUR1_FIELD: // h - dateFormatField = Field.HOUR1; - hour = calendar.get(Calendar.HOUR); - appendNumber(buffer, count, hour == 0 ? 12 : hour); - break; - case HOUR0_FIELD: // K - dateFormatField = Field.HOUR0; - field = Calendar.HOUR; - break; - case TIMEZONE_FIELD: // z - dateFormatField = Field.TIME_ZONE; - appendTimeZone(buffer, count, true); - break; - case (TIMEZONE_FIELD + 1): // Z - dateFormatField = Field.TIME_ZONE; - appendTimeZone(buffer, count, false); - break; - } - if (field != -1) { - appendNumber(buffer, count, calendar.get(field)); - } - - if (fields != null) { - position = new FieldPosition(dateFormatField); - position.setBeginIndex(beginPosition); - position.setEndIndex(buffer.length()); - fields.add(position); - } else { - // Set to the first occurrence - if ((position.getFieldAttribute() == dateFormatField || (position - .getFieldAttribute() == null && position.getField() == index)) - && position.getEndIndex() == 0) { - position.setBeginIndex(beginPosition); - position.setEndIndex(buffer.length()); - } - } - } - - private void appendTimeZone(StringBuffer buffer, int count, - boolean generalTimezone) { - // cannot call TimeZone.getDisplayName() because it would not use - // the DateFormatSymbols of this SimpleDateFormat - - if (generalTimezone) { - String id = calendar.getTimeZone().getID(); - // BEGIN android-changed - String[][] zones = formatData.internalZoneStrings(); - // END android-changed - String[] zone = null; - for (String[] element : zones) { - if (id.equals(element[0])) { - zone = element; - break; - } - } - if (zone == null) { - int offset = calendar.get(Calendar.ZONE_OFFSET) - + calendar.get(Calendar.DST_OFFSET); - char sign = '+'; - if (offset < 0) { - sign = '-'; - offset = -offset; - } - buffer.append("GMT"); //$NON-NLS-1$ - buffer.append(sign); - appendNumber(buffer, 2, offset / 3600000); - buffer.append(':'); - appendNumber(buffer, 2, (offset % 3600000) / 60000); - } else { - int daylight = calendar.get(Calendar.DST_OFFSET) == 0 ? 0 : 2; - if (count < 4) { - buffer.append(zone[2 + daylight]); - } else { - buffer.append(zone[1 + daylight]); - } - } - } else { - int offset = calendar.get(Calendar.ZONE_OFFSET) - + calendar.get(Calendar.DST_OFFSET); - char sign = '+'; - if (offset < 0) { - sign = '-'; - offset = -offset; - } - buffer.append(sign); - appendNumber(buffer, 2, offset / 3600000); - appendNumber(buffer, 2, (offset % 3600000) / 60000); - } - } - - private void appendNumber(StringBuffer buffer, int count, int value) { - int minimumIntegerDigits = numberFormat.getMinimumIntegerDigits(); - numberFormat.setMinimumIntegerDigits(count); - numberFormat.format(new Integer(value), buffer, new FieldPosition(0)); - numberFormat.setMinimumIntegerDigits(minimumIntegerDigits); - } - /** * Changes the pattern of this simple date format to the specified pattern * which uses localized pattern characters. * * @param template * the localized pattern. - * @since Android 1.0 */ public void applyLocalizedPattern(String template) { + // BEGIN android-changed pattern = convertPattern(template, formatData.getLocalPatternChars(), patternChars, true); + // END android-changed } /** @@ -631,37 +550,81 @@ public class SimpleDateFormat extends DateFormat { * * @param template * the non-localized pattern. - * @exception IllegalArgumentException + * @throws NullPointerException + * if the pattern is {@code null}. + * @throws IllegalArgumentException * if the pattern is invalid. - * @since Android 1.0 */ public void applyPattern(String template) { validatePattern(template); + // BEGIN android-removed + // /* + // * ICU spec explicitly mentions that "ICU interprets a single 'y' + // * differently than Java." We need to do a trick here to follow Java + // * spec. + // */ + // String templateForICU = patternForICU(template); + // icuFormat.applyPattern(templateForICU); + // END android-removed pattern = template; } + /** + * Converts the Java-spec pattern into an equivalent pattern used by ICU. + * + * @param p + * the Java-spec style pattern. + * @return the ICU-style pattern. + */ + @SuppressWarnings("nls") + private String patternForICU(String p) { + String[] subPatterns = p.split("'"); + boolean quote = false; + boolean first = true; + StringBuilder result = new StringBuilder(); + for (String subPattern : subPatterns) { + if (!quote) { + // replace 'y' with 'yy' for ICU to follow Java spec + result.append((first ? "" : "'") + + subPattern.replaceAll("(? fields = new Vector(); - - // format the date, and find fields - formatImpl(date, buffer, null, fields); - - // create and AttributedString with the formatted buffer - AttributedString as = new AttributedString(buffer.toString()); - - // add DateFormat field attributes to the AttributedString - for (int i = 0; i < fields.size(); i++) { - FieldPosition pos = fields.elementAt(i); - Format.Field attribute = pos.getFieldAttribute(); - as.addAttribute(attribute, attribute, pos.getBeginIndex(), pos - .getEndIndex()); - } - - // return the CharacterIterator from AttributedString - return as.getIterator(); - } - - /** - * Formats the specified date as a string using the pattern of this date - * format and appends the string to the specified string buffer. - *

                        - * If the {@code field} member of {@code field} contains a value specifying - * a format field, then its {@code beginIndex} and {@code endIndex} members - * will be updated with the position of the first occurrence of this field - * in the formatted text. - *

                        - * - * @param date - * the date to format. - * @param buffer - * the target string buffer to append the formatted date/time to. - * @param field - * on input: an optional alignment field; on output: the offsets - * of the alignment field in the formatted text. - * @return the string buffer. - * @throws IllegalArgumentException - * if there are invalid characters in the pattern. - * @since Android 1.0 - */ - @Override - public StringBuffer format(Date date, StringBuffer buffer, - FieldPosition field) { - return formatImpl(date, buffer, field, null); - } - - /** - * Validates the format character. - * - * @param format - * the format character - * - * @throws IllegalArgumentException - * when the format character is invalid - */ - private void validateFormat(char format) { - int index = patternChars.indexOf(format); - if (index == -1) { - // text.03=Unknown pattern character - '{0}' - throw new IllegalArgumentException(Messages.getString( - "text.03", format)); //$NON-NLS-1$ - } - } - - /** - * Validates the pattern. - * - * @param template - * the pattern to validate. - * - * @throws NullPointerException - * if the pattern is null - * @throws IllegalArgumentException - * if the pattern is invalid - */ - private void validatePattern(String template) { - boolean quote = false; - int next, last = -1, count = 0; - - final int patternLength = template.length(); - for (int i = 0; i < patternLength; i++) { - next = (template.charAt(i)); - if (next == '\'') { - if (count > 0) { - validateFormat((char) last); - count = 0; - } - if (last == next) { - last = -1; - } else { - last = next; - } - quote = !quote; - continue; - } - if (!quote - && (last == next || (next >= 'a' && next <= 'z') || (next >= 'A' && next <= 'Z'))) { - if (last == next) { - count++; - } else { - if (count > 0) { - validateFormat((char) last); - } - last = next; - count = 1; - } - } else { - if (count > 0) { - validateFormat((char) last); - count = 0; - } - last = -1; - } - } - if (count > 0) { - validateFormat((char) last); + if (object instanceof Number) { + return formatToCharacterIteratorImpl(new Date(((Number) object) + .longValue())); } + throw new IllegalArgumentException(); + + } + + private AttributedCharacterIterator formatToCharacterIteratorImpl(Date date) { + StringBuffer buffer = new StringBuffer(); + Vector fields = new Vector(); - if (quote) { - // text.04=Unterminated quote {0} - throw new IllegalArgumentException(Messages.getString("text.04")); //$NON-NLS-1$ + // format the date, and find fields + formatImpl(date, buffer, null, fields); + + // create and AttributedString with the formatted buffer + AttributedString as = new AttributedString(buffer.toString()); + + // add DateFormat field attributes to the AttributedString + for (int i = 0; i < fields.size(); i++) { + FieldPosition pos = fields.elementAt(i); + Format.Field attribute = pos.getFieldAttribute(); + as.addAttribute(attribute, attribute, pos.getBeginIndex(), pos + .getEndIndex()); } + // return the CharacterIterator from AttributedString + return as.getIterator(); } /** @@ -874,11 +725,9 @@ public class SimpleDateFormat extends DateFormat { * @param fields * Vector used to store the FieldPositions for each field in this * date - * * @return the formatted Date - * - * @exception IllegalArgumentException - * when the object cannot be formatted by this Format + * @throws IllegalArgumentException + * if the object cannot be formatted by this Format. */ private StringBuffer formatImpl(Date date, StringBuffer buffer, FieldPosition field, Vector fields) { @@ -932,13 +781,247 @@ public class SimpleDateFormat extends DateFormat { } return buffer; } + + private void append(StringBuffer buffer, FieldPosition position, + Vector fields, char format, int count) { + int field = -1; + int index = patternChars.indexOf(format); + if (index == -1) { + // text.03=Unknown pattern character - '{0}' + throw new IllegalArgumentException(Messages.getString( + "text.03", format)); //$NON-NLS-1$ + } + + int beginPosition = buffer.length(); + Field dateFormatField = null; + switch (index) { + case ERA_FIELD: + dateFormatField = Field.ERA; + buffer.append(formatData.eras[calendar.get(Calendar.ERA)]); + break; + case YEAR_FIELD: + dateFormatField = Field.YEAR; + int year = calendar.get(Calendar.YEAR); + if (count < 4) { + appendNumber(buffer, 2, year %= 100); + } else { + appendNumber(buffer, count, year); + } + break; + case MONTH_FIELD: + dateFormatField = Field.MONTH; + int month = calendar.get(Calendar.MONTH); + if (count <= 2) { + appendNumber(buffer, count, month + 1); + } else if (count == 3) { + buffer.append(formatData.shortMonths[month]); + } else { + buffer.append(formatData.months[month]); + } + break; + case DATE_FIELD: + dateFormatField = Field.DAY_OF_MONTH; + field = Calendar.DATE; + break; + case HOUR_OF_DAY1_FIELD: // k + dateFormatField = Field.HOUR_OF_DAY1; + int hour = calendar.get(Calendar.HOUR_OF_DAY); + appendNumber(buffer, count, hour == 0 ? 24 : hour); + break; + case HOUR_OF_DAY0_FIELD: // H + dateFormatField = Field.HOUR_OF_DAY0; + field = Calendar.HOUR_OF_DAY; + break; + case MINUTE_FIELD: + dateFormatField = Field.MINUTE; + field = Calendar.MINUTE; + break; + case SECOND_FIELD: + dateFormatField = Field.SECOND; + field = Calendar.SECOND; + break; + case MILLISECOND_FIELD: + dateFormatField = Field.MILLISECOND; + int value = calendar.get(Calendar.MILLISECOND); + appendNumber(buffer, count, value); + break; + case DAY_OF_WEEK_FIELD: + dateFormatField = Field.DAY_OF_WEEK; + int day = calendar.get(Calendar.DAY_OF_WEEK); + if (count < 4) { + buffer.append(formatData.shortWeekdays[day]); + } else { + buffer.append(formatData.weekdays[day]); + } + break; + case DAY_OF_YEAR_FIELD: + dateFormatField = Field.DAY_OF_YEAR; + field = Calendar.DAY_OF_YEAR; + break; + case DAY_OF_WEEK_IN_MONTH_FIELD: + dateFormatField = Field.DAY_OF_WEEK_IN_MONTH; + field = Calendar.DAY_OF_WEEK_IN_MONTH; + break; + case WEEK_OF_YEAR_FIELD: + dateFormatField = Field.WEEK_OF_YEAR; + field = Calendar.WEEK_OF_YEAR; + break; + case WEEK_OF_MONTH_FIELD: + dateFormatField = Field.WEEK_OF_MONTH; + field = Calendar.WEEK_OF_MONTH; + break; + case AM_PM_FIELD: + dateFormatField = Field.AM_PM; + buffer.append(formatData.ampms[calendar.get(Calendar.AM_PM)]); + break; + case HOUR1_FIELD: // h + dateFormatField = Field.HOUR1; + hour = calendar.get(Calendar.HOUR); + appendNumber(buffer, count, hour == 0 ? 12 : hour); + break; + case HOUR0_FIELD: // K + dateFormatField = Field.HOUR0; + field = Calendar.HOUR; + break; + case TIMEZONE_FIELD: // z + dateFormatField = Field.TIME_ZONE; + appendTimeZone(buffer, count, true); + break; + // BEGIN android-changed + case (TIMEZONE_FIELD + 1): // Z + dateFormatField = Field.TIME_ZONE; + appendTimeZone(buffer, count, false); + break; + // END android-changed + } + if (field != -1) { + appendNumber(buffer, count, calendar.get(field)); + } + + if (fields != null) { + position = new FieldPosition(dateFormatField); + position.setBeginIndex(beginPosition); + position.setEndIndex(buffer.length()); + fields.add(position); + } else { + // Set to the first occurrence + if ((position.getFieldAttribute() == dateFormatField || (position + .getFieldAttribute() == null && position.getField() == index)) + && position.getEndIndex() == 0) { + position.setBeginIndex(beginPosition); + position.setEndIndex(buffer.length()); + } + } + } + + private void appendTimeZone(StringBuffer buffer, int count, + boolean generalTimezone) { + // cannot call TimeZone.getDisplayName() because it would not use + // the DateFormatSymbols of this SimpleDateFormat + + if (generalTimezone) { + String id = calendar.getTimeZone().getID(); + // BEGIN android-changed + String[][] zones = formatData.internalZoneStrings(); + // END android-changed + String[] zone = null; + for (String[] element : zones) { + if (id.equals(element[0])) { + zone = element; + break; + } + } + if (zone == null) { + int offset = calendar.get(Calendar.ZONE_OFFSET) + + calendar.get(Calendar.DST_OFFSET); + char sign = '+'; + if (offset < 0) { + sign = '-'; + offset = -offset; + } + buffer.append("GMT"); //$NON-NLS-1$ + buffer.append(sign); + appendNumber(buffer, 2, offset / 3600000); + buffer.append(':'); + appendNumber(buffer, 2, (offset % 3600000) / 60000); + } else { + int daylight = calendar.get(Calendar.DST_OFFSET) == 0 ? 0 : 2; + if (count < 4) { + buffer.append(zone[2 + daylight]); + } else { + buffer.append(zone[1 + daylight]); + } + } + } else { + int offset = calendar.get(Calendar.ZONE_OFFSET) + + calendar.get(Calendar.DST_OFFSET); + char sign = '+'; + if (offset < 0) { + sign = '-'; + offset = -offset; + } + buffer.append(sign); + appendNumber(buffer, 2, offset / 3600000); + appendNumber(buffer, 2, (offset % 3600000) / 60000); + } + } + + private void appendNumber(StringBuffer buffer, int count, int value) { + int minimumIntegerDigits = numberFormat.getMinimumIntegerDigits(); + numberFormat.setMinimumIntegerDigits(count); + numberFormat.format(new Integer(value), buffer, new FieldPosition(0)); + numberFormat.setMinimumIntegerDigits(minimumIntegerDigits); + } + + // BEGIN android-added + private Date error(ParsePosition position, int offset, TimeZone zone) { + position.setErrorIndex(offset); + calendar.setTimeZone(zone); + return null; + } + // END android-added + + /** + * Formats the specified date as a string using the pattern of this date + * format and appends the string to the specified string buffer. + *

                        + * If the {@code field} member of {@code field} contains a value specifying + * a format field, then its {@code beginIndex} and {@code endIndex} members + * will be updated with the position of the first occurrence of this field + * in the formatted text. + * + * @param date + * the date to format. + * @param buffer + * the target string buffer to append the formatted date/time to. + * @param fieldPos + * on input: an optional alignment field; on output: the offsets + * of the alignment field in the formatted text. + * @return the string buffer. + * @throws IllegalArgumentException + * if there are invalid characters in the pattern. + */ + @Override + public StringBuffer format(Date date, StringBuffer buffer, + FieldPosition fieldPos) { + // BEGIN android-changed + // Harmony delegates to ICU's SimpleDateFormat, we implement it directly + return formatImpl(date, buffer, fieldPos, null); + // END android-changed + } + + // BEGIN android-removed + // private com.ibm.icu.text.DateFormat.Field toICUField( + // DateFormat.Field attribute) { + // ... + // } + // END android-removed /** - * Returns the date which is the start of the one hundred year period for + * Answers the Date which is the start of the one hundred year period for * two digits year values. * - * @return a date. - * @since Android 1.0 + * @return a Date */ public Date get2DigitYearStart() { return defaultCenturyStart; @@ -946,9 +1029,8 @@ public class SimpleDateFormat extends DateFormat { /** * Returns the {@code DateFormatSymbols} used by this simple date format. - * + * * @return the {@code DateFormatSymbols} object. - * @since Android 1.0 */ public DateFormatSymbols getDateFormatSymbols() { // Return a clone so the arrays in the ResourceBundle are not modified @@ -961,6 +1043,7 @@ public class SimpleDateFormat extends DateFormat { + creationYear; } + // BEGIN android-added private int parse(String string, int offset, char format, int count) { int index = patternChars.indexOf(format); if (index == -1) { @@ -1085,6 +1168,7 @@ public class SimpleDateFormat extends DateFormat { } return offset; } + // END android-added /** * Parses a date from the specified string starting at the index specified @@ -1092,7 +1176,7 @@ public class SimpleDateFormat extends DateFormat { * of the {@code ParsePosition} is updated to the index following the parsed * text. On error, the index is unchanged and the error index of {@code * ParsePosition} is set to the index where the error occurred. - * + * * @param string * the string to parse using the pattern of this simple date * format. @@ -1106,10 +1190,11 @@ public class SimpleDateFormat extends DateFormat { * error. * @throws IllegalArgumentException * if there are invalid characters in the pattern. - * @since Android 1.0 */ @Override public Date parse(String string, ParsePosition position) { + // BEGIN android-changed + // Harmony delegates to ICU's SimpleDateFormat, we implement it directly boolean quote = false; int next, last = -1, count = 0, offset = position.getIndex(); int length = string.length(); @@ -1178,8 +1263,10 @@ public class SimpleDateFormat extends DateFormat { position.setIndex(offset); calendar.setTimeZone(zone); return date; + // END android-changed } + // BEGIN android-added private Number parseNumber(int max, String string, ParsePosition position) { int digit, length = string.length(), result = 0; int index = position.getIndex(); @@ -1299,6 +1386,7 @@ public class SimpleDateFormat extends DateFormat { } return -offset - 1; } + // END android-added /** * Sets the date which is the start of the one hundred year period for two @@ -1306,9 +1394,11 @@ public class SimpleDateFormat extends DateFormat { * * @param date * the new date. - * @since Android 1.0 */ public void set2DigitYearStart(Date date) { + // BEGIN android-removed + // icuFormat.set2DigitYearStart(date); + // END android-removed defaultCenturyStart = date; Calendar cal = new GregorianCalendar(); cal.setTime(date); @@ -1320,9 +1410,13 @@ public class SimpleDateFormat extends DateFormat { * * @param value * the new {@code DateFormatSymbols} object. - * @since Android 1.0 */ public void setDateFormatSymbols(DateFormatSymbols value) { + // BEGIN android-removed + // com.ibm.icu.text.DateFormatSymbols icuSymbols = new com.ibm.icu.text.DateFormatSymbols(); + // copySymbols(value, icuSymbols); + // icuFormat.setDateFormatSymbols(icuSymbols); + // END android-removed formatData = (DateFormatSymbols) value.clone(); } @@ -1331,11 +1425,12 @@ public class SimpleDateFormat extends DateFormat { * characters. * * @return the localized pattern. - * @since Android 1.0 */ public String toLocalizedPattern() { + // BEGIN android-changed return convertPattern(pattern, patternChars, formatData .getLocalPatternChars(), false); + // END android-changed } /** @@ -1343,7 +1438,6 @@ public class SimpleDateFormat extends DateFormat { * pattern characters. * * @return the non-localized pattern. - * @since Android 1.0 */ public String toPattern() { return pattern; diff --git a/libcore/text/src/main/java/java/text/StringCharacterIterator.java b/libcore/text/src/main/java/java/text/StringCharacterIterator.java index 5d02cebda..8ef03415d 100644 --- a/libcore/text/src/main/java/java/text/StringCharacterIterator.java +++ b/libcore/text/src/main/java/java/text/StringCharacterIterator.java @@ -19,8 +19,6 @@ package java.text; /** * An implementation of {@link CharacterIterator} for strings. - * - * @since Android 1.0 */ public final class StringCharacterIterator implements CharacterIterator { @@ -35,7 +33,6 @@ public final class StringCharacterIterator implements CharacterIterator { * * @param value * the source string to iterate over. - * @since Android 1.0 */ public StringCharacterIterator(String value) { string = value; @@ -53,10 +50,9 @@ public final class StringCharacterIterator implements CharacterIterator { * the source string to iterate over. * @param location * the current index. - * @exception IllegalArgumentException - * if {@code location} is negative or greater than the length - * of the source string. - * @since Android 1.0 + * @throws IllegalArgumentException + * if {@code location} is negative or greater than the length + * of the source string. */ public StringCharacterIterator(String value, int location) { string = value; @@ -80,11 +76,10 @@ public final class StringCharacterIterator implements CharacterIterator { * the index one past the last character to iterate. * @param location * the current index. - * @exception IllegalArgumentException - * if {@code start < 0}, {@code start > end}, - * {@code location < start}, {@code location > end} or if - * {@code end} is greater than the length of {@code value}. - * @since Android 1.0 + * @throws IllegalArgumentException + * if {@code start < 0}, {@code start > end}, {@code location < + * start}, {@code location > end} or if {@code end} is greater + * than the length of {@code value}. */ public StringCharacterIterator(String value, int start, int end, int location) { @@ -104,7 +99,6 @@ public final class StringCharacterIterator implements CharacterIterator { * * @return a shallow copy of this iterator. * @see java.lang.Cloneable - * @since Android 1.0 */ @Override public Object clone() { @@ -120,7 +114,6 @@ public final class StringCharacterIterator implements CharacterIterator { * * @return the current character, or {@code DONE} if the current index is * past the end. - * @since Android 1.0 */ public char current() { if (offset == end) { @@ -140,7 +133,6 @@ public final class StringCharacterIterator implements CharacterIterator { * @return {@code true} if the specified object is equal to this * {@code StringCharacterIterator}; {@code false} otherwise. * @see #hashCode - * @since Android 1.0 */ @Override public boolean equals(Object object) { @@ -158,7 +150,6 @@ public final class StringCharacterIterator implements CharacterIterator { * * @return the character at the begin index or {@code DONE} if the begin * index is equal to the end index. - * @since Android 1.0 */ public char first() { if (start == end) { @@ -172,7 +163,6 @@ public final class StringCharacterIterator implements CharacterIterator { * Returns the begin index in the source string. * * @return the index of the first character of the iteration. - * @since Android 1.0 */ public int getBeginIndex() { return start; @@ -182,7 +172,6 @@ public final class StringCharacterIterator implements CharacterIterator { * Returns the end index in the source string. * * @return the index one past the last character of the iteration. - * @since Android 1.0 */ public int getEndIndex() { return end; @@ -192,7 +181,6 @@ public final class StringCharacterIterator implements CharacterIterator { * Returns the current index in the source string. * * @return the current index. - * @since Android 1.0 */ public int getIndex() { return offset; @@ -209,9 +197,8 @@ public final class StringCharacterIterator implements CharacterIterator { * * @return the character before the end index or {@code DONE} if the begin * index is equal to the end index. - * @since Android 1.0 */ - public char last() { + public char last() { if (start == end) { return DONE; } @@ -219,14 +206,13 @@ public final class StringCharacterIterator implements CharacterIterator { return string.charAt(offset); } - /** - * Increments the current index and returns the character at the new index. - * - * @return the character at the next index, or {@code DONE} if the next - * index would be past the end. - * @since Android 1.0 - */ - public char next() { + /** + * Increments the current index and returns the character at the new index. + * + * @return the character at the next index, or {@code DONE} if the next + * index would be past the end. + */ + public char next() { if (offset >= (end - 1)) { offset = end; return DONE; @@ -234,13 +220,12 @@ public final class StringCharacterIterator implements CharacterIterator { return string.charAt(++offset); } - /** - * Decrements the current index and returns the character at the new index. - * - * @return the character at the previous index, or {@code DONE} if the - * previous index would be past the beginning. - * @since Android 1.0 - */ + /** + * Decrements the current index and returns the character at the new index. + * + * @return the character at the previous index, or {@code DONE} if the + * previous index would be past the beginning. + */ public char previous() { if (offset == start) { return DONE; @@ -255,10 +240,9 @@ public final class StringCharacterIterator implements CharacterIterator { * the index the current position is set to. * @return the character at the new index, or {@code DONE} if * {@code location} is set to the end index. - * @exception IllegalArgumentException - * if {@code location} is smaller than the begin index or - * greater than the end index. - * @since Android 1.0 + * @throws IllegalArgumentException + * if {@code location} is smaller than the begin index or greater + * than the end index. */ public char setIndex(int location) { if (location < start || location > end) { @@ -277,7 +261,6 @@ public final class StringCharacterIterator implements CharacterIterator { * * @param value * the new source string. - * @since Android 1.0 */ public void setText(String value) { string = value; diff --git a/libcore/text/src/main/java/org/apache/harmony/text/BidiWrapper.java b/libcore/text/src/main/java/org/apache/harmony/text/BidiWrapper.java index 240fcdfa5..f9dc8da4b 100644 --- a/libcore/text/src/main/java/org/apache/harmony/text/BidiWrapper.java +++ b/libcore/text/src/main/java/org/apache/harmony/text/BidiWrapper.java @@ -18,7 +18,8 @@ package org.apache.harmony.text; /** - * TODO: type description + * Dalvik Bidi wrapper. Derived from an old version of Harmony; today they call + * straight through to ICU4J. */ public final class BidiWrapper { diff --git a/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/BidiTest.java b/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/BidiTest.java index e3ea9dc66..deef841c6 100644 --- a/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/BidiTest.java +++ b/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/BidiTest.java @@ -1757,8 +1757,13 @@ public class BidiTest extends TestCase { // Expected } -// Outsourced to _AndroidFailure: -// bidi.createLineBidi(2, 2); + // BEGIN android-removed + // Outsourced to _AndroidFailure: + // try { + // bidi.createLineBidi(2, 2); + // } catch (IllegalArgumentException expected) { + // } + // END android-removed try { bidi.createLineBidi(2, 4); @@ -1974,19 +1979,23 @@ public class BidiTest extends TestCase { @TestTargetNew( level = TestLevel.PARTIAL_COMPLETE, - notes = "Doesn't verify any int value between 0 and getRunCount().", + notes = "", method = "getRunLimit", args = {int.class} ) + @KnownFailure("Doesn't verify any int value between 0 and getRunCount().") public void testGetRunLimit() { bd = new Bidi("text", Bidi.DIRECTION_LEFT_TO_RIGHT); try { assertTrue(4 == bd.getRunLimit(-1)); - } catch (Exception e) { - fail("Unexpected exception: " + e); + } catch (IllegalArgumentException e) { + // Expected for illegal run limit + return; } + + fail("Expected IllegalArgumentException to be thrown for invalid run limit"); } - + @TestTargetNew( level = TestLevel.COMPLETE, notes = "", @@ -2052,13 +2061,17 @@ public class BidiTest extends TestCase { method = "Bidi", args = {java.text.AttributedCharacterIterator.class} ) + @KnownFailure("Doesn't verify any int value between 0 and getRunCount().") public void testBidiConstructor_Iterator() { AttributedString paragraph = new AttributedString("text"); bd = new Bidi(paragraph.getIterator()); try { assertTrue(4 == bd.getRunLimit(1)); - } catch (Exception e) { - fail("Unexpected exception: " + e); + } catch (IllegalArgumentException e) { + // Expected for illegal run limit + return; } + + fail("Expected IllegalArgumentException to be thrown for invalid run limit"); } } diff --git a/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/DataFormatFieldTest.java b/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/DataFormatFieldTest.java index ff05e6422..ff6dd8ed7 100644 --- a/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/DataFormatFieldTest.java +++ b/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/DataFormatFieldTest.java @@ -207,7 +207,6 @@ public class DataFormatFieldTest extends TestCase { method = "readResolve", args = {} ) - @KnownFailure("readResolve does not work properly") public void test_readResolve() { // test for method java.lang.Object readResolve() diff --git a/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/DateFormatSymbolsTest.java b/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/DateFormatSymbolsTest.java index 4151dc890..a0d9615fc 100644 --- a/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/DateFormatSymbolsTest.java +++ b/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/DateFormatSymbolsTest.java @@ -16,11 +16,14 @@ */ package org.apache.harmony.text.tests.java.text; -import dalvik.annotation.TestTargets; import dalvik.annotation.TestLevel; -import dalvik.annotation.TestTargetNew; import dalvik.annotation.TestTargetClass; +import dalvik.annotation.TestTargetNew; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.text.DateFormatSymbols; import java.util.Arrays; import java.util.Locale; @@ -42,11 +45,7 @@ public class DateFormatSymbolsTest extends junit.framework.TestCase { public void test_Constructor() { // Test for method java.text.DateFormatSymbols() // Used in tests - try { - new DateFormatSymbols(); - } catch (Exception e) { - fail("Constructor failed."); - } + new DateFormatSymbols(); } /** @@ -60,11 +59,7 @@ public class DateFormatSymbolsTest extends junit.framework.TestCase { ) public void test_ConstructorLjava_util_Locale() { // Test for method java.text.DateFormatSymbols(java.util.Locale) - try { - new DateFormatSymbols(new Locale("en", "us")); - } catch (Exception e) { - fail("Constructor failed."); - } + new DateFormatSymbols(new Locale("en", "us")); } /** @@ -156,8 +151,10 @@ public class DateFormatSymbolsTest extends junit.framework.TestCase { String retVal = dfs.getLocalPatternChars(); String val = "GyMdkHmsSEDFwWahKzZ"; + // Harmony uses a different set of pattern chars + // String val = "GyMdkHmsSEDFwWahKzYeugAZvcLQqV"; - assertTrue("Returned incorrect pattern string", retVal.equals(val)); + assertEquals("Returned incorrect pattern string", val, retVal); } /** @@ -175,9 +172,9 @@ public class DateFormatSymbolsTest extends junit.framework.TestCase { String[] retVal = dfs.getMonths(); String[] val = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", - "December", "" }; - if (retVal.length != val.length) - fail("Returned wrong array: " + retVal.length); + "December", ""}; + // Note: Harmony doesn't include "" at the end of the array + assertEquals("Returned wrong array: ", val.length, retVal.length); for (int i = 0; i < val.length; i++) assertTrue("Array values do not match", retVal[i].equals(val[i])); } @@ -196,9 +193,9 @@ public class DateFormatSymbolsTest extends junit.framework.TestCase { // java.text.DateFormatSymbols.getShortMonths() String[] retVal = dfs.getShortMonths(); String[] val = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", - "Aug", "Sep", "Oct", "Nov", "Dec", "" }; - if (retVal.length != val.length) - fail("Returned wrong array"); + "Aug", "Sep", "Oct", "Nov", "Dec", ""}; + // Note: Harmony doesn't include "" at the end of the array + assertEquals("Returned wrong array: ", val.length, retVal.length); for (int i = 0; i < val.length; i++) assertTrue("Array values do not match", retVal[i].equals(val[i])); } @@ -482,4 +479,32 @@ public class DateFormatSymbolsTest extends junit.framework.TestCase { */ protected void tearDown() { } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Checks serialization mechanism.", + method = "!SerializationSelf", + args = {} + ) + public void test_serialization() throws Exception { + DateFormatSymbols symbols = new DateFormatSymbols(Locale.FRANCE); + String[][] zoneStrings = symbols.getZoneStrings(); + assertNotNull(zoneStrings); + + // serialize + ByteArrayOutputStream byteOStream = new ByteArrayOutputStream(); + ObjectOutputStream objectOStream = new ObjectOutputStream(byteOStream); + objectOStream.writeObject(symbols); + + // and deserialize + ObjectInputStream objectIStream = new ObjectInputStream( + new ByteArrayInputStream(byteOStream.toByteArray())); + DateFormatSymbols symbolsD = (DateFormatSymbols) objectIStream + .readObject(); + + // The associated currency will not persist + String[][] zoneStringsD = symbolsD.getZoneStrings(); + assertNotNull(zoneStringsD); + assertEquals(symbols, symbolsD); + } } diff --git a/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/DateFormatTest.java b/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/DateFormatTest.java index e70aaf210..e7609a83c 100644 --- a/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/DateFormatTest.java +++ b/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/DateFormatTest.java @@ -1079,4 +1079,43 @@ public class DateFormatTest extends junit.framework.TestCase { fail("Unexpected exception " + e.toString()); } } + + /** + * @tests java.text.DateFormat#parse(String) + */ + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "", + method = "parse", + args = {java.lang.String.class} + ) + public void test_parse_LString() { + DateFormat format = DateFormat.getInstance(); + try { + format.parse("not a Date"); + fail("should throw ParseException first"); + } catch (ParseException e) { + assertNotNull(e.getMessage()); + } + } + + /** + * @tests java.text.DateFormat#setLenient(boolean) + */ + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "", + method = "setLenient", + args = {boolean.class} + ) + public void test_setLenient() { + Date d = null; + DateFormat output = new SimpleDateFormat("MM/dd/yy"); + output.setLenient(false); + try { + d = output.parse("01/01/-1"); + fail("Should throw ParseException here."); + } catch (ParseException e) {} + } + } diff --git a/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/DecimalFormatSymbolsTest.java b/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/DecimalFormatSymbolsTest.java index ce2e6d505..47107cd62 100644 --- a/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/DecimalFormatSymbolsTest.java +++ b/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/DecimalFormatSymbolsTest.java @@ -149,8 +149,8 @@ public class DecimalFormatSymbolsTest extends TestCase { @KnownFailure("some locales were removed last minute in cupcake") public void test_getCurrency() { Currency currency = Currency.getInstance("USD"); - assertTrue("Returned incorrect currency", - dfsUS.getCurrency() == currency); + assertEquals("Returned incorrect currency", + dfsUS.getCurrency(), currency); // use cs_CZ instead //Currency currK = Currency.getInstance("KRW"); @@ -446,8 +446,8 @@ public class DecimalFormatSymbolsTest extends TestCase { dfs.setCurrency(currency); assertTrue("Returned incorrect currency", currency == dfs.getCurrency()); - assertTrue("Returned incorrect currency symbol", currency.getSymbol( - locale).equals(dfs.getCurrencySymbol())); + assertEquals("Returned incorrect currency symbol", currency.getSymbol( + locale), dfs.getCurrencySymbol()); assertTrue("Returned incorrect international currency symbol", currency .getCurrencyCode().equals(dfs.getInternationalCurrencySymbol())); } @@ -551,8 +551,8 @@ public class DecimalFormatSymbolsTest extends TestCase { assertTrue("Test1: Returned incorrect currency", currency == dfs .getCurrency()); - assertTrue("Test1: Returned incorrect currency symbol", currency - .getSymbol(locale).equals(dfs.getCurrencySymbol())); + assertEquals("Test1: Returned incorrect currency symbol", currency + .getSymbol(locale), dfs.getCurrencySymbol()); assertTrue("Test1: Returned incorrect international currency symbol", currency.getCurrencyCode().equals( dfs.getInternationalCurrencySymbol())); @@ -754,8 +754,43 @@ public class DecimalFormatSymbolsTest extends TestCase { i.close(); } } catch (Exception e) { - // ignore } } + assertDecimalFormatSymbolsRIFrance(dfs); + } + + static void assertDecimalFormatSymbolsRIFrance(DecimalFormatSymbols dfs) { + // Values based on Java 1.5 RI DecimalFormatSymbols for Locale.FRANCE + /* + * currency = [EUR] + * currencySymbol = [€][U+20ac] + * decimalSeparator = [,][U+002c] + * digit = [#][U+0023] + * groupingSeparator = [ ][U+00a0] + * infinity = [∞][U+221e] + * internationalCurrencySymbol = [EUR] + * minusSign = [-][U+002d] + * monetaryDecimalSeparator = [,][U+002c] + * naN = [�][U+fffd] + * patternSeparator = [;][U+003b] + * perMill = [‰][U+2030] + * percent = [%][U+0025] + * zeroDigit = [0][U+0030] + */ + assertEquals("EUR", dfs.getCurrency().getCurrencyCode()); + assertEquals("\u20AC", dfs.getCurrencySymbol()); + assertEquals(',', dfs.getDecimalSeparator()); + assertEquals('#', dfs.getDigit()); + assertEquals('\u00a0', dfs.getGroupingSeparator()); + assertEquals("\u221e", dfs.getInfinity()); + assertEquals("EUR", dfs.getInternationalCurrencySymbol()); + assertEquals('-', dfs.getMinusSign()); + assertEquals(',', dfs.getMonetaryDecimalSeparator()); + // RI's default NaN is U+FFFD, Harmony's is based on ICU + assertEquals("\uFFFD", dfs.getNaN()); + assertEquals('\u003b', dfs.getPatternSeparator()); + assertEquals('\u2030', dfs.getPerMill()); + assertEquals('%', dfs.getPercent()); + assertEquals('0', dfs.getZeroDigit()); } } diff --git a/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/DecimalFormatTest.java b/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/DecimalFormatTest.java index a802ad89a..aa36abce6 100644 --- a/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/DecimalFormatTest.java +++ b/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/DecimalFormatTest.java @@ -31,6 +31,7 @@ import tests.support.Support_BitSet; import tests.support.Support_DecimalFormat; import java.io.ObjectInputStream; +import java.io.IOException; import java.math.BigDecimal; import java.math.BigInteger; import java.text.AttributedCharacterIterator; @@ -40,6 +41,7 @@ import java.text.FieldPosition; import java.text.NumberFormat; import java.text.ParseException; import java.text.ParsePosition; +import java.util.BitSet; import java.util.Currency; import java.util.Locale; @@ -1495,7 +1497,7 @@ public class DecimalFormatTest extends TestCase { .t_format_with_FieldPosition(); int failCount = 0; - Support_BitSet failures = new Support_BitSet(); + BitSet failures = new BitSet(); final DecimalFormatSymbols dfs = new DecimalFormatSymbols(Locale.US); @@ -1729,7 +1731,7 @@ public class DecimalFormatTest extends TestCase { @KnownFailure("Something seems wrong with Android implementation, here!") public void test_formatJLjava_lang_StringBufferLjava_text_FieldPosition() { int failCount = 0; - Support_BitSet failures = new Support_BitSet(); + BitSet failures = new BitSet(); final DecimalFormatSymbols dfs = new DecimalFormatSymbols(Locale.US); @@ -2437,7 +2439,7 @@ public class DecimalFormatTest extends TestCase { args = {} ) @KnownFailure("a regression. This didn't fail before") - public void test_serializationHarmonyRICompatible() { + public void test_serializationHarmonyRICompatible() throws Exception { NumberFormat nf = NumberFormat.getInstance(Locale.FRANCE); DecimalFormat df = null; @@ -2455,8 +2457,6 @@ public class DecimalFormatTest extends TestCase { oinput = new ObjectInputStream(this.getClass().getResource( "/serialization/java/text/DecimalFormat.ser").openStream()); deserializedDF = (DecimalFormat) oinput.readObject(); - } catch (Exception e) { - fail("Error occurs during deserialization"); } finally { try { if (null != oinput) { @@ -2473,8 +2473,7 @@ public class DecimalFormatTest extends TestCase { assertEquals(df.getPositiveSuffix(), deserializedDF.getPositiveSuffix()); assertEquals(df.getCurrency(), deserializedDF.getCurrency()); - assertEquals(df.getDecimalFormatSymbols(), deserializedDF - .getDecimalFormatSymbols()); + DecimalFormatSymbolsTest.assertDecimalFormatSymbolsRIFrance(deserializedDF.getDecimalFormatSymbols()); assertEquals(df.getGroupingSize(), df.getGroupingSize()); assertEquals(df.getMaximumFractionDigits(), deserializedDF @@ -2545,11 +2544,7 @@ public class DecimalFormatTest extends TestCase { ) public void testSetDecimalFormatSymbolsAsNull() { // Regression for HARMONY-1070 - try { - DecimalFormat format = (DecimalFormat) DecimalFormat.getInstance(); - format.setDecimalFormatSymbols(null); - } catch (Exception e) { - fail("Unexpected exception caught: " + e); - } + DecimalFormat format = (DecimalFormat) DecimalFormat.getInstance(); + format.setDecimalFormatSymbols(null); } } diff --git a/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/MessageFormatTest.java b/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/MessageFormatTest.java index 0265a510b..57a120694 100644 --- a/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/MessageFormatTest.java +++ b/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/MessageFormatTest.java @@ -145,22 +145,15 @@ public class MessageFormatTest extends TestCase { assertEquals("Simple string", "Test message", new MessageFormat( "Test message").format(new Object[0])); - try { - result = new MessageFormat("Don't").format(new Object[0]); - assertTrue("Should not throw IllegalArgumentException: " + result, + result = new MessageFormat("Don't").format(new Object[0]); + assertTrue("Should not throw IllegalArgumentException: " + result, "Dont".equals(result)); - } catch (Exception e) { - fail("Unexpected exception: " + e); - } try { new MessageFormat("Invalid {1,foobar} format descriptor!"); fail("Expected test_ConstructorLjava_lang_String to throw IAE."); } catch (IllegalArgumentException ex) { // expected - } catch (Throwable ex) { - fail("Expected test_ConstructorLjava_lang_String to throw IAE, not a " - + ex.getClass().getName()); } try { @@ -168,9 +161,6 @@ public class MessageFormatTest extends TestCase { "Invalid {1,date,invalid-spec} format descriptor!"); } catch (IllegalArgumentException ex) { // expected - } catch (Throwable ex) { - fail("Expected test_ConstructorLjava_lang_String to throw IAE, not a " - + ex.getClass().getName()); } checkSerialization(new MessageFormat("")); @@ -1177,10 +1167,10 @@ public class MessageFormatTest extends TestCase { * java.text.MessageFormat#parse(java.lang.String). */ @TestTargetNew( - level = TestLevel.COMPLETE, - notes = "", - method = "parse", - args = {java.lang.String.class} + level = TestLevel.COMPLETE, + notes = "", + method = "parse", + args = {java.lang.String.class} ) public void test_parseLjava_lang_String() throws ParseException { String pattern = "A {3, number, currency} B {2, time} C {0, number, percent} D {4} E {1,choice,0#off|1#on} F {0, date}"; @@ -1242,10 +1232,10 @@ public class MessageFormatTest extends TestCase { * argument ParsePosition as null. */ @TestTargetNew( - level = TestLevel.COMPLETE, - notes = "", - method = "parseObject", - args = {java.lang.String.class, java.text.ParsePosition.class} + level = TestLevel.COMPLETE, + notes = "", + method = "parseObject", + args = {java.lang.String.class, java.text.ParsePosition.class} ) public void test_parseObjectLjava_lang_StringLjavajava_text_ParsePosition() { MessageFormat mf = new MessageFormat("{0,number,#.##}, {0,number,#.#}"); @@ -1288,10 +1278,10 @@ public class MessageFormatTest extends TestCase { } } @TestTargetNew( - level = TestLevel.PARTIAL, - notes = "Regression test. Doesn't verifies exception.", - method = "format", - args = {java.lang.Object.class} + level = TestLevel.PARTIAL, + notes = "Regression test. Doesn't verifies exception.", + method = "format", + args = {java.lang.Object.class} ) public void test_format_Object() { // Regression for HARMONY-1875 @@ -1304,4 +1294,35 @@ public class MessageFormatTest extends TestCase { assertEquals(etalon, obj.format(new Object[] { new Date() })); } + /** + * @tests java.text.MessageFormat#parse(java.lang.String) + */ + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "", + method = "parse", + args = {java.lang.String.class} + ) + public void test_parse() throws ParseException { + // Regression for HARMONY-63 + MessageFormat mf = new MessageFormat("{0,number,#,####}", Locale.US); + Object[] res = mf.parse("1,00,00"); + assertEquals("Assert 0: incorrect size of parsed data ", 1, res.length); + assertEquals("Assert 1: parsed value incorrectly", new Long(10000), (Long)res[0]); + } + + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "", + method = "format", + args = {java.lang.String.class, java.lang.Object[].class} + ) + public void testHARMONY5323() { + Object []messageArgs = new Object[11]; + for (int i = 0; i < messageArgs.length; i++) + messageArgs[i] = "dumb"+i; + + String res = MessageFormat.format("bgcolor=\"{10}\"", messageArgs); + assertEquals(res, "bgcolor=\"dumb10\""); + } } diff --git a/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/NumberFormatTest.java b/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/NumberFormatTest.java index 93e58db02..0480704d9 100644 --- a/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/NumberFormatTest.java +++ b/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/NumberFormatTest.java @@ -206,7 +206,7 @@ public class NumberFormatTest extends TestCase { "#,##0;#,##0-", format.toPattern()); assertEquals( "Test8: NumberFormat.getIntegerInstance(new Locale(\"ar\", \"AE\")).format(-35.76) returned wrong value", - "36-", format.format(-35.76)); + "\u0666-", format.format(-6)); assertEquals( "Test9: NumberFormat.getIntegerInstance(new Locale(\"ar\", \"AE\")).parse(\"-36-\") returned wrong number", new Long(-36), format.parse("36-")); diff --git a/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/ParsePositionTest.java b/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/ParsePositionTest.java index 9c1fb212f..89cb7e404 100644 --- a/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/ParsePositionTest.java +++ b/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/ParsePositionTest.java @@ -39,15 +39,10 @@ public class ParsePositionTest extends junit.framework.TestCase { ) public void test_ConstructorI() { // Test for method java.text.ParsePosition(int) - try { - ParsePosition pp1 = new ParsePosition(Integer.MIN_VALUE); - assertTrue("Initialization failed.", - pp1.getIndex() == Integer.MIN_VALUE); - assertEquals("Initialization failed.", -1, pp1.getErrorIndex()); - } catch (Exception e) { - fail("Constructor failed."); - } - + ParsePosition pp1 = new ParsePosition(Integer.MIN_VALUE); + assertTrue("Initialization failed.", + pp1.getIndex() == Integer.MIN_VALUE); + assertEquals("Initialization failed.", -1, pp1.getErrorIndex()); } /** diff --git a/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/Support_DecimalFormat.java b/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/Support_DecimalFormat.java new file mode 100644 index 000000000..2fe110757 --- /dev/null +++ b/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/Support_DecimalFormat.java @@ -0,0 +1,287 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.text.tests.java.text; + +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.util.Locale; +import java.util.Vector; + + +public class Support_DecimalFormat extends Support_Format { + + public Support_DecimalFormat(String p1) { + super(p1); + } + + @Override + public void runTest() { + t_formatToCharacterIterator(); + t_format_with_FieldPosition(); + } + + public static void main(String[] args) { + new Support_DecimalFormat("").runTest(); + } + + public void t_format_with_FieldPosition() { + DecimalFormat format = (DecimalFormat) NumberFormat + .getCurrencyInstance(Locale.US); + Number number = new Double(10000000.76); + String text = "$10,000,000.76"; + + t_FormatWithField(0, format, number, text, NumberFormat.Field.CURRENCY, + 0, 1); + t_FormatWithField(1, format, number, text, NumberFormat.Field.INTEGER, + 1, 11); + t_FormatWithField(2, format, number, text, + NumberFormat.Field.GROUPING_SEPARATOR, 3, 4); + t_FormatWithField(3, format, number, text, + NumberFormat.Field.DECIMAL_SEPARATOR, 11, 12); + t_FormatWithField(4, format, number, text, NumberFormat.Field.FRACTION, + 12, 14); + + // test fields that are not included in the formatted text + t_FormatWithField(5, format, number, text, NumberFormat.Field.SIGN, 0, + 0); + t_FormatWithField(6, format, number, text, NumberFormat.Field.EXPONENT, + 0, 0); + t_FormatWithField(7, format, number, text, + NumberFormat.Field.EXPONENT_SIGN, 0, 0); + t_FormatWithField(8, format, number, text, + NumberFormat.Field.EXPONENT_SYMBOL, 0, 0); + t_FormatWithField(9, format, number, text, NumberFormat.Field.PERCENT, + 0, 0); + t_FormatWithField(10, format, number, text, + NumberFormat.Field.PERMILLE, 0, 0); + + // test Exponential + format = new DecimalFormat("000000000.0#E0"); + text = "100000007.6E-1"; + t_FormatWithField(11, format, number, text, NumberFormat.Field.INTEGER, + 0, 9); + t_FormatWithField(12, format, number, text, + NumberFormat.Field.DECIMAL_SEPARATOR, 9, 10); + t_FormatWithField(13, format, number, text, + NumberFormat.Field.FRACTION, 10, 11); + t_FormatWithField(14, format, number, text, + NumberFormat.Field.EXPONENT_SYMBOL, 11, 12); + t_FormatWithField(15, format, number, text, + NumberFormat.Field.EXPONENT_SIGN, 12, 13); + t_FormatWithField(16, format, number, text, + NumberFormat.Field.EXPONENT, 13, 14); + + // test fields that are not included in the formatted text + t_FormatWithField(17, format, number, text, + NumberFormat.Field.GROUPING_SEPARATOR, 0, 0); + t_FormatWithField(18, format, number, text, NumberFormat.Field.SIGN, 0, + 0); + t_FormatWithField(19, format, number, text, + NumberFormat.Field.CURRENCY, 0, 0); + t_FormatWithField(20, format, number, text, NumberFormat.Field.PERCENT, + 0, 0); + t_FormatWithField(21, format, number, text, + NumberFormat.Field.PERMILLE, 0, 0); + + // test currency instance with TR Locale + number = new Double(350.76); + format = (DecimalFormat) NumberFormat.getCurrencyInstance(new Locale( + "tr", "TR")); + text = "351 TL"; + t_FormatWithField(22, format, number, text, NumberFormat.Field.INTEGER, + 0, 3); + t_FormatWithField(23, format, number, text, + NumberFormat.Field.CURRENCY, 4, 6); + + // test fields that are not included in the formatted text + t_FormatWithField(25, format, number, text, + NumberFormat.Field.GROUPING_SEPARATOR, 0, 0); + t_FormatWithField(26, format, number, text, + NumberFormat.Field.DECIMAL_SEPARATOR, 0, 0); + t_FormatWithField(27, format, number, text, NumberFormat.Field.SIGN, 0, + 0); + t_FormatWithField(28, format, number, text, + NumberFormat.Field.EXPONENT, 0, 0); + t_FormatWithField(29, format, number, text, + NumberFormat.Field.EXPONENT_SIGN, 0, 0); + t_FormatWithField(30, format, number, text, + NumberFormat.Field.EXPONENT_SYMBOL, 0, 0); + t_FormatWithField(31, format, number, text, NumberFormat.Field.PERCENT, + 0, 0); + t_FormatWithField(32, format, number, text, + NumberFormat.Field.PERMILLE, 0, 0); + + } + + public void t_formatToCharacterIterator() { + + Number number = new Double(350.76); + Number negativeNumber = new Double(-350.76); + + Locale us = Locale.US; + Locale tr = new Locale("tr", "TR"); + + // test number instance + t_Format(1, number, NumberFormat.getNumberInstance(us), + getNumberVectorUS()); + + // test integer instance + // testFormat(2, number, NumberFormat.getIntegerInstance(us), + // getPercentVectorUS()); + + // test percent instance + t_Format(3, number, NumberFormat.getPercentInstance(us), + getPercentVectorUS()); + + // test permille pattern + DecimalFormat format = new DecimalFormat("###0.##\u2030"); + t_Format(4, number, format, getPermilleVector()); + + // test exponential pattern with positive exponent + format = new DecimalFormat("00.0#E0"); + t_Format(5, number, format, getPositiveExponentVector()); + + // test exponential pattern with negative exponent + format = new DecimalFormat("0000.0#E0"); + t_Format(6, number, format, getNegativeExponentVector()); + + // test currency instance with US Locale + t_Format(7, number, NumberFormat.getCurrencyInstance(us), + getPositiveCurrencyVectorUS()); + + // test negative currency instance with US Locale + t_Format(8, negativeNumber, NumberFormat.getCurrencyInstance(us), + getNegativeCurrencyVectorUS()); + + // test currency instance with TR Locale + t_Format(9, number, NumberFormat.getCurrencyInstance(tr), + getPositiveCurrencyVectorTR()); + + // test negative currency instance with TR Locale + t_Format(10, negativeNumber, NumberFormat.getCurrencyInstance(tr), + getNegativeCurrencyVectorTR()); + + // test multiple grouping seperators + number = new Long(100300400); + t_Format(11, number, NumberFormat.getNumberInstance(us), + getNumberVector2US()); + + // test 0 + number = new Long(0); + t_Format(12, number, NumberFormat.getNumberInstance(us), + getZeroVector()); + } + + private static Vector getNumberVectorUS() { + Vector v = new Vector(); + v.add(new FieldContainer(0, 3, NumberFormat.Field.INTEGER)); + v.add(new FieldContainer(3, 4, NumberFormat.Field.DECIMAL_SEPARATOR)); + v.add(new FieldContainer(4, 6, NumberFormat.Field.FRACTION)); + return v; + } + + private static Vector getPositiveCurrencyVectorTR() { + Vector v = new Vector(); + v.add(new FieldContainer(0, 3, NumberFormat.Field.INTEGER)); + v.add(new FieldContainer(4, 6, NumberFormat.Field.CURRENCY)); + return v; + } + + private static Vector getNegativeCurrencyVectorTR() { + Vector v = new Vector(); + v.add(new FieldContainer(0, 1, NumberFormat.Field.SIGN)); + v.add(new FieldContainer(1, 4, NumberFormat.Field.INTEGER)); + v.add(new FieldContainer(5, 7, NumberFormat.Field.CURRENCY)); + return v; + } + + private static Vector getPositiveCurrencyVectorUS() { + Vector v = new Vector(); + v.add(new FieldContainer(0, 1, NumberFormat.Field.CURRENCY)); + v.add(new FieldContainer(1, 4, NumberFormat.Field.INTEGER)); + v.add(new FieldContainer(4, 5, NumberFormat.Field.DECIMAL_SEPARATOR)); + v.add(new FieldContainer(5, 7, NumberFormat.Field.FRACTION)); + return v; + } + + private static Vector getNegativeCurrencyVectorUS() { + Vector v = new Vector(); + v.add(new FieldContainer(1, 2, NumberFormat.Field.CURRENCY)); + v.add(new FieldContainer(2, 5, NumberFormat.Field.INTEGER)); + v.add(new FieldContainer(5, 6, NumberFormat.Field.DECIMAL_SEPARATOR)); + v.add(new FieldContainer(6, 8, NumberFormat.Field.FRACTION)); + return v; + } + + private static Vector getPercentVectorUS() { + Vector v = new Vector(); + v.add(new FieldContainer(0, 2, NumberFormat.Field.INTEGER)); + v.add(new FieldContainer(2, 3, NumberFormat.Field.INTEGER)); + v.add(new FieldContainer(2, 3, NumberFormat.Field.GROUPING_SEPARATOR)); + v.add(new FieldContainer(3, 6, NumberFormat.Field.INTEGER)); + v.add(new FieldContainer(6, 7, NumberFormat.Field.PERCENT)); + return v; + } + + private static Vector getPermilleVector() { + Vector v = new Vector(); + v.add(new FieldContainer(0, 6, NumberFormat.Field.INTEGER)); + v.add(new FieldContainer(6, 7, NumberFormat.Field.PERMILLE)); + return v; + } + + private static Vector getNegativeExponentVector() { + Vector v = new Vector(); + v.add(new FieldContainer(0, 4, NumberFormat.Field.INTEGER)); + v.add(new FieldContainer(4, 5, NumberFormat.Field.DECIMAL_SEPARATOR)); + v.add(new FieldContainer(5, 6, NumberFormat.Field.FRACTION)); + v.add(new FieldContainer(6, 7, NumberFormat.Field.EXPONENT_SYMBOL)); + v.add(new FieldContainer(7, 8, NumberFormat.Field.EXPONENT_SIGN)); + v.add(new FieldContainer(8, 9, NumberFormat.Field.EXPONENT)); + return v; + } + + private static Vector getPositiveExponentVector() { + Vector v = new Vector(); + v.add(new FieldContainer(0, 2, NumberFormat.Field.INTEGER)); + v.add(new FieldContainer(2, 3, NumberFormat.Field.DECIMAL_SEPARATOR)); + v.add(new FieldContainer(3, 5, NumberFormat.Field.FRACTION)); + v.add(new FieldContainer(5, 6, NumberFormat.Field.EXPONENT_SYMBOL)); + v.add(new FieldContainer(6, 7, NumberFormat.Field.EXPONENT)); + return v; + } + + private static Vector getNumberVector2US() { + Vector v = new Vector(); + v.add(new FieldContainer(0, 3, NumberFormat.Field.INTEGER)); + v.add(new FieldContainer(3, 4, NumberFormat.Field.GROUPING_SEPARATOR)); + v.add(new FieldContainer(3, 4, NumberFormat.Field.INTEGER)); + v.add(new FieldContainer(4, 7, NumberFormat.Field.INTEGER)); + v.add(new FieldContainer(7, 8, NumberFormat.Field.GROUPING_SEPARATOR)); + v.add(new FieldContainer(7, 8, NumberFormat.Field.INTEGER)); + v.add(new FieldContainer(8, 11, NumberFormat.Field.INTEGER)); + return v; + } + + private static Vector getZeroVector() { + Vector v = new Vector(); + v.add(new FieldContainer(0, 1, NumberFormat.Field.INTEGER)); + return v; + } + +} diff --git a/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/Support_Format.java b/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/Support_Format.java new file mode 100644 index 000000000..ffcdf1cd7 --- /dev/null +++ b/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/Support_Format.java @@ -0,0 +1,143 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.text.tests.java.text; + +import java.text.AttributedCharacterIterator; +import java.text.FieldPosition; +import java.text.Format; +import java.text.AttributedCharacterIterator.Attribute; +import java.util.Iterator; +import java.util.Vector; +import junit.framework.TestCase; + +public class Support_Format extends TestCase { + + protected String text; + + public Support_Format(String p1) { + super(p1); + } + + protected void t_FormatWithField(int count, Format format, Object object, + String text, Format.Field field, int begin, int end) { + StringBuffer buffer = new StringBuffer(); + FieldPosition pos = new FieldPosition(field); + format.format(object, buffer, pos); + + // System.out.println(buffer); + // System.out.println(pos); + + if (text == null) { + assertEquals("Test " + count + ": incorrect formatted text", + this.text, buffer.toString()); + } else { + assertEquals("Test " + count + ": incorrect formatted text", text, + buffer.toString()); + } + + assertEquals("Test " + count + ": incorrect begin index for field " + + field, begin, pos.getBeginIndex()); + assertEquals("Test " + count + ": incorrect end index for field" + + field, end, pos.getEndIndex()); + } + + protected void t_Format(int count, Object object, Format format, + Vector expectedResults) { + // System.out.println(format.format(object)); + Vector results = findFields(format.formatToCharacterIterator(object)); + assertTrue("Test " + count + + ": Format returned incorrect CharacterIterator for " + + format.format(object), compare(results, expectedResults)); + } + + /** + * compares two vectors regardless of the order of their elements + */ + protected static boolean compare(Vector vector1, Vector vector2) { + return vector1.size() == vector2.size() && vector1.containsAll(vector2); + } + + /** + * finds attributes with regards to char index in this + * AttributedCharacterIterator, and puts them in a vector + * + * @param iterator + * @return a vector, each entry in this vector are of type FieldContainer , + * which stores start and end indexes and an attribute this range + * has + */ + protected static Vector findFields(AttributedCharacterIterator iterator) { + Vector result = new Vector(); + while (iterator.getIndex() != iterator.getEndIndex()) { + int start = iterator.getRunStart(); + int end = iterator.getRunLimit(); + + Iterator it = iterator.getAttributes().keySet().iterator(); + while (it.hasNext()) { + AttributedCharacterIterator.Attribute attribute = it.next(); + Object value = iterator.getAttribute(attribute); + result.add(new FieldContainer(start, end, attribute, value)); + // System.out.println(start + " " + end + ": " + attribute + ", + // " + value ); + // System.out.println("v.add(new FieldContainer(" + start +"," + + // end +"," + attribute+ "," + value+ "));"); + } + iterator.setIndex(end); + } + return result; + } + + protected static class FieldContainer { + int start, end; + + AttributedCharacterIterator.Attribute attribute; + + Object value; + + // called from support_decimalformat and support_simpledateformat tests + public FieldContainer(int start, int end, + AttributedCharacterIterator.Attribute attribute) { + this(start, end, attribute, attribute); + } + + // called from support_messageformat tests + public FieldContainer(int start, int end, Attribute attribute, int value) { + this(start, end, attribute, new Integer(value)); + } + + // called from support_messageformat tests + public FieldContainer(int start, int end, Attribute attribute, + Object value) { + this.start = start; + this.end = end; + this.attribute = attribute; + this.value = value; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof FieldContainer)) { + return false; + } + + FieldContainer fc = (FieldContainer) obj; + return (start == fc.start && end == fc.end + && attribute == fc.attribute && value.equals(fc.value)); + } + } +} diff --git a/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/Support_MessageFormat.java b/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/Support_MessageFormat.java new file mode 100644 index 000000000..454caeb03 --- /dev/null +++ b/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/Support_MessageFormat.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.text.tests.java.text; + +import java.text.DateFormat; +import java.text.MessageFormat; +import java.text.NumberFormat; +import java.text.MessageFormat.Field; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.Locale; +import java.util.Vector; + + +public class Support_MessageFormat extends Support_Format { + + public Support_MessageFormat(String p1) { + super(p1); + } + + @Override + public void runTest() { + t_formatToCharacterIterator(); + t_format_with_FieldPosition(); + } + + public static void main(String[] args) { + new Support_MessageFormat("").runTest(); + } + + public void t_format_with_FieldPosition() { + + String pattern = "On {4,date} at {3,time}, he ate {2,number, integer} hamburger{2,choice,1#|1 getMessageVector1() { + Vector v = new Vector(); + v.add(new FieldContainer(3, 6, Field.ARGUMENT, 4)); + v.add(new FieldContainer(3, 6, DateFormat.Field.MONTH)); + v.add(new FieldContainer(6, 7, Field.ARGUMENT, 4)); + v.add(new FieldContainer(7, 9, Field.ARGUMENT, 4)); + v.add(new FieldContainer(7, 9, DateFormat.Field.DAY_OF_MONTH)); + v.add(new FieldContainer(9, 11, Field.ARGUMENT, 4)); + v.add(new FieldContainer(11, 15, Field.ARGUMENT, 4)); + v.add(new FieldContainer(11, 15, DateFormat.Field.YEAR)); + v.add(new FieldContainer(19, 20, Field.ARGUMENT, 3)); + v.add(new FieldContainer(19, 20, DateFormat.Field.HOUR1)); + v.add(new FieldContainer(20, 21, Field.ARGUMENT, 3)); + v.add(new FieldContainer(21, 23, Field.ARGUMENT, 3)); + v.add(new FieldContainer(21, 23, DateFormat.Field.MINUTE)); + v.add(new FieldContainer(23, 24, Field.ARGUMENT, 3)); + v.add(new FieldContainer(24, 26, Field.ARGUMENT, 3)); + v.add(new FieldContainer(24, 26, DateFormat.Field.SECOND)); + v.add(new FieldContainer(26, 27, Field.ARGUMENT, 3)); + v.add(new FieldContainer(27, 29, Field.ARGUMENT, 3)); + v.add(new FieldContainer(27, 29, DateFormat.Field.AM_PM)); + v.add(new FieldContainer(38, 39, Field.ARGUMENT, 2)); + v.add(new FieldContainer(38, 39, NumberFormat.Field.INTEGER)); + v.add(new FieldContainer(49, 50, Field.ARGUMENT, 2)); + v.add(new FieldContainer(61, 62, Field.ARGUMENT, 1)); + v.add(new FieldContainer(61, 62, NumberFormat.Field.INTEGER)); + v.add(new FieldContainer(62, 63, Field.ARGUMENT, 1)); + v.add(new FieldContainer(62, 63, NumberFormat.Field.DECIMAL_SEPARATOR)); + v.add(new FieldContainer(63, 64, Field.ARGUMENT, 1)); + v.add(new FieldContainer(63, 64, NumberFormat.Field.FRACTION)); + v.add(new FieldContainer(90, 106, Field.ARGUMENT, 0)); + return v; + } + +} diff --git a/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/Support_SimpleDateFormat.java b/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/Support_SimpleDateFormat.java new file mode 100644 index 000000000..24a436bdc --- /dev/null +++ b/libcore/text/src/test/java/org/apache/harmony/text/tests/java/text/Support_SimpleDateFormat.java @@ -0,0 +1,266 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.text.tests.java.text; + +import java.text.DateFormat; +import java.text.NumberFormat; +import java.text.SimpleDateFormat; +import java.text.DateFormat.Field; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.Locale; +import java.util.TimeZone; +import java.util.Vector; + +import org.apache.harmony.text.tests.java.text.Support_Format.FieldContainer; + + +public class Support_SimpleDateFormat extends Support_Format { + + public Support_SimpleDateFormat(String p1) { + super(p1); + } + + @Override + public void runTest() { + t_formatToCharacterIterator(); + t_format_with_FieldPosition(); + } + + public static void main(String[] args) { + new Support_SimpleDateFormat("").runTest(); + } + + public void t_format_with_FieldPosition() { + TimeZone tz = TimeZone.getTimeZone("EST"); + Calendar cal = new GregorianCalendar(tz); + cal.set(1999, Calendar.SEPTEMBER, 13, 17, 19, 01); + cal.set(Calendar.MILLISECOND, 0); + Date date = cal.getTime(); + SimpleDateFormat format = (SimpleDateFormat) DateFormat + .getDateInstance(DateFormat.DEFAULT, Locale.US); + format.setTimeZone(tz); + + // test with all pattern chars, and multiple occurances + format + .applyPattern("G GGGG y yy yyyy M MM MMM MMMM d dd ddd k kk kkk H HH HHH h hh hhh m mmm s ss sss S SS SSS EE EEEE D DD DDD F FF w www W WWW a aaa K KKK z zzzz Z ZZZZ"); + + StringBuffer textbuffer = new StringBuffer( + "AD Anno Domini 99 99 1999 9 09 Sep September 13 13 013 17 17 017 17 17 017 5 05"); + textbuffer + .append(" 005 19 019 1 01 001 0 00 000 Mon Monday 256 256 256 2 02 38 038 3 003 PM"); + textbuffer.append(" PM 5 005 GMT-05:00 GMT-05:00 -0500 GMT-05:00"); + + // to avoid passing the huge Stringbuffer each time. + super.text = textbuffer.toString(); + + // test if field positions are set correctly for these fields occuring + // multiple times. + t_FormatWithField(0, format, date, null, Field.ERA, 0, 2); + t_FormatWithField(1, format, date, null, Field.YEAR, 15, 17); + t_FormatWithField(2, format, date, null, Field.MONTH, 26, 27); + t_FormatWithField(3, format, date, null, Field.DAY_OF_MONTH, 45, 47); + t_FormatWithField(4, format, date, null, Field.HOUR_OF_DAY1, 55, 57); + t_FormatWithField(5, format, date, null, Field.HOUR_OF_DAY0, 65, 67); + t_FormatWithField(6, format, date, null, Field.HOUR1, 75, 76); + t_FormatWithField(7, format, date, null, Field.MINUTE, 84, 86); + t_FormatWithField(8, format, date, null, Field.SECOND, 91, 92); + t_FormatWithField(9, format, date, null, Field.MILLISECOND, 100, 101); + t_FormatWithField(10, format, date, null, Field.DAY_OF_WEEK, 109, 112); + t_FormatWithField(11, format, date, null, Field.DAY_OF_YEAR, 120, 123); + t_FormatWithField(12, format, date, null, Field.DAY_OF_WEEK_IN_MONTH, + 132, 133); + t_FormatWithField(13, format, date, null, Field.WEEK_OF_YEAR, 137, 139); + t_FormatWithField(14, format, date, null, Field.WEEK_OF_MONTH, 144, 145); + t_FormatWithField(15, format, date, null, Field.AM_PM, 150, 152); + t_FormatWithField(16, format, date, null, Field.HOUR0, 158, 159); + t_FormatWithField(17, format, date, null, Field.TIME_ZONE, 164, 173); + + // test fields that are not included in the formatted text + t_FormatWithField(18, format, date, null, + NumberFormat.Field.EXPONENT_SIGN, 0, 0); + + // test with simple example + format.applyPattern("h:m z"); + + super.text = "5:19 GMT-05:00"; + t_FormatWithField(21, format, date, null, Field.HOUR1, 0, 1); + t_FormatWithField(22, format, date, null, Field.MINUTE, 2, 4); + t_FormatWithField(23, format, date, null, Field.TIME_ZONE, 5, 14); + + // test fields that are not included in the formatted text + + t_FormatWithField(24, format, date, null, Field.ERA, 0, 0); + t_FormatWithField(25, format, date, null, Field.YEAR, 0, 0); + t_FormatWithField(26, format, date, null, Field.MONTH, 0, 0); + t_FormatWithField(27, format, date, null, Field.DAY_OF_MONTH, 0, 0); + t_FormatWithField(28, format, date, null, Field.HOUR_OF_DAY1, 0, 0); + t_FormatWithField(29, format, date, null, Field.HOUR_OF_DAY0, 0, 0); + t_FormatWithField(30, format, date, null, Field.SECOND, 0, 0); + t_FormatWithField(31, format, date, null, Field.MILLISECOND, 0, 0); + t_FormatWithField(32, format, date, null, Field.DAY_OF_WEEK, 0, 0); + t_FormatWithField(33, format, date, null, Field.DAY_OF_YEAR, 0, 0); + t_FormatWithField(34, format, date, null, Field.DAY_OF_WEEK_IN_MONTH, + 0, 0); + t_FormatWithField(35, format, date, null, Field.WEEK_OF_YEAR, 0, 0); + t_FormatWithField(36, format, date, null, Field.WEEK_OF_MONTH, 0, 0); + t_FormatWithField(37, format, date, null, Field.AM_PM, 0, 0); + t_FormatWithField(38, format, date, null, Field.HOUR0, 0, 0); + + t_FormatWithField(39, format, date, null, NumberFormat.Field.EXPONENT, + 0, 0); + + // test with simple example with pattern char Z + format.applyPattern("h:m Z z"); + super.text = "5:19 -0500 GMT-05:00"; + t_FormatWithField(40, format, date, null, Field.HOUR1, 0, 1); + t_FormatWithField(41, format, date, null, Field.MINUTE, 2, 4); + t_FormatWithField(42, format, date, null, Field.TIME_ZONE, 5, 10); + } + + public void t_formatToCharacterIterator() { + TimeZone tz = TimeZone.getTimeZone("EST"); + Calendar cal = new GregorianCalendar(tz); + cal.set(1999, Calendar.SEPTEMBER, 13, 17, 19, 01); + cal.set(Calendar.MILLISECOND, 0); + Date date = cal.getTime(); + SimpleDateFormat format = (SimpleDateFormat) DateFormat + .getDateInstance(DateFormat.DEFAULT, Locale.US); + format.setTimeZone(tz); + + format.applyPattern("yyyyMMddHHmmss"); + t_Format(1, date, format, getDateVector1()); + + format.applyPattern("w W dd MMMM yyyy EEEE"); + t_Format(2, date, format, getDateVector2()); + + format.applyPattern("h:m z"); + t_Format(3, date, format, getDateVector3()); + + format.applyPattern("h:m Z"); + t_Format(5, date, format, getDateVector5()); + + // with all pattern chars, and multiple occurances + format + .applyPattern("G GGGG y yy yyyy M MM MMM MMMM d dd ddd k kk kkk H HH HHH h hh hhh m mmm s ss sss S SS SSS EE EEEE D DD DDD F FF w www W WWW a aaa K KKK z zzzz Z ZZZZ"); + t_Format(4, date, format, getDateVector4()); + } + + private Vector getDateVector1() { + // "19990913171901" + Vector v = new Vector(); + v.add(new FieldContainer(0, 4, Field.YEAR)); + v.add(new FieldContainer(4, 6, Field.MONTH)); + v.add(new FieldContainer(6, 8, Field.DAY_OF_MONTH)); + v.add(new FieldContainer(8, 10, Field.HOUR_OF_DAY0)); + v.add(new FieldContainer(10, 12, Field.MINUTE)); + v.add(new FieldContainer(12, 14, Field.SECOND)); + return v; + } + + private Vector getDateVector2() { + // "12 3 5 March 2002 Monday" + Vector v = new Vector(); + v.add(new FieldContainer(0, 2, Field.WEEK_OF_YEAR)); + v.add(new FieldContainer(3, 4, Field.WEEK_OF_MONTH)); + v.add(new FieldContainer(5, 7, Field.DAY_OF_MONTH)); + v.add(new FieldContainer(8, 17, Field.MONTH)); + v.add(new FieldContainer(18, 22, Field.YEAR)); + v.add(new FieldContainer(23, 29, Field.DAY_OF_WEEK)); + return v; + } + + private Vector getDateVector3() { + // "5:19 EDT" + Vector v = new Vector(); + v.add(new FieldContainer(0, 1, Field.HOUR1)); + v.add(new FieldContainer(2, 4, Field.MINUTE)); + v.add(new FieldContainer(5, 14, Field.TIME_ZONE)); + return v; + } + + private Vector getDateVector5() { + // "5:19 -0400" + Vector v = new Vector(); + v.add(new FieldContainer(0, 1, Field.HOUR1)); + v.add(new FieldContainer(2, 4, Field.MINUTE)); + v.add(new FieldContainer(5, 10, Field.TIME_ZONE)); + return v; + } + + private Vector getDateVector4() { + Vector v = new Vector(); + + // "AD AD 99 99 1999 9 09 Sep September 13 13 013 17 17 017 17 17 017 5 + // 05 + // 005 19 019 1 01 001 0 00 000 Mon Monday 256 256 256 2 02 38 038 3 003 + // PM + // PM 5 005 EDT Eastern Daylight Time -0400 -0400" + v.add(new FieldContainer(0, 2, Field.ERA)); + v.add(new FieldContainer(3, 5, Field.ERA)); + v.add(new FieldContainer(6, 8, Field.YEAR)); + v.add(new FieldContainer(9, 11, Field.YEAR)); + v.add(new FieldContainer(12, 16, Field.YEAR)); + v.add(new FieldContainer(17, 18, Field.MONTH)); + v.add(new FieldContainer(19, 21, Field.MONTH)); + v.add(new FieldContainer(22, 25, Field.MONTH)); + v.add(new FieldContainer(26, 35, Field.MONTH)); + v.add(new FieldContainer(36, 38, Field.DAY_OF_MONTH)); + v.add(new FieldContainer(39, 41, Field.DAY_OF_MONTH)); + v.add(new FieldContainer(42, 45, Field.DAY_OF_MONTH)); + v.add(new FieldContainer(46, 48, Field.HOUR_OF_DAY1)); + v.add(new FieldContainer(49, 51, Field.HOUR_OF_DAY1)); + v.add(new FieldContainer(52, 55, Field.HOUR_OF_DAY1)); + v.add(new FieldContainer(56, 58, Field.HOUR_OF_DAY0)); + v.add(new FieldContainer(59, 61, Field.HOUR_OF_DAY0)); + v.add(new FieldContainer(62, 65, Field.HOUR_OF_DAY0)); + v.add(new FieldContainer(66, 67, Field.HOUR1)); + v.add(new FieldContainer(68, 70, Field.HOUR1)); + v.add(new FieldContainer(71, 74, Field.HOUR1)); + v.add(new FieldContainer(75, 77, Field.MINUTE)); + v.add(new FieldContainer(78, 81, Field.MINUTE)); + v.add(new FieldContainer(82, 83, Field.SECOND)); + v.add(new FieldContainer(84, 86, Field.SECOND)); + v.add(new FieldContainer(87, 90, Field.SECOND)); + v.add(new FieldContainer(91, 92, Field.MILLISECOND)); + v.add(new FieldContainer(93, 95, Field.MILLISECOND)); + v.add(new FieldContainer(96, 99, Field.MILLISECOND)); + v.add(new FieldContainer(100, 103, Field.DAY_OF_WEEK)); + v.add(new FieldContainer(104, 110, Field.DAY_OF_WEEK)); + v.add(new FieldContainer(111, 114, Field.DAY_OF_YEAR)); + v.add(new FieldContainer(115, 118, Field.DAY_OF_YEAR)); + v.add(new FieldContainer(119, 122, Field.DAY_OF_YEAR)); + v.add(new FieldContainer(123, 124, Field.DAY_OF_WEEK_IN_MONTH)); + v.add(new FieldContainer(125, 127, Field.DAY_OF_WEEK_IN_MONTH)); + v.add(new FieldContainer(128, 130, Field.WEEK_OF_YEAR)); + v.add(new FieldContainer(131, 134, Field.WEEK_OF_YEAR)); + v.add(new FieldContainer(135, 136, Field.WEEK_OF_MONTH)); + v.add(new FieldContainer(137, 140, Field.WEEK_OF_MONTH)); + v.add(new FieldContainer(141, 143, Field.AM_PM)); + v.add(new FieldContainer(145, 147, Field.AM_PM)); + v.add(new FieldContainer(149, 150, Field.HOUR0)); + v.add(new FieldContainer(151, 154, Field.HOUR0)); + v.add(new FieldContainer(155, 164, Field.TIME_ZONE)); + v.add(new FieldContainer(165, 174, Field.TIME_ZONE)); + v.add(new FieldContainer(175, 180, Field.TIME_ZONE)); + v.add(new FieldContainer(181, 186, Field.TIME_ZONE)); + return v; + } + +} diff --git a/libcore/tools/integrate/Android.mk b/libcore/tools/integrate/Android.mk new file mode 100644 index 000000000..629a5fd6b --- /dev/null +++ b/libcore/tools/integrate/Android.mk @@ -0,0 +1,19 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + Command.java \ + Filesystem.java \ + Git.java \ + Module.java \ + Modules.java \ + MappedDirectory.java \ + PullHarmonyCode.java \ + Svn.java + +LOCAL_MODULE:= integrate + +include $(BUILD_HOST_JAVA_LIBRARY) + +include $(call all-subdir-makefiles) diff --git a/libcore/tools/integrate/Command.java b/libcore/tools/integrate/Command.java new file mode 100644 index 000000000..5e7796fa4 --- /dev/null +++ b/libcore/tools/integrate/Command.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +/** + * An out of process executable. + */ +class Command { + + private final List args; + private final boolean permitNonZeroExitStatus; + + Command(String... args) { + this(Arrays.asList(args)); + } + + Command(List args) { + this.args = new ArrayList(args); + this.permitNonZeroExitStatus = false; + } + + private Command(Builder builder) { + this.args = new ArrayList(builder.args); + this.permitNonZeroExitStatus = builder.permitNonZeroExitStatus; + } + + static class Builder { + private final List args = new ArrayList(); + private boolean permitNonZeroExitStatus = false; + + public Builder args(String... args) { + return args(Arrays.asList(args)); + } + + public Builder args(Collection args) { + this.args.addAll(args); + return this; + } + + public Builder permitNonZeroExitStatus() { + permitNonZeroExitStatus = true; + return this; + } + + public Command build() { + return new Command(this); + } + + public List execute() { + return build().execute(); + } + } + + public List execute() { + try { + Process process = new ProcessBuilder() + .command(args) + .redirectErrorStream(true) + .start(); + + BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream())); + List outputLines = new ArrayList(); + String outputLine; + while ((outputLine = in.readLine()) != null) { + outputLines.add(outputLine); + } + + if (process.waitFor() != 0 && !permitNonZeroExitStatus) { + StringBuilder message = new StringBuilder(); + for (String line : outputLines) { + message.append("\n").append(line); + } + throw new RuntimeException("Process failed: " + args + message); + } + + return outputLines; + } catch (IOException e) { + throw new RuntimeException("Process failed: " + args, e); + } catch (InterruptedException e) { + throw new RuntimeException("Process failed: " + args, e); + } + } + +} diff --git a/libcore/tools/integrate/Filesystem.java b/libcore/tools/integrate/Filesystem.java new file mode 100644 index 000000000..a9c1789f4 --- /dev/null +++ b/libcore/tools/integrate/Filesystem.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.util.Collection; +import java.util.List; + +/** + * Factory for filesystem commands. + */ +class Filesystem { + + public void move(String source, String target) { + new Command("mv", source, target).execute(); + } + + /** + * Moves all of the files in {@code source} to {@code target}, one at a + * time. Unlike {@code move}, this approach works even if the target + * directory is nonempty. + */ + public int moveContents(String source, String target) { + List files = new Command("find", source, "-type", "f") .execute(); + for (String file : files) { + String targetFile = target + "/" + file.substring(source.length()); + mkdir(parent(targetFile)); + new Command("mv", "-i", file, targetFile).execute(); + } + return files.size(); + } + + private String parent(String file) { + return file.substring(0, file.lastIndexOf('/')); + } + + public void mkdir(String dir) { + new Command("mkdir", "-p", dir).execute(); + } + + public List find(String where, String name) { + return new Command("find", where, "-name", name).execute(); + } + + public void rm(Collection files) { + new Command.Builder().args("rm", "-r").args(files).execute(); + } + + public void rm(String file) { + new Command("rm", "-r", file).execute(); + } +} diff --git a/libcore/tools/integrate/Git.java b/libcore/tools/integrate/Git.java new file mode 100644 index 000000000..da7dcfafc --- /dev/null +++ b/libcore/tools/integrate/Git.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Factory for git commands. + */ +class Git { + + private static final Pattern STATUS_DELETED + = Pattern.compile("#\\tdeleted: (.*)"); + + public void branch(String newBranch) { + branch(newBranch, "HEAD"); + } + + /** + * @param base another branch, or a revision identifier like {@code HEAD}. + */ + public void branch(String newBranch, String base) { + // -b is used by git to branch from another checkout + new Command("git", "checkout", "-b", newBranch, base).execute(); + } + + public void commit(String message) { + new Command("git", "commit", "-m", message).execute(); + } + + public void add(String path) { + new Command("git", "add", path).execute(); + } + + public void remove(Collection paths) { + new Command.Builder().args("git", "rm").args(paths).execute(); + } + + public List merge(String otherBranch) { + return new Command.Builder() + .args("git", "merge", "-s", "recursive", otherBranch) + .permitNonZeroExitStatus() + .execute(); + } + + /** + * Returns the files that have been deleted from the filesystem, but that + * don't exist in the active git change. + */ + public List listDeleted() { + List statusLines = new Command.Builder() + .args("git", "status") + .permitNonZeroExitStatus() + .execute(); + + List deletedFiles = new ArrayList(); + Matcher matcher = STATUS_DELETED.matcher(""); + for (String line : statusLines) { + matcher.reset(line); + if (matcher.matches()) { + deletedFiles.add(matcher.group(1)); + } + } + return deletedFiles; + } + + public void rm(List files) { + new Command.Builder() + .args("git", "rm").args(files) + .permitNonZeroExitStatus() + .build() + .execute(); + } +} diff --git a/libcore/tools/integrate/MappedDirectory.java b/libcore/tools/integrate/MappedDirectory.java new file mode 100644 index 000000000..8e28d29ac --- /dev/null +++ b/libcore/tools/integrate/MappedDirectory.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * A logical directory that has a different location in Harmony and Dalvik. + */ +class MappedDirectory { + + private final String svnPath; + private final String gitPath; + + public MappedDirectory(String svnPath, String gitPath) { + this.svnPath = svnPath; + this.gitPath = gitPath; + } + + public String svnPath() { + return svnPath; + } + + public String gitPath() { + return gitPath; + } + + @Override public String toString() { + return "svn:" + svnPath + " -> git:" + gitPath; + } +} diff --git a/libcore/tools/integrate/Module.java b/libcore/tools/integrate/Module.java new file mode 100644 index 000000000..5cb7035e1 --- /dev/null +++ b/libcore/tools/integrate/Module.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.util.LinkedHashSet; +import java.util.Set; + +/** + * A logical unit of code shared between Apache Harmony and Dalvik. + */ +class Module { + + private final String svnBaseUrl; + private final String path; + private final Set mappedDirectories; + + public String getSvnBaseUrl() { + return svnBaseUrl; + } + + public String path() { + return path; + } + + public Set getMappedDirectories() { + return mappedDirectories; + } + + private Module(Builder builder) { + this.svnBaseUrl = builder.svnBaseUrl; + this.path = builder.path; + this.mappedDirectories = new LinkedHashSet(builder.mappedDirectories); + } + + public static class Builder { + private final String svnBaseUrl; + private final String path; + private final Set mappedDirectories + = new LinkedHashSet(); + + public Builder(String svnBaseUrl, String path) { + this.svnBaseUrl = svnBaseUrl; + this.path = path; + } + + public Builder mapDirectory(String svnPath, String gitPath) { + mappedDirectories.add(new MappedDirectory(svnPath, gitPath)); + return this; + } + + public Module build() { + return new Module(this); + } + } +} diff --git a/libcore/tools/integrate/Modules.java b/libcore/tools/integrate/Modules.java new file mode 100644 index 000000000..2475852f5 --- /dev/null +++ b/libcore/tools/integrate/Modules.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Constants that define modules shared by Harmony and Dalvik. + */ +public class Modules { + + private static final String SVN_ROOT + = "http://svn.apache.org/repos/asf/harmony/enhanced/classlib/trunk/modules"; + + public static final Module ARCHIVE = new Module.Builder(SVN_ROOT, "archive") + .mapDirectory("archive/src/main/native/archive/shared", + "archive/src/main/native") + .mapDirectory("archive/src/main/native/zip/shared", + "archive/src/main/native") + .build(); + + public static final Module CRYPTO = new Module.Builder(SVN_ROOT, "crypto") + .mapDirectory("crypto/src/test/api/java.injected/javax", + "crypto/src/test/java/org/apache/harmony/crypto/tests/javax") + .mapDirectory("crypto/src/test/api/java", + "crypto/src/test/java") + .mapDirectory("crypto/src/test/resources/serialization", + "crypto/src/test/java/serialization") + .mapDirectory("crypto/src/test/support/common/java", + "crypto/src/test/java") + .build(); + + public static final Module REGEX + = new Module.Builder(SVN_ROOT, "regex").build(); + + public static final Module SECURITY = new Module.Builder(SVN_ROOT, "security") + .mapDirectory("security/src/main/java/common", + "security/src/main/java") + .mapDirectory("security/src/main/java/unix/org", + "security/src/main/java/org") + .mapDirectory("security/src/test/api/java", + "security/src/test/java") + .build(); + + public static final Module TEXT + = new Module.Builder(SVN_ROOT, "text").build(); + + public static final Module X_NET + = new Module.Builder(SVN_ROOT, "x-net").build(); + + // TODO: add the other modules +} diff --git a/libcore/tools/integrate/PullHarmonyCode.java b/libcore/tools/integrate/PullHarmonyCode.java new file mode 100644 index 000000000..671080118 --- /dev/null +++ b/libcore/tools/integrate/PullHarmonyCode.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.util.List; +import java.util.UUID; + +/** + * Download two versions of Apache Harmony from their SVN version, and use it + * to perform a three-way merge with Dalvik. + */ +public class PullHarmonyCode { + + private final int currentVersion; + private final int targetVersion; + + public PullHarmonyCode(int currentVersion, int targetVersion) { + this.currentVersion = currentVersion; + this.targetVersion = targetVersion; + } + + public void pull(Module module) { + String path = module.path(); + String svnOldBranch = path + "_" + currentVersion; + String svnNewBranch = path + "_" + targetVersion; + String dalvikBranch = path + "_dalvik"; + + Git git = new Git(); + Filesystem filesystem = new Filesystem(); + Svn svn = new Svn(); + + // Assume we're starting with the current Dalvik code. Tuck this away + // somewhere while we rewrite history. + String temp = "/tmp/" + UUID.randomUUID(); + filesystem.mkdir(temp); + + // To prepare a three-way-merge, we need a common starting point: the + // time at which Dalvik and Harmony were most the same. We'll use the + // previous Harmony SVN code as this starting point. We grab the old + // code from their repository, and commit it as a git branch. + System.out.print("Creating branch " + svnOldBranch + "..."); + git.branch(svnOldBranch); + filesystem.move(path, temp + "/" + path); + svn.checkOut(currentVersion, module.getSvnBaseUrl() + "/" + path); + filesystem.rm(filesystem.find(path, ".svn")); + for (MappedDirectory mappedDirectory : module.getMappedDirectories()) { + filesystem.moveContents(mappedDirectory.svnPath(), mappedDirectory.gitPath()); + } + git.rm(git.listDeleted()); + git.add(path); + git.commit(svnOldBranch); + System.out.println("done"); + + // Create a branch that's derived from the starting point. It will + // contain all of the changes Harmony has made from then until now. + System.out.print("Creating branch " + svnNewBranch + "..."); + git.branch(svnNewBranch, svnOldBranch); + filesystem.rm(path); + svn.checkOut(targetVersion, module.getSvnBaseUrl() + "/" + path); + filesystem.rm(filesystem.find(path, ".svn")); + for (MappedDirectory mappedDirectory : module.getMappedDirectories()) { + filesystem.moveContents(mappedDirectory.svnPath(), mappedDirectory.gitPath()); + } + git.rm(git.listDeleted()); + git.add(path); + git.commit(svnNewBranch); + System.out.println("done"); + + // Create another branch that's derived from the starting point. It will + // contain all of the changes Dalvik has made from then until now. + System.out.print("Creating branch " + dalvikBranch + "..."); + git.branch(dalvikBranch, svnOldBranch); + filesystem.rm(path); + filesystem.move(temp + "/" + path, path); + git.rm(git.listDeleted()); + git.add(path); + git.commit(dalvikBranch); + System.out.println("done"); + + // Merge the two sets of changes together: Harmony's and Dalvik's. By + // initializing a common starting point, git can make better decisions + // when the two new versions differ. For example, if today's Dalvik has + // a method that today's Harmony does not, it may be because Dalvik + // added it, or because Harmony deleted it! + System.out.println("Merging " + svnNewBranch + " into " + dalvikBranch + ":"); + List mergeResults = git.merge(svnNewBranch); + for (String mergeResult : mergeResults) { + System.out.print(" "); + System.out.println(mergeResult); + } + } + + public static void main(String[] args) { +// new PullHarmonyCode(527399, 802921).pull(Modules.CRYPTO); + new PullHarmonyCode(772995, 802921).pull(Modules.ARCHIVE); + } +} diff --git a/libcore/tools/integrate/Svn.java b/libcore/tools/integrate/Svn.java new file mode 100644 index 000000000..dc9be35b4 --- /dev/null +++ b/libcore/tools/integrate/Svn.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Factory for Subversion commands. + */ +class Svn { + + public void checkOut(int version, String url) { + new Command("svn", "co", "-r", "" + version, url).execute(); + } +} diff --git a/libcore/x-net/src/main/java/javax/net/DefaultServerSocketFactory.java b/libcore/x-net/src/main/java/javax/net/DefaultServerSocketFactory.java index 0b309df75..9e31be462 100644 --- a/libcore/x-net/src/main/java/javax/net/DefaultServerSocketFactory.java +++ b/libcore/x-net/src/main/java/javax/net/DefaultServerSocketFactory.java @@ -15,11 +15,6 @@ * limitations under the License. */ -/** -* @author Boris V. Kuznetsov -* @version $Revision$ -*/ - package javax.net; import java.io.IOException; @@ -27,24 +22,27 @@ import java.net.InetAddress; import java.net.ServerSocket; /** - * Default implementation of javax.net.ServerSocketFactory. - * - * @since Android 1.0 - * + * Default implementation of {@link javax.net.ServerSocketFactory} */ -class DefaultServerSocketFactory extends ServerSocketFactory { +final class DefaultServerSocketFactory extends ServerSocketFactory { + + DefaultServerSocketFactory() { + super(); + } + @Override public ServerSocket createServerSocket(int port) throws IOException { return new ServerSocket(port); } - public ServerSocket createServerSocket(int port, int backlog) - throws IOException { + @Override + public ServerSocket createServerSocket(int port, int backlog) throws IOException { return new ServerSocket(port, backlog); } - public ServerSocket createServerSocket(int port, int backlog, - InetAddress iAddress) throws IOException { + @Override + public ServerSocket createServerSocket(int port, int backlog, InetAddress iAddress) + throws IOException { return new ServerSocket(port, backlog, iAddress); } diff --git a/libcore/x-net/src/main/java/javax/net/DefaultSocketFactory.java b/libcore/x-net/src/main/java/javax/net/DefaultSocketFactory.java index 8aa82d9ef..010c720c0 100644 --- a/libcore/x-net/src/main/java/javax/net/DefaultSocketFactory.java +++ b/libcore/x-net/src/main/java/javax/net/DefaultSocketFactory.java @@ -15,11 +15,6 @@ * limitations under the License. */ -/** -* @author Boris V. Kuznetsov -* @version $Revision$ -*/ - package javax.net; import java.io.IOException; @@ -28,32 +23,38 @@ import java.net.Socket; import java.net.UnknownHostException; /** - * Default implementation of javax.net.SocketFactory - * - * @since Android 1.0 + * Default implementation of {@link javax.net.SocketFactory} */ -class DefaultSocketFactory extends SocketFactory { +final class DefaultSocketFactory extends SocketFactory { + + DefaultSocketFactory() { + super(); + } + @Override public Socket createSocket() throws IOException { return new Socket(); } - public Socket createSocket(String host, int port) throws IOException, - UnknownHostException { + @Override + public Socket createSocket(String host, int port) throws IOException, UnknownHostException { return new Socket(host, port); } - public Socket createSocket(String host, int port, InetAddress localHost, - int localPort) throws IOException, UnknownHostException { + @Override + public Socket createSocket(String host, int port, InetAddress localHost, int localPort) + throws IOException, UnknownHostException { return new Socket(host, port, localHost, localPort); } + @Override public Socket createSocket(InetAddress host, int port) throws IOException { return new Socket(host, port); } - public Socket createSocket(InetAddress address, int port, - InetAddress localAddress, int localPort) throws IOException { + @Override + public Socket createSocket(InetAddress address, int port, InetAddress localAddress, + int localPort) throws IOException { return new Socket(address, port, localAddress, localPort); } } diff --git a/libcore/x-net/src/main/java/javax/net/ServerSocketFactory.java b/libcore/x-net/src/main/java/javax/net/ServerSocketFactory.java index 28a79f667..f2d2c0da3 100644 --- a/libcore/x-net/src/main/java/javax/net/ServerSocketFactory.java +++ b/libcore/x-net/src/main/java/javax/net/ServerSocketFactory.java @@ -15,11 +15,6 @@ * limitations under the License. */ -/** -* @author Boris V. Kuznetsov -* @version $Revision$ -*/ - package javax.net; import java.io.IOException; @@ -30,66 +25,59 @@ import java.net.SocketException; /** * This abstract class defines methods to create server sockets. It can be * subclassed to create specific server socket types. - * - * @since Android 1.0 */ public abstract class ServerSocketFactory { - static ServerSocketFactory defaultFactory; - - /** - * Creates a new {@code ServerSocketFactory} instance. - * - * @since Android 1.0 - */ - protected ServerSocketFactory() { - } + private static ServerSocketFactory defaultFactory; /** * Gets the default server socket factory of the system which can be used to * create new server sockets without creating a subclass of this factory. - * + * * @return the system default server socket factory. - * @since Android 1.0 */ public static synchronized ServerSocketFactory getDefault() { if (defaultFactory == null) { - defaultFactory = new DefaultServerSocketFactory(); + defaultFactory = new DefaultServerSocketFactory(); } return defaultFactory; } + /** + * Creates a new {@code ServerSocketFactory} instance. + */ + protected ServerSocketFactory() { + super(); + } + /** * Creates a new server socket which is not bound to any local address. This * method has to be overridden by a subclass otherwise a {@code * SocketException} is thrown. - * + * * @return the created unbound server socket. * @throws IOException * if an error occurs while creating a new server socket. - * @since Android 1.0 */ public ServerSocket createServerSocket() throws IOException { - // follow RI's behavior + // follow RI's behavior throw new SocketException("Unbound server sockets not implemented"); } /** * Creates a new server socket which is bound to the given port. - * + * * @param port * the port on which the created socket has to listen. * @return the created bound server socket. * @throws IOException * if an error occurs while creating a new server socket. - * @since Android 1.0 */ - public abstract ServerSocket createServerSocket(int port) - throws IOException; + public abstract ServerSocket createServerSocket(int port) throws IOException; /** * Creates a new server socket which is bound to the given port and * configures its maximum of queued connections. - * + * * @param port * the port on which the created socket has to listen. * @param backlog @@ -97,15 +85,13 @@ public abstract class ServerSocketFactory { * @return the created bound server socket. * @throws IOException * if an error occurs while creating a new server socket. - * @since Android 1.0 */ - public abstract ServerSocket createServerSocket(int port, int backlog) - throws IOException; + public abstract ServerSocket createServerSocket(int port, int backlog) throws IOException; /** * Creates a new server socket which is bound to the given address on the * specified port and configures its maximum of queued connections. - * + * * @param port * the port on which the created socket has to listen. * @param backlog @@ -116,9 +102,8 @@ public abstract class ServerSocketFactory { * @return the created bound server socket. * @throws IOException * if an error occurs while creating a new server socket. - * @since Android 1.0 */ - public abstract ServerSocket createServerSocket(int port, int backlog, - InetAddress iAddress) throws IOException; + public abstract ServerSocket createServerSocket(int port, int backlog, InetAddress iAddress) + throws IOException; -} \ No newline at end of file +} diff --git a/libcore/x-net/src/main/java/javax/net/SocketFactory.java b/libcore/x-net/src/main/java/javax/net/SocketFactory.java index 6e5017e1f..eb0cfcbf4 100644 --- a/libcore/x-net/src/main/java/javax/net/SocketFactory.java +++ b/libcore/x-net/src/main/java/javax/net/SocketFactory.java @@ -15,11 +15,6 @@ * limitations under the License. */ -/** -* @author Boris V. Kuznetsov -* @version $Revision$ -*/ - package javax.net; import java.io.IOException; @@ -31,27 +26,16 @@ import java.net.UnknownHostException; /** * This abstract class defines methods to create sockets. It can be subclassed * to create specific socket types with additional socket-level functionality. - * - * @since Android 1.0 */ public abstract class SocketFactory { - static SocketFactory defaultFactory; - - /** - * Creates a new {@code SocketFactory} instance. - * - * @since Android 1.0 - */ - protected SocketFactory() { - } + private static SocketFactory defaultFactory; /** * Gets the default socket factory of the system which can be used to create * new sockets without creating a subclass of this factory. - * + * * @return the system default socket factory. - * @since Android 1.0 */ public static synchronized SocketFactory getDefault() { if (defaultFactory == null) { @@ -60,18 +44,24 @@ public abstract class SocketFactory { return defaultFactory; } + /** + * Creates a new {@code SocketFactory} instance. + */ + protected SocketFactory() { + super(); + } + /** * Creates a new socket which is not connected to any remote host. This * method has to be overridden by a subclass otherwise a {@code * SocketException} is thrown. - * + * * @return the created unconnected socket. * @throws IOException * if an error occurs while creating a new socket. - * @since Android 1.0 */ public Socket createSocket() throws IOException { - // follow RI's behavior + // follow RI's behavior throw new SocketException("Unconnected sockets not implemented"); } @@ -79,7 +69,7 @@ public abstract class SocketFactory { * Creates a new socket which is connected to the remote host specified by * the parameters {@code host} and {@code port}. The socket is bound to any * available local address and port. - * + * * @param host * the remote host address the socket has to be connected to. * @param port @@ -91,17 +81,16 @@ public abstract class SocketFactory { * @throws UnknownHostException * if the specified host is unknown or the IP address could not * be resolved. - * @since Android 1.0 */ - public abstract Socket createSocket(String host, int port) - throws IOException, UnknownHostException; + public abstract Socket createSocket(String host, int port) throws IOException, + UnknownHostException; /** * Creates a new socket which is connected to the remote host specified by * the parameters {@code host} and {@code port}. The socket is bound to the * local network interface specified by the InetAddress {@code localHost} on * port {@code localPort}. - * + * * @param host * the remote host address the socket has to be connected to. * @param port @@ -118,17 +107,15 @@ public abstract class SocketFactory { * @throws UnknownHostException * if the specified host is unknown or the IP address could not * be resolved. - * @since Android 1.0 */ - public abstract Socket createSocket(String host, int port, - InetAddress localHost, int localPort) throws IOException, - UnknownHostException; + public abstract Socket createSocket(String host, int port, InetAddress localHost, int localPort) + throws IOException, UnknownHostException; /** * Creates a new socket which is connected to the remote host specified by * the InetAddress {@code host}. The socket is bound to any available local * address and port. - * + * * @param host * the host address the socket has to be connected to. * @param port @@ -137,17 +124,16 @@ public abstract class SocketFactory { * @return the created connected socket. * @throws IOException * if an error occurs while creating a new socket. - * @since Android 1.0 */ - public abstract Socket createSocket(InetAddress host, int port) - throws IOException; + public abstract Socket createSocket(InetAddress host, int port) throws IOException; + /** * Creates a new socket which is connected to the remote host specified by * the InetAddress {@code address}. The socket is bound to the local network * interface specified by the InetAddress {@code localHost} on port {@code * localPort}. - * + * * @param address * the remote host address the socket has to be connected to. * @param port @@ -161,8 +147,7 @@ public abstract class SocketFactory { * @return the created connected socket. * @throws IOException * if an error occurs while creating a new socket. - * @since Android 1.0 */ - public abstract Socket createSocket(InetAddress address, int port, - InetAddress localAddress, int localPort) throws IOException; + public abstract Socket createSocket(InetAddress address, int port, InetAddress localAddress, + int localPort) throws IOException; } diff --git a/libcore/x-net/src/main/java/javax/net/ssl/CertPathTrustManagerParameters.java b/libcore/x-net/src/main/java/javax/net/ssl/CertPathTrustManagerParameters.java index 59036637f..dcf7a4d5a 100644 --- a/libcore/x-net/src/main/java/javax/net/ssl/CertPathTrustManagerParameters.java +++ b/libcore/x-net/src/main/java/javax/net/ssl/CertPathTrustManagerParameters.java @@ -22,11 +22,12 @@ import java.security.cert.CertPathParameters; /** * Certification path parameters to provide to certification path * based {@link TrustManager}. - * @since Android 1.0 + * + * @since 1.5 */ public class CertPathTrustManagerParameters implements ManagerFactoryParameters { - private CertPathParameters param; + private final CertPathParameters param; /** * Creates a new {@code CertPathTrustManagerParameters} with the specified @@ -34,7 +35,6 @@ public class CertPathTrustManagerParameters implements ManagerFactoryParameters * * @param parameters * the certification path parameters. - * @since Android 1.0 */ public CertPathTrustManagerParameters(CertPathParameters parameters) { param = (CertPathParameters) parameters.clone(); @@ -44,10 +44,9 @@ public class CertPathTrustManagerParameters implements ManagerFactoryParameters * Returns a copy of the certification path parameters. * * @return a copy of the certification path parameters. - * @since Android 1.0 */ public CertPathParameters getParameters() { return (CertPathParameters) param.clone(); } -} \ No newline at end of file +} diff --git a/libcore/x-net/src/main/java/javax/net/ssl/ContextImpl.java b/libcore/x-net/src/main/java/javax/net/ssl/ContextImpl.java deleted file mode 100644 index 096cbba55..000000000 --- a/libcore/x-net/src/main/java/javax/net/ssl/ContextImpl.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package javax.net.ssl; - -import java.security.Provider; - -/** - * Support class for this package. - * - * @since Android 1.0 - */ - -class ContextImpl extends SSLContext { - public ContextImpl(SSLContextSpi contextSpi, Provider provider, - String protocol) { - super(contextSpi, provider, protocol); - } -} \ No newline at end of file diff --git a/libcore/x-net/src/main/java/javax/net/ssl/DefaultSSLContext.java b/libcore/x-net/src/main/java/javax/net/ssl/DefaultSSLContext.java index d2ab2f492..a12d385be 100644 --- a/libcore/x-net/src/main/java/javax/net/ssl/DefaultSSLContext.java +++ b/libcore/x-net/src/main/java/javax/net/ssl/DefaultSSLContext.java @@ -19,28 +19,24 @@ package javax.net.ssl; import java.io.FileInputStream; import java.security.AccessController; +import java.security.KeyStore; +import java.security.PrivilegedAction; import java.security.Provider; import java.security.Security; -import java.security.KeyStore; -import java.util.Iterator; import org.apache.harmony.security.fortress.Engine; import org.apache.harmony.security.fortress.Services; - /** * Support class for this package. - * - * @since Android 1.0 */ - -class DefaultSSLContext { +final class DefaultSSLContext { private static SSLContext defaultSSLContext; - public static SSLContext getContext() { + static synchronized SSLContext getContext() { if (defaultSSLContext == null) { defaultSSLContext = AccessController - .doPrivileged(new java.security.PrivilegedAction() { + .doPrivileged(new PrivilegedAction() { public SSLContext run() { return findDefault(); } @@ -51,40 +47,37 @@ class DefaultSSLContext { private static SSLContext findDefault() { // FIXME EXPORT CONTROL - Provider.Service service; - for (Iterator it1 = Services.getProvidersList().iterator(); it1 - .hasNext();) { - service = Engine.door.getService((Provider) it1.next(), - "SSLContext"); + for (Provider provider : Services.getProvidersList()) { + final Provider.Service service = Engine.door.getService(provider, "SSLContext"); if (service != null) { try { - SSLContext con = new ContextImpl( - (SSLContextSpi) service.newInstance(null), - service.getProvider(), - service.getAlgorithm()); + SSLContext con = new SSLContext((SSLContextSpi) service.newInstance(null), + service.getProvider(), service.getAlgorithm()); - //TODO javax.net.ssl.keyStoreProvider, javax.net.ssl.trustStoreProvider system property + /* + * TODO + * javax.net.ssl.keyStoreProvider, + * javax.net.ssl.trustStoreProvider system property + */ + // find KeyStore, KeyManagers KeyManager[] keyManagers = null; - KeyStore ks = KeyStore.getInstance(KeyStore - .getDefaultType()); - String keystore = System - .getProperty("javax.net.ssl.keyStore"); - String keystorepwd = System - .getProperty("javax.net.ssl.keyStorePassword"); + KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); + String keystore = System.getProperty("javax.net.ssl.keyStore"); + String keystorepwd = System.getProperty("javax.net.ssl.keyStorePassword"); char[] pwd = null; if (keystorepwd != null) { pwd = keystorepwd.toCharArray(); } if (keystore != null) { - FileInputStream fis = new java.io.FileInputStream( - keystore); - ks.load(fis, pwd); - fis.close(); - + FileInputStream fis = new FileInputStream(keystore); + try { + ks.load(fis, pwd); + } finally { + fis.close(); + } KeyManagerFactory kmf; - String kmfAlg = Security - .getProperty("ssl.KeyManagerFactory.algorithm"); + String kmfAlg = Security.getProperty("ssl.KeyManagerFactory.algorithm"); if (kmfAlg == null) { kmfAlg = "SunX509"; } @@ -96,21 +89,21 @@ class DefaultSSLContext { // find TrustStore, TrustManagers TrustManager[] trustManagers = null; keystore = System.getProperty("javax.net.ssl.trustStore"); - keystorepwd = System - .getProperty("javax.net.ssl.trustStorePassword"); + keystorepwd = System.getProperty("javax.net.ssl.trustStorePassword"); pwd = null; if (keystorepwd != null) { pwd = keystorepwd.toCharArray(); } - //TODO Defaults: jssecacerts; cacerts + // TODO Defaults: jssecacerts; cacerts if (keystore != null) { - FileInputStream fis = new java.io.FileInputStream( - keystore); - ks.load(fis, pwd); - fis.close(); + FileInputStream fis = new FileInputStream(keystore); + try { + ks.load(fis, pwd); + } finally { + fis.close(); + } TrustManagerFactory tmf; - String tmfAlg = Security - .getProperty("ssl.TrustManagerFactory.algorithm"); + String tmfAlg = Security.getProperty("ssl.TrustManagerFactory.algorithm"); if (tmfAlg == null) { tmfAlg = "PKIX"; } @@ -122,7 +115,6 @@ class DefaultSSLContext { con.init(keyManagers, trustManagers, null); return con; } catch (Exception e) { - // e.printStackTrace(); // ignore and try another } } diff --git a/libcore/x-net/src/main/java/javax/net/ssl/DefaultSSLServerSocketFactory.java b/libcore/x-net/src/main/java/javax/net/ssl/DefaultSSLServerSocketFactory.java index c41f61a95..662084116 100644 --- a/libcore/x-net/src/main/java/javax/net/ssl/DefaultSSLServerSocketFactory.java +++ b/libcore/x-net/src/main/java/javax/net/ssl/DefaultSSLServerSocketFactory.java @@ -29,33 +29,36 @@ import java.net.SocketException; */ class DefaultSSLServerSocketFactory extends SSLServerSocketFactory { - private String errMessage; - + private final String errMessage; + + DefaultSSLServerSocketFactory(String mes) { + errMessage = mes; + } + + @Override public String[] getDefaultCipherSuites() { return new String[0]; } + @Override public String[] getSupportedCipherSuites() { return new String[0]; } + @Override public ServerSocket createServerSocket(int port) throws IOException { throw new SocketException(errMessage); } - - public ServerSocket createServerSocket(int port, int backlog) - throws IOException { + @Override + public ServerSocket createServerSocket(int port, int backlog) throws IOException { throw new SocketException(errMessage); } - public ServerSocket createServerSocket(int port, int backlog, - InetAddress iAddress) throws IOException { + @Override + public ServerSocket createServerSocket(int port, int backlog, InetAddress iAddress) + throws IOException { throw new SocketException(errMessage); } - - DefaultSSLServerSocketFactory(String mes) { - errMessage = mes; - } } diff --git a/libcore/x-net/src/main/java/javax/net/ssl/DefaultSSLSocketFactory.java b/libcore/x-net/src/main/java/javax/net/ssl/DefaultSSLSocketFactory.java index fc4e34072..4035a0e63 100644 --- a/libcore/x-net/src/main/java/javax/net/ssl/DefaultSSLSocketFactory.java +++ b/libcore/x-net/src/main/java/javax/net/ssl/DefaultSSLSocketFactory.java @@ -26,61 +26,51 @@ import java.net.UnknownHostException; /** * Default inoperative implementation of javax.net.ssl.SSLSocketFactory * - * @since Android 1.0 */ class DefaultSSLSocketFactory extends SSLSocketFactory { - - private String errMessage; - + + private final String errMessage; + + DefaultSSLSocketFactory(String mes) { + errMessage = mes; + } + + @Override public String[] getDefaultCipherSuites() { return new String[0]; } + @Override public String[] getSupportedCipherSuites() { return new String[0]; } - /** - * @see javax.net.ssl.SSLSocketFactory#createSocket(java.net.Socket, java.lang.String, int, boolean) - */ - public Socket createSocket(Socket s, String host, int port, - boolean autoClose) throws IOException { + @Override + public Socket createSocket(Socket s, String host, int port, boolean autoClose) + throws IOException { throw new SocketException(errMessage); } - /** - * @see javax.net.SocketFactory#createSocket(java.lang.String, int) - */ - public Socket createSocket(String host, int port) throws IOException, - UnknownHostException { + @Override + public Socket createSocket(String host, int port) throws IOException, UnknownHostException { throw new SocketException(errMessage); } - /** - * @see javax.net.SocketFactory#createSocket(java.lang.String, int, java.net.InetAddress, int) - */ - public Socket createSocket(String host, int port, InetAddress localHost, - int localPort) throws IOException, UnknownHostException { + @Override + public Socket createSocket(String host, int port, InetAddress localHost, int localPort) + throws IOException, UnknownHostException { throw new SocketException(errMessage); } - /** - * @see javax.net.SocketFactory#createSocket(java.net.InetAddress, int) - */ + @Override public Socket createSocket(InetAddress host, int port) throws IOException { throw new SocketException(errMessage); } - /** - * @see javax.net.SocketFactory#createSocket(java.net.InetAddress, int, java.net.InetAddress, int) - */ - public Socket createSocket(InetAddress address, int port, - InetAddress localAddress, int localPort) throws IOException { + @Override + public Socket createSocket(InetAddress address, int port, InetAddress localAddress, + int localPort) throws IOException { throw new SocketException(errMessage); } - - DefaultSSLSocketFactory(String mes) { - errMessage = mes; - } } diff --git a/libcore/x-net/src/main/java/javax/net/ssl/HandshakeCompletedEvent.java b/libcore/x-net/src/main/java/javax/net/ssl/HandshakeCompletedEvent.java index 5ec566699..461828066 100644 --- a/libcore/x-net/src/main/java/javax/net/ssl/HandshakeCompletedEvent.java +++ b/libcore/x-net/src/main/java/javax/net/ssl/HandshakeCompletedEvent.java @@ -26,11 +26,8 @@ import java.util.EventObject; /** * The event object encapsulating the information about a completed SSL * handshake on a SSL connection. - * - * @since Android 1.0 */ -public class HandshakeCompletedEvent extends EventObject implements - Serializable { +public class HandshakeCompletedEvent extends EventObject implements Serializable { /** * The 5.0 spec. doesn't declare this serialVersionUID field In order to be @@ -43,12 +40,11 @@ public class HandshakeCompletedEvent extends EventObject implements /** * Creates a new {@code HandshakeCompletedEvent} with the specified SSL * socket and SSL session. - * + * * @param sock * the SSL socket. * @param s * the SSL session. - * @since Android 1.0 */ public HandshakeCompletedEvent(SSLSocket sock, SSLSession s) { super(sock); @@ -57,9 +53,8 @@ public class HandshakeCompletedEvent extends EventObject implements /** * Returns the SSL session associated with this event. - * + * * @return the SSL session associated with this event. - * @since Android 1.0 */ public SSLSession getSession() { return session; @@ -67,9 +62,8 @@ public class HandshakeCompletedEvent extends EventObject implements /** * Returns the name of the cipher suite negotiated during this handshake. - * + * * @return the name of the cipher suite negotiated during this handshake. - * @since Android 1.0 */ public String getCipherSuite() { return session.getCipherSuite(); @@ -78,11 +72,10 @@ public class HandshakeCompletedEvent extends EventObject implements /** * Returns the list of local certificates used during the handshake. These * certificates were sent to the peer. - * + * * @return Returns the list of certificates used during the handshake with * the local identity certificate followed by CAs, or {@code null} * if no certificates were used during the handshake. - * @since Android 1.0 */ public Certificate[] getLocalCertificates() { return session.getLocalCertificates(); @@ -91,15 +84,13 @@ public class HandshakeCompletedEvent extends EventObject implements /** * Return the list of certificates identifying the peer during the * handshake. - * + * * @return the list of certificates identifying the peer with the peer's * identity certificate followed by CAs. * @throws SSLPeerUnverifiedException * if the identity of the peer has not been verified. - * @since Android 1.0 */ - public Certificate[] getPeerCertificates() - throws SSLPeerUnverifiedException { + public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException { return session.getPeerCertificates(); } @@ -109,48 +100,42 @@ public class HandshakeCompletedEvent extends EventObject implements * certificates. *

                        * Replaced by: {@link #getPeerCertificates()} - *

                        - * + * * @return the list of certificates identifying the peer * @throws SSLPeerUnverifiedException * if the identity of the peer has not been verified. - * @since Android 1.0 */ - public X509Certificate[] getPeerCertificateChain() - throws SSLPeerUnverifiedException { + public X509Certificate[] getPeerCertificateChain() throws SSLPeerUnverifiedException { return session.getPeerCertificateChain(); } /** * Returns the {@code Principal} identifying the peer. - * + * * @return the {@code Principal} identifying the peer. * @throws SSLPeerUnverifiedException * if the identity of the peer has not been verified. - * @since Android 1.0 */ public Principal getPeerPrincipal() throws SSLPeerUnverifiedException { return session.getPeerPrincipal(); } - + /** * Returns the {@code Principal} used to identify during the handshake. - * + * * @return the {@code Principal} used to identify during the handshake. - * @since Android 1.0 */ public Principal getLocalPrincipal() { return session.getLocalPrincipal(); } - + /** * Returns the SSL socket that produced this event. - * + * * @return the SSL socket that produced this event. - * @since Android 1.0 */ public SSLSocket getSocket() { - return (SSLSocket)this.source; + return (SSLSocket) this.source; } -} \ No newline at end of file +} diff --git a/libcore/x-net/src/main/java/javax/net/ssl/HandshakeCompletedListener.java b/libcore/x-net/src/main/java/javax/net/ssl/HandshakeCompletedListener.java index 9ffcbc1fa..5032c6338 100644 --- a/libcore/x-net/src/main/java/javax/net/ssl/HandshakeCompletedListener.java +++ b/libcore/x-net/src/main/java/javax/net/ssl/HandshakeCompletedListener.java @@ -22,17 +22,13 @@ import java.util.EventListener; /** * The listener to be implemented to receive event notifications on completion * of SSL handshake on an SSL connection. - * - * @since Android 1.0 */ public interface HandshakeCompletedListener extends EventListener { - /** * The callback method that is invoked when a SSL handshake is completed. - * + * * @param event * the information on the completed SSL handshake event. - * @since Android 1.0 */ - public void handshakeCompleted(HandshakeCompletedEvent event); -} \ No newline at end of file + void handshakeCompleted(HandshakeCompletedEvent event); +} diff --git a/libcore/x-net/src/main/java/javax/net/ssl/HostnameVerifier.java b/libcore/x-net/src/main/java/javax/net/ssl/HostnameVerifier.java index fe767efc1..805762ec7 100644 --- a/libcore/x-net/src/main/java/javax/net/ssl/HostnameVerifier.java +++ b/libcore/x-net/src/main/java/javax/net/ssl/HostnameVerifier.java @@ -23,23 +23,18 @@ package javax.net.ssl; * This is an extended verification option that implementers can provide. It is to be used * during a handshake if the URL's hostname does not match the peer's * identification hostname. - *

                        - * - * @since Android 1.0 */ public interface HostnameVerifier { - /** * Verifies that the specified hostname is allowed within the specified SSL * session. - * + * * @param hostname * the hostname. * @param session * the SSL session of the connection. * @return {@code true} if the specified hostname is allowed, otherwise * {@code false}. - * @since Android 1.0 */ - public boolean verify(String hostname, SSLSession session); -} \ No newline at end of file + boolean verify(String hostname, SSLSession session); +} diff --git a/libcore/x-net/src/main/java/javax/net/ssl/HttpsURLConnection.java b/libcore/x-net/src/main/java/javax/net/ssl/HttpsURLConnection.java index 0a95fb15d..8c49690c7 100644 --- a/libcore/x-net/src/main/java/javax/net/ssl/HttpsURLConnection.java +++ b/libcore/x-net/src/main/java/javax/net/ssl/HttpsURLConnection.java @@ -26,8 +26,6 @@ import java.security.cert.X509Certificate; /** * This abstract subclass of {@code HttpURLConnection} defines methods for * managing HTTPS connections according to the description given by RFC 2818. - * - * @since Android 1.0 */ public abstract class HttpsURLConnection extends HttpURLConnection { @@ -36,141 +34,150 @@ public abstract class HttpsURLConnection extends HttpURLConnection { private static SSLSocketFactory defaultSSLSocketFactory = (SSLSocketFactory) SSLSocketFactory .getDefault(); + /** + * Sets the default hostname verifier to be used by new instances. + * + * @param v + * the new default hostname verifier + * @throws IllegalArgumentException + * if the specified verifier is {@code null}. + */ + public static void setDefaultHostnameVerifier(HostnameVerifier v) { + if (v == null) { + throw new IllegalArgumentException("HostnameVerifier is null"); + } + defaultHostnameVerifier = v; + } + + /** + * Returns the default hostname verifier. + * + * @return the default hostname verifier. + */ + public static HostnameVerifier getDefaultHostnameVerifier() { + return defaultHostnameVerifier; + } + + /** + * Sets the default SSL socket factory to be used by new instances. + * + * @param sf + * the new default SSL socket factory. + * @throws IllegalArgumentException + * if the specified socket factory is {@code null}. + */ + public static void setDefaultSSLSocketFactory(SSLSocketFactory sf) { + if (sf == null) { + throw new IllegalArgumentException("SSLSocketFactory is null"); + } + defaultSSLSocketFactory = sf; + } + + /** + * Returns the default SSL socket factory for new instances. + * + * @return the default SSL socket factory for new instances. + */ + public static SSLSocketFactory getDefaultSSLSocketFactory() { + return defaultSSLSocketFactory; + } + /** * The host name verifier used by this connection. It is initialized from * the default hostname verifier * {@link #setDefaultHostnameVerifier(HostnameVerifier)} or * {@link #getDefaultHostnameVerifier()}. - * - * @since Android 1.0 */ protected HostnameVerifier hostnameVerifier; - private static SSLSocketFactory socketFactory; + private SSLSocketFactory sslSocketFactory; /** * Creates a new {@code HttpsURLConnection} with the specified {@code URL}. - * + * * @param url * the {@code URL} to connect to. - * @since Android 1.0 */ protected HttpsURLConnection(URL url) { super(url); hostnameVerifier = defaultHostnameVerifier; - socketFactory = defaultSSLSocketFactory; + sslSocketFactory = defaultSSLSocketFactory; } /** * Returns the name of the cipher suite negotiated during the SSL handshake. - * + * * @return the name of the cipher suite negotiated during the SSL handshake. * @throws IllegalStateException * if no connection has been established yet. - * @since Android 1.0 */ public abstract String getCipherSuite(); /** * Returns the list of local certificates used during the handshake. These * certificates were sent to the peer. - * + * * @return Returns the list of certificates used during the handshake with * the local identity certificate followed by CAs, or {@code null} * if no certificates were used during the handshake. * @throws IllegalStateException * if no connection has been established yet. - * @since Android 1.0 - */ + */ public abstract Certificate[] getLocalCertificates(); /** * Return the list of certificates identifying the peer during the * handshake. - * + * * @return the list of certificates identifying the peer with the peer's * identity certificate followed by CAs. * @throws SSLPeerUnverifiedException * if the identity of the peer has not been verified.. * @throws IllegalStateException * if no connection has been established yet. - * @since Android 1.0 - */ - public abstract Certificate[] getServerCertificates() - throws SSLPeerUnverifiedException; + */ + public abstract Certificate[] getServerCertificates() throws SSLPeerUnverifiedException; /** * Returns the {@code Principal} identifying the peer. - * + * * @return the {@code Principal} identifying the peer. * @throws SSLPeerUnverifiedException * if the identity of the peer has not been verified. * @throws IllegalStateException * if no connection has been established yet. - * @since Android 1.0 */ public Principal getPeerPrincipal() throws SSLPeerUnverifiedException { Certificate[] certs = getServerCertificates(); - if (certs == null || certs.length == 0 || - (!(certs[0] instanceof X509Certificate))) { - throw new SSLPeerUnverifiedException( - "No server's end-entity certificate"); + if (certs == null || certs.length == 0 || (!(certs[0] instanceof X509Certificate))) { + throw new SSLPeerUnverifiedException("No server's end-entity certificate"); } return ((X509Certificate) certs[0]).getSubjectX500Principal(); } /** * Returns the {@code Principal} used to identify the local host during the handshake. - * + * * @return the {@code Principal} used to identify the local host during the handshake, or * {@code null} if none was used. * @throws IllegalStateException * if no connection has been established yet. - * @since Android 1.0 - */ + */ public Principal getLocalPrincipal() { Certificate[] certs = getLocalCertificates(); - if (certs == null || certs.length == 0 - || (!(certs[0] instanceof X509Certificate))) { + if (certs == null || certs.length == 0 || (!(certs[0] instanceof X509Certificate))) { return null; } return ((X509Certificate) certs[0]).getSubjectX500Principal(); } - /** - * Sets the default hostname verifier to be used by new instances. - * - * @param v - * the new default hostname verifier - * @throws IllegalArgumentException - * if the specified verifier is {@code null}. - * @since Android 1.0 - */ - public static void setDefaultHostnameVerifier(HostnameVerifier v) { - if (v == null) { - throw new IllegalArgumentException("HostnameVerifier is null"); - } - defaultHostnameVerifier = v; - } - - /** - * Returns the default hostname verifier. - * - * @return the default hostname verifier. - * @since Android 1.0 - */ - public static HostnameVerifier getDefaultHostnameVerifier() { - return defaultHostnameVerifier; - } - /** * Sets the hostname verifier for this instance. - * + * * @param v * the hostname verifier for this instance. * @throws IllegalArgumentException * if the specified verifier is {@code null}. - * @since Android 1.0 */ public void setHostnameVerifier(HostnameVerifier v) { if (v == null) { @@ -181,64 +188,35 @@ public abstract class HttpsURLConnection extends HttpURLConnection { /** * Returns the hostname verifier used by this instance. - * + * * @return the hostname verifier used by this instance. - * @since Android 1.0 */ public HostnameVerifier getHostnameVerifier() { return hostnameVerifier; } - /** - * Sets the default SSL socket factory to be used by new instances. - * - * @param sf - * the new default SSL socket factory. - * @throws IllegalArgumentException - * if the specified socket factory is {@code null}. - * @since Android 1.0 - */ - public static void setDefaultSSLSocketFactory(SSLSocketFactory sf) { - if (sf == null) { - throw new IllegalArgumentException("SSLSocketFactory is null"); - } - defaultSSLSocketFactory = sf; - } - - /** - * Returns the default SSL socket factory for new instances. - * - * @return the default SSL socket factory for new instances. - * @since Android 1.0 - */ - public static SSLSocketFactory getDefaultSSLSocketFactory() { - return defaultSSLSocketFactory; - } - /** * Sets the SSL socket factory for this instance. - * + * * @param sf * the SSL socket factory to be used by this instance. * @throws IllegalArgumentException * if the specified socket factory is {@code null}. - * @since Android 1.0 */ public void setSSLSocketFactory(SSLSocketFactory sf) { if (sf == null) { throw new IllegalArgumentException("SSLSocketFactory is null"); } - socketFactory = sf; + sslSocketFactory = sf; } /** * Returns the SSL socket factory used by this instance. - * + * * @return the SSL socket factory used by this instance. - * @since Android 1.0 */ public SSLSocketFactory getSSLSocketFactory() { - return socketFactory; + return sslSocketFactory; } } diff --git a/libcore/x-net/src/main/java/javax/net/ssl/KeyManager.java b/libcore/x-net/src/main/java/javax/net/ssl/KeyManager.java index 08939f78a..30c803206 100644 --- a/libcore/x-net/src/main/java/javax/net/ssl/KeyManager.java +++ b/libcore/x-net/src/main/java/javax/net/ssl/KeyManager.java @@ -18,12 +18,10 @@ package javax.net.ssl; /** - * This is the interface to implement in order to mark a class as a JSSE key managers - * so that key managers can be easily grouped. - * The key managers are responsible for handling the keys used to - * authenticate the local side to its peer, - * - * @since Android 1.0 + * This is the interface to implement in order to mark a class as a JSSE key + * managers so that key managers can be easily grouped. The key managers are + * responsible for handling the keys used to authenticate the local side to its + * peer, */ public interface KeyManager { -} \ No newline at end of file +} diff --git a/libcore/x-net/src/main/java/javax/net/ssl/KeyManagerFactory.java b/libcore/x-net/src/main/java/javax/net/ssl/KeyManagerFactory.java index a47d736f5..99a37a83e 100644 --- a/libcore/x-net/src/main/java/javax/net/ssl/KeyManagerFactory.java +++ b/libcore/x-net/src/main/java/javax/net/ssl/KeyManagerFactory.java @@ -17,23 +17,21 @@ package javax.net.ssl; -import org.apache.harmony.security.fortress.Engine; - import java.security.AccessController; import java.security.InvalidAlgorithmParameterException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; +import java.security.PrivilegedAction; import java.security.Provider; import java.security.Security; import java.security.UnrecoverableKeyException; +import org.apache.harmony.security.fortress.Engine; /** * The public API for {@code KeyManagerFactory} implementations. - * - * @since Android 1.0 */ public class KeyManagerFactory { // Store KeyManagerFactory service name @@ -45,47 +43,26 @@ public class KeyManagerFactory { // Store default property name private static final String PROPERTY_NAME = "ssl.KeyManagerFactory.algorithm"; - // Store used provider - private final Provider provider; - - // Store used KeyManagerFactorySpi implementation - private final KeyManagerFactorySpi spiImpl; - - // Store used algorithm - private final String algorithm; - - /** - * Creates a new {@code KeyManagerFactory}. - * - * @param factorySpi - * the implementation delegate. - * @param provider - * the provider. - * @param algorithm - * the key management algorithm name. - * @since Android 1.0 - */ - protected KeyManagerFactory(KeyManagerFactorySpi factorySpi, - Provider provider, String algorithm) { - this.provider = provider; - this.algorithm = algorithm; - this.spiImpl = factorySpi; - } - /** - * Returns the name of the key management algorithm. - * - * @return the name of the key management algorithm. - * @since Android 1.0 + * Returns the default key manager factory algorithm name. + *

                        + * The default algorithm name is specified by the security property: + * {@code 'ssl.KeyManagerFactory.algorithm'}. + * + * @return the default algorithm name. */ - public final String getAlgorithm() { - return algorithm; + public static final String getDefaultAlgorithm() { + return AccessController.doPrivileged(new PrivilegedAction() { + public String run() { + return Security.getProperty(PROPERTY_NAME); + } + }); } /** * Creates a new {@code KeyManagerFactory} instance for the specified key * management algorithm. - * + * * @param algorithm * the name of the requested key management algorithm. * @return a key manager factory for the requested algorithm. @@ -94,24 +71,23 @@ public class KeyManagerFactory { * @throws NullPointerException * if {@code algorithm} is {@code null} (instead of * NoSuchAlgorithmException as in 1.4 release) - * @since Android 1.0 */ public static final KeyManagerFactory getInstance(String algorithm) throws NoSuchAlgorithmException { if (algorithm == null) { - throw new NullPointerException("algorith is null"); + throw new NullPointerException("algorithm is null"); } synchronized (engine) { engine.getInstance(algorithm, null); - return new KeyManagerFactory((KeyManagerFactorySpi) engine.spi, - engine.provider, algorithm); + return new KeyManagerFactory((KeyManagerFactorySpi) engine.spi, engine.provider, + algorithm); } } /** * Creates a new {@code KeyManagerFactory} instance for the specified key * management algorithm from the specified provider. - * + * * @param algorithm * the name of the requested key management algorithm name. * @param provider @@ -126,11 +102,9 @@ public class KeyManagerFactory { * @throws NullPointerException * if {@code algorithm} is {@code null} (instead of * NoSuchAlgorithmException as in 1.4 release) - * @since Android 1.0 */ - public static final KeyManagerFactory getInstance(String algorithm, - String provider) throws NoSuchAlgorithmException, - NoSuchProviderException { + public static final KeyManagerFactory getInstance(String algorithm, String provider) + throws NoSuchAlgorithmException, NoSuchProviderException { if ((provider == null) || (provider.length() == 0)) { throw new IllegalArgumentException("Provider is null or empty"); } @@ -144,7 +118,7 @@ public class KeyManagerFactory { /** * Creates a new {@code KeyManagerFactory} instance for the specified key * management algorithm from the specified provider. - * + * * @param algorithm * the name of the requested key management algorithm name. * @param provider @@ -156,28 +130,60 @@ public class KeyManagerFactory { * @throws NullPointerException * if {@code algorithm} is {@code null} (instead of * NoSuchAlgorithmException as in 1.4 release) - * @since Android 1.0 */ - public static final KeyManagerFactory getInstance(String algorithm, - Provider provider) throws NoSuchAlgorithmException { + public static final KeyManagerFactory getInstance(String algorithm, Provider provider) + throws NoSuchAlgorithmException { if (provider == null) { throw new IllegalArgumentException("Provider is null"); } if (algorithm == null) { - throw new NullPointerException("algorith is null"); + throw new NullPointerException("algorithm is null"); } synchronized (engine) { engine.getInstance(algorithm, provider, null); - return new KeyManagerFactory((KeyManagerFactorySpi) engine.spi, - provider, algorithm); + return new KeyManagerFactory((KeyManagerFactorySpi) engine.spi, provider, algorithm); } } + // Store used provider + private final Provider provider; + + // Store used KeyManagerFactorySpi implementation + private final KeyManagerFactorySpi spiImpl; + + // Store used algorithm + private final String algorithm; + + /** + * Creates a new {@code KeyManagerFactory}. + * + * @param factorySpi + * the implementation delegate. + * @param provider + * the provider. + * @param algorithm + * the key management algorithm name. + */ + protected KeyManagerFactory(KeyManagerFactorySpi factorySpi, Provider provider, String algorithm) { + super(); + this.provider = provider; + this.algorithm = algorithm; + this.spiImpl = factorySpi; + } + + /** + * Returns the name of the key management algorithm. + * + * @return the name of the key management algorithm. + */ + public final String getAlgorithm() { + return algorithm; + } + /** * Returns the provider for this {@code KeyManagerFactory} instance. - * + * * @return the provider for this {@code KeyManagerFactory} instance. - * @since Android 1.0 */ public final Provider getProvider() { return provider; @@ -185,7 +191,7 @@ public class KeyManagerFactory { /** * Initializes this instance with the specified key store and password. - * + * * @param ks * the key store or {@code null} to use the default key store. * @param password @@ -197,55 +203,31 @@ public class KeyManagerFactory { * if a required algorithm is not available. * @throws UnrecoverableKeyException * if a key cannot be recovered. - * @since Android 1.0 */ - public final void init(KeyStore ks, char[] password) - throws KeyStoreException, NoSuchAlgorithmException, - UnrecoverableKeyException { + public final void init(KeyStore ks, char[] password) throws KeyStoreException, + NoSuchAlgorithmException, UnrecoverableKeyException { spiImpl.engineInit(ks, password); } /** * Initializes this instance with the specified factory parameters. - * + * * @param spec * the factory parameters. * @throws InvalidAlgorithmParameterException * if an error occurs. - * @since Android 1.0 */ - public final void init(ManagerFactoryParameters spec) - throws InvalidAlgorithmParameterException { + public final void init(ManagerFactoryParameters spec) throws InvalidAlgorithmParameterException { spiImpl.engineInit(spec); } /** * Returns a list of key managers, one instance for each type of key in the * key store. - * + * * @return a list of key managers. - * @since Android 1.0 */ public final KeyManager[] getKeyManagers() { return spiImpl.engineGetKeyManagers(); } - - /** - * Returns the default key manager factory algorithm name. - *

                        - * The default algorithm name is specified by the security property: - * {@code 'ssl.KeyManagerFactory.algorithm'}. - *

                        - * - * @return the default algorithm name. - * @since Android 1.0 - */ - public static final String getDefaultAlgorithm() { - return AccessController - .doPrivileged(new java.security.PrivilegedAction() { - public String run() { - return Security.getProperty(PROPERTY_NAME); - } - }); - } -} \ No newline at end of file +} diff --git a/libcore/x-net/src/main/java/javax/net/ssl/KeyManagerFactorySpi.java b/libcore/x-net/src/main/java/javax/net/ssl/KeyManagerFactorySpi.java index 7cdccf875..39925f9e7 100644 --- a/libcore/x-net/src/main/java/javax/net/ssl/KeyManagerFactorySpi.java +++ b/libcore/x-net/src/main/java/javax/net/ssl/KeyManagerFactorySpi.java @@ -26,23 +26,19 @@ import java.security.UnrecoverableKeyException; /** * The Service Provider Interface (SPI) for the * {@code KeyManagerFactory} class. - * - * @since Android 1.0 */ - public abstract class KeyManagerFactorySpi { - + /** * Creates a new {@code KeyManagerFactorySpi} instance. - * - * @since Android 1.0 */ public KeyManagerFactorySpi() { + super(); } /** * Initializes this instance with the specified key store and password. - * + * * @param ks * the key store or {@code null} to use the default key store. * @param password @@ -53,20 +49,17 @@ public abstract class KeyManagerFactorySpi { * if a required algorithm is not available. * @throws UnrecoverableKeyException * if a key cannot be recovered. - * @since Android 1.0 */ - protected abstract void engineInit(KeyStore ks, char[] password) - throws KeyStoreException, NoSuchAlgorithmException, - UnrecoverableKeyException; + protected abstract void engineInit(KeyStore ks, char[] password) throws KeyStoreException, + NoSuchAlgorithmException, UnrecoverableKeyException; /** * Initializes this instance with the specified factory parameters. - * + * * @param spec * the factory parameters. * @throws InvalidAlgorithmParameterException * if an error occurs. - * @since Android 1.0 */ protected abstract void engineInit(ManagerFactoryParameters spec) throws InvalidAlgorithmParameterException; @@ -74,9 +67,8 @@ public abstract class KeyManagerFactorySpi { /** * Returns a list of key managers, one instance for each type of key in the * key store. - * + * * @return a list of key managers. - * @since Android 1.0 */ protected abstract KeyManager[] engineGetKeyManagers(); -} \ No newline at end of file +} diff --git a/libcore/x-net/src/main/java/javax/net/ssl/KeyStoreBuilderParameters.java b/libcore/x-net/src/main/java/javax/net/ssl/KeyStoreBuilderParameters.java index b000fd59d..d30cc8aa1 100644 --- a/libcore/x-net/src/main/java/javax/net/ssl/KeyStoreBuilderParameters.java +++ b/libcore/x-net/src/main/java/javax/net/ssl/KeyStoreBuilderParameters.java @@ -25,12 +25,13 @@ import java.security.KeyStore; /** * The parameters for {@code KeyManager}s. The parameters are a list of * {@code KeyStore.Builder}s. - * - * @since Android 1.0 + * + * @since 1.5 + * @see KeyStore.Builder */ public class KeyStoreBuilderParameters implements ManagerFactoryParameters { - private List ksbuilders; + private final List ksbuilders; /** * Creates a new {@code KeyStoreBuilderParameters} with the specified key @@ -38,13 +39,10 @@ public class KeyStoreBuilderParameters implements ManagerFactoryParameters { * * @param builder * the key store builder. - * @since Android 1.0 */ public KeyStoreBuilderParameters(KeyStore.Builder builder) { - ksbuilders = new ArrayList(); - if (builder != null) { - ksbuilders.add(builder); - } + super(); + ksbuilders = Collections.singletonList(builder); } /** @@ -55,16 +53,17 @@ public class KeyStoreBuilderParameters implements ManagerFactoryParameters { * the list of key store builders * @throws IllegalArgumentException * if the specified list is empty. - * @since Android 1.0 */ + @SuppressWarnings("unchecked") public KeyStoreBuilderParameters(List parameters) { + super(); if (parameters == null) { throw new NullPointerException("Builders list is null"); } if (parameters.isEmpty()) { throw new IllegalArgumentException("Builders list is empty"); } - ksbuilders = new ArrayList(parameters); + ksbuilders = Collections.unmodifiableList(new ArrayList(parameters)); } /** @@ -72,9 +71,9 @@ public class KeyStoreBuilderParameters implements ManagerFactoryParameters { * with this parameters instance. * * @return the unmodifiable list of {@code KeyStore.Builder}s. - * @since Android 1.0 */ + @SuppressWarnings("unchecked") public List getParameters() { - return Collections.unmodifiableList(ksbuilders); + return ksbuilders; } } diff --git a/libcore/x-net/src/main/java/javax/net/ssl/ManagerFactoryParameters.java b/libcore/x-net/src/main/java/javax/net/ssl/ManagerFactoryParameters.java index 8909e6260..b90deeb0d 100644 --- a/libcore/x-net/src/main/java/javax/net/ssl/ManagerFactoryParameters.java +++ b/libcore/x-net/src/main/java/javax/net/ssl/ManagerFactoryParameters.java @@ -20,8 +20,8 @@ package javax.net.ssl; /** * The marker interface for key manager factory parameters. Its purpose is to * group key manager factory parameters objects. - * - * @since Android 1.0 + * + * @since 1.4 */ public interface ManagerFactoryParameters { -} \ No newline at end of file +} diff --git a/libcore/x-net/src/main/java/javax/net/ssl/SSLContext.java b/libcore/x-net/src/main/java/javax/net/ssl/SSLContext.java index 10d3a60fb..8a0a1579f 100644 --- a/libcore/x-net/src/main/java/javax/net/ssl/SSLContext.java +++ b/libcore/x-net/src/main/java/javax/net/ssl/SSLContext.java @@ -30,10 +30,7 @@ import org.apache.harmony.security.fortress.Engine; /** * The public API for secure socket protocol implementations. It acts as factory * for {@code SSLSocketFactory}'s and {@code SSLEngine}s. - * - * @since Android 1.0 */ - public class SSLContext { // StoreSSLContext service name private static final String SERVICE = "SSLContext"; @@ -41,36 +38,9 @@ public class SSLContext { // Used to access common engine functionality private static Engine engine = new Engine(SERVICE); - // Storeused provider - private final Provider provider; - - // Storeused SSLContextSpi implementation - private final SSLContextSpi spiImpl; - - // Storeused protocol - private final String protocol; - - /** - * Creates a new {@code SSLContext}. - * - * @param contextSpi - * the implementation delegate. - * @param provider - * the provider. - * @param protocol - * the protocol name. - * @since Android 1.0 - */ - protected SSLContext(SSLContextSpi contextSpi, Provider provider, - String protocol) { - this.provider = provider; - this.protocol = protocol; - this.spiImpl = contextSpi; - } - /** * Creates a new {@code SSLContext} instance for the specified protocol. - * + * * @param protocol * the requested protocol to create a context for. * @return the created {@code SSLContext} instance. @@ -79,24 +49,21 @@ public class SSLContext { * @throws NullPointerException * if {@code protocol} is {@code null} (instead of * NoSuchAlgorithmException as in 1.4 release) - * @since Android 1.0 */ - public static SSLContext getInstance(String protocol) - throws NoSuchAlgorithmException { + public static SSLContext getInstance(String protocol) throws NoSuchAlgorithmException { if (protocol == null) { throw new NullPointerException("protocol is null"); } synchronized (engine) { engine.getInstance(protocol, null); - return new SSLContext((SSLContextSpi) engine.spi, engine.provider, - protocol); + return new SSLContext((SSLContextSpi) engine.spi, engine.provider, protocol); } } /** * Creates a new {@code SSLContext} instance for the specified protocol from * the specified provider. - * + * * @param protocol * the requested protocol to create a context for. * @param provider @@ -110,7 +77,6 @@ public class SSLContext { * @throws NullPointerException * if {@code protocol} is {@code null} (instead of * NoSuchAlgorithmException as in 1.4 release) - * @since Android 1.0 */ public static SSLContext getInstance(String protocol, String provider) throws NoSuchAlgorithmException, NoSuchProviderException { @@ -130,7 +96,7 @@ public class SSLContext { /** * Creates a new {@code SSLContext} instance for the specified protocol from * the specified provider. - * + * * @param protocol * the requested protocol to create a context for * @param provider @@ -142,7 +108,6 @@ public class SSLContext { * @throws NullPointerException * if {@code protocol} is {@code null} (instead of * NoSuchAlgorithmException as in 1.4 release) - * @since Android 1.0 */ public static SSLContext getInstance(String protocol, Provider provider) throws NoSuchAlgorithmException { @@ -158,11 +123,32 @@ public class SSLContext { } } + private final Provider provider; + + private final SSLContextSpi spiImpl; + + private final String protocol; + + /** + * Creates a new {@code SSLContext}. + * + * @param contextSpi + * the implementation delegate. + * @param provider + * the provider. + * @param protocol + * the protocol name. + */ + protected SSLContext(SSLContextSpi contextSpi, Provider provider, String protocol) { + this.provider = provider; + this.protocol = protocol; + this.spiImpl = contextSpi; + } + /** * Returns the name of the secure socket protocol of this instance. - * + * * @return the name of the secure socket protocol of this instance. - * @since Android 1.0 */ public final String getProtocol() { return protocol; @@ -170,9 +156,8 @@ public class SSLContext { /** * Returns the provider of this {@code SSLContext} instance. - * + * * @return the provider of this {@code SSLContext} instance. - * @since Android 1.0 */ public final Provider getProvider() { return provider; @@ -182,7 +167,7 @@ public class SSLContext { * Initializes this {@code SSLContext} instance. All of the arguments are * optional, and the security providers will be searched for the required * implementations of the needed algorithms. - * + * * @param km * the key sources or {@code null}. * @param tm @@ -191,7 +176,6 @@ public class SSLContext { * the randomness source or {@code null.} * @throws KeyManagementException * if initializing this instance fails. - * @since Android 1.0 */ public final void init(KeyManager[] km, TrustManager[] tm, SecureRandom sr) throws KeyManagementException { @@ -200,9 +184,8 @@ public class SSLContext { /** * Returns a socket factory for this instance. - * + * * @return a socket factory for this instance. - * @since Android 1.0 */ public final SSLSocketFactory getSocketFactory() { return spiImpl.engineGetSocketFactory(); @@ -210,9 +193,8 @@ public class SSLContext { /** * Returns a server socket factory for this instance. - * + * * @return a server socket factory for this instance. - * @since Android 1.0 */ public final SSLServerSocketFactory getServerSocketFactory() { return spiImpl.engineGetServerSocketFactory(); @@ -220,11 +202,10 @@ public class SSLContext { /** * Creates an {@code SSLEngine} instance from this context. - * + * * @return an {@code SSLEngine} instance from this context. * @throws UnsupportedOperationException * if the provider does not support the operation. - * @since Android 1.0 */ public final SSLEngine createSSLEngine() { return spiImpl.engineCreateSSLEngine(); @@ -233,7 +214,7 @@ public class SSLContext { /** * Creates an {@code SSLEngine} instance from this context with the * specified hostname and port. - * + * * @param peerHost * the name of the host * @param peerPort @@ -241,7 +222,6 @@ public class SSLContext { * @return an {@code SSLEngine} instance from this context. * @throws UnsupportedOperationException * if the provider does not support the operation. - * @since Android 1.0 */ public final SSLEngine createSSLEngine(String peerHost, int peerPort) { return spiImpl.engineCreateSSLEngine(peerHost, peerPort); @@ -250,11 +230,10 @@ public class SSLContext { /** * Returns the SSL session context that encapsulates the set of SSL sessions * that can be used for handshake of server-side SSL sockets. - * + * * @return the SSL server session context for this context or {@code null} * if the underlying provider does not provide an implementation of * the {@code SSLSessionContext} interface. - * @since Android 1.0 */ public final SSLSessionContext getServerSessionContext() { return spiImpl.engineGetServerSessionContext(); @@ -263,13 +242,12 @@ public class SSLContext { /** * Returns the SSL session context that encapsulates the set of SSL sessions * that can be used for handshake of client-side SSL sockets. - * + * * @return the SSL client session context for this context or {@code null} * if the underlying provider does not provide an implementation of * the {@code SSLSessionContext} interface. - * @since Android 1.0 */ public final SSLSessionContext getClientSessionContext() { return spiImpl.engineGetClientSessionContext(); } -} \ No newline at end of file +} diff --git a/libcore/x-net/src/main/java/javax/net/ssl/SSLContextSpi.java b/libcore/x-net/src/main/java/javax/net/ssl/SSLContextSpi.java index 6b2a60efd..44d2c59e2 100644 --- a/libcore/x-net/src/main/java/javax/net/ssl/SSLContextSpi.java +++ b/libcore/x-net/src/main/java/javax/net/ssl/SSLContextSpi.java @@ -20,28 +20,23 @@ package javax.net.ssl; import java.security.KeyManagementException; import java.security.SecureRandom; - /** * The Service Provider Interface (SPI) for the {@code SSLContext} class. - * - * @since Android 1.0 */ - public abstract class SSLContextSpi { /** * Creates a new {@code SSLContextSpi} instance. - * - * @since Android 1.0 */ public SSLContextSpi() { + super(); } /** * Initializes this {@code SSLContext} instance. All of the arguments are * optional, and the security providers will be searched for the required * implementations of the needed algorithms. - * + * * @param km * the key sources or {@code null}. * @param tm @@ -50,31 +45,28 @@ public abstract class SSLContextSpi { * the randomness source or {@code null.} * @throws KeyManagementException * if initializing this instance fails. - * @since Android 1.0 - */ - protected abstract void engineInit(KeyManager[] km, TrustManager[] tm, - SecureRandom sr) throws KeyManagementException; + */ + protected abstract void engineInit(KeyManager[] km, TrustManager[] tm, SecureRandom sr) + throws KeyManagementException; /** * Returns a socket factory for this instance. - * + * * @return a socket factory for this instance. - * @since Android 1.0 */ protected abstract SSLSocketFactory engineGetSocketFactory(); /** * Returns a server socket factory for this instance. - * + * * @return a server socket factory for this instance. - * @since Android 1.0 */ protected abstract SSLServerSocketFactory engineGetServerSocketFactory(); /** * Creates an {@code SSLEngine} instance from this context with the * specified hostname and port. - * + * * @param host * the name of the host * @param port @@ -82,40 +74,36 @@ public abstract class SSLContextSpi { * @return an {@code SSLEngine} instance from this context. * @throws UnsupportedOperationException * if the provider does not support the operation. - * @since Android 1.0 */ protected abstract SSLEngine engineCreateSSLEngine(String host, int port); /** * Creates an {@code SSLEngine} instance from this context. - * + * * @return an {@code SSLEngine} instance from this context. * @throws UnsupportedOperationException * if the provider does not support the operation. - * @since Android 1.0 */ protected abstract SSLEngine engineCreateSSLEngine(); /** * Returns the SSL session context that encapsulates the set of SSL sessions * that can be used for the server side of the SSL handshake. - * + * * @return the SSL server session context for this context or {@code null} * if the underlying provider does not provide an implementation of * the {@code SSLSessionContext} interface. - * @since Android 1.0 */ protected abstract SSLSessionContext engineGetServerSessionContext(); /** * Returns the SSL session context that encapsulates the set of SSL sessions * that can be used for the client side of the SSL handshake. - * + * * @return the SSL client session context for this context or {@code null} * if the underlying provider does not provide an implementation of * the {@code SSLSessionContext} interface. - * @since Android 1.0 */ protected abstract SSLSessionContext engineGetClientSessionContext(); -} \ No newline at end of file +} diff --git a/libcore/x-net/src/main/java/javax/net/ssl/SSLEngine.java b/libcore/x-net/src/main/java/javax/net/ssl/SSLEngine.java index be5d266a2..46e11a4f3 100644 --- a/libcore/x-net/src/main/java/javax/net/ssl/SSLEngine.java +++ b/libcore/x-net/src/main/java/javax/net/ssl/SSLEngine.java @@ -15,48 +15,62 @@ * limitations under the License. */ - package javax.net.ssl; +package javax.net.ssl; import java.nio.ByteBuffer; -import java.nio.ReadOnlyBufferException; /** * The abstract implementation of secure communications using SSL, TLS, or other - * protocols. It includes the setup, handshake, and encrypt/decrypt functionality - * needed to create a secure connection. - * - * @since Android 1.0 + * protocols. It includes the setup, handshake, and encrypt/decrypt + * functionality needed to create a secure connection. + * + * @since 1.5 */ public abstract class SSLEngine { - // Store host value - private final String host; - - // Store port value - private final int port; + private final String peerHost; + private final int peerPort; /** * Creates a new {@code SSLEngine} instance. - * - * @since Android 1.0 */ protected SSLEngine() { - host = null; - port = -1; + super(); + peerHost = null; + peerPort = -1; } /** * Creates a new {@code SSLEngine} instance with the specified host and * port. - * + * * @param host * the name of the host. * @param port * the port of the host. - * @since Android 1.0 */ protected SSLEngine(String host, int port) { - this.host = host; - this.port = port; + super(); + this.peerHost = host; + this.peerPort = port; + } + + /** + * Returns the name of the peer host. + * + * @return the name of the peer host, or {@code null} if none is available. + */ + public String getPeerHost() { + return peerHost; + } + + /** + * Returns the port number of the peer host. + * + * @return the port number of the peer host, or {@code -1} is none is + * available. + */ + public int getPeerPort() { + return peerPort; } /** @@ -65,33 +79,28 @@ public abstract class SSLEngine { * Calling this method is not needed for the initial handshake: it will be * called by {@code wrap} or {@code unwrap} if the initial handshake has not * been started yet. - *

                        - * + * * @throws SSLException * if starting the handshake fails. * @throws IllegalStateException * if the engine does not have all the needed settings (e.g. * client/server mode not set). - * @since Android 1.0 */ public abstract void beginHandshake() throws SSLException; /** * Notifies this engine instance that no more inbound network data will be * sent to this engine. - * + * * @throws SSLException * if this engine did not receive a needed protocol specific * close notification message from the peer. - * @since Android 1.0 */ public abstract void closeInbound() throws SSLException; /** * Notifies this engine instance that no more outbound application data will * be sent to this engine. - * - * @since Android 1.0 */ public abstract void closeOutbound(); @@ -102,84 +111,56 @@ public abstract class SSLEngine { * that a delegated task result is needed. In this case the * {@link Runnable#run() run} method of the returned {@code Runnable} * delegated task must be called. - * + * * @return a delegate task, or {@code null} if none are available. - * @since Android 1.0 */ public abstract Runnable getDelegatedTask(); /** * Returns the SSL cipher suite names that are enabled in this engine * instance. - * + * * @return the SSL cipher suite names that are enabled in this engine * instance. - * @since Android 1.0 */ public abstract String[] getEnabledCipherSuites(); /** * Returns the protocol version names that are enabled in this engine * instance. - * + * * @return the protocol version names that are enabled in this engine * instance. - * @since Android 1.0 */ public abstract String[] getEnabledProtocols(); /** * Returns whether new SSL sessions may be established by this engine. - * + * * @return {@code true} if new session may be established, {@code false} if * existing sessions must be reused. - * @since Android 1.0 */ public abstract boolean getEnableSessionCreation(); /** * Returns the status of the handshake of this engine instance. - * + * * @return the status of the handshake of this engine instance. - * @since Android 1.0 */ public abstract SSLEngineResult.HandshakeStatus getHandshakeStatus(); /** * Returns whether this engine instance will require client authentication. - * + * * @return {@code true} if this engine will require client authentication, * {@code false} if no client authentication is needed. - * @since Android 1.0 */ public abstract boolean getNeedClientAuth(); - /** - * Returns the name of the peer host. - * - * @return the name of the peer host, or {@code null} if none is available. - * @since Android 1.0 - */ - public String getPeerHost() { - return host; - } - - /** - * Returns the port number of the peer host. - * - * @return the port number of the peer host, or {@code -1} is none is - * available. - * @since Android 1.0 - */ - public int getPeerPort() { - return port; - } - /** * Returns the SSL session for this engine instance. - * + * * @return the SSL session for this engine instance. - * @since Android 1.0 */ public abstract SSLSession getSession(); @@ -187,55 +168,49 @@ public abstract class SSLEngine { * Returns the SSL cipher suite names that are supported by this engine. * These cipher suites can be enabled using * {@link #setEnabledCipherSuites(String[])}. - * + * * @return the SSL cipher suite names that are supported by this engine. - * @since Android 1.0 */ public abstract String[] getSupportedCipherSuites(); /** * Returns the protocol names that are supported by this engine. These * protocols can be enables using {@link #setEnabledProtocols(String[])}. - * + * * @return the protocol names that are supported by this engine. - * @since Android 1.0 */ public abstract String[] getSupportedProtocols(); /** * Returns whether this engine is set to act in client mode when * handshaking. - * + * * @return {@code true} if the engine is set to do handshaking in client * mode. - * @since Android 1.0 */ public abstract boolean getUseClientMode(); /** * Returns whether this engine will request client authentication. - * + * * @return {@code true} if client authentication will be requested, * {@code false} otherwise. - * @since Android 1.0 */ public abstract boolean getWantClientAuth(); /** * Returns whether no more inbound data will be accepted by this engine. - * + * * @return {@code true} if no more inbound data will be accepted by this * engine, {@code false} otherwise. - * @since Android 1.0 */ public abstract boolean isInboundDone(); /** * Returns whether no more outbound data will be produced by this engine. - * + * * @return {@code true} if no more outbound data will be producted by this * engine, {@code otherwise} false. - * @since Android 1.0 */ public abstract boolean isOutboundDone(); @@ -243,13 +218,12 @@ public abstract class SSLEngine { * Sets the SSL cipher suite names that should be enabled in this engine * instance. Only cipher suites listed by {@code getSupportedCipherSuites()} * are allowed. - * + * * @param suites * the SSL cipher suite names to be enabled. * @throws IllegalArgumentException * if one of the specified cipher suites is not supported, or if * {@code suites} is {@code null}. - * @since Android 1.0 */ public abstract void setEnabledCipherSuites(String[] suites); @@ -257,23 +231,21 @@ public abstract class SSLEngine { * Sets the protocol version names that should be enabled in this engine * instance. Only protocols listed by {@code getSupportedProtocols()} are * allowed. - * + * * @param protocols * the protocol version names to be enabled. * @throws IllegalArgumentException * if one of the protocol version names is not supported, or if * {@code protocols} is {@code null}. - * @since Android 1.0 */ public abstract void setEnabledProtocols(String[] protocols); /** * Sets whether new SSL sessions may be established by this engine instance. - * + * * @param flag * {@code true} if new SSL sessions may be established, * {@code false} if existing SSL sessions must be reused. - * @since Android 1.0 */ public abstract void setEnableSessionCreation(boolean flag); @@ -286,25 +258,23 @@ public abstract class SSLEngine { *
                      • no authentication needed
                      • * * This method overrides the setting of {@link #setWantClientAuth(boolean)}. - * + * * @param need * {@code true} if client authentication is required, * {@code false} if no authentication is needed. - * @since Android 1.0 */ public abstract void setNeedClientAuth(boolean need); /** * Sets whether this engine should act in client (or server) mode when * handshaking. - * + * * @param mode * {@code true} if this engine should act in client mode, * {@code false} if not. * @throws IllegalArgumentException * if this method is called after starting the initial * handshake. - * @since Android 1.0 */ public abstract void setUseClientMode(boolean mode); @@ -317,11 +287,10 @@ public abstract class SSLEngine { *
                      • no authentication needed
                      • * * This method overrides the setting of {@link #setNeedClientAuth(boolean)}. - * + * * @param want * {@code true} if client authentication should be requested, * {@code false} if no authentication is needed. - * @since Android 1.0 */ public abstract void setWantClientAuth(boolean want); @@ -329,7 +298,7 @@ public abstract class SSLEngine { * Decodes the incoming network data buffer into application data buffers. * If a handshake has not been started yet, it will automatically be * started. - * + * * @param src * the buffer with incoming network data * @param dsts @@ -339,14 +308,14 @@ public abstract class SSLEngine { * the offset in the array of destination buffers to which data * is to be transferred. * @param length - * the maximum number of destination buffers to be used. + * the maximum number of destination buffers to be used. * @return the result object of this operation. * @throws SSLException * if a problem occurred while processing the data. * @throws IndexOutOfBoundsException * if {@code length} is greater than * {@code dsts.length - offset}. - * @throws ReadOnlyBufferException + * @throws java.nio.ReadOnlyBufferException * if one of the destination buffers is read-only. * @throws IllegalArgumentException * if {@code src}, {@code dsts}, or one of the entries in @@ -354,16 +323,15 @@ public abstract class SSLEngine { * @throws IllegalStateException * if the engine does not have all the needed settings (e.g. * client/server mode not set). - * @since Android 1.0 */ - public abstract SSLEngineResult unwrap(ByteBuffer src, ByteBuffer[] dsts, - int offset, int length) throws SSLException; + public abstract SSLEngineResult unwrap(ByteBuffer src, ByteBuffer[] dsts, int offset, int length) + throws SSLException; /** * Encodes the outgoing application data buffers into the network data * buffer. If a handshake has not been started yet, it will automatically be * started. - * + * * @param srcs * the array of source buffers of outgoing application data. * @param offset @@ -379,7 +347,7 @@ public abstract class SSLEngine { * @throws IndexOutOfBoundsException * if {@code length} is greater than * {@code srcs.length - offset}. - * @throws ReadOnlyBufferException + * @throws java.nio.ReadOnlyBufferException * if the destination buffer is readonly. * @throws IllegalArgumentException * if {@code srcs}, {@code dst}, or one the entries in @@ -387,10 +355,9 @@ public abstract class SSLEngine { * @throws IllegalStateException * if the engine does not have all the needed settings (e.g. * client/server mode not set). - * @since Android 1.0 */ - public abstract SSLEngineResult wrap(ByteBuffer[] srcs, int offset, - int length, ByteBuffer dst) throws SSLException; + public abstract SSLEngineResult wrap(ByteBuffer[] srcs, int offset, int length, ByteBuffer dst) + throws SSLException; /** * Decodes the incoming network data buffer into the application data @@ -404,26 +371,15 @@ public abstract class SSLEngine { * @return the result object of this operation. * @throws SSLException * if a problem occurred while processing the data. - * @throws ReadOnlyBufferException + * @throws java.nio.ReadOnlyBufferException * if one of the destination buffers is read-only. * @throws IllegalArgumentException * if {@code src} or {@code dst} is {@code null}. * @throws IllegalStateException * if the engine does not have all the needed settings (e.g. * client/server mode not set). - * @since Android 1.0 - */ - public SSLEngineResult unwrap(ByteBuffer src, ByteBuffer dst) - throws SSLException { -// if (src == null) { -// throw new IllegalArgumentException("Byte buffer src is null"); -// } -// if (dst == null) { -// throw new IllegalArgumentException("Byte buffer dst is null"); -// } -// if (dst.isReadOnly()) { -// throw new ReadOnlyBufferException(); -// } + */ + public SSLEngineResult unwrap(ByteBuffer src, ByteBuffer dst) throws SSLException { return unwrap(src, new ByteBuffer[] { dst }, 0, 1); } @@ -431,7 +387,7 @@ public abstract class SSLEngine { * Decodes the incoming network data buffer into the application data * buffers. If a handshake has not been started yet, it will automatically * be started. - * + * * @param src * the buffer with incoming network data * @param dsts @@ -440,32 +396,18 @@ public abstract class SSLEngine { * @return the result object of this operation. * @throws SSLException * if a problem occurred while processing the data. - * @throws ReadOnlyBufferException + * @throws java.nio.ReadOnlyBufferException * if one of the destination buffers is read-only. * @throws IllegalArgumentException * if {@code src} or {@code dsts} is {@code null}. * @throws IllegalStateException * if the engine does not have all the needed settings (e.g. * client/server mode not set). - * @since Android 1.0 */ - public SSLEngineResult unwrap(ByteBuffer src, ByteBuffer[] dsts) - throws SSLException { -// if (src == null) { -// throw new IllegalArgumentException("Byte buffer src is null"); -// } + public SSLEngineResult unwrap(ByteBuffer src, ByteBuffer[] dsts) throws SSLException { if (dsts == null) { throw new IllegalArgumentException("Byte buffer array dsts is null"); } -// for (int i = 0; i < dsts.length; i++) { -// if (dsts[i] == null) { -// throw new IllegalArgumentException("Byte buffer dsts[" + i -// + "] is null"); -// } -// if (dsts[i].isReadOnly()) { -// throw new ReadOnlyBufferException(); -// } -// } return unwrap(src, dsts, 0, dsts.length); } @@ -481,32 +423,18 @@ public abstract class SSLEngine { * @return the result object of this operation. * @throws SSLException * if a problem occurred while processing the data. - * @throws ReadOnlyBufferException + * @throws java.nio.ReadOnlyBufferException * if the destination buffer is readonly. * @throws IllegalArgumentException * if {@code srcs} or {@code dst} is {@code null}. * @throws IllegalStateException * if the engine does not have all the needed settings (e.g. * client/server mode not set). - * @since Android 1.0 */ - public SSLEngineResult wrap(ByteBuffer[] srcs, ByteBuffer dst) - throws SSLException { + public SSLEngineResult wrap(ByteBuffer[] srcs, ByteBuffer dst) throws SSLException { if (srcs == null) { throw new IllegalArgumentException("Byte buffer array srcs is null"); } -// for (int i = 0; i < srcs.length; i++) { -// if (srcs[i] == null) { -// throw new IllegalArgumentException("Byte buffer srcs[" + i -// + "] is null"); -// } -// } -// if (dst == null) { -// throw new IllegalArgumentException("Byte buffer array dst is null"); -// } -// if (dst.isReadOnly()) { -// throw new ReadOnlyBufferException(); -// } return wrap(srcs, 0, srcs.length, dst); } @@ -522,26 +450,15 @@ public abstract class SSLEngine { * @return the result object of this operation. * @throws SSLException * if a problem occurred while processing the data. - * @throws ReadOnlyBufferException + * @throws java.nio.ReadOnlyBufferException * if the destination buffer is readonly. * @throws IllegalArgumentException * if {@code src} or {@code dst} is {@code null}. * @throws IllegalStateException * if the engine does not have all the needed settings (e.g. * client/server mode not set). - * @since Android 1.0 - */ - public SSLEngineResult wrap(ByteBuffer src, ByteBuffer dst) - throws SSLException { -// if (src == null) { -// throw new IllegalArgumentException("Byte buffer src is null"); -// } -// if (dst == null) { -// throw new IllegalArgumentException("Byte buffer dst is null"); -// } -// if (dst.isReadOnly()) { -// throw new ReadOnlyBufferException(); -// } + */ + public SSLEngineResult wrap(ByteBuffer src, ByteBuffer dst) throws SSLException { return wrap(new ByteBuffer[] { src }, 0, 1, dst); } } diff --git a/libcore/x-net/src/main/java/javax/net/ssl/SSLEngineResult.java b/libcore/x-net/src/main/java/javax/net/ssl/SSLEngineResult.java index dc55836d3..8a98831f7 100644 --- a/libcore/x-net/src/main/java/javax/net/ssl/SSLEngineResult.java +++ b/libcore/x-net/src/main/java/javax/net/ssl/SSLEngineResult.java @@ -20,11 +20,64 @@ package javax.net.ssl; /** * The result object describing the state of the {@code SSLEngine} produced * by the {@code wrap()} and {@code unwrap()} operations. - * - * @since Android 1.0 */ public class SSLEngineResult { - + + /** + * The {@code enum} describing the state of the current handshake. + */ + public enum HandshakeStatus { + /** + * No handshake in progress. + */ + NOT_HANDSHAKING, + /** + * The handshake is finished. + */ + FINISHED, + /** + * The results of one (or more) delegated tasks are needed to continue + * the handshake. + */ + NEED_TASK, + /** + * The engine must send data to the remote side to continue the + * handshake. + */ + NEED_WRAP, + /** + * The engine needs to receive data from the remote side to continue the + * handshake. + */ + NEED_UNWRAP + } + + /** + * The {@code enum} describing the result of the {@code SSLEngine} + * operation. + */ + public static enum Status { + /** + * The size of the destination buffer is too small to hold the result of + * the current operation. + */ + BUFFER_OVERFLOW, + /** + * There were not enough bytes available in the source buffer to + * complete the current operation. + */ + BUFFER_UNDERFLOW, + /** + * The operation closed this side of the communication or was already + * closed. + */ + CLOSED, + /** + * The operation completed successfully. + */ + OK + } + // Store Status object private final SSLEngineResult.Status status; @@ -40,7 +93,7 @@ public class SSLEngineResult { /** * Creates a new {@code SSLEngineResult} instance with the specified state * values. - * + * * @param status * the return value of the {@code SSLEngine} operation. * @param handshakeStatus @@ -53,11 +106,9 @@ public class SSLEngineResult { * if {@code status} or {@code handshakeStatus} is {@code null}, * or if {@code bytesConsumed} or {@code bytesProduces} are * negative. - * @since Android 1.0 */ public SSLEngineResult(SSLEngineResult.Status status, - SSLEngineResult.HandshakeStatus handshakeStatus, int bytesConsumed, - int bytesProduced) { + SSLEngineResult.HandshakeStatus handshakeStatus, int bytesConsumed, int bytesProduced) { if (status == null) { throw new IllegalArgumentException("status is null"); } @@ -78,9 +129,8 @@ public class SSLEngineResult { /** * Returns the return value of the {@code SSLEngine} operation. - * + * * @return the return value of the {@code SSLEngine} operation. - * @since Android 1.0 */ public final Status getStatus() { return status; @@ -88,9 +138,8 @@ public class SSLEngineResult { /** * Returns the status of the current handshake. - * + * * @return the status of the current handshake. - * @since Android 1.0 */ public final HandshakeStatus getHandshakeStatus() { return handshakeStatus; @@ -98,9 +147,8 @@ public class SSLEngineResult { /** * Returns the number of bytes retrieved from the source buffer(s). - * + * * @return the number of bytes retrieved from the source buffer(s). - * @since Android 1.0 */ public final int bytesConsumed() { return bytesConsumed; @@ -108,106 +156,17 @@ public class SSLEngineResult { /** * Returns the number of bytes transferred to the destination buffer(s). - * + * * @return the number of bytes transferred to the destination buffer(s). - * @since Android 1.0 */ public final int bytesProduced() { return bytesProduced; } - /** - * Returns a string representation of this instance. - * - * @return a string representation of this instance. - * @since Android 1.0 - */ + @Override public String toString() { - StringBuffer sb = new StringBuffer("SSLEngineReport: Status = "); - sb.append(status.toString()); - sb.append(" HandshakeStatus = "); - sb.append(handshakeStatus.toString()); - sb.append("\n bytesConsumed = "); - sb.append(Integer.toString(bytesConsumed)); - sb.append(" bytesProduced = "); - sb.append(Integer.toString(bytesProduced)); - return sb.toString(); - } - - /** - * The {@code enum} describing the state of the current handshake. - * - * @since Android 1.0 - */ - public enum HandshakeStatus { - /** - * No handshake in progress. - * - * @since Android 1.0 - */ - NOT_HANDSHAKING, - /** - * The handshake is finished. - * - * @since Android 1.0 - */ - FINISHED, - /** - * The results of one (or more) delegated tasks are needed to continue - * the handshake. - * - * @since Android 1.0 - */ - NEED_TASK, - /** - * The engine must send data to the remote side to continue the - * handshake. - * - * @since Android 1.0 - */ - NEED_WRAP, - /** - * The engine needs to receive data from the remote side to continue the - * handshake. - * - * @since Android 1.0 - */ - NEED_UNWRAP - } - - /** - * The {@code enum} describing the result of the {@code SSLEngine} - * operation. - * - * @since Android 1.0 - */ - public static enum Status { - /** - * The size of the destination buffer is too small to hold the result of - * the current operation. - * - * @since Android 1.0 - */ - BUFFER_OVERFLOW, - /** - * There were not enough bytes available in the source buffer to - * complete the current operation. - * - * @since Android 1.0 - */ - BUFFER_UNDERFLOW, - /** - * The operation closed this side of the communication or was already - * closed. - * - * @since Android 1.0 - */ - CLOSED, - /** - * The operation completed successfully. - * - * @since Android 1.0 - */ - OK + return "SSLEngineReport: Status = " + status + " HandshakeStatus = " + handshakeStatus + + "\n bytesConsumed = " + bytesConsumed + " bytesProduced = " + + bytesProduced; } -} \ No newline at end of file +} diff --git a/libcore/x-net/src/main/java/javax/net/ssl/SSLException.java b/libcore/x-net/src/main/java/javax/net/ssl/SSLException.java index 43ba493dc..5d716f79b 100644 --- a/libcore/x-net/src/main/java/javax/net/ssl/SSLException.java +++ b/libcore/x-net/src/main/java/javax/net/ssl/SSLException.java @@ -21,19 +21,15 @@ import java.io.IOException; /** * The base class for all SSL related exceptions. - * - * @since Android 1.0 */ public class SSLException extends IOException { - private static final long serialVersionUID = 4511006460650708967L; /** * Creates a new {@code SSLException} with the specified reason. - * + * * @param reason * the reason for the exception. - * @since Android 1.0 */ public SSLException(String reason) { super(reason); @@ -41,12 +37,11 @@ public class SSLException extends IOException { /** * Creates a new {@code SSLException} with the specified message and cause. - * + * * @param message * the detail message for the exception. * @param cause * the cause. - * @since Android 1.0 */ public SSLException(String message, Throwable cause) { super(message); @@ -55,10 +50,9 @@ public class SSLException extends IOException { /** * Creates a new {@code SSLException} with the specified cause. - * + * * @param cause * the cause - * @since Android 1.0 */ public SSLException(Throwable cause) { super(cause == null ? null : cause.toString()); diff --git a/libcore/x-net/src/main/java/javax/net/ssl/SSLHandshakeException.java b/libcore/x-net/src/main/java/javax/net/ssl/SSLHandshakeException.java index 81e44dc45..1c17ae711 100644 --- a/libcore/x-net/src/main/java/javax/net/ssl/SSLHandshakeException.java +++ b/libcore/x-net/src/main/java/javax/net/ssl/SSLHandshakeException.java @@ -20,19 +20,16 @@ package javax.net.ssl; /** * The exception that is thrown when a handshake could not be completed * successfully. - * - * @since Android 1.0 */ public class SSLHandshakeException extends SSLException { - + private static final long serialVersionUID = -5045881315018326890L; /** * Creates a new {@code SSLHandshakeException} with the specified message. - * + * * @param reason * the detail message for the exception. - * @since Android 1.0 */ public SSLHandshakeException(String reason) { super(reason); diff --git a/libcore/x-net/src/main/java/javax/net/ssl/SSLKeyException.java b/libcore/x-net/src/main/java/javax/net/ssl/SSLKeyException.java index cc4bc8496..6d8167658 100644 --- a/libcore/x-net/src/main/java/javax/net/ssl/SSLKeyException.java +++ b/libcore/x-net/src/main/java/javax/net/ssl/SSLKeyException.java @@ -18,22 +18,19 @@ package javax.net.ssl; /** - * The exception that is thrown when an invalid SSL key is encountered. - * - * @since Android 1.0 + * The exception that is thrown when an invalid SSL key is encountered. */ public class SSLKeyException extends SSLException { private static final long serialVersionUID = -8071664081941937874L; - + /** * Creates a new {@code SSLKeyException} with the specified message. - * + * * @param reason * the detail message for the exception. - * @since Android 1.0 */ public SSLKeyException(String reason) { super(reason); } -} \ No newline at end of file +} diff --git a/libcore/x-net/src/main/java/javax/net/ssl/SSLPeerUnverifiedException.java b/libcore/x-net/src/main/java/javax/net/ssl/SSLPeerUnverifiedException.java index 6e5734a82..bb5bd6485 100644 --- a/libcore/x-net/src/main/java/javax/net/ssl/SSLPeerUnverifiedException.java +++ b/libcore/x-net/src/main/java/javax/net/ssl/SSLPeerUnverifiedException.java @@ -20,22 +20,19 @@ package javax.net.ssl; /** * The exception that is thrown when the identity of a peer has not beed * verified. - * - * @since Android 1.0 */ public class SSLPeerUnverifiedException extends SSLException { - + private static final long serialVersionUID = -8919512675000600547L; /** * Creates a new {@code SSLPeerUnverifiedException} with the specified * message. - * + * * @param reason * the detail message for the exception. - * @since Android 1.0 */ public SSLPeerUnverifiedException(String reason) { super(reason); } -} \ No newline at end of file +} diff --git a/libcore/x-net/src/main/java/javax/net/ssl/SSLPermission.java b/libcore/x-net/src/main/java/javax/net/ssl/SSLPermission.java index afc1abb85..5b5c76f3c 100644 --- a/libcore/x-net/src/main/java/javax/net/ssl/SSLPermission.java +++ b/libcore/x-net/src/main/java/javax/net/ssl/SSLPermission.java @@ -23,26 +23,22 @@ import java.security.BasicPermission; * The class representing a network permission. *

                        * The following permissions are defined, allowing the specified action: - *

                        *
                        *
                        {@code "setHostnameVerifier"}
                        - *
                        setting a callback object for additional verification of a hostname mismatch.
                        + *
                        setting a callback object for additional verification of a hostname mismatch.
                        *
                        {@code "getSSLSessionContext"}
                        *
                        getting the {@code SSLSessionContext} of an {@code SSLSession}.
                        - *
                        - * - * @since Android 1.0 + * */ public final class SSLPermission extends BasicPermission { - + private static final long serialVersionUID = -3456898025505876775L; - + /** * Creates a new {@code SSLPermission} with the specified name. - * + * * @param name * the permission name. - * @since Android 1.0 */ public SSLPermission(String name) { super(name); @@ -50,14 +46,13 @@ public final class SSLPermission extends BasicPermission { /** * Creates a new {@code SSLPermission} with the specified name. - * + * * @param name * the permission name. * @param actions * is ignored and should be {@code null}. - * @since Android 1.0 */ public SSLPermission(String name, String actions) { super(name, actions); } -} \ No newline at end of file +} diff --git a/libcore/x-net/src/main/java/javax/net/ssl/SSLProtocolException.java b/libcore/x-net/src/main/java/javax/net/ssl/SSLProtocolException.java index ef49ced1a..50ed74dc2 100644 --- a/libcore/x-net/src/main/java/javax/net/ssl/SSLProtocolException.java +++ b/libcore/x-net/src/main/java/javax/net/ssl/SSLProtocolException.java @@ -20,21 +20,18 @@ package javax.net.ssl; /** * The exception that is thrown when an error in the operation of the SSL * protocol is encountered. - * - * @since Android 1.0 */ public class SSLProtocolException extends SSLException { - + private static final long serialVersionUID = 5445067063799134928L; /** * Creates a new {@code SSLProtocolException} with the specified message. - * + * * @param reason * the detail message for the exception. - * @since Android 1.0 */ public SSLProtocolException(String reason) { super(reason); } -} \ No newline at end of file +} diff --git a/libcore/x-net/src/main/java/javax/net/ssl/SSLServerSocket.java b/libcore/x-net/src/main/java/javax/net/ssl/SSLServerSocket.java index dc41556f0..8bd89181c 100644 --- a/libcore/x-net/src/main/java/javax/net/ssl/SSLServerSocket.java +++ b/libcore/x-net/src/main/java/javax/net/ssl/SSLServerSocket.java @@ -24,20 +24,16 @@ import java.net.ServerSocket; /** * The extension of {@code ServerSocket} which provides secure server sockets * based on protocols like SSL, TLS, or others. - * - * @since Android 1.0 */ public abstract class SSLServerSocket extends ServerSocket { - + /** * Only to be used by subclasses. *

                        * Creates a TCP server socket with the default authentication context. - *

                        - * + * * @throws IOException * if creating the socket fails. - * @since Android 1.0 */ protected SSLServerSocket() throws IOException { super(); @@ -49,12 +45,10 @@ public abstract class SSLServerSocket extends ServerSocket { * Creates a TCP server socket on the specified port with the default * authentication context. The connection's default backlog size is 50 * connections. - *

                        * @param port * the port to listen on. * @throws IOException * if creating the socket fails. - * @since Android 1.0 */ protected SSLServerSocket(int port) throws IOException { super(port); @@ -65,14 +59,13 @@ public abstract class SSLServerSocket extends ServerSocket { *

                        * Creates a TCP server socket on the specified port using the specified * backlog and the default authentication context. - * + * * @param port * the port to listen on. * @param backlog * the number of pending connections to queue. * @throws IOException * if creating the socket fails. - * @since Android 1.0 */ protected SSLServerSocket(int port, int backlog) throws IOException { super(port, backlog); @@ -84,8 +77,7 @@ public abstract class SSLServerSocket extends ServerSocket { * Creates a TCP server socket on the specified port, using the specified * backlog, listening on the specified interface, and using the default * authentication context. - *

                        - * + * * @param port * the port the listen on. * @param backlog @@ -94,20 +86,17 @@ public abstract class SSLServerSocket extends ServerSocket { * the address of the interface to accept connections on. * @throws IOException * if creating the socket fails. - * @since Android 1.0 */ - protected SSLServerSocket(int port, int backlog, InetAddress address) - throws IOException { + protected SSLServerSocket(int port, int backlog, InetAddress address) throws IOException { super(port, backlog, address); } - + /** * Returns the names of the enabled cipher suites to be used for new * connections. - * + * * @return the names of the enabled cipher suites to be used for new * connections. - * @since Android 1.0 */ public abstract String[] getEnabledCipherSuites(); @@ -115,50 +104,45 @@ public abstract class SSLServerSocket extends ServerSocket { * Sets the names of the cipher suites to be enabled for new connections. * Only cipher suites returned by {@link #getSupportedCipherSuites()} are * allowed. - * + * * @param suites * the names of the to be enabled cipher suites. * @throws IllegalArgumentException * if one of the cipher suite names is not supported. - * @since Android 1.0 */ public abstract void setEnabledCipherSuites(String[] suites); /** * Returns the names of the supported cipher suites. - * + * * @return the names of the supported cipher suites. - * @since Android 1.0 */ public abstract String[] getSupportedCipherSuites(); /** * Returns the names of the supported protocols. - * + * * @return the names of the supported protocols. - * @since Android 1.0 */ public abstract String[] getSupportedProtocols(); /** * Returns the names of the enabled protocols to be used for new * connections. - * + * * @return the names of the enabled protocols to be used for new * connections. - * @since Android 1.0 */ public abstract String[] getEnabledProtocols(); /** * Sets the names of the protocols to be enabled for new connections. Only * protocols returned by {@link #getSupportedProtocols()} are allowed. - * + * * @param protocols * the names of the to be enabled protocols. * @throws IllegalArgumentException * if one of the protocols is not supported. - * @since Android 1.0 */ public abstract void setEnabledProtocols(String[] protocols); @@ -171,21 +155,19 @@ public abstract class SSLServerSocket extends ServerSocket { *
                      • no authentication needed
                      • * * This method overrides the setting of {@link #setWantClientAuth(boolean)}. - * + * * @param need * {@code true} if client authentication is required, * {@code false} if no authentication is needed. - * @since Android 1.0 */ public abstract void setNeedClientAuth(boolean need); /** * Returns whether server-mode connections will be configured to require * client authentication. - * + * * @return {@code true} if client authentication is required, {@code false} * if no client authentication is needed. - * @since Android 1.0 */ public abstract boolean getNeedClientAuth(); @@ -198,59 +180,53 @@ public abstract class SSLServerSocket extends ServerSocket { *
                      • no authentication needed
                      • * * This method overrides the setting of {@link #setNeedClientAuth(boolean)}. - * + * * @param want * {@code true} if client authentication should be requested, * {@code false} if no authentication is needed. - * @since Android 1.0 */ public abstract void setWantClientAuth(boolean want); /** * Returns whether server-mode connections will be configured to request * client authentication. - * + * * @return {@code true} is client authentication will be requested, * {@code false} if no client authentication is needed. - * @since Android 1.0 */ public abstract boolean getWantClientAuth(); /** * Sets whether new connections should act in client mode when handshaking. - * + * * @param mode * {@code true} if new connections should act in client mode, * {@code false} if not. - * @since Android 1.0 */ public abstract void setUseClientMode(boolean mode); /** * Returns whether new connection will act in client mode when handshaking. - * + * * @return {@code true} if new connections will act in client mode when * handshaking, {@code false} if not. - * @since Android 1.0 */ public abstract boolean getUseClientMode(); /** * Sets whether new SSL sessions may be established for new connections. - * + * * @param flag * {@code true} if new SSL sessions may be established, * {@code false} if existing SSL sessions must be reused. - * @since Android 1.0 */ public abstract void setEnableSessionCreation(boolean flag); - + /** * Returns whether new SSL sessions may be established for new connections. - * + * * @return {@code true} if new SSL sessions may be established, * {@code false} if existing SSL sessions must be reused. - * @since Android 1.0 */ public abstract boolean getEnableSessionCreation(); } diff --git a/libcore/x-net/src/main/java/javax/net/ssl/SSLServerSocketFactory.java b/libcore/x-net/src/main/java/javax/net/ssl/SSLServerSocketFactory.java index 14f467c8e..ccb2c5d4f 100644 --- a/libcore/x-net/src/main/java/javax/net/ssl/SSLServerSocketFactory.java +++ b/libcore/x-net/src/main/java/javax/net/ssl/SSLServerSocketFactory.java @@ -18,59 +18,46 @@ package javax.net.ssl; import java.security.AccessController; +import java.security.PrivilegedAction; import java.security.Security; import javax.net.ServerSocketFactory; /** * The factory for SSL server sockets. - * - * @since Android 1.0 */ public abstract class SSLServerSocketFactory extends ServerSocketFactory { -// TODO EXPORT CONTROL - + // TODO EXPORT CONTROL + // The default SSL socket factory private static ServerSocketFactory defaultServerSocketFactory; private static String defaultName; - - /** - * Creates a new {@code SSLServerSocketFactory} instance. - * - * @since Android 1.0 - */ - protected SSLServerSocketFactory() { - super(); - } /** * Returns the default {@code SSLServerSocketFactory} instance. The default * implementation is defined by the security property * "ssl.ServerSocketFactory.provider". - * + * * @return the default {@code SSLServerSocketFactory} instance. - * @since Android 1.0 */ - public static ServerSocketFactory getDefault() { + public static synchronized ServerSocketFactory getDefault() { if (defaultServerSocketFactory != null) { return defaultServerSocketFactory; } if (defaultName == null) { - AccessController.doPrivileged(new java.security.PrivilegedAction(){ - public Object run() { + AccessController.doPrivileged(new PrivilegedAction() { + public Void run() { defaultName = Security.getProperty("ssl.ServerSocketFactory.provider"); - if (defaultName != null) { + if (defaultName != null) { ClassLoader cl = Thread.currentThread().getContextClassLoader(); if (cl == null) { cl = ClassLoader.getSystemClassLoader(); } try { - defaultServerSocketFactory = (ServerSocketFactory) Class - .forName(defaultName, true, cl) - .newInstance(); + final Class ssfc = Class.forName(defaultName, true, cl); + defaultServerSocketFactory = (ServerSocketFactory) ssfc.newInstance(); } catch (Exception e) { - return e; } } return null; @@ -81,31 +68,36 @@ public abstract class SSLServerSocketFactory extends ServerSocketFactory { // Try to find in providers SSLContext context = DefaultSSLContext.getContext(); if (context != null) { - defaultServerSocketFactory = context.getServerSocketFactory(); + defaultServerSocketFactory = context.getServerSocketFactory(); } } if (defaultServerSocketFactory == null) { // Use internal dummy implementation - defaultServerSocketFactory = new DefaultSSLServerSocketFactory("No ServerSocketFactory installed"); - } + defaultServerSocketFactory = new DefaultSSLServerSocketFactory( + "No ServerSocketFactory installed"); + } return defaultServerSocketFactory; } - + + /** + * Creates a new {@code SSLServerSocketFactory} instance. + */ + protected SSLServerSocketFactory() { + super(); + } + /** * Returns the names of the cipher suites that are enabled by default. - * + * * @return the names of the cipher suites that are enabled by default - * @since Android 1.0 */ public abstract String[] getDefaultCipherSuites(); - + /** * Returns the list of supported cipher suites that could be enabled for an * SSL connection created by this factory. - * + * * @return the list of supported cipher suites - * @since Android 1.0 */ public abstract String[] getSupportedCipherSuites(); - } diff --git a/libcore/x-net/src/main/java/javax/net/ssl/SSLSession.java b/libcore/x-net/src/main/java/javax/net/ssl/SSLSession.java index 553d74f50..14a312afd 100644 --- a/libcore/x-net/src/main/java/javax/net/ssl/SSLSession.java +++ b/libcore/x-net/src/main/java/javax/net/ssl/SSLSession.java @@ -15,7 +15,6 @@ * limitations under the License. */ - package javax.net.ssl; import java.security.Principal; @@ -24,78 +23,68 @@ import javax.security.cert.X509Certificate; /** * The interface representing an SSL session. - * - * @since Android 1.0 */ public interface SSLSession { /** * Returns the maximum size that an application buffer can be for this * session. - * + * * @return the maximum application buffer size. - * @since Android 1.0 */ public int getApplicationBufferSize(); /** * Returns the name of the cipher suite used in this session. - * + * * @return the name of the cipher suite used in this session. - * @since Android 1.0 */ public String getCipherSuite(); /** * Returns the time this session was created, in milliseconds since midnight * January 1st 1970 UTC. - * + * * @return the time the session was created. - * @since Android 1.0 */ public long getCreationTime(); /** * Returns this sessions identifier. - * + * * @return this sessions identifier. - * @since Android 1.0 */ public byte[] getId(); /** * Returns the time this session was last accessed, in milliseconds since * midnight January 1st 1970 UTC. - * + * * @return the time this session was last accessed. - * @since Android 1.0 */ public long getLastAccessedTime(); /** * Returns the list of certificates that were used to identify the local * side to the peer during the handshake. - * + * * @return the list of certificates, ordered from local certificate to * CA's certificates. - * @since Android 1.0 */ public Certificate[] getLocalCertificates(); /** * Returns the principal used to identify the local side to the peer during * the handshake. - * + * * @return the principal used to identify the local side. - * @since Android 1.0 */ public Principal getLocalPrincipal(); /** * Returns the maximum size that a network buffer can be for this session. - * + * * @return the maximum network buffer size. - * @since Android 1.0 */ public int getPacketBufferSize(); @@ -105,67 +94,58 @@ public interface SSLSession { *

                        * Note: this method exists for compatility reasons, use * {@link #getPeerCertificates()} instead. - *

                        - * + * * @return the list of certificates, ordered from the identity certificate to * the CA's certificates * @throws SSLPeerUnverifiedException * if the identity of the peer is not verified. - * @since Android 1.0 */ - public X509Certificate[] getPeerCertificateChain() - throws SSLPeerUnverifiedException; + public X509Certificate[] getPeerCertificateChain() throws SSLPeerUnverifiedException; /** * Returns the list of certificates the peer used to identify itself during * the handshake. - * + * * @return the list of certificates, ordered from the identity certificate to * the CA's certificates. * @throws SSLPeerUnverifiedException * if the identity of the peer is not verified. - * @since Android 1.0 */ - public Certificate[] getPeerCertificates() - throws SSLPeerUnverifiedException; + public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException; /** * Returns the host name of the peer of this session. The host name is not * authenticated. - * + * * @return the host name of the peer of this session, or {@code null} if no * host name is available. - * @since Android 1.0 */ public String getPeerHost(); /** * Returns the port number of the peer of this session. The port number is * not authenticated. - * + * * @return the port number of the peer, of {@code -1} is no port number is * available. - * @since Android 1.0 */ public int getPeerPort(); /** * Returns the principal identifying the peer during the handshake. - * + * * @return the principal identifying the peer. * @throws SSLPeerUnverifiedException * if the identity of the peer has not been verified. - * @since Android 1.0 */ public Principal getPeerPrincipal() throws SSLPeerUnverifiedException; /** * Returns the protocol name that is used for all connections in this * session. - * + * * @return the protocol name that is used for all connections in this * session. - * @since Android 1.0 */ public String getProtocol(); @@ -174,17 +154,16 @@ public interface SSLSession { * security manager is installed, the * {@code SSLPermission("getSSLSessionContext"} is checked with the security * manager. - * + * * @return the context of this session or {@code null} if no context is * available. - * @since Android 1.0 */ public SSLSessionContext getSessionContext(); /** * Returns the object bound to the specified name in this session's * application layer data. - * + * * @param name * the name of the bound value. * @return the value bound to the specified name, or {@code null} if the @@ -192,7 +171,6 @@ public interface SSLSession { * access control context. * @throws IllegalArgumentException * if {@code name} is {@code null}. - * @since Android 1.0 */ public Object getValue(String name); @@ -202,11 +180,9 @@ public interface SSLSession { *

                        * Depending on the current access control context, the list of object names * may be different. - *

                        - * + * * @return the list of the object names bound to this session's application * layer data. - * @since Android 1.0 */ public String[] getValueNames(); @@ -215,17 +191,13 @@ public interface SSLSession { *

                        * No new connections can be created, but any existing connection remains * valid until it is closed. - *

                        - * - * @since Android 1.0 */ public void invalidate(); /** * Returns whether this session is valid. - * + * * @return {@code true} if this session is valid, otherwise {@code false}. - * @since Android 1.0 */ public boolean isValid(); @@ -235,15 +207,13 @@ public interface SSLSession { *

                        * For bindings (new or existing) implementing the * {@code SSLSessionBindingListener} interface the object will be notified. - *

                        - * + * * @param name * the name to bind the object to. * @param value * the object to bind. * @throws IllegalArgumentException * if either {@code name} or {@code value} is {@code null}. - * @since Android 1.0 */ public void putValue(String name, Object value); @@ -251,12 +221,11 @@ public interface SSLSession { * Removes the binding for the specified name in this session's application * layer data. If the existing binding implements the * {@code SSLSessionBindingListener} interface the object will be notified. - * + * * @param name * the binding to remove. * @throws IllegalArgumentException * if {@code name} is {@code null}. - * @since Android 1.0 */ public void removeValue(String name); -} \ No newline at end of file +} diff --git a/libcore/x-net/src/main/java/javax/net/ssl/SSLSessionBindingEvent.java b/libcore/x-net/src/main/java/javax/net/ssl/SSLSessionBindingEvent.java index dbda78723..19ae835d1 100644 --- a/libcore/x-net/src/main/java/javax/net/ssl/SSLSessionBindingEvent.java +++ b/libcore/x-net/src/main/java/javax/net/ssl/SSLSessionBindingEvent.java @@ -22,54 +22,54 @@ import java.util.EventObject; /** * The event sent to an {@code SSLSessionBindingListener} when the listener - * object is bound ({@link SSLSession#putValue(String, Object)}) or unbound + * object is bound ({@link SSLSession#putValue(String, Object)}) or unbound * ({@link SSLSession#removeValue(String)}) to an {@code SSLSession}. - * - * @since Android 1.0 */ public class SSLSessionBindingEvent extends EventObject implements Serializable { + /** + * The 5.0 spec. doesn't declare this serialVersionUID field In order to be compatible it is + * explicitly declared here + */ private static final long serialVersionUID = 3989172637106345L; - private String name; + /** + * @serial include + */ + private final String name; /** * Creates a new {@code SSLSessionBindingEvent} for the specified session * indicating a binding event for the specified name. - * + * * @param session * the session for which the event occurs. * @param name * the name of the object being (un)bound. - * @since Android 1.0 */ public SSLSessionBindingEvent(SSLSession session, String name) { super(session); this.name = name; } - + /** * Returns the name of the binding being added or removed. - * + * * @return the name of the binding. - * @since Android 1.0 */ public String getName() { return name; } - + /** * Returns the session to which the binding is added or from which it is * removed. - * + * * @return the session to which the binding is added or from which it is * removed. - * @since Android 1.0 */ public SSLSession getSession() { - return (SSLSession)this.source; + return (SSLSession) this.source; } - - -} \ No newline at end of file +} diff --git a/libcore/x-net/src/main/java/javax/net/ssl/SSLSessionBindingListener.java b/libcore/x-net/src/main/java/javax/net/ssl/SSLSessionBindingListener.java index 7781c539f..43ad74599 100644 --- a/libcore/x-net/src/main/java/javax/net/ssl/SSLSessionBindingListener.java +++ b/libcore/x-net/src/main/java/javax/net/ssl/SSLSessionBindingListener.java @@ -21,28 +21,24 @@ import java.util.EventListener; /** * The interface to be implemented by any object that requires notification when - * data objects are bound to (or unbound from) an {@code SSLSession}. - * - * @since Android 1.0 + * data objects are bound to (or unbound from) an {@code SSLSession}. */ public interface SSLSessionBindingListener extends EventListener { /** * Notifies this listener when a value is bound to an {@code SSLSession}. - * + * * @param event * the event data. - * @since Android 1.0 */ public void valueBound(SSLSessionBindingEvent event); /** * Notifies this listener when a value is unbound from an {@code SSLSession}. - * + * * @param event * the event data. - * @since Android 1.0 */ public void valueUnbound(SSLSessionBindingEvent event); -} \ No newline at end of file +} diff --git a/libcore/x-net/src/main/java/javax/net/ssl/SSLSessionContext.java b/libcore/x-net/src/main/java/javax/net/ssl/SSLSessionContext.java index 9d831f382..154376ebf 100644 --- a/libcore/x-net/src/main/java/javax/net/ssl/SSLSessionContext.java +++ b/libcore/x-net/src/main/java/javax/net/ssl/SSLSessionContext.java @@ -20,72 +20,63 @@ package javax.net.ssl; import java.util.Enumeration; /** - * A collection of {@code SSLSession}s. - * - * @since Android 1.0 + * A collection of {@code SSLSession}s. */ public interface SSLSessionContext { - /** * Returns an iterable of all session identifiers in this session context. - * + * * @return an iterable of all session identifiers in this session context. - * @since Android 1.0 */ + @SuppressWarnings("unchecked") public Enumeration getIds(); /** * Returns the session for the specified session identifier. - * + * * @param sessionId * the session identifier of the session to look up. * @return the session for the specified session identifier, or {@code null} * if the specified session identifier does not refer to a session * in this context. - * @since Android 1.0 */ public SSLSession getSession(byte[] sessionId); /** * Returns the size of the session cache for this session context. - * + * * @return the size of the session cache for this session context, or * {@code zero} if unlimited. - * @since Android 1.0 */ public int getSessionCacheSize(); /** * Returns the timeout for sessions in this session context. Sessions * exceeding the timeout are invalidated. - * + * * @return the timeout in seconds, or {@code zero} if unlimited. - * @since Android 1.0 */ public int getSessionTimeout(); /** * Sets the size of the session cache for this session context. - * + * * @param size * the size of the session cache, or {@code zero} for unlimited * cache size. * @throws IllegalArgumentException * if {@code size} is negative. - * @since Android 1.0 */ public void setSessionCacheSize(int size) throws IllegalArgumentException; /** * Sets the timeout for sessions in this context. Sessions exceeding the * timeout are invalidated. - * + * * @param seconds * the timeout in seconds, or {@code zero} if unlimited. * @throws IllegalArgumentException * if {@code seconds} is negative. - * @since Android 1.0 */ public void setSessionTimeout(int seconds) throws IllegalArgumentException; - -} \ No newline at end of file +} diff --git a/libcore/x-net/src/main/java/javax/net/ssl/SSLSocket.java b/libcore/x-net/src/main/java/javax/net/ssl/SSLSocket.java index 7320416ef..4a708431d 100644 --- a/libcore/x-net/src/main/java/javax/net/ssl/SSLSocket.java +++ b/libcore/x-net/src/main/java/javax/net/ssl/SSLSocket.java @@ -24,19 +24,14 @@ import java.net.UnknownHostException; /** * The extension of {@code Socket} providing secure protocols like SSL (Secure - * Socket Layer") or TLS (Transport Layer Security). - * - * @since Android 1.0 + * Socket Layer") or TLS (Transport Layer Security). */ public abstract class SSLSocket extends Socket { - + /** * Only to be used by subclasses. *

                        * Creates a TCP socket. - *

                        - * - * @since Android 1.0 */ protected SSLSocket() { super(); @@ -47,8 +42,7 @@ public abstract class SSLSocket extends Socket { *

                        * Creates a TCP socket connection to the specified host at the specified * port. - *

                        - * + * * @param host * the host name to connect to. * @param port @@ -57,10 +51,8 @@ public abstract class SSLSocket extends Socket { * if creating the socket fails. * @throws UnknownHostException * if the specified host is not known. - * @since Android 1.0 */ - protected SSLSocket(String host, int port) throws IOException, - UnknownHostException { + protected SSLSocket(String host, int port) throws IOException, UnknownHostException { super(host, port); } @@ -69,15 +61,13 @@ public abstract class SSLSocket extends Socket { *

                        * Creates a TCP socket connection to the specified address at the specified * port. - *

                        - * + * * @param address * the address to connect to. * @param port * the port number to connect to. * @throws IOException * if creating the socket fails. - * @since Android 1.0 */ protected SSLSocket(InetAddress address, int port) throws IOException { super(address, port); @@ -88,8 +78,7 @@ public abstract class SSLSocket extends Socket { *

                        * Creates a TCP socket connection to the specified host at the specified * port with the client side bound to the specified address and port. - *

                        - * + * * @param host * the host name to connect to. * @param port @@ -102,10 +91,9 @@ public abstract class SSLSocket extends Socket { * if creating the socket fails. * @throws UnknownHostException * if the specified host is not known. - * @since Android 1.0 */ - protected SSLSocket(String host, int port, InetAddress clientAddress, - int clientPort) throws IOException, UnknownHostException { + protected SSLSocket(String host, int port, InetAddress clientAddress, int clientPort) + throws IOException, UnknownHostException { super(host, port, clientAddress, clientPort); } @@ -114,8 +102,7 @@ public abstract class SSLSocket extends Socket { *

                        * Creates a TCP socket connection to the specified address at the specified * port with the client side bound to the specified address and port. - *

                        - * + * * @param address * the address to connect to. * @param port @@ -126,132 +113,119 @@ public abstract class SSLSocket extends Socket { * the client port number to bind to. * @throws IOException * if creating the socket fails. - * @since Android 1.0 */ - protected SSLSocket(InetAddress address, int port, - InetAddress clientAddress, int clientPort) throws IOException { + protected SSLSocket(InetAddress address, int port, InetAddress clientAddress, int clientPort) + throws IOException { super(address, port, clientAddress, clientPort); } - + /** * Returns the names of the supported cipher suites. - * + * * @return the names of the supported cipher suites. - * @since Android 1.0 */ public abstract String[] getSupportedCipherSuites(); - + /** * Returns the names of the enabled cipher suites. - * + * * @return the names of the enabled cipher suites. - * @since Android 1.0 */ public abstract String[] getEnabledCipherSuites(); - + /** * Sets the names of the cipher suites to be enabled. * Only cipher suites returned by {@link #getSupportedCipherSuites()} are * allowed. - * + * * @param suites * the names of the to be enabled cipher suites. * @throws IllegalArgumentException * if one of the cipher suite names is not supported. - * @since Android 1.0 */ public abstract void setEnabledCipherSuites(String[] suites); - + /** * Returns the names of the supported protocols. - * + * * @return the names of the supported protocols. - * @since Android 1.0 */ public abstract String[] getSupportedProtocols(); - + /** * Returns the names of the enabled protocols. - * + * * @return the names of the enabled protocols. - * @since Android 1.0 */ public abstract String[] getEnabledProtocols(); - + /** * Sets the names of the protocols to be enabled. Only * protocols returned by {@link #getSupportedProtocols()} are allowed. - * + * * @param protocols * the names of the to be enabled protocols. * @throws IllegalArgumentException * if one of the protocols is not supported. - * @since Android 1.0 */ public abstract void setEnabledProtocols(String[] protocols); - + /** * Returns the {@code SSLSession} for this connection. If necessary, a * handshake will be initiated, in which case this method will block until the handshake * has been established. If the handshake fails, an invalid session object * will be returned. - * + * * @return the session object. - * @since Android 1.0 */ public abstract SSLSession getSession(); - + /** * Registers the specified listener to receive notification on completion of a * handshake on this connection. - * + * * @param listener * the listener to register. * @throws IllegalArgumentException * if {@code listener} is {@code null}. - * @since Android 1.0 */ public abstract void addHandshakeCompletedListener(HandshakeCompletedListener listener); - + /** * Removes the specified handshake completion listener. - * + * * @param listener * the listener to remove. * @throws IllegalArgumentException * if the specified listener is not registered or {@code null}. - * @since Android 1.0 */ public abstract void removeHandshakeCompletedListener(HandshakeCompletedListener listener); - + /** * Starts a new SSL handshake on this connection. - * + * * @throws IOException * if an error occurs. - * @since Android 1.0 */ public abstract void startHandshake() throws IOException; - + /** * Sets whether this connection should act in client mode when handshaking. - * + * * @param mode * {@code true} if this connection should act in client mode, * {@code false} if not. - * @since Android 1.0 */ public abstract void setUseClientMode(boolean mode); - + /** * Returns whether this connection will act in client mode when handshaking. - * + * * @return {@code true} if this connections will act in client mode when * handshaking, {@code false} if not. - * @since Android 1.0 */ public abstract boolean getUseClientMode(); - + /** * Sets whether this connection should require client authentication. This * is only useful for sockets in server mode. The client authentication is @@ -262,24 +236,22 @@ public abstract class SSLSocket extends Socket { *
                      • no authentication needed
                      • * * This method overrides the setting of {@link #setWantClientAuth(boolean)}. - * + * * @param need * {@code true} if client authentication is required, * {@code false} if no authentication is needed. - * @since Android 1.0 */ public abstract void setNeedClientAuth(boolean need); - + /** * Returns whether this connection requires client authentication. * This is only useful for sockets in server mode. - * + * * @return {@code true} if client authentication is required, {@code false} * if no client authentication is needed. - * @since Android 1.0 */ public abstract boolean getNeedClientAuth(); - + /** * Sets whether this connections should request client authentication. This * is only useful for sockets in server mode. The client authentication is @@ -290,42 +262,38 @@ public abstract class SSLSocket extends Socket { *
                      • no authentication needed
                      • * * This method overrides the setting of {@link #setNeedClientAuth(boolean)}. - * + * * @param want * {@code true} if client authentication should be requested, * {@code false} if not authentication is needed. - * @since Android 1.0 */ public abstract void setWantClientAuth(boolean want); - + /** * Returns whether this connections will request client authentication. - * + * * @return {@code true} is client authentication will be requested, * {@code false} if no client authentication is needed. - * @since Android 1.0 */ public abstract boolean getWantClientAuth(); - + /** * Sets whether new SSL sessions may be created by this socket or if * existing sessions must be reused. - * + * * @param flag * {@code true} if new sessions may be created, otherwise * {@code false}. - * @since Android 1.0 */ public abstract void setEnableSessionCreation(boolean flag); - + /** * Returns whether new SSL sessions may be created by this socket or if * existing sessions must be reused. - * + * * @return {@code true} if new sessions may be created, otherwise * {@code false}. - * @since Android 1.0 */ public abstract boolean getEnableSessionCreation(); - -} \ No newline at end of file + +} diff --git a/libcore/x-net/src/main/java/javax/net/ssl/SSLSocketFactory.java b/libcore/x-net/src/main/java/javax/net/ssl/SSLSocketFactory.java index 2b7c03e53..d9db0999b 100644 --- a/libcore/x-net/src/main/java/javax/net/ssl/SSLSocketFactory.java +++ b/libcore/x-net/src/main/java/javax/net/ssl/SSLSocketFactory.java @@ -20,10 +20,9 @@ package javax.net.ssl; import java.io.IOException; import java.net.Socket; import java.security.AccessController; +import java.security.PrivilegedAction; import java.security.Security; // BEGIN android-added -import java.lang.reflect.Method; -import java.net.UnknownHostException; import java.util.logging.Logger; // END android-added @@ -31,77 +30,63 @@ import javax.net.SocketFactory; /** * The abstract factory implementation to create {@code SSLSocket}s. - * - * @since Android 1.0 */ public abstract class SSLSocketFactory extends SocketFactory { + // FIXME EXPORT CONTROL // The default SSL socket factory private static SocketFactory defaultSocketFactory; private static String defaultName; - /** - * Creates a new {@code SSLSocketFactory}. - * - * @since Android 1.0 - */ - public SSLSocketFactory() { - super(); - } - /** * Returns the default {@code SSLSocketFactory} instance. The default is * defined by the security property {@code 'ssl.SocketFactory.provider'}. - * + * * @return the default ssl socket factory instance. - * @since Android 1.0 */ - public static SocketFactory getDefault() { - synchronized (SSLSocketFactory.class) { - if (defaultSocketFactory != null) { - // BEGIN android-added - log("SSLSocketFactory", "Using factory " + defaultSocketFactory); - // END android-added - return defaultSocketFactory; - } - if (defaultName == null) { - AccessController.doPrivileged(new java.security.PrivilegedAction(){ - public Object run() { - defaultName = Security.getProperty("ssl.SocketFactory.provider"); - if (defaultName != null) { - ClassLoader cl = Thread.currentThread().getContextClassLoader(); - if (cl == null) { - cl = ClassLoader.getSystemClassLoader(); - } - try { - defaultSocketFactory = (SocketFactory) Class.forName( - defaultName, true, cl).newInstance(); - } catch (Exception e) { - return e; - } + public static synchronized SocketFactory getDefault() { + if (defaultSocketFactory != null) { + // BEGIN android-added + log("SSLSocketFactory", "Using factory " + defaultSocketFactory); + // END android-added + return defaultSocketFactory; + } + if (defaultName == null) { + AccessController.doPrivileged(new PrivilegedAction() { + public Void run() { + defaultName = Security.getProperty("ssl.SocketFactory.provider"); + if (defaultName != null) { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + if (cl == null) { + cl = ClassLoader.getSystemClassLoader(); + } + try { + final Class sfc = Class.forName(defaultName, true, cl); + defaultSocketFactory = (SocketFactory) sfc.newInstance(); + } catch (Exception e) { } - return null; } - }); - } - - if (defaultSocketFactory == null) { - // Try to find in providers - SSLContext context = DefaultSSLContext.getContext(); - if (context != null) { - defaultSocketFactory = context.getSocketFactory(); + return null; } + }); + } + + if (defaultSocketFactory == null) { + // Try to find in providers + SSLContext context = DefaultSSLContext.getContext(); + if (context != null) { + defaultSocketFactory = context.getSocketFactory(); } - if (defaultSocketFactory == null) { - // Use internal implementation - defaultSocketFactory = new DefaultSSLSocketFactory("No SSLSocketFactory installed"); - } + } + if (defaultSocketFactory == null) { + // Use internal implementation + defaultSocketFactory = new DefaultSSLSocketFactory("No SSLSocketFactory installed"); + } // BEGIN android-added log("SSLSocketFactory", "Using factory " + defaultSocketFactory); // END android-added - return defaultSocketFactory; - } + return defaultSocketFactory; } // BEGIN android-added @@ -111,27 +96,32 @@ public abstract class SSLSocketFactory extends SocketFactory { } // END android-added + /** + * Creates a new {@code SSLSocketFactory}. + */ + public SSLSocketFactory() { + super(); + } + /** * Returns the names of the cipher suites that are enabled by default. - * + * * @return the names of the cipher suites that are enabled by default. - * @since Android 1.0 */ public abstract String[] getDefaultCipherSuites(); /** * Returns the names of the cipher suites that are supported and could be * enabled for an SSL connection. - * + * * @return the names of the cipher suites that are supported. - * @since Android 1.0 */ public abstract String[] getSupportedCipherSuites(); /** * Creates an {@code SSLSocket} over the specified socket that is connected * to the specified host at the specified port. - * + * * @param s * the socket. * @param host @@ -145,11 +135,9 @@ public abstract class SSLSocketFactory extends SocketFactory { * @return the creates ssl socket. * @throws IOException * if creating the socket fails. - * @throws UnknownHostException + * @throws java.net.UnknownHostException * if the host is unknown. - * @since Android 1.0 */ - public abstract Socket createSocket(Socket s, String host, int port, - boolean autoClose) throws IOException; - + public abstract Socket createSocket(Socket s, String host, int port, boolean autoClose) + throws IOException; } diff --git a/libcore/x-net/src/main/java/javax/net/ssl/TrustManager.java b/libcore/x-net/src/main/java/javax/net/ssl/TrustManager.java index 5d8afcd42..9bdb16bc7 100644 --- a/libcore/x-net/src/main/java/javax/net/ssl/TrustManager.java +++ b/libcore/x-net/src/main/java/javax/net/ssl/TrustManager.java @@ -20,11 +20,9 @@ package javax.net.ssl; /** * The marker interface for JSSE trust managers. The purpose is to group trust * managers. The responsibility a trust manager is to handle the trust data used to - * make trust decisions for deciding whether credentials of a peer should be + * make trust decisions for deciding whether credentials of a peer should be * accepted, * @see TrustManagerFactory - * - * @since Android 1.0 */ public interface TrustManager { -} \ No newline at end of file +} diff --git a/libcore/x-net/src/main/java/javax/net/ssl/TrustManagerFactory.java b/libcore/x-net/src/main/java/javax/net/ssl/TrustManagerFactory.java index 480eb2685..6d9e4c99f 100644 --- a/libcore/x-net/src/main/java/javax/net/ssl/TrustManagerFactory.java +++ b/libcore/x-net/src/main/java/javax/net/ssl/TrustManagerFactory.java @@ -23,18 +23,15 @@ import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; +import java.security.PrivilegedAction; import java.security.Provider; import java.security.Security; import org.apache.harmony.security.fortress.Engine; - - /** * The factory for {@code TrustManager}s based on {@code KeyStore} or provider * specific implementation. - * - * @since Android 1.0 */ public class TrustManagerFactory { // Store TrustManager service name @@ -46,49 +43,25 @@ public class TrustManagerFactory { // Store default property name private static final String PROPERTYNAME = "ssl.TrustManagerFactory.algorithm"; - // Store used provider - private final Provider provider; - - // Storeused TrustManagerFactorySpi implementation - private final TrustManagerFactorySpi spiImpl; - - // Store used algorithm - private final String algorithm; - - /** - * Creates a new {@code TrustManagerFactory} instance. - * - * @param factorySpi - * the implementation delegate. - * @param provider - * the provider - * @param algorithm - * the algorithm name. - * @since Android 1.0 - */ - protected TrustManagerFactory(TrustManagerFactorySpi factorySpi, - Provider provider, String algorithm) { - this.provider = provider; - this.algorithm = algorithm; - this.spiImpl = factorySpi; - } - /** - * Returns the name of this {@code TrustManagerFactory} algorithm - * implementation. - * - * @return the name of this {@code TrustManagerFactory} algorithm - * implementation. - * @since Android 1.0 + * Returns the default algorithm name for the {@code TrustManagerFactory}. The + * default algorithm name is specified by the security property + * {@code 'ssl.TrustManagerFactory.algorithm'}. + * + * @return the default algorithm name. */ - public final String getAlgorithm() { - return algorithm; + public static final String getDefaultAlgorithm() { + return AccessController.doPrivileged(new PrivilegedAction() { + public String run() { + return Security.getProperty(PROPERTYNAME); + } + }); } /** * Creates a new {@code TrustManagerFactory} instance for the specified * trust management algorithm. - * + * * @param algorithm * the name of the requested trust management algorithm. * @return a trust manager factory for the requested algorithm. @@ -97,7 +70,6 @@ public class TrustManagerFactory { * @throws NullPointerException * if {@code algorithm} is {@code null} (instead of * NoSuchAlgorithmException as in 1.4 release) - * @since Android 1.0 */ public static final TrustManagerFactory getInstance(String algorithm) throws NoSuchAlgorithmException { @@ -106,15 +78,15 @@ public class TrustManagerFactory { } synchronized (engine) { engine.getInstance(algorithm, null); - return new TrustManagerFactory((TrustManagerFactorySpi) engine.spi, - engine.provider, algorithm); + return new TrustManagerFactory((TrustManagerFactorySpi) engine.spi, engine.provider, + algorithm); } } /** * Creates a new {@code TrustManagerFactory} instance for the specified * trust management algorithm from the specified provider. - * + * * @param algorithm * the name of the requested trust management algorithm name. * @param provider @@ -129,11 +101,9 @@ public class TrustManagerFactory { * @throws NullPointerException * if {@code algorithm} is {@code null} (instead of * NoSuchAlgorithmException as in 1.4 release) - * @since Android 1.0 */ - public static final TrustManagerFactory getInstance(String algorithm, - String provider) throws NoSuchAlgorithmException, - NoSuchProviderException { + public static final TrustManagerFactory getInstance(String algorithm, String provider) + throws NoSuchAlgorithmException, NoSuchProviderException { if ((provider == null) || (provider.length() == 0)) { throw new IllegalArgumentException("Provider is null oe empty"); } @@ -147,7 +117,7 @@ public class TrustManagerFactory { /** * Creates a new {@code TrustManagerFactory} instance for the specified * trust management algorithm from the specified provider. - * + * * @param algorithm * the name of the requested key management algorithm name. * @param provider @@ -159,10 +129,9 @@ public class TrustManagerFactory { * @throws NullPointerException * if {@code algorithm} is {@code null} (instead of * NoSuchAlgorithmException as in 1.4 release) - * @since Android 1.0 */ - public static final TrustManagerFactory getInstance(String algorithm, - Provider provider) throws NoSuchAlgorithmException { + public static final TrustManagerFactory getInstance(String algorithm, Provider provider) + throws NoSuchAlgorithmException { if (provider == null) { throw new IllegalArgumentException("Provider is null"); } @@ -171,16 +140,51 @@ public class TrustManagerFactory { } synchronized (engine) { engine.getInstance(algorithm, provider, null); - return new TrustManagerFactory((TrustManagerFactorySpi) engine.spi, - provider, algorithm); + return new TrustManagerFactory((TrustManagerFactorySpi) engine.spi, provider, algorithm); } } + // Store used provider + private final Provider provider; + + // Store used TrustManagerFactorySpi implementation + private final TrustManagerFactorySpi spiImpl; + + // Store used algorithm + private final String algorithm; + + /** + * Creates a new {@code TrustManagerFactory} instance. + * + * @param factorySpi + * the implementation delegate. + * @param provider + * the provider + * @param algorithm + * the algorithm name. + */ + protected TrustManagerFactory(TrustManagerFactorySpi factorySpi, Provider provider, + String algorithm) { + this.provider = provider; + this.algorithm = algorithm; + this.spiImpl = factorySpi; + } + + /** + * Returns the name of this {@code TrustManagerFactory} algorithm + * implementation. + * + * @return the name of this {@code TrustManagerFactory} algorithm + * implementation. + */ + public final String getAlgorithm() { + return algorithm; + } + /** * Returns the provider for this {@code TrustManagerFactory} instance. - * + * * @return the provider for this {@code TrustManagerFactory} instance. - * @since Android 1.0 */ public final Provider getProvider() { return provider; @@ -189,12 +193,11 @@ public class TrustManagerFactory { /** * Initializes this factory instance with the specified keystore as source * of certificate authorities and trust material. - * + * * @param ks * the keystore or {@code null}. * @throws KeyStoreException * if the initialization fails. - * @since Android 1.0 */ public final void init(KeyStore ks) throws KeyStoreException { spiImpl.engineInit(ks); @@ -203,43 +206,24 @@ public class TrustManagerFactory { /** * Initializes this factory instance with the specified provider-specific * parameters for a source of trust material. - * + * * @param spec * the provider-specific parameters. * @throws InvalidAlgorithmParameterException * if the initialization fails. - * @since Android 1.0 */ - public final void init(ManagerFactoryParameters spec) - throws InvalidAlgorithmParameterException { + public final void init(ManagerFactoryParameters spec) throws InvalidAlgorithmParameterException { spiImpl.engineInit(spec); } /** * Returns the list of {@code TrustManager}s with one entry for each type * of trust material. - * + * * @return the list of {@code TrustManager}s - * @since Android 1.0 */ public final TrustManager[] getTrustManagers() { return spiImpl.engineGetTrustManagers(); } - /** - * Returns the default algorithm name for the {@code TrustManagerFactory}. The - * default algorithm name is specified by the security property - * {@code 'ssl.TrustManagerFactory.algorithm'}. - * - * @return the default algorithm name. - * @since Android 1.0 - */ - public static final String getDefaultAlgorithm() { - return AccessController - .doPrivileged(new java.security.PrivilegedAction() { - public String run() { - return Security.getProperty(PROPERTYNAME); - } - }); - } -} \ No newline at end of file +} diff --git a/libcore/x-net/src/main/java/javax/net/ssl/TrustManagerFactorySpi.java b/libcore/x-net/src/main/java/javax/net/ssl/TrustManagerFactorySpi.java index 08e213e19..1b04c5bbc 100644 --- a/libcore/x-net/src/main/java/javax/net/ssl/TrustManagerFactorySpi.java +++ b/libcore/x-net/src/main/java/javax/net/ssl/TrustManagerFactorySpi.java @@ -24,40 +24,35 @@ import java.security.KeyStoreException; /** * The Service Provider Interface (SPI) for the * {@code TrustManagerFactory} class. - * - * @since Android 1.0 */ public abstract class TrustManagerFactorySpi { /** * Creates a new {@code TrustManagerFactorySpi} instance. - * - * @since Android 1.0 */ public TrustManagerFactorySpi() { + super(); } /** * Initializes this factory instance with the specified keystore as source * of certificate authorities and trust material. - * + * * @param ks * the keystore or {@code null}. * @throws KeyStoreException * if the initialization fails. - * @since Android 1.0 */ protected abstract void engineInit(KeyStore ks) throws KeyStoreException; /** * Initializes this factory instance with the specified provider-specific * parameters for a source of trust material. - * + * * @param spec * the provider-specific parameters. * @throws InvalidAlgorithmParameterException * if the initialization fails. - * @since Android 1.0 */ protected abstract void engineInit(ManagerFactoryParameters spec) throws InvalidAlgorithmParameterException; @@ -65,9 +60,8 @@ public abstract class TrustManagerFactorySpi { /** * Returns the list of {@code TrustManager}s with one entry for each type * of trust material. - * + * * @return the list of {@code TrustManager}s - * @since Android 1.0 */ protected abstract TrustManager[] engineGetTrustManagers(); -} \ No newline at end of file +} diff --git a/libcore/x-net/src/main/java/javax/net/ssl/X509ExtendedKeyManager.java b/libcore/x-net/src/main/java/javax/net/ssl/X509ExtendedKeyManager.java index bd5570d57..3298d8efa 100644 --- a/libcore/x-net/src/main/java/javax/net/ssl/X509ExtendedKeyManager.java +++ b/libcore/x-net/src/main/java/javax/net/ssl/X509ExtendedKeyManager.java @@ -21,27 +21,22 @@ import java.security.Principal; /** * The abstract extension for the {@code X509KeyManager} interface. - * - * @since Android 1.0 */ public abstract class X509ExtendedKeyManager implements X509KeyManager { - + /** * To be used by subclasses only. *

                        * Creates a new {@code X509ExtendedKeyManager} instance. - *

                        - * - * @since Android 1.0 */ protected X509ExtendedKeyManager() { super(); } /** - * Chooses a alias for the client side of an SSL connection to authenticate + * Chooses an alias for the client side of an SSL connection to authenticate * it with the specified public key type and certificate issuers. - * + * * @param keyType * the list of public key algorithm names. * @param issuers @@ -52,7 +47,6 @@ public abstract class X509ExtendedKeyManager implements X509KeyManager { * no engine is predefined. * @return the alias name of a matching key or {@code null} if there are no * matches. - * @since Android 1.0 */ public String chooseEngineClientAlias(String[] keyType, Principal[] issuers, SSLEngine engine) { @@ -60,9 +54,9 @@ public abstract class X509ExtendedKeyManager implements X509KeyManager { } /** - * Chooses a alias for the server side of an SSL connection to authenticate + * Chooses an alias for the server side of an SSL connection to authenticate * it with the specified public key type and certificate issuers. - * + * * @param keyType * the list of public key algorithm names. * @param issuers @@ -73,11 +67,10 @@ public abstract class X509ExtendedKeyManager implements X509KeyManager { * no engine is predefined. * @return the alias name of a matching key or {@code null} if there are no * matches. - * @since Android 1.0 */ public String chooseEngineServerAlias(String keyType, Principal[] issuers, SSLEngine engine) { return null; } -} \ No newline at end of file +} diff --git a/libcore/x-net/src/main/java/javax/net/ssl/X509KeyManager.java b/libcore/x-net/src/main/java/javax/net/ssl/X509KeyManager.java index f65ae4e04..aebc427e7 100644 --- a/libcore/x-net/src/main/java/javax/net/ssl/X509KeyManager.java +++ b/libcore/x-net/src/main/java/javax/net/ssl/X509KeyManager.java @@ -24,15 +24,13 @@ import java.security.cert.X509Certificate; /** * A Key Manager for X509 certificate-based key pairs. - * - * @since Android 1.0 */ public interface X509KeyManager extends KeyManager { /** * Chooses an alias for the client side of an SSL connection to authenticate * it with the specified public key type and certificate issuers. - * + * * @param keyType * the list of public key algorithm names. * @param issuers @@ -43,7 +41,6 @@ public interface X509KeyManager extends KeyManager { * the alias selected does not depend on a specific socket. * @return the alias name of a matching key or {@code null} if there are no * matches. - * @since Android 1.0 */ public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket); @@ -51,7 +48,7 @@ public interface X509KeyManager extends KeyManager { /** * Chooses an alias for the server side of an SSL connection to authenticate * it with the specified public key type and certificate issuers. - * + * * @param keyType * the list of public key algorithm type names. * @param issuers @@ -62,26 +59,24 @@ public interface X509KeyManager extends KeyManager { * the alias selected does not depend on a specific socket. * @return the alias name of a matching key or {@code null} if there are no * matches. - * @since Android 1.0 */ public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket); /** * Returns the certificate chain for the specified alias. - * + * * @param alias * the alias to get the certificate chain for. * @return the certificate chain for the specified alias, or {@code null} if * the alias cannot be found. - * @since Android 1.0 */ public X509Certificate[] getCertificateChain(String alias); /** * Returns the client aliases for the specified public key type and list of * certificate issuers. - * + * * @param keyType * the public key algorithm type name. * @param issuers @@ -89,14 +84,13 @@ public interface X509KeyManager extends KeyManager { * will do. * @return the client aliases for the specified public key type, or * {@code null} if there are no matching aliases. - * @since Android 1.0 */ public String[] getClientAliases(String keyType, Principal[] issuers); /** * Returns the server aliases for the specified public key type and list of * certificate issuers. - * + * * @param keyType * the public key algorithm type name. * @param issuers @@ -104,18 +98,16 @@ public interface X509KeyManager extends KeyManager { * will do. * @return the client aliases for the specified public key type, or * {@code null} if there are no matching aliases. - * @since Android 1.0 */ public String[] getServerAliases(String keyType, Principal[] issuers); /** * Returns the private key for the specified alias. - * + * * @param alias * the alias to get the private key for. * @return the private key for the specified alias, or {@code null} if the * alias cannot be found. - * @since Android 1.0 */ public PrivateKey getPrivateKey(String alias); -} \ No newline at end of file +} diff --git a/libcore/x-net/src/main/java/javax/net/ssl/X509TrustManager.java b/libcore/x-net/src/main/java/javax/net/ssl/X509TrustManager.java index 135c0e7fa..7d7827e49 100644 --- a/libcore/x-net/src/main/java/javax/net/ssl/X509TrustManager.java +++ b/libcore/x-net/src/main/java/javax/net/ssl/X509TrustManager.java @@ -22,9 +22,7 @@ import java.security.cert.X509Certificate; /** * The trust manager for X509 certificates to be used to perform authentication - * for secure sockets. - * - * @since Android 1.0 + * for secure sockets. */ public interface X509TrustManager extends TrustManager { @@ -32,7 +30,7 @@ public interface X509TrustManager extends TrustManager { * Checks whether the specified certificate chain (partial or complete) can * be validated and is trusted for client authentication for the specified * authentication type. - * + * * @param chain * the certificate chain to validate. * @param authType @@ -43,7 +41,6 @@ public interface X509TrustManager extends TrustManager { * if the specified certificate chain is empty or {@code null}, * or if the specified authentication type is {@code null} or an * empty string. - * @since Android 1.0 */ public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException; @@ -53,7 +50,7 @@ public interface X509TrustManager extends TrustManager { * Checks whether the specified certificate chain (partial or complete) can * be validated and is trusted for server authentication for the specified * key exchange algorithm. - * + * * @param chain * the certificate chain to validate. * @param authType @@ -64,7 +61,6 @@ public interface X509TrustManager extends TrustManager { * if the specified certificate chain is empty or {@code null}, * or if the specified authentication type is {@code null} or an * empty string. - * @since Android 1.0 */ public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException; @@ -72,10 +68,9 @@ public interface X509TrustManager extends TrustManager { /** * Returns the list of certificate issuer authorities which are trusted for * authentication of peers. - * + * * @return the list of certificate issuer authorities which are trusted for * authentication of peers. - * @since Android 1.0 */ public X509Certificate[] getAcceptedIssuers(); -} \ No newline at end of file +} diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/AlertException.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/AlertException.java index edf76383b..f6073645f 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/AlertException.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/AlertException.java @@ -15,21 +15,17 @@ * limitations under the License. */ -/** - * @author Alexander Y. Kleymenov - * @version $Revision$ - */ - package org.apache.harmony.xnet.provider.jsse; import javax.net.ssl.SSLException; /** - * This exception is used to signalize the fatal alert - * occured during the work of protocol. + * This exception is used to signal that a fatal alert has occurred while working through the + * protocol. */ public class AlertException extends RuntimeException { + private static final long serialVersionUID = -4448327177165687581L; // SSLException to be thrown to application side private final SSLException reason; // alert description code @@ -37,11 +33,11 @@ public class AlertException extends RuntimeException { /** * Constructs the instance. - * @param description: The alert description code. - * @see org.apache.harmony.xnet.provider.jsse.AlertProtocol - * @param reason: The SSLException to be thrown to application - * side after alert processing (sending the record with alert, - * shoutdown work, etc). + * + * @param description The alert description code from {@link AlertProtocol} + * @param reason The SSLException to be thrown to application side after alert processing + * (sending the record with alert, shutdown work, etc). + * @see AlertProtocol */ protected AlertException(byte description, SSLException reason) { super(reason); @@ -50,8 +46,8 @@ public class AlertException extends RuntimeException { } /** - * Returns the reason of alert. This reason should be rethrown - * after alert protcessin. + * Returns the reason of alert. This reason should be rethrown after alert processing. + * * @return the reason of alert. */ protected SSLException getReason() { @@ -60,9 +56,9 @@ public class AlertException extends RuntimeException { /** * Returns alert's description code. - * @return byte value describing the occured alert. - * @see org.apache.harmony.xnet.provider.jsse.AlertProtocol for more information about possible - * reason codes. + * + * @return alert description code from {@link AlertProtocol} + * @see AlertProtocol for more information about possible reason codes. */ protected byte getDescriptionCode() { return description; diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/AlertProtocol.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/AlertProtocol.java index 8f108751d..a12d00ada 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/AlertProtocol.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/AlertProtocol.java @@ -15,11 +15,6 @@ * limitations under the License. */ -/** - * @author Alexander Y. Kleymenov - * @version $Revision$ - */ - package org.apache.harmony.xnet.provider.jsse; import org.apache.harmony.xnet.provider.jsse.SSLRecordProtocol; @@ -136,8 +131,6 @@ public class AlertProtocol { * Defines the description code of the no_renegotiation alert */ protected static final byte NO_RENEGOTIATION = 100; - - // holds level and description codes private final byte[] alert = new byte[2]; // record protocol to be used to wrap the alerts @@ -271,7 +264,7 @@ public class AlertProtocol { /** * Returns the record with reported alert message. * The returned array of bytes is ready to be sent to another peer. - * Note, that this method does not automatically set the state of allert + * Note, that this method does not automatically set the state of alert * protocol in "no alert" state, so after wrapping the method setProcessed * should be called. */ @@ -281,7 +274,7 @@ public class AlertProtocol { } /** - * Shutdownes the protocol. It will be impossiblke to use the instance + * Shutdown the protocol. It will be impossible to use the instance * after the calling of this method. */ protected void shutdown() { diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/Appendable.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/Appendable.java index 1485bae53..070f42a08 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/Appendable.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/Appendable.java @@ -15,24 +15,19 @@ * limitations under the License. */ -/** - * @author Alexander Y. Kleymenov - * @version $Revision$ - */ - package org.apache.harmony.xnet.provider.jsse; /** - * This interface represents the ability of the input stream related - * classes to provide additianal data to be read. + * This interface represents the ability of the input stream related classes to provide additional + * data to be read. */ public interface Appendable { /** * Provides the additional data to be read. - * @param src: the source data to be appended. + * + * @param src the source data to be appended. */ public void append(byte[] src); } - diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/CertificateMessage.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/CertificateMessage.java index 6aac128dd..8065860e7 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/CertificateMessage.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/CertificateMessage.java @@ -15,11 +15,6 @@ * limitations under the License. */ -/** - * @author Boris Kuznetsov - * @version $Revision$ - */ - package org.apache.harmony.xnet.provider.jsse; import org.apache.harmony.xnet.provider.jsse.Message; @@ -28,6 +23,7 @@ import org.apache.harmony.xnet.provider.jsse.HandshakeIODataStream; import org.apache.harmony.xnet.provider.jsse.AlertProtocol; import java.io.IOException; +import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; @@ -35,7 +31,6 @@ import java.security.cert.X509Certificate; import java.util.Vector; /** - * * Represents server/client certificate message * @see TLS * 1.0 spec., 7.4.2. Server certificate; 7.4.6. Client certificate @@ -80,7 +75,7 @@ public class CertificateMessage extends Message { fatalAlert(AlertProtocol.INTERNAL_ERROR, "INTERNAL ERROR", e); return; } - Vector certs_vector = new Vector(); + Vector certs_vector = new Vector(); int size = 0; int enc_size = 0; while (l > 0) { @@ -141,6 +136,7 @@ public class CertificateMessage extends Message { * * @param out */ + @Override public void send(HandshakeIODataStream out) { int total_length = 0; @@ -172,6 +168,7 @@ public class CertificateMessage extends Message { * * @return */ + @Override public int getType() { return Handshake.CERTIFICATE; } diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/CertificateRequest.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/CertificateRequest.java index 8bedccdbd..7b27787be 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/CertificateRequest.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/CertificateRequest.java @@ -15,11 +15,6 @@ * limitations under the License. */ -/** - * @author Boris Kuznetsov - * @version $Revision$ - */ - package org.apache.harmony.xnet.provider.jsse; import org.apache.harmony.xnet.provider.jsse.Message; @@ -110,7 +105,7 @@ public class CertificateRequest extends Message { certificate_authorities = new X500Principal[size]; int totalPrincipalsLength = 0; int principalLength = 0; - Vector principals = new Vector(); + Vector principals = new Vector(); while (totalPrincipalsLength < size) { principalLength = in.readUint16(); // encoded X500Principal size principals.add(new X500Principal(in)); @@ -119,7 +114,7 @@ public class CertificateRequest extends Message { } certificate_authorities = new X500Principal[principals.size()]; for (int i = 0; i < certificate_authorities.length; i++) { - certificate_authorities[i] = (X500Principal) principals.elementAt(i); + certificate_authorities[i] = principals.elementAt(i); } this.length = 3 + certificate_types.length + totalPrincipalsLength; if (this.length != length) { @@ -134,6 +129,7 @@ public class CertificateRequest extends Message { * * @param out */ + @Override public void send(HandshakeIODataStream out) { out.writeUint8(certificate_types.length); @@ -156,6 +152,7 @@ public class CertificateRequest extends Message { * * @return */ + @Override public int getType() { return Handshake.CERTIFICATE_REQUEST; } diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/CertificateVerify.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/CertificateVerify.java index 183b8aaad..9b18ecbcc 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/CertificateVerify.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/CertificateVerify.java @@ -15,11 +15,6 @@ * limitations under the License. */ -/** - * @author Boris Kuznetsov - * @version $Revision$ - */ - package org.apache.harmony.xnet.provider.jsse; import org.apache.harmony.xnet.provider.jsse.Message; @@ -30,7 +25,6 @@ import org.apache.harmony.xnet.provider.jsse.AlertProtocol; import java.io.IOException; /** - * * Represents certificate verify message * @see TLS 1.0 spec., 7.4.8. * Certificate verify @@ -83,6 +77,7 @@ public class CertificateVerify extends Message { * * @param out */ + @Override public void send(HandshakeIODataStream out) { if (signedHash.length != 0) { out.writeUint16(signedHash.length); @@ -95,6 +90,7 @@ public class CertificateVerify extends Message { * * @return */ + @Override public int getType() { return Handshake.CERTIFICATE_VERIFY; } diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/CipherSuite.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/CipherSuite.java index 835238668..f0841958c 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/CipherSuite.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/CipherSuite.java @@ -15,11 +15,6 @@ * limitations under the License. */ -/** - * @author Boris Kuznetsov - * @version $Revision$ - */ - package org.apache.harmony.xnet.provider.jsse; import java.security.GeneralSecurityException; @@ -277,29 +272,29 @@ public class CipherSuite { }; // hash for quick access to cipher suite by name - private static Hashtable cuitesByName; + private static Hashtable cuitesByName; /** - * array of supported sipher suites. + * array of supported cipher suites. * Set of supported suites is defined at the moment provider's start */ -// TODO Dinamical supported suites: new providers may be dynamically -// added/removed and the set of supportes suites may be changed +// TODO Dynamically supported suites: new providers may be dynamically +// added/removed and the set of supported suites may be changed static CipherSuite[] supportedCipherSuites; /** - * array of supported sipher suites names + * array of supported cipher suites names */ static String[] supportedCipherSuiteNames; /** - * default sipher suites + * default cipher suites */ static CipherSuite[] defaultCipherSuites; static { int count = 0; - cuitesByName = new Hashtable(); + cuitesByName = new Hashtable(); for (int i = 0; i < cuitesByCode.length; i++) { cuitesByName.put(cuitesByCode[i].getName(), cuitesByCode[i]); if (cuitesByCode[i].supported) { @@ -353,7 +348,7 @@ public class CipherSuite { * @return */ public static CipherSuite getByName(String name) { - return (CipherSuite) cuitesByName.get(name); + return cuitesByName.get(name); } /** @@ -364,8 +359,8 @@ public class CipherSuite { * @return */ public static CipherSuite getByCode(byte b1, byte b2) { - if (b1 != 0 || b2 > cuitesByCode.length) { - // Unknoun + if (b1 != 0 || (b2 & 0xFF) > cuitesByCode.length) { + // Unknown return new CipherSuite("UNKNOUN_" + b1 + "_" + b2, false, 0, "", "", new byte[] { b1, b2 }); } @@ -383,7 +378,7 @@ public class CipherSuite { */ public static CipherSuite getByCode(byte b1, byte b2, byte b3) { if (b1 == 0 && b2 == 0) { - if (b3 <= cuitesByCode.length) { + if ((b3 & 0xFF) <= cuitesByCode.length) { return cuitesByCode[b3]; } } @@ -523,7 +518,7 @@ public class CipherSuite { * @return */ public static String[] getSupportedCipherSuiteNames() { - return (String[]) supportedCipherSuiteNames.clone(); + return supportedCipherSuiteNames.clone(); } /** @@ -545,6 +540,7 @@ public class CipherSuite { /** * Returns cipher suite description */ + @Override public String toString() { return name + ": " + cipherSuiteCode[0] + " " + cipherSuiteCode[1]; } @@ -552,6 +548,7 @@ public class CipherSuite { /** * Compares this cipher suite to the specified object. */ + @Override public boolean equals(Object obj) { if (obj instanceof CipherSuite && this.cipherSuiteCode[0] == ((CipherSuite) obj).cipherSuiteCode[0] @@ -610,3 +607,4 @@ public class CipherSuite { } } + diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientHandshakeImpl.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientHandshakeImpl.java index 55a06f5c1..b488a0e25 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientHandshakeImpl.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientHandshakeImpl.java @@ -15,11 +15,6 @@ * limitations under the License. */ -/** - * @author Boris Kuznetsov - * @version $Revision$ - */ - package org.apache.harmony.xnet.provider.jsse; import java.io.IOException; @@ -52,7 +47,7 @@ import javax.net.ssl.X509ExtendedKeyManager; * Handshake protocol operates on top of the Record Protocol. * It is responsible for session negotiating. * - * The implementation proceses inbound server handshake messages, + * The implementation processes inbound server handshake messages, * creates and sends respond messages. Outbound messages are supplied * to Record Protocol. Detected errors are reported to the Alert protocol. * @@ -75,6 +70,7 @@ public class ClientHandshakeImpl extends HandshakeProtocol { * Starts handshake * */ + @Override public void start() { if (session == null) { // initial handshake session = findSessionToResume(); @@ -139,9 +135,10 @@ public class ClientHandshakeImpl extends HandshakeProtocol { } /** - * Proceses inbound handshake messages + * Processes inbound handshake messages * @param bytes */ + @Override public void unwrap(byte[] bytes) { if (this.delegatedTaskErr != null) { Exception e = this.delegatedTaskErr; @@ -248,7 +245,7 @@ public class ClientHandshakeImpl extends HandshakeProtocol { session.protocol = servProt; recordProtocol.setVersion(session.protocol.version); session.cipherSuite = serverHello.cipher_suite; - session.id = (byte[]) serverHello.session_id.clone(); + session.id = serverHello.session_id.clone(); session.serverRandom = serverHello.random; break; case 11: // CERTIFICATE @@ -285,15 +282,12 @@ public class ClientHandshakeImpl extends HandshakeProtocol { } serverHelloDone = new ServerHelloDone(io_stream, length); if (this.nonBlocking) { - delegatedTasks.add(new DelegatedTask( - new PrivilegedExceptionAction(){ - public Object run() throws Exception { + delegatedTasks.add(new DelegatedTask(new PrivilegedExceptionAction() { + public Void run() throws Exception { processServerHelloDone(); return null; - } - }, - this, - AccessController.getContext())); + } + }, this, AccessController.getContext())); return; } processServerHelloDone(); @@ -338,6 +332,7 @@ public class ClientHandshakeImpl extends HandshakeProtocol { * @ see TLS 1.0 spec., E.1. Version 2 client hello * @param bytes */ + @Override public void unwrapSSLv2(byte[] bytes) { unexpectedMessage(); } @@ -345,6 +340,7 @@ public class ClientHandshakeImpl extends HandshakeProtocol { /** * Creates and sends Finished message */ + @Override protected void makeFinished() { byte[] verify_data; if (serverHello.server_version[1] == 1) { @@ -591,8 +587,9 @@ public class ClientHandshakeImpl extends HandshakeProtocol { } /** - * Proceses ChangeCipherSpec message + * Processes ChangeCipherSpec message */ + @Override public void receiveChangeCipherSpec() { if (isResuming) { if (serverHello == null) { @@ -628,3 +625,4 @@ public class ClientHandshakeImpl extends HandshakeProtocol { } } + diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientHello.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientHello.java index aa811fb41..576410554 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientHello.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientHello.java @@ -15,11 +15,6 @@ * limitations under the License. */ -/** -* @author Boris Kuznetsov -* @version $Revision$ -*/ - package org.apache.harmony.xnet.provider.jsse; import java.io.IOException; @@ -27,7 +22,6 @@ import java.security.SecureRandom; import java.util.Arrays; /** - * * Represents Client Hello message * @see TLS 1.0 spec., 7.4.1.2. * Client hello @@ -176,6 +170,7 @@ public class ClientHello extends Message { * Sends message * @param out */ + @Override public void send(HandshakeIODataStream out) { out.write(client_version); out.write(random); @@ -204,6 +199,7 @@ public class ClientHello extends Message { * Returns message type * @return */ + @Override public int getType() { return Handshake.CLIENT_HELLO; } diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientKeyExchange.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientKeyExchange.java index a2084567d..af751c2fd 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientKeyExchange.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientKeyExchange.java @@ -15,11 +15,6 @@ * limitations under the License. */ -/** -* @author Boris Kuznetsov -* @version $Revision$ -*/ - package org.apache.harmony.xnet.provider.jsse; import org.apache.harmony.xnet.provider.jsse.Message; @@ -30,7 +25,6 @@ import java.io.IOException; import java.math.BigInteger; /** - * * Represents client key exchange message * @see TLS 1.0 spec., 7.4.7. * Client key exchange message @@ -129,6 +123,7 @@ public class ClientKeyExchange extends Message { * Sends message * @param out */ + @Override public void send(HandshakeIODataStream out) { if (exchange_keys.length != 0) { if (!isRSA || isTLS) {// DH or TLSv1 RSA @@ -142,6 +137,7 @@ public class ClientKeyExchange extends Message { * Returns message type * @return */ + @Override public int getType() { return Handshake.CLIENT_KEY_EXCHANGE; } diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ConnectionState.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ConnectionState.java index 63bad5d2c..49a7af9d2 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ConnectionState.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ConnectionState.java @@ -15,11 +15,6 @@ * limitations under the License. */ -/** - * @author Alexander Y. Kleymenov - * @version $Revision$ - */ - package org.apache.harmony.xnet.provider.jsse; import org.apache.harmony.xnet.provider.jsse.Logger; diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ConnectionStateSSLv3.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ConnectionStateSSLv3.java index 078bf58fd..07bd340da 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ConnectionStateSSLv3.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ConnectionStateSSLv3.java @@ -15,11 +15,6 @@ * limitations under the License. */ -/** - * @author Alexander Y. Kleymenov - * @version $Revision$ - */ - package org.apache.harmony.xnet.provider.jsse; import java.security.GeneralSecurityException; @@ -31,7 +26,7 @@ import javax.crypto.spec.SecretKeySpec; import javax.net.ssl.SSLProtocolException; /** - * This class incapsulates the operating environment of the SSL v3 + * This class encapsulates the operating environment of the SSL v3 * (http://wp.netscape.com/eng/ssl3) Record Protocol and provides * relating encryption/decryption functionality. * The work functionality is based on the security @@ -218,8 +213,9 @@ public class ConnectionStateSSLv3 extends ConnectionState { /** * Creates the GenericStreamCipher or GenericBlockCipher * data structure for specified data of specified type. - * @throws org.apache.harmony.xnet.provider.jsse.AlertException if alert was occured. + * @throws AlertException if alert was occurred. */ + @Override protected byte[] encrypt(byte type, byte[] fragment, int offset, int len) { try { int content_mac_length = len + hash_size; @@ -282,6 +278,7 @@ public class ConnectionStateSSLv3 extends ConnectionState { * the specified type from the provided data. * @throws AlertException if alert was occured. */ + @Override protected byte[] decrypt(byte type, byte[] fragment, int offset, int len) { // plain data of the Generic[Stream|Block]Cipher structure @@ -344,9 +341,10 @@ public class ConnectionStateSSLv3 extends ConnectionState { } /** - * Shutdownes the protocol. It will be impossiblke to use the instance + * Shutdown the protocol. It will be impossible to use the instance * after the calling of this method. */ + @Override protected void shutdown() { Arrays.fill(mac_write_secret, (byte) 0); Arrays.fill(mac_read_secret, (byte) 0); diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ConnectionStateTLS.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ConnectionStateTLS.java index 1b21b17cc..949e65560 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ConnectionStateTLS.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ConnectionStateTLS.java @@ -15,11 +15,6 @@ * limitations under the License. */ -/** - * @author Alexander Y. Kleymenov - * @version $Revision$ - */ - package org.apache.harmony.xnet.provider.jsse; import org.apache.harmony.xnet.provider.jsse.AlertException; @@ -36,7 +31,7 @@ import javax.crypto.spec.SecretKeySpec; import javax.net.ssl.SSLProtocolException; /** - * This class incapsulates the operating environment of the TLS v1 + * This class encapsulates the operating environment of the TLS v1 * (http://www.ietf.org/rfc/rfc2246.txt) Record Protocol and provides * relating encryption/decryption functionality. * The work functionality is based on the security @@ -44,7 +39,7 @@ import javax.net.ssl.SSLProtocolException; */ public class ConnectionStateTLS extends ConnectionState { - // Precomputed prf label values: + // Pre-calculated prf label values: // "key expansion".getBytes() private static byte[] KEY_EXPANSION_LABEL = { (byte) 0x6B, (byte) 0x65, (byte) 0x79, (byte) 0x20, (byte) 0x65, @@ -235,8 +230,9 @@ public class ConnectionStateTLS extends ConnectionState { /** * Creates the GenericStreamCipher or GenericBlockCipher * data structure for specified data of specified type. - * @throws org.apache.harmony.xnet.provider.jsse.AlertException if alert was occured. + * @throws AlertException if alert was occurred. */ + @Override protected byte[] encrypt(byte type, byte[] fragment, int offset, int len) { try { int content_mac_length = len + hash_size; @@ -298,8 +294,9 @@ public class ConnectionStateTLS extends ConnectionState { * Retrieves the fragment of the Plaintext structure of * the specified type from the provided data representing * the Generic[Stream|Block]Cipher structure. - * @throws org.apache.harmony.xnet.provider.jsse.AlertException if alert was occured. + * @throws AlertException if alert was occurred. */ + @Override protected byte[] decrypt(byte type, byte[] fragment, int offset, int len) { // plain data of the Generic[Stream|Block]Cipher structure diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ContentType.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ContentType.java index dedfe6421..69704f544 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ContentType.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ContentType.java @@ -15,11 +15,6 @@ * limitations under the License. */ -/** - * @author Alexander Y. Kleymenov - * @version $Revision$ - */ - package org.apache.harmony.xnet.provider.jsse; /** diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/DHParameters.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/DHParameters.java index 1a441a531..441fc5f5f 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/DHParameters.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/DHParameters.java @@ -14,11 +14,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -/** - * @author Boris Kuznetsov - * @version $Revision$ - */ package org.apache.harmony.xnet.provider.jsse; /** diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/DataStream.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/DataStream.java index b52b838da..ffc86124d 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/DataStream.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/DataStream.java @@ -15,11 +15,6 @@ * limitations under the License. */ -/** - * @author Alexander Y. Kleymenov - * @version $Revision$ - */ - package org.apache.harmony.xnet.provider.jsse; /** diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/DelegatedTask.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/DelegatedTask.java index ea6ba7805..3b2e10313 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/DelegatedTask.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/DelegatedTask.java @@ -15,11 +15,6 @@ * limitations under the License. */ -/** - * @author Boris Kuznetsov - * @version $Revision$ - */ - package org.apache.harmony.xnet.provider.jsse; import org.apache.harmony.xnet.provider.jsse.HandshakeProtocol; @@ -35,7 +30,7 @@ import java.security.PrivilegedExceptionAction; public class DelegatedTask implements Runnable { private final HandshakeProtocol handshaker; - private final PrivilegedExceptionAction action; + private final PrivilegedExceptionAction action; private final AccessControlContext context; /** @@ -44,7 +39,7 @@ public class DelegatedTask implements Runnable { * @param handshaker * @param context */ - public DelegatedTask(PrivilegedExceptionAction action, HandshakeProtocol handshaker, AccessControlContext context) { + public DelegatedTask(PrivilegedExceptionAction action, HandshakeProtocol handshaker, AccessControlContext context) { this.action = action; this.handshaker = handshaker; this.context = context; diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/DigitalSignature.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/DigitalSignature.java index a8794dfd5..a0f18b4de 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/DigitalSignature.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/DigitalSignature.java @@ -14,26 +14,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -/** - * @author Boris Kuznetsov - * @version $Revision$ - */ package org.apache.harmony.xnet.provider.jsse; -import org.apache.harmony.xnet.provider.jsse.AlertException; - +import java.security.DigestException; +import java.security.InvalidKeyException; import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.Signature; +import java.security.SignatureException; import java.security.cert.Certificate; import java.util.Arrays; +import javax.crypto.BadPaddingException; import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; import javax.net.ssl.SSLException; /** - * This class represents Signature type, as descrybed in TLS v 1.0 Protocol + * This class represents Signature type, as described in TLS v 1.0 Protocol * specification, 7.4.3. It allow to init, update and sign hash. Hash algorithm * depends on SignatureAlgorithm. * @@ -56,10 +56,10 @@ import javax.net.ssl.SSLException; */ public class DigitalSignature { - private MessageDigest md5 = null; - private MessageDigest sha = null; - private Signature signature = null; - private Cipher cipher = null; + private final MessageDigest md5; + private final MessageDigest sha; + private final Signature signature; + private final Cipher cipher; private byte[] md5_hash; private byte[] sha_hash; @@ -69,33 +69,35 @@ public class DigitalSignature { * @param keyExchange */ public DigitalSignature(int keyExchange) { - try { + try { + sha = MessageDigest.getInstance("SHA-1"); + if (keyExchange == CipherSuite.KeyExchange_RSA_EXPORT || keyExchange == CipherSuite.KeyExchange_RSA || keyExchange == CipherSuite.KeyExchange_DHE_RSA || keyExchange == CipherSuite.KeyExchange_DHE_RSA_EXPORT) { // SignatureAlgorithm is rsa md5 = MessageDigest.getInstance("MD5"); - sha = MessageDigest.getInstance("SHA-1"); cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); + signature = null; } else if (keyExchange == CipherSuite.KeyExchange_DHE_DSS || keyExchange == CipherSuite.KeyExchange_DHE_DSS_EXPORT ) { // SignatureAlgorithm is dsa - sha = MessageDigest.getInstance("SHA-1"); signature = Signature.getInstance("NONEwithDSA"); -// The Signature should be empty in case of anonimous signature algorithm: -// } else if (keyExchange == CipherSuite.KeyExchange_DH_anon || -// keyExchange == CipherSuite.KeyExchange_DH_anon_EXPORT) { -// + cipher = null; + md5 = null; + } else { + cipher = null; + signature = null; + md5 = null; } - } catch (Exception e) { - throw new AlertException( - AlertProtocol.INTERNAL_ERROR, - new SSLException( - "INTERNAL ERROR: Unexpected exception on digital signature", - e)); - } - + } catch (NoSuchAlgorithmException e) { + // this should never happen + throw new AssertionError(e); + } catch (NoSuchPaddingException e) { + // this should never happen + throw new AssertionError(e); + } } /** @@ -109,8 +111,9 @@ public class DigitalSignature { } else if (cipher != null) { cipher.init(Cipher.ENCRYPT_MODE, key); } - } catch (Exception e){ - e.printStackTrace(); + } catch (InvalidKeyException e){ + throw new AlertException(AlertProtocol.BAD_CERTIFICATE, + new SSLException("init - invalid private key", e)); } } @@ -125,8 +128,9 @@ public class DigitalSignature { } else if (cipher != null) { cipher.init(Cipher.DECRYPT_MODE, cert); } - } catch (Exception e){ - e.printStackTrace(); + } catch (InvalidKeyException e){ + throw new AlertException(AlertProtocol.BAD_CERTIFICATE, + new SSLException("init - invalid certificate", e)); } } @@ -135,16 +139,12 @@ public class DigitalSignature { * @param data */ public void update(byte[] data) { - try { - if (sha != null) { - sha.update(data); - } - if (md5 != null) { - md5.update(data); - } - } catch (Exception e){ - e.printStackTrace(); - } + if (sha != null) { + sha.update(data); + } + if (md5 != null) { + md5.update(data); + } } /** @@ -197,10 +197,15 @@ public class DigitalSignature { return cipher.doFinal(); } return new byte[0]; - } catch (Exception e){ - e.printStackTrace(); + } catch (DigestException e){ + return new byte[0]; + } catch (SignatureException e){ + return new byte[0]; + } catch (BadPaddingException e){ return new byte[0]; - } + } catch (IllegalBlockSizeException e){ + return new byte[0]; + } } /** @@ -209,34 +214,40 @@ public class DigitalSignature { * @return true if verified */ public boolean verifySignature(byte[] data) { - try { - if (signature != null) { + if (signature != null) { + try { return signature.verify(data); - } else if (cipher != null) { - byte[] decrypt = cipher.doFinal(data); - byte[] md5_sha; - if (md5_hash != null && sha_hash != null) { - md5_sha = new byte[md5_hash.length + sha_hash.length]; - System.arraycopy(md5_hash, 0, md5_sha, 0, md5_hash.length); - System.arraycopy(sha_hash, 0, md5_sha, md5_hash.length, sha_hash.length); - } else if (md5_hash != null) { - md5_sha = md5_hash; - } else { - md5_sha = sha_hash; - } - if (Arrays.equals(decrypt, md5_sha)) { - return true; - } else { - return false; - } - } else if (data == null || data.length == 0) { - return true; - } else { + } catch (SignatureException e) { return false; } - } catch (Exception e){ - e.printStackTrace(); + } + + if (cipher != null) { + final byte[] decrypt; + try { + decrypt = cipher.doFinal(data); + } catch (IllegalBlockSizeException e) { + return false; + } catch (BadPaddingException e) { return false; + } + + final byte[] md5_sha; + if (md5_hash != null && sha_hash != null) { + md5_sha = new byte[md5_hash.length + sha_hash.length]; + System.arraycopy(md5_hash, 0, md5_sha, 0, md5_hash.length); + System.arraycopy(sha_hash, 0, md5_sha, md5_hash.length, sha_hash.length); + } else if (md5_hash != null) { + md5_sha = md5_hash; + } else { + md5_sha = sha_hash; + } + + return Arrays.equals(decrypt, md5_sha); + } else if (data == null || data.length == 0) { + return true; + } else { + return false; } } diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/EndOfBufferException.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/EndOfBufferException.java index b2bcafe16..1dcdd205d 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/EndOfBufferException.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/EndOfBufferException.java @@ -15,25 +15,18 @@ * limitations under the License. */ -/** - * @author Alexander Y. Kleymenov - * @version $Revision$ - */ - package org.apache.harmony.xnet.provider.jsse; import java.io.IOException; /** - * This class represents the exception signalizing that - * data could not be read from the stream because - * underlying input stream reached its end. + * This exception indicates that data could not be read from the stream because the underlying input + * stream reached its end. */ public class EndOfBufferException extends IOException { - /** - * Constructor - */ + private static final long serialVersionUID = 1838636631255369519L; + public EndOfBufferException() { super(); } diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/EndOfSourceException.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/EndOfSourceException.java index fbc1eafe9..631679ae7 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/EndOfSourceException.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/EndOfSourceException.java @@ -15,28 +15,20 @@ * limitations under the License. */ -/** - * @author Alexander Y. Kleymenov - * @version $Revision$ - */ - package org.apache.harmony.xnet.provider.jsse; import java.io.IOException; /** - * This class represents the exception signalizing that - * data could not be read from the buffered stream because - * underlying data buffer was exhausted. + * This exception indicates that data could not be read from the buffered stream because underlying + * data buffer was exhausted. */ public class EndOfSourceException extends IOException { - /** - * Constructor - */ + private static final long serialVersionUID = -4673611435974054413L; + public EndOfSourceException() { super(); } } - diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/Finished.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/Finished.java index d0f1fe198..6b555c679 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/Finished.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/Finished.java @@ -15,11 +15,6 @@ * limitations under the License. */ -/** -* @author Boris Kuznetsov -* @version $Revision$ -*/ - package org.apache.harmony.xnet.provider.jsse; import org.apache.harmony.xnet.provider.jsse.Message; @@ -63,6 +58,7 @@ public class Finished extends Message { } } + @Override public void send(HandshakeIODataStream out) { out.write(data); } @@ -71,6 +67,7 @@ public class Finished extends Message { * Returns message type * @return */ + @Override public int getType() { return Handshake.FINISHED; } diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/Handshake.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/Handshake.java index 4668b8cff..64e73ddeb 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/Handshake.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/Handshake.java @@ -15,11 +15,6 @@ * limitations under the License. */ -/** - * @author Boris Kuznetsov - * @version $Revision$ - */ - package org.apache.harmony.xnet.provider.jsse; /** diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/HandshakeIODataStream.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/HandshakeIODataStream.java index b5c455380..74cc27dc3 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/HandshakeIODataStream.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/HandshakeIODataStream.java @@ -15,11 +15,6 @@ * limitations under the License. */ -/** - * @author Alexander Y. Kleymenov - * @version $Revision$ - */ - package org.apache.harmony.xnet.provider.jsse; import org.apache.harmony.xnet.provider.jsse.AlertException; @@ -101,14 +96,17 @@ public class HandshakeIODataStream // position of the last byte to read + 1 private int read_pos_end; + @Override public int available() { return read_pos_end - read_pos; } + @Override public boolean markSupported() { return true; } + @Override public void mark(int limit) { marked_pos = read_pos; } @@ -117,6 +115,7 @@ public class HandshakeIODataStream marked_pos = read_pos; } + @Override public void reset() { read_pos = marked_pos; } @@ -138,6 +137,7 @@ public class HandshakeIODataStream * @param byte: byte * @return */ + @Override public int read() throws IOException { if (read_pos == read_pos_end) { //return -1; @@ -151,6 +151,7 @@ public class HandshakeIODataStream * @param new: long * @return */ + @Override public byte[] read(int length) throws IOException { if (length > available()) { throw new EndOfBufferException(); @@ -161,6 +162,7 @@ public class HandshakeIODataStream return res; } + @Override public int read(byte[] dest, int offset, int length) throws IOException { if (length > available()) { throw new EndOfBufferException(); @@ -174,7 +176,7 @@ public class HandshakeIODataStream /** * Appends the income data to be read by handshake protocol. - * The attempts to overflow the buffer by meens of this methos + * The attempts to overflow the buffer by means of this methods * seem to be futile because of: * 1. The SSL protocol specifies the maximum size of the record * and record protocol does not pass huge messages. diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/HandshakeProtocol.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/HandshakeProtocol.java index 606e5c7c6..657939888 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/HandshakeProtocol.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/HandshakeProtocol.java @@ -15,11 +15,6 @@ * limitations under the License. */ -/** - * @author Boris Kuznetsov - * @version $Revision$ - */ - package org.apache.harmony.xnet.provider.jsse; import java.math.BigInteger; @@ -89,7 +84,7 @@ public abstract class HandshakeProtocol { /** * Delegated tasks for this handshake implementation */ - protected Vector delegatedTasks = new Vector(); + protected Vector delegatedTasks = new Vector(); /** * Indicates non-blocking handshake @@ -169,13 +164,13 @@ public abstract class HandshakeProtocol { if (owner instanceof SSLEngineImpl) { engineOwner = (SSLEngineImpl) owner; nonBlocking = true; - this.parameters = (SSLParameters) engineOwner.sslParameters; + this.parameters = engineOwner.sslParameters; } // BEGIN android-removed // else if (owner instanceof SSLSocketImpl) { // socketOwner = (SSLSocketImpl) owner; // nonBlocking = false; - // this.parameters = (SSLParameters) socketOwner.sslParameters; + // this.parameters = socketOwner.sslParameters; // } // END android-removed } @@ -482,11 +477,8 @@ public abstract class HandshakeProtocol { public Runnable getTask() { if (delegatedTasks.isEmpty()) { return null; - } else { - Runnable task = (Runnable)delegatedTasks.firstElement(); - delegatedTasks.remove(0); - return task; } + return delegatedTasks.remove(0); } /** @@ -523,7 +515,7 @@ public abstract class HandshakeProtocol { mod = ((RSAKey) pk).getModulus(); } else { KeyFactory kf = KeyFactory.getInstance("RSA"); - mod = ((RSAPublicKeySpec) kf.getKeySpec(pk, RSAPublicKeySpec.class)) + mod = kf.getKeySpec(pk, RSAPublicKeySpec.class) .getModulus(); } return mod.bitLength(); diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/HelloRequest.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/HelloRequest.java index 2ce406192..40d4a718e 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/HelloRequest.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/HelloRequest.java @@ -15,10 +15,6 @@ * limitations under the License. */ -/** -* @author Boris Kuznetsov -* @version $Revision$ -*/ package org.apache.harmony.xnet.provider.jsse; import org.apache.harmony.xnet.provider.jsse.Message; @@ -60,9 +56,11 @@ public class HelloRequest extends Message { * Sends message * @param out */ + @Override public void send(HandshakeIODataStream out) { } + @Override public int length() { return 0; } @@ -71,6 +69,7 @@ public class HelloRequest extends Message { * Returns message type * @return */ + @Override public int getType() { return Handshake.HELLO_REQUEST; } diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/JSSEProvider.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/JSSEProvider.java index e65f8321e..33b0a451f 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/JSSEProvider.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/JSSEProvider.java @@ -15,14 +15,10 @@ * limitations under the License. */ -/** - * @author Alexander Y. Kleymenov - * @version $Revision$ - */ - package org.apache.harmony.xnet.provider.jsse; import java.security.AccessController; +import java.security.PrivilegedAction; import java.security.Provider; /** @@ -67,7 +63,7 @@ import java.security.Provider; * TLS_DH_anon_WITH_DES_CBC_SHA * TLS_DH_anon_WITH_3DES_EDE_CBC_SHA * - * The real set of availible cipher suites depends on set of availible + * The real set of available cipher suites depends on set of available * crypto algorithms. These algorithms must be provided by some crypto * provider. * @@ -108,17 +104,16 @@ import java.security.Provider; */ public final class JSSEProvider extends Provider { + private static final long serialVersionUID = 3075686092260669675L; + public JSSEProvider() { super("HarmonyJSSE", 1.0, "Harmony JSSE Provider"); - AccessController.doPrivileged(new java.security.PrivilegedAction() { + AccessController.doPrivileged(new PrivilegedAction() { public Void run() { - put("SSLContext.TLS", - "org.apache.harmony.xnet.provider.jsse.SSLContextImpl"); + put("SSLContext.TLS", SSLContextImpl.class.getName()); put("Alg.Alias.SSLContext.TLSv1", "TLS"); - put("KeyManagerFactory.X509", - "org.apache.harmony.xnet.provider.jsse.KeyManagerFactoryImpl"); - put("TrustManagerFactory.X509", - "org.apache.harmony.xnet.provider.jsse.TrustManagerFactoryImpl"); + put("KeyManagerFactory.X509", KeyManagerFactoryImpl.class.getName()); + put("TrustManagerFactory.X509", TrustManagerFactoryImpl.class.getName()); // BEGIN android-added put("MessageDigest.SHA-1", "org.apache.harmony.xnet.provider.jsse.OpenSSLMessageDigestJDK$SHA1"); put("Alg.Alias.MessageDigest.SHA1", "SHA-1"); @@ -138,4 +133,3 @@ public final class JSSEProvider extends Provider { }); } } - diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/KeyManagerFactoryImpl.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/KeyManagerFactoryImpl.java index 1daf80c8a..3b55299a2 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/KeyManagerFactoryImpl.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/KeyManagerFactoryImpl.java @@ -14,11 +14,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -/** - * @author Boris Kuznetsov - * @version $Revision$ - */ package org.apache.harmony.xnet.provider.jsse; import java.io.File; @@ -53,13 +48,14 @@ public class KeyManagerFactoryImpl extends KeyManagerFactorySpi { * @see javax.net.ssl.KeyManagerFactorySpi#engineInit(KeyStore ks, char[] * password) */ + @Override public void engineInit(KeyStore ks, char[] password) throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException { if (ks != null) { keyStore = ks; if (password != null) { - pwd = (char[]) password.clone(); + pwd = password.clone(); } else { pwd = new char[0]; } @@ -115,6 +111,7 @@ public class KeyManagerFactoryImpl extends KeyManagerFactorySpi { * @see javax.net.ssl.KeyManagerFactorySpi#engineInit(ManagerFactoryParameters * spec) */ + @Override public void engineInit(ManagerFactoryParameters spec) throws InvalidAlgorithmParameterException { throw new InvalidAlgorithmParameterException( @@ -125,6 +122,7 @@ public class KeyManagerFactoryImpl extends KeyManagerFactorySpi { /** * @see javax.net.ssl.KeyManagerFactorySpi#engineGetKeyManagers() */ + @Override public KeyManager[] engineGetKeyManagers() { if (keyStore == null) { throw new IllegalStateException("KeyManagerFactory is not initialized"); diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/KeyManagerImpl.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/KeyManagerImpl.java index b7451d523..f63170f93 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/KeyManagerImpl.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/KeyManagerImpl.java @@ -14,11 +14,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -/** - * @author Boris Kuznetsov - * @version $Revision$ - */ package org.apache.harmony.xnet.provider.jsse; import java.net.Socket; @@ -28,6 +23,7 @@ import java.security.NoSuchAlgorithmException; import java.security.Principal; import java.security.PrivateKey; import java.security.UnrecoverableEntryException; +import java.security.KeyStore.PrivateKeyEntry; import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.util.Enumeration; @@ -40,11 +36,11 @@ import javax.security.auth.x500.X500Principal; /** * KeyManager implementation. - * This implementation uses hashed key store information. - * It works faster than retrieving all of the data from the key store. - * Any key store changes, that happen after key manager was created, have no effect. - * The implementation does not use peer information (host, port) - * that may be obtained from socket or engine. + * + * This implementation uses hashed key store information. It works faster than retrieving all of the + * data from the key store. Any key store changes, that happen after key manager was created, have + * no effect. The implementation does not use peer information (host, port) that may be obtained + * from socket or engine. * * @see javax.net.ssl.KeyManager * @@ -52,7 +48,7 @@ import javax.security.auth.x500.X500Principal; public class KeyManagerImpl extends X509ExtendedKeyManager { // hashed key store information - private final Hashtable hash = new Hashtable(); + private final Hashtable hash; /** * Creates Key manager @@ -61,21 +57,20 @@ public class KeyManagerImpl extends X509ExtendedKeyManager { * @param pwd */ public KeyManagerImpl(KeyStore keyStore, char[] pwd) { - String alias; - KeyStore.PrivateKeyEntry entry; - Enumeration aliases; + super(); + this.hash = new Hashtable(); + final Enumeration aliases; try { aliases = keyStore.aliases(); } catch (KeyStoreException e) { return; } for (; aliases.hasMoreElements();) { - alias = (String) aliases.nextElement(); + final String alias = aliases.nextElement(); try { - if (keyStore.entryInstanceOf(alias, - KeyStore.PrivateKeyEntry.class)) { - entry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(alias, - new KeyStore.PasswordProtection(pwd)); + if (keyStore.entryInstanceOf(alias, KeyStore.PrivateKeyEntry.class)) { + final KeyStore.PrivateKeyEntry entry = (KeyStore.PrivateKeyEntry) keyStore + .getEntry(alias, new KeyStore.PasswordProtection(pwd)); hash.put(alias, entry); } } catch (KeyStoreException e) { @@ -86,41 +81,18 @@ public class KeyManagerImpl extends X509ExtendedKeyManager { continue; } } - } - /** - * @see javax.net.ssl.X509ExtendedKeyManager#chooseClientAlias(String[] - * keyType, Principal[] issuers, Socket socket) - */ - public String chooseClientAlias(String[] keyType, Principal[] issuers, - Socket socket) { - String[] al = chooseAlias(keyType, issuers); - if (al != null) { - return al[0]; - } else { - return null; - } + public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) { + final String[] al = chooseAlias(keyType, issuers); + return (al == null ? null : al[0]); } - /** - * @see javax.net.ssl.X509ExtendedKeyManager#chooseServerAlias(String - * keyType, Principal[] issuers, Socket socket) - */ - public String chooseServerAlias(String keyType, Principal[] issuers, - Socket socket) { - String[] al = chooseAlias(new String[] { keyType }, issuers); - if (al != null) { - return al[0]; - } else { - return null; - } + public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) { + final String[] al = chooseAlias(new String[] { keyType }, issuers); + return (al == null ? null : al[0]); } - /** - * @see javax.net.ssl.X509ExtendedKeyManager#getCertificateChain(String - * alias) - */ public X509Certificate[] getCertificateChain(String alias) { // BEGIN android-changed if (alias == null) { @@ -128,8 +100,7 @@ public class KeyManagerImpl extends X509ExtendedKeyManager { } // END android-changed if (hash.containsKey(alias)) { - Certificate[] certs = ((KeyStore.PrivateKeyEntry) hash.get(alias)) - .getCertificateChain(); + Certificate[] certs = hash.get(alias).getCertificateChain(); if (certs[0] instanceof X509Certificate) { X509Certificate[] xcerts = new X509Certificate[certs.length]; for (int i = 0; i < certs.length; i++) { @@ -142,25 +113,14 @@ public class KeyManagerImpl extends X509ExtendedKeyManager { } - /** - * @see javax.net.ssl.X509ExtendedKeyManager#getClientAliases(String - * keyType, Principal[] issuers) - */ public String[] getClientAliases(String keyType, Principal[] issuers) { return chooseAlias(new String[] { keyType }, issuers); } - /** - * @see javax.net.ssl.X509ExtendedKeyManager#getServerAliases(String - * keyType, Principal[] issuers) - */ public String[] getServerAliases(String keyType, Principal[] issuers) { return chooseAlias(new String[] { keyType }, issuers); } - /** - * @see javax.net.ssl.X509ExtendedKeyManager#getPrivateKey(String alias) - */ public PrivateKey getPrivateKey(String alias) { // BEGIN android-changed if (alias == null) { @@ -168,53 +128,33 @@ public class KeyManagerImpl extends X509ExtendedKeyManager { } // END android-changed if (hash.containsKey(alias)) { - return ((KeyStore.PrivateKeyEntry) hash.get(alias)).getPrivateKey(); + return hash.get(alias).getPrivateKey(); } return null; } - /** - * @see javax.net.ssl.X509ExtendedKeyManager#chooseEngineClientAlias(String[] - * keyType, Principal[] issuers, SSLEngine engine) - */ - public String chooseEngineClientAlias(String[] keyType, - Principal[] issuers, SSLEngine engine) { - String[] al = chooseAlias(keyType, issuers); - if (al != null) { - return al[0]; - } else { - return null; - } + @Override + public String chooseEngineClientAlias(String[] keyType, Principal[] issuers, SSLEngine engine) { + final String[] al = chooseAlias(keyType, issuers); + return (al == null ? null : al[0]); } - /** - * @see javax.net.ssl.X509ExtendedKeyManager#chooseEngineServerAlias(String - * keyType, Principal[] issuers, SSLEngine engine) - */ - public String chooseEngineServerAlias(String keyType, Principal[] issuers, - SSLEngine engine) { - String[] al = chooseAlias(new String[] { keyType }, issuers); - if (al != null) { - return al[0]; - } else { - return null; - } + @Override + public String chooseEngineServerAlias(String keyType, Principal[] issuers, SSLEngine engine) { + final String[] al = chooseAlias(new String[] { keyType }, issuers); + return (al == null ? null : al[0]); } private String[] chooseAlias(String[] keyType, Principal[] issuers) { - String alias; - KeyStore.PrivateKeyEntry entry; - if (keyType == null || keyType.length == 0) { return null; } - Vector found = new Vector(); - int count = 0; - for (Enumeration aliases = hash.keys(); aliases.hasMoreElements();) { - alias = (String) aliases.nextElement(); - entry = (KeyStore.PrivateKeyEntry) hash.get(alias); - Certificate[] certs = entry.getCertificateChain(); - String alg = certs[0].getPublicKey().getAlgorithm(); + Vector found = new Vector(); + for (Enumeration aliases = hash.keys(); aliases.hasMoreElements();) { + final String alias = aliases.nextElement(); + final KeyStore.PrivateKeyEntry entry = hash.get(alias); + final Certificate[] certs = entry.getCertificateChain(); + final String alg = certs[0].getPublicKey().getAlgorithm(); for (int i = 0; i < keyType.length; i++) { if (alg.equals(keyType[i])) { if (issuers != null && issuers.length != 0) { @@ -226,7 +166,6 @@ public class KeyManagerImpl extends X509ExtendedKeyManager { for (int iii = 0; iii < issuers.length; iii++) { if (issuer.equals(issuers[iii])) { found.add(alias); - count++; break loop; } } @@ -235,18 +174,13 @@ public class KeyManagerImpl extends X509ExtendedKeyManager { } } else { found.add(alias); - count++; } } } } - if (count > 0) { - String[] result = new String[count]; - found.toArray(result); - return result; - } else { - return null; + if (!found.isEmpty()) { + return found.toArray(new String[found.size()]); } + return null; } - } diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/Logger.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/Logger.java index 5b7ba2cb0..c06aa7e7c 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/Logger.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/Logger.java @@ -15,11 +15,6 @@ * limitations under the License. */ -/** - * @author Alexander Y. Kleymenov - * @version $Revision$ - */ - package org.apache.harmony.xnet.provider.jsse; import java.io.PrintStream; @@ -41,6 +36,7 @@ public class Logger { prefix = name + "["+Thread.currentThread().getName()+"] "; } + @Override public void print(String msg) { for (int i=0; i protocolsByName = new Hashtable(4); /** * @@ -112,7 +79,7 @@ public class ProtocolVersion { * @return */ public static ProtocolVersion getByName(String name) { - return (ProtocolVersion) protocolsByName.get(name); + return protocolsByName.get(name); } /** @@ -161,4 +128,31 @@ public class ProtocolVersion { protocolsByName.put("TLS", TLSv1); } + /** + * Protocol name + */ + public final String name; + + /** + * Protocol version as byte array + */ + public final byte[] version; + + private ProtocolVersion(String name, byte[] version) { + this.name = name; + this.version = version; + } + + /** + * Compares this ProtocolVersion to the specified object. + */ + @Override + public boolean equals(Object o) { + if (o instanceof ProtocolVersion + && this.version[0] == ((ProtocolVersion) o).version[0] + && this.version[1] == ((ProtocolVersion) o).version[1]) { + return true; + } + return false; + } } \ No newline at end of file diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLBufferedInput.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLBufferedInput.java index 44009b99a..a15047022 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLBufferedInput.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLBufferedInput.java @@ -15,11 +15,6 @@ * limitations under the License. */ -/** - * @author Alexander Y. Kleymenov - * @version $Revision$ - */ - package org.apache.harmony.xnet.provider.jsse; import org.apache.harmony.xnet.provider.jsse.SSLInputStream; @@ -57,6 +52,7 @@ public class SSLBufferedInput extends SSLInputStream { /** * Returns the number of bytes available for reading. */ + @Override public int available() throws IOException { // in assumption that the buffer has been set return in.remaining(); @@ -73,6 +69,7 @@ public class SSLBufferedInput extends SSLInputStream { * Reads the following byte value. If there are no bytes in the source * buffer, method throws java.nio.BufferUnderflowException. */ + @Override public int read() throws IOException { // TODO: implement optimized read(int) // and read(byte[], int, int) methods diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLContextImpl.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLContextImpl.java index 2e4de04a3..c39e3ff00 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLContextImpl.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLContextImpl.java @@ -15,11 +15,6 @@ * limitations under the License. */ -/** - * @author Alexander Y. Kleymenov - * @version $Revision$ - */ - package org.apache.harmony.xnet.provider.jsse; import org.apache.harmony.xnet.provider.jsse.SSLEngineImpl; @@ -61,6 +56,7 @@ public class SSLContextImpl extends SSLContextSpi { super(); } + @Override public void engineInit(KeyManager[] kms, TrustManager[] tms, SecureRandom sr) throws KeyManagementException { engineInit(kms, tms, sr, null, null); @@ -96,6 +92,7 @@ public class SSLContextImpl extends SSLContextSpi { return new OpenSSLSocketFactoryImpl(sslParameters); } + @Override public SSLServerSocketFactory engineGetServerSocketFactory() { if (sslParameters == null) { throw new IllegalStateException("SSLContext is not initiallized."); @@ -103,6 +100,7 @@ public class SSLContextImpl extends SSLContextSpi { return new OpenSSLServerSocketFactoryImpl(sslParameters); } + @Override public SSLEngine engineCreateSSLEngine(String host, int port) { if (sslParameters == null) { throw new IllegalStateException("SSLContext is not initiallized."); @@ -111,6 +109,7 @@ public class SSLContextImpl extends SSLContextSpi { (SSLParameters) sslParameters.clone()); } + @Override public SSLEngine engineCreateSSLEngine() { if (sslParameters == null) { throw new IllegalStateException("SSLContext is not initiallized."); @@ -118,10 +117,12 @@ public class SSLContextImpl extends SSLContextSpi { return new SSLEngineImpl((SSLParameters) sslParameters.clone()); } + @Override public ServerSessionContext engineGetServerSessionContext() { return serverSessionContext; } + @Override public ClientSessionContext engineGetClientSessionContext() { return clientSessionContext; } diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLEngineAppData.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLEngineAppData.java index 698723b3a..9a2cb5e0c 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLEngineAppData.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLEngineAppData.java @@ -15,11 +15,6 @@ * limitations under the License. */ -/** - * @author Alexander Y. Kleymenov - * @version $Revision$ - */ - package org.apache.harmony.xnet.provider.jsse; import org.apache.harmony.xnet.provider.jsse.AlertException; @@ -77,11 +72,10 @@ public class SSLEngineAppData implements org.apache.harmony.xnet.provider.jsse.A pos = len; // data was written, exit break; - } else { - // write chunk of data - dsts[i].put(buffer, pos, rem); - pos += rem; } + // write chunk of data + dsts[i].put(buffer, pos, rem); + pos += rem; } if (pos != len) { // The data did not feet into the buffers, diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLEngineDataStream.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLEngineDataStream.java index bc135773a..e209dd143 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLEngineDataStream.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLEngineDataStream.java @@ -15,11 +15,6 @@ * limitations under the License. */ -/** - * @author Alexander Y. Kleymenov - * @version $Revision$ - */ - package org.apache.harmony.xnet.provider.jsse; import java.nio.ByteBuffer; diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLEngineImpl.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLEngineImpl.java index 383e1465a..c28a31163 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLEngineImpl.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLEngineImpl.java @@ -15,11 +15,6 @@ * limitations under the License. */ -/** - * @author Alexander Y. Kleymenov - * @version $Revision$ - */ - package org.apache.harmony.xnet.provider.jsse; import org.apache.harmony.xnet.provider.jsse.AlertException; @@ -114,6 +109,7 @@ public class SSLEngineImpl extends SSLEngine { * @see javax.net.ssl.SSLEngine#beginHandshake() method documentation * for more information */ + @Override public void beginHandshake() throws SSLException { if (engine_was_closed) { throw new SSLException("Engine has already been closed."); @@ -143,6 +139,7 @@ public class SSLEngineImpl extends SSLEngine { * @see javax.net.ssl.SSLEngine#closeInbound() method documentation * for more information */ + @Override public void closeInbound() throws SSLException { if (logger != null) { logger.println("closeInbound() "+isInboundDone); @@ -173,6 +170,7 @@ public class SSLEngineImpl extends SSLEngine { * @see javax.net.ssl.SSLEngine#closeOutbound() method documentation * for more information */ + @Override public void closeOutbound() { if (logger != null) { logger.println("closeOutbound() "+isOutboundDone); @@ -199,6 +197,7 @@ public class SSLEngineImpl extends SSLEngine { * @see javax.net.ssl.SSLEngine#getDelegatedTask() method documentation * for more information */ + @Override public Runnable getDelegatedTask() { return handshakeProtocol.getTask(); } @@ -209,6 +208,7 @@ public class SSLEngineImpl extends SSLEngine { * @see javax.net.ssl.SSLEngine#getSupportedCipherSuites() method * documentation for more information */ + @Override public String[] getSupportedCipherSuites() { return CipherSuite.getSupportedCipherSuiteNames(); } @@ -220,6 +220,7 @@ public class SSLEngineImpl extends SSLEngine { * @see javax.net.ssl.SSLEngine#getEnabledCipherSuites() method * documentation for more information */ + @Override public String[] getEnabledCipherSuites() { return sslParameters.getEnabledCipherSuites(); } @@ -229,6 +230,7 @@ public class SSLEngineImpl extends SSLEngine { * @see javax.net.ssl.SSLEngine#setEnabledCipherSuites(String[]) method * documentation for more information */ + @Override public void setEnabledCipherSuites(String[] suites) { sslParameters.setEnabledCipherSuites(suites); } @@ -238,8 +240,9 @@ public class SSLEngineImpl extends SSLEngine { * @see javax.net.ssl.SSLEngine#getSupportedProtocols() method * documentation for more information */ + @Override public String[] getSupportedProtocols() { - return (String[]) ProtocolVersion.supportedProtocols.clone(); + return ProtocolVersion.supportedProtocols.clone(); } /** @@ -247,6 +250,7 @@ public class SSLEngineImpl extends SSLEngine { * @see javax.net.ssl.SSLEngine#getEnabledProtocols() method * documentation for more information */ + @Override public String[] getEnabledProtocols() { return sslParameters.getEnabledProtocols(); } @@ -256,6 +260,7 @@ public class SSLEngineImpl extends SSLEngine { * @see javax.net.ssl.SSLEngine#setEnabledProtocols(String[]) method * documentation for more information */ + @Override public void setEnabledProtocols(String[] protocols) { sslParameters.setEnabledProtocols(protocols); } @@ -265,6 +270,7 @@ public class SSLEngineImpl extends SSLEngine { * @see javax.net.ssl.SSLEngine#setUseClientMode(boolean) method * documentation for more information */ + @Override public void setUseClientMode(boolean mode) { if (handshake_started) { throw new IllegalArgumentException( @@ -279,6 +285,7 @@ public class SSLEngineImpl extends SSLEngine { * @see javax.net.ssl.SSLEngine#getUseClientMode() method * documentation for more information */ + @Override public boolean getUseClientMode() { return sslParameters.getUseClientMode(); } @@ -288,6 +295,7 @@ public class SSLEngineImpl extends SSLEngine { * @see javax.net.ssl.SSLEngine#setNeedClientAuth(boolean) method * documentation for more information */ + @Override public void setNeedClientAuth(boolean need) { sslParameters.setNeedClientAuth(need); } @@ -297,6 +305,7 @@ public class SSLEngineImpl extends SSLEngine { * @see javax.net.ssl.SSLEngine#getNeedClientAuth() method * documentation for more information */ + @Override public boolean getNeedClientAuth() { return sslParameters.getNeedClientAuth(); } @@ -306,6 +315,7 @@ public class SSLEngineImpl extends SSLEngine { * @see javax.net.ssl.SSLEngine#setWantClientAuth(boolean) method * documentation for more information */ + @Override public void setWantClientAuth(boolean want) { sslParameters.setWantClientAuth(want); } @@ -315,6 +325,7 @@ public class SSLEngineImpl extends SSLEngine { * @see javax.net.ssl.SSLEngine#getWantClientAuth() method * documentation for more information */ + @Override public boolean getWantClientAuth() { return sslParameters.getWantClientAuth(); } @@ -324,6 +335,7 @@ public class SSLEngineImpl extends SSLEngine { * @see javax.net.ssl.SSLEngine#setEnableSessionCreation(boolean) method * documentation for more information */ + @Override public void setEnableSessionCreation(boolean flag) { sslParameters.setEnableSessionCreation(flag); } @@ -333,6 +345,7 @@ public class SSLEngineImpl extends SSLEngine { * @see javax.net.ssl.SSLEngine#getEnableSessionCreation() method * documentation for more information */ + @Override public boolean getEnableSessionCreation() { return sslParameters.getEnableSessionCreation(); } @@ -344,6 +357,7 @@ public class SSLEngineImpl extends SSLEngine { * @see javax.net.ssl.SSLEngine#getHandshakeStatus() method * documentation for more information */ + @Override public SSLEngineResult.HandshakeStatus getHandshakeStatus() { if (!handshake_started || engine_was_shutteddown) { // initial handshake has not been started yet @@ -365,12 +379,12 @@ public class SSLEngineImpl extends SSLEngine { * @see javax.net.ssl.SSLEngine#getSession() method * documentation for more information */ + @Override public SSLSession getSession() { if (session != null) { return session; - } else { - return SSLSessionImpl.NULL_SESSION; } + return SSLSessionImpl.NULL_SESSION; } /** @@ -378,6 +392,7 @@ public class SSLEngineImpl extends SSLEngine { * @see javax.net.ssl.SSLEngine#isInboundDone() method * documentation for more information */ + @Override public boolean isInboundDone() { return isInboundDone || engine_was_closed; } @@ -387,6 +402,7 @@ public class SSLEngineImpl extends SSLEngine { * @see javax.net.ssl.SSLEngine#isOutboundDone() method * documentation for more information */ + @Override public boolean isOutboundDone() { return isOutboundDone; } @@ -402,6 +418,7 @@ public class SSLEngineImpl extends SSLEngine { * @see javax.net.ssl.SSLEngine#unwrap(ByteBuffer,ByteBuffer[],int,int) * method documentation for more information */ + @Override public SSLEngineResult unwrap(ByteBuffer src, ByteBuffer[] dsts, int offset, int length) throws SSLException { if (engine_was_shutteddown) { @@ -566,6 +583,7 @@ public class SSLEngineImpl extends SSLEngine { * @see javax.net.ssl.SSLEngine#wrap(ByteBuffer[],int,int,ByteBuffer) method * documentation for more information */ + @Override public SSLEngineResult wrap(ByteBuffer[] srcs, int offset, int len, ByteBuffer dst) throws SSLException { if (engine_was_shutteddown) { diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLInputStream.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLInputStream.java index bd9b6cf22..6c23a912d 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLInputStream.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLInputStream.java @@ -15,11 +15,6 @@ * limitations under the License. */ -/** - * @author Alexander Y. Kleymenov - * @version $Revision$ - */ - package org.apache.harmony.xnet.provider.jsse; import java.io.IOException; @@ -37,6 +32,7 @@ public abstract class SSLInputStream extends InputStream { /** * @see java.io.InputStream#available() */ + @Override public abstract int available() throws IOException; /** @@ -49,11 +45,13 @@ public abstract class SSLInputStream extends InputStream { * @see org.apache.harmony.xnet.provider.jsse.SSLBufferedInput#read() * @see org.apache.harmony.xnet.provider.jsse.HandshakeIODataStream#read() */ + @Override public abstract int read() throws IOException; /** * @see java.io.InputStream#skip(long) */ + @Override public long skip(long n) throws IOException { long skept = n; while (n > 0) { @@ -119,6 +117,7 @@ public abstract class SSLInputStream extends InputStream { /** * @see java.io.InputStream#read(byte[],int,int) */ + @Override public int read(byte[] b, int off, int len) throws IOException { int read_b; int i = 0; diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLParameters.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLParameters.java index d91388a28..89916dee2 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLParameters.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLParameters.java @@ -15,11 +15,6 @@ * limitations under the License. */ -/** - * @author Alexander Y. Kleymenov - * @version $Revision$ - */ - package org.apache.harmony.xnet.provider.jsse; import java.security.KeyManagementException; @@ -317,7 +312,7 @@ public class SSLParameters implements Cloneable { enabledCipherSuiteNames[i] = enabledCipherSuites[i].getName(); } } - return (String[]) enabledCipherSuiteNames.clone(); + return enabledCipherSuiteNames.clone(); } /** @@ -345,7 +340,7 @@ public class SSLParameters implements Cloneable { * @return the set of enabled protocols */ protected String[] getEnabledProtocols() { - return (String[]) enabledProtocols.clone(); + return enabledProtocols.clone(); } /** @@ -436,6 +431,7 @@ public class SSLParameters implements Cloneable { * Returns the clone of this object. * @return the clone. */ + @Override protected Object clone() { // BEGIN android-changed try { diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLRecordProtocol.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLRecordProtocol.java index 4428820fd..423a817af 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLRecordProtocol.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLRecordProtocol.java @@ -15,11 +15,6 @@ * limitations under the License. */ -/** - * @author Alexander Y. Kleymenov - * @version $Revision$ - */ - package org.apache.harmony.xnet.provider.jsse; import org.apache.harmony.xnet.provider.jsse.AlertException; @@ -33,7 +28,7 @@ import javax.net.ssl.SSLProtocolException; * This class performs functionality dedicated to SSL record layer. * It unpacks and routes income data to the appropriate * client protocol (handshake, alert, application data protocols) - * and paketizes outcome data into SSL/TLS records. + * and packages outcome data into SSL/TLS records. * Initially created object has null connection state and does not * perform any cryptography computations over the income/outcome data. * After handshake protocol agreed upon security parameters they are placed @@ -179,14 +174,13 @@ public class SSLRecordProtocol { } if (activeReadState == null) { return record_size; - } else { - return activeReadState.getContentSize(record_size); } + return activeReadState.getContentSize(record_size); } /** * Depending on the Connection State (Session) encrypts and compress - * the provided data, and packs it into TLSCiphertext structute. + * the provided data, and packs it into TLSCiphertext structure. * @param content_type: int * @param fragment: byte[] * @return ssl packet created over the current connection state @@ -198,7 +192,7 @@ public class SSLRecordProtocol { /** * Depending on the Connection State (Session) encrypts and compress - * the provided data, and packs it into TLSCiphertext structute. + * the provided data, and packs it into TLSCiphertext structure. * @param content_type: int * @param fragment: byte[] * @return ssl packet created over the current connection state @@ -374,7 +368,7 @@ public class SSLRecordProtocol { type)); } } else { - in.skip((long) 2); // just skip the version number + in.skip(2); // just skip the version number } int length = in.readUint16(); if (logger != null) { diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSessionImpl.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSessionImpl.java index 4510c96c8..5d46568bb 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSessionImpl.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSessionImpl.java @@ -15,11 +15,6 @@ * limitations under the License. */ -/** - * @author Boris Kuznetsov - * @version $Revision$ - */ - package org.apache.harmony.xnet.provider.jsse; import java.security.AccessControlContext; @@ -29,7 +24,8 @@ import java.security.SecureRandom; import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; -import java.util.Iterator; +import java.util.HashMap; +import java.util.Map; import java.util.Vector; import javax.net.ssl.SSLPeerUnverifiedException; @@ -39,25 +35,67 @@ import javax.net.ssl.SSLSessionBindingEvent; import javax.net.ssl.SSLSessionBindingListener; import javax.net.ssl.SSLSessionContext; -import org.apache.harmony.luni.util.TwoKeyHashMap; - /** * * SSLSession implementation - * + * * @see javax.net.ssl.SSLSession */ -public class SSLSessionImpl implements SSLSession { +public class SSLSessionImpl implements SSLSession, Cloneable { /** - * Session object reporting an invalid cipher suite of - * "SSL_NULL_WITH_NULL_NULL" + * Session object reporting an invalid cipher suite of "SSL_NULL_WITH_NULL_NULL" */ public static final SSLSessionImpl NULL_SESSION = new SSLSessionImpl(null); + /** + * Container class for the 'value' map's keys. + */ + private static final class ValueKey { + final String name; + final AccessControlContext acc; + + ValueKey(String name) { + super(); + this.name = name; + this.acc = AccessController.getContext(); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((acc == null) ? 0 : acc.hashCode()); + result = prime * result + ((name == null) ? 0 : name.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (!(obj instanceof ValueKey)) + return false; + ValueKey other = (ValueKey) obj; + if (acc == null) { + if (other.acc != null) + return false; + } else if (!acc.equals(other.acc)) + return false; + if (name == null) { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) + return false; + return true; + } + } + private long creationTime; private boolean isValid = true; - private TwoKeyHashMap values = new TwoKeyHashMap(); + private Map values = new HashMap(); /** * ID of the session @@ -65,7 +103,7 @@ public class SSLSessionImpl implements SSLSession { byte[] id; /** - * Last time the session was accessed + * Last time the session was accessed */ long lastAccessedTime; @@ -87,7 +125,7 @@ public class SSLSessionImpl implements SSLSession { // END android-changed /** - * certificates were sent to the peer + * certificates were sent to the peer */ X509Certificate[] localCertificates; @@ -97,21 +135,20 @@ public class SSLSessionImpl implements SSLSession { X509Certificate[] peerCertificates; /** - * Peer host name + * Peer host name */ - String peerHost; + private String peerHost; /** * Peer port number */ - int peerPort = -1; + private int peerPort = -1; /** * Master secret */ byte[] master_secret; - /** * clientRandom */ @@ -125,10 +162,11 @@ public class SSLSessionImpl implements SSLSession { /** * True if this entity is considered the server */ - boolean isServer = false; + final boolean isServer; /** * Creates SSLSession implementation + * * @param cipher_suite * @param sr */ @@ -143,11 +181,11 @@ public class SSLSessionImpl implements SSLSession { this.cipherSuite = cipher_suite; id = new byte[32]; sr.nextBytes(id); - long time = new java.util.Date().getTime() / 1000; + long time = creationTime / 1000; id[28] = (byte) ((time & 0xFF000000) >>> 24); - id[29] = (byte) ((time & 0xFF0000) >>> 16); - id[30] = (byte) ((time & 0xFF00) >>> 8); - id[31] = (byte) (time & 0xFF); + id[29] = (byte) ((time & 0x00FF0000) >>> 16); + id[30] = (byte) ((time & 0x0000FF00) >>> 8); + id[31] = (byte) ((time & 0x000000FF)); isServer = true; } @@ -155,78 +193,48 @@ public class SSLSessionImpl implements SSLSession { /** * Creates SSLSession implementation + * * @param sr */ public SSLSessionImpl(SecureRandom sr) { this(null, sr); } - private SSLSessionImpl() { - } - - /** - * @see javax.net.ssl.SSLSession#getApplicationBufferSize() - */ public int getApplicationBufferSize() { return SSLRecordProtocol.MAX_DATA_LENGTH; } - /** - * @see javax.net.ssl.SSLSession#getCipherSuite() - */ public String getCipherSuite() { return cipherSuite.getName(); } - /** - * @see javax.net.ssl.SSLSession#getCreationTime() - */ public long getCreationTime() { return creationTime; } - /** - * @see javax.net.ssl.SSLSession#getId() - */ public byte[] getId() { return id; } - /** - * @see javax.net.ssl.SSLSession#getLastAccessedTime() - */ public long getLastAccessedTime() { return lastAccessedTime; } - /** - * @see javax.net.ssl.SSLSession#getLocalCertificates() - */ public Certificate[] getLocalCertificates() { return localCertificates; } - /** - * @see javax.net.ssl.SSLSession#getLocalPrincipal() - */ public Principal getLocalPrincipal() { if (localCertificates != null && localCertificates.length > 0) { return localCertificates[0].getSubjectX500Principal(); - } else { - return null; } + return null; } - /** - * @see javax.net.ssl.SSLSession#getPacketBufferSize() - */ public int getPacketBufferSize() { return SSLRecordProtocol.MAX_SSL_PACKET_SIZE; } - /** - * @see javax.net.ssl.SSLSession#getPeerCertificateChain() - */ public javax.security.cert.X509Certificate[] getPeerCertificateChain() throws SSLPeerUnverifiedException { if (peerCertificates == null) { @@ -235,8 +243,8 @@ public class SSLSessionImpl implements SSLSession { javax.security.cert.X509Certificate[] certs = new javax.security.cert.X509Certificate[peerCertificates.length]; for (int i = 0; i < certs.length; i++) { try { - certs[i] = javax.security.cert.X509Certificate - .getInstance(peerCertificates[i].getEncoded()); + certs[i] = javax.security.cert.X509Certificate.getInstance(peerCertificates[i] + .getEncoded()); } catch (javax.security.cert.CertificateException e) { } catch (CertificateEncodingException e) { } @@ -244,34 +252,21 @@ public class SSLSessionImpl implements SSLSession { return certs; } - /** - * @see javax.net.ssl.SSLSession#getPeerCertificates() - */ - public Certificate[] getPeerCertificates() - throws SSLPeerUnverifiedException { + public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException { if (peerCertificates == null) { throw new SSLPeerUnverifiedException("No peer certificate"); } return peerCertificates; } - /** - * @see javax.net.ssl.SSLSession#getPeerHost() - */ public String getPeerHost() { return peerHost; } - /** - * @see javax.net.ssl.SSLSession#getPeerPort() - */ public int getPeerPort() { return peerPort; } - /** - * @see javax.net.ssl.SSLSession#getPeerPrincipal() - */ public Principal getPeerPrincipal() throws SSLPeerUnverifiedException { if (peerCertificates == null) { throw new SSLPeerUnverifiedException("No peer certificate"); @@ -279,16 +274,10 @@ public class SSLSessionImpl implements SSLSession { return peerCertificates[0].getSubjectX500Principal(); } - /** - * @see javax.net.ssl.SSLSession#getProtocol() - */ public String getProtocol() { return protocol.name; } - /** - * @see javax.net.ssl.SSLSession#getSessionContext() - */ public SSLSessionContext getSessionContext() { SecurityManager sm = System.getSecurityManager(); if (sm != null) { @@ -297,109 +286,71 @@ public class SSLSessionImpl implements SSLSession { return context; } - /** - * @see javax.net.ssl.SSLSession#getValue(String name) - */ public Object getValue(String name) { if (name == null) { throw new IllegalArgumentException("Parameter is null"); } - return values.get(name, AccessController.getContext()); + return values.get(new ValueKey(name)); } - /** - * @see javax.net.ssl.SSLSession#getValueNames() - */ public String[] getValueNames() { - Vector v = new Vector(); - AccessControlContext current = AccessController.getContext(); - AccessControlContext cont; - for (Iterator it = values.entrySet().iterator(); it.hasNext();) { - TwoKeyHashMap.Entry entry = (TwoKeyHashMap.Entry) it.next(); - cont = (AccessControlContext) entry.getKey2(); - if ((current == null && cont == null) - || (current != null && current.equals(cont))) { - v.add(entry.getKey1()); + final Vector v = new Vector(); + final AccessControlContext currAcc = AccessController.getContext(); + for (ValueKey key : values.keySet()) { + if ((currAcc == null && key.acc == null) + || (currAcc != null && currAcc.equals(key.acc))) { + v.add(key.name); } } - return (String[]) v.toArray(new String[0]); + return v.toArray(new String[v.size()]); } - /** - * @see javax.net.ssl.SSLSession#invalidate() - */ public void invalidate() { isValid = false; } - /** - * @see javax.net.ssl.SSLSession#isValid() - */ public boolean isValid() { - if (isValid - && context != null - && context.getSessionTimeout() != 0 - && lastAccessedTime + context.getSessionTimeout() > System - .currentTimeMillis()) { + if (isValid && context != null && context.getSessionTimeout() != 0 + && lastAccessedTime + context.getSessionTimeout() > System.currentTimeMillis()) { isValid = false; } return isValid; } - /** - * @see javax.net.ssl.SSLSession#putValue(String name, Object value) - */ public void putValue(String name, Object value) { if (name == null || value == null) { throw new IllegalArgumentException("Parameter is null"); } - Object old = values.put(name, AccessController.getContext(), value); + Object old = values.put(new ValueKey(name), value); if (value instanceof SSLSessionBindingListener) { - ((SSLSessionBindingListener) value) - .valueBound(new SSLSessionBindingEvent(this, name)); + ((SSLSessionBindingListener) value).valueBound(new SSLSessionBindingEvent(this, name)); } if (old != null && old instanceof SSLSessionBindingListener) { - ((SSLSessionBindingListener) old) - .valueUnbound(new SSLSessionBindingEvent(this, name)); + ((SSLSessionBindingListener) old).valueUnbound(new SSLSessionBindingEvent(this, name)); } } - /** - * @see javax.net.ssl.SSLSession#removeValue(String name) - */ public void removeValue(String name) { if (name == null) { throw new IllegalArgumentException("Parameter is null"); } - values.remove(name, AccessController.getContext()); + values.remove(new ValueKey(name)); } + @Override public Object clone() { - SSLSessionImpl ses = new SSLSessionImpl(); - ses.id = this.id; - ses.creationTime = this.creationTime; - ses.lastAccessedTime = this.lastAccessedTime; - ses.isValid = this.isValid; - ses.cipherSuite = this.cipherSuite; - ses.localCertificates = this.localCertificates; - ses.peerCertificates = this.peerCertificates; - ses.master_secret = this.master_secret; - ses.clientRandom = this.clientRandom; - ses.serverRandom = this.serverRandom; - ses.peerHost = this.peerHost; - ses.peerPort = this.peerPort; - ses.isServer = this.isServer; - ses.context = this.context; - ses.protocol = this.protocol; - ses.values = this.values; - return ses; + try { + return super.clone(); + } catch (CloneNotSupportedException e) { + throw new AssertionError(e); + } } - /** * Sets the address of the peer + * * @param peerHost * @param peerPort */ diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLStreamedInput.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLStreamedInput.java index efabef833..c04065395 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLStreamedInput.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLStreamedInput.java @@ -15,11 +15,6 @@ * limitations under the License. */ -/** - * @author Alexander Y. Kleymenov - * @version $Revision$ - */ - package org.apache.harmony.xnet.provider.jsse; import java.io.IOException; @@ -37,6 +32,7 @@ public class SSLStreamedInput extends SSLInputStream { this.in = in; } + @Override public int available() throws IOException { return in.available(); } @@ -49,6 +45,7 @@ public class SSLStreamedInput extends SSLInputStream { * @throws org.apache.harmony.xnet.provider.jsse.EndOfSourceException if the end of the underlying * stream has been reached. */ + @Override public int read() throws IOException { int res = in.read(); if (res < 0) { diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLv3Constants.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLv3Constants.java index 1a03f7fa4..07aaca89a 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLv3Constants.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLv3Constants.java @@ -15,11 +15,6 @@ * limitations under the License. */ -/** - * @author Boris Kuznetsov - * @version $Revision$ - */ - package org.apache.harmony.xnet.provider.jsse; /** diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerHandshakeImpl.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerHandshakeImpl.java index f6eef23bf..3bb096bc8 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerHandshakeImpl.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerHandshakeImpl.java @@ -15,11 +15,6 @@ * limitations under the License. */ -/** -* @author Boris Kuznetsov -* @version $Revision$ -*/ - package org.apache.harmony.xnet.provider.jsse; import org.apache.harmony.xnet.provider.jsse.SSLv3Constants; @@ -56,7 +51,7 @@ import javax.net.ssl.X509TrustManager; * Handshake protocol operates on top of the Record Protocol. * It responsible for negotiating a session. * - * The implementation proceses inbound client handshake messages, + * The implementation processes inbound client handshake messages, * creates and sends respond messages. Outbound messages are supplied * to Record Protocol. Detected errors are reported to the Alert protocol. * @@ -82,6 +77,7 @@ public class ServerHandshakeImpl extends HandshakeProtocol { /** * Start session negotiation */ + @Override public void start() { if (session == null) { // initial handshake status = NEED_UNWRAP; @@ -101,6 +97,7 @@ public class ServerHandshakeImpl extends HandshakeProtocol { * Proceses inbound handshake messages * @param bytes */ + @Override public void unwrap(byte[] bytes) { io_stream.append(bytes); @@ -128,15 +125,12 @@ public class ServerHandshakeImpl extends HandshakeProtocol { needSendHelloRequest = false; clientHello = new ClientHello(io_stream, length); if (nonBlocking) { - delegatedTasks.add(new DelegatedTask( - new PrivilegedExceptionAction(){ - public Object run() throws Exception { + delegatedTasks.add(new DelegatedTask(new PrivilegedExceptionAction() { + public Void run() throws Exception { processClientHello(); return null; - } - }, - this, - AccessController.getContext())); + } + }, this, AccessController.getContext())); return; } processClientHello(); @@ -152,7 +146,7 @@ public class ServerHandshakeImpl extends HandshakeProtocol { if (clientCert.certs.length == 0) { if (parameters.getNeedClientAuth()) { fatalAlert(AlertProtocol.HANDSHAKE_FAILURE, - "HANDSHAKE FAILURE: no client certificate recived"); + "HANDSHAKE FAILURE: no client certificate received"); } } else { String authType = clientCert.certs[0].getPublicKey() @@ -324,32 +318,27 @@ public class ServerHandshakeImpl extends HandshakeProtocol { * @ see TLS 1.0 spec., E.1. Version 2 client hello * @param bytes */ + @Override public void unwrapSSLv2(byte[] bytes) { + io_stream.append(bytes); + io_stream.mark(); try { - io_stream.append(bytes); - io_stream.mark(); - try { - clientHello = new ClientHello(io_stream); - } catch (IOException e) { - io_stream.reset(); - return; - } - if (nonBlocking) { - delegatedTasks.add(new DelegatedTask( - new PrivilegedExceptionAction(){ - public Object run() throws Exception { - processClientHello(); - return null; + clientHello = new ClientHello(io_stream); + } catch (IOException e) { + io_stream.reset(); + return; + } + if (nonBlocking) { + delegatedTasks.add(new DelegatedTask( + new PrivilegedExceptionAction() { + public Void run() throws Exception { + processClientHello(); + return null; } - }, - this, - AccessController.getContext())); - return; - } - processClientHello(); - } catch (Exception e) { - fatalAlert(AlertProtocol.INTERNAL_ERROR, "INTERNAL ERROR", e); + }, this, AccessController.getContext())); + return; } + processClientHello(); } /** @@ -407,10 +396,9 @@ public class ServerHandshakeImpl extends HandshakeProtocol { status = NOT_HANDSHAKING; clearMessages(); return; - } else { - fatalAlert(AlertProtocol.HANDSHAKE_FAILURE, - "SSL Session may not be created"); } + // throw AlertException + fatalAlert(AlertProtocol.HANDSHAKE_FAILURE, "SSL Session may not be created"); } session = null; } else { @@ -569,7 +557,7 @@ public class ServerHandshakeImpl extends HandshakeProtocol { } catch (NoSuchAlgorithmException e) { kf = KeyFactory.getInstance("DiffieHellman"); } - dhkeySpec = (DHPublicKeySpec) kf.getKeySpec(dhkey, + dhkeySpec = kf.getKeySpec(dhkey, DHPublicKeySpec.class); } if (!cipher_suite.isAnonymous()) { // calculate signed_params @@ -654,6 +642,7 @@ public class ServerHandshakeImpl extends HandshakeProtocol { /** * Creates and sends finished message */ + @Override protected void makeFinished() { byte[] verify_data; boolean isTLS = (serverHello.server_version[1] == 1); // TLS 1.0 protocol @@ -702,8 +691,9 @@ public class ServerHandshakeImpl extends HandshakeProtocol { } /** - * Proceses inbound ChangeCipherSpec message + * Processes inbound ChangeCipherSpec message */ + @Override public void receiveChangeCipherSpec() { if (isResuming) { if (serverFinished == null) { diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerHello.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerHello.java index 036528815..1cd962448 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerHello.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerHello.java @@ -15,11 +15,6 @@ * limitations under the License. */ -/** -* @author Boris Kuznetsov -* @version $Revision$ -*/ - package org.apache.harmony.xnet.provider.jsse; import org.apache.harmony.xnet.provider.jsse.Message; @@ -112,6 +107,7 @@ public class ServerHello extends Message { * Sends message * @param out */ + @Override public void send(HandshakeIODataStream out) { out.write(server_version); out.write(random); @@ -134,6 +130,7 @@ public class ServerHello extends Message { * Returns message type * @return */ + @Override public int getType() { return Handshake.SERVER_HELLO; } diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerHelloDone.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerHelloDone.java index e794ed942..73b6a8123 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerHelloDone.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerHelloDone.java @@ -15,11 +15,6 @@ * limitations under the License. */ -/** -* @author Boris Kuznetsov -* @version $Revision$ -*/ - package org.apache.harmony.xnet.provider.jsse; import org.apache.harmony.xnet.provider.jsse.Message; @@ -59,6 +54,7 @@ public class ServerHelloDone extends Message { * Sends message * @param out */ + @Override public void send(HandshakeIODataStream out) { } @@ -66,6 +62,7 @@ public class ServerHelloDone extends Message { * Returns message length * @return */ + @Override public int length() { return 0; } @@ -74,6 +71,7 @@ public class ServerHelloDone extends Message { * Returns message type * @return */ + @Override public int getType() { return Handshake.SERVER_HELLO_DONE; } diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerKeyExchange.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerKeyExchange.java index 1d93ecef5..446b7b4c6 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerKeyExchange.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/ServerKeyExchange.java @@ -15,11 +15,6 @@ * limitations under the License. */ -/** -* @author Boris Kuznetsov -* @version $Revision$ -*/ - package org.apache.harmony.xnet.provider.jsse; import org.apache.harmony.xnet.provider.jsse.Message; @@ -150,6 +145,7 @@ public class ServerKeyExchange extends Message { * Sends message * @param out */ + @Override public void send(HandshakeIODataStream out) { out.writeUint16(bytes1.length); out.write(bytes1); @@ -189,6 +185,7 @@ public class ServerKeyExchange extends Message { * Returns message type * @return */ + @Override public int getType() { return Handshake.SERVER_KEY_EXCHANGE; } diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustManagerFactoryImpl.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustManagerFactoryImpl.java index b96d1ab5d..c473864b5 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustManagerFactoryImpl.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustManagerFactoryImpl.java @@ -15,11 +15,6 @@ * limitations under the License. */ -/** - * @author Boris Kuznetsov - * @version $Revision$ - */ - package org.apache.harmony.xnet.provider.jsse; import java.io.File; @@ -50,6 +45,7 @@ public class TrustManagerFactoryImpl extends TrustManagerFactorySpi { /** * @see javax.net.ssl.TrustManagerFactorySpi#engineInit(KeyStore) */ + @Override public void engineInit(KeyStore ks) throws KeyStoreException { if (ks != null) { keyStore = ks; @@ -117,6 +113,7 @@ public class TrustManagerFactoryImpl extends TrustManagerFactorySpi { /** * @see javax.net.ssl#engineInit(ManagerFactoryParameters) */ + @Override public void engineInit(ManagerFactoryParameters spec) throws InvalidAlgorithmParameterException { throw new InvalidAlgorithmParameterException( @@ -126,6 +123,7 @@ public class TrustManagerFactoryImpl extends TrustManagerFactorySpi { /** * @see javax.net.ssl#engineGetTrustManagers() */ + @Override public TrustManager[] engineGetTrustManagers() { if (keyStore == null) { throw new IllegalStateException( diff --git a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustManagerImpl.java b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustManagerImpl.java index 15756bde4..543dfb2b4 100644 --- a/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustManagerImpl.java +++ b/libcore/x-net/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustManagerImpl.java @@ -15,11 +15,6 @@ * limitations under the License. */ -/** - * @author Boris Kuznetsov - * @version $Revision$ - */ - package org.apache.harmony.xnet.provider.jsse; import org.bouncycastle.jce.provider.IndexedPKIXParameters; @@ -49,11 +44,11 @@ import java.security.cert.CertificateEncodingException; // END android-added /** - * + * * TrustManager implementation. The implementation is based on CertPathValidator * PKIX and CertificateFactory X509 implementations. This implementations should * be provided by some certification provider. - * + * * @see javax.net.ssl.X509TrustManager */ public class TrustManagerImpl implements X509TrustManager { @@ -68,20 +63,18 @@ public class TrustManagerImpl implements X509TrustManager { /** * Creates trust manager implementation - * + * * @param ks */ public TrustManagerImpl(KeyStore ks) { try { validator = CertPathValidator.getInstance("PKIX"); factory = CertificateFactory.getInstance("X509"); - String alias; - X509Certificate cert; byte[] nameConstrains = null; - Set trusted = new HashSet(); - for (Enumeration en = ks.aliases(); en.hasMoreElements();) { - alias = (String) en.nextElement(); - cert = (X509Certificate) ks.getCertificate(alias); + Set trusted = new HashSet(); + for (Enumeration en = ks.aliases(); en.hasMoreElements();) { + final String alias = en.nextElement(); + final X509Certificate cert = (X509Certificate) ks.getCertificate(alias); if (cert != null) { trusted.add(new TrustAnchor(cert, nameConstrains)); } @@ -151,70 +144,77 @@ public class TrustManagerImpl implements X509TrustManager { throws CertificateException { if (chain == null || chain.length == 0 || authType == null || authType.length() == 0) { - throw new IllegalArgumentException("null or zero-length parameter"); + throw new IllegalArgumentException( + "null or zero-length parameter"); } if (err != null) { throw new CertificateException(err); } - // BEGIN android-added - // Cater for degenerate special case where we can't - // establish an actual certificate chain the usual way, - // but have the peer certificate in our trust store. - if (isDirectlyTrustedCert(chain)) { - return; - } - // END android-added +// BEGIN android-changed + CertificateException ce = null; try { - // BEGIN android-changed - CertPath certPath = factory.generateCertPath(Arrays.asList(chain)); + CertPath certPath = factory.generateCertPath( + Arrays.asList(chain)); if (!Arrays.equals(chain[0].getEncoded(), - ((X509Certificate)certPath.getCertificates().get(0)) - .getEncoded())) { - // sanity check failed (shouldn't ever happen, but we are using pretty remote code) + certPath.getCertificates().get(0).getEncoded())) { + // Sanity check failed (shouldn't ever happen, but we are + // using pretty remote code) throw new CertificateException("Certificate chain error"); } validator.validate(certPath, params); - // END android-changed } catch (InvalidAlgorithmParameterException e) { - throw new CertificateException(e); + ce = new CertificateException(e); } catch (CertPathValidatorException e) { - throw new CertificateException(e); + ce = new CertificateException(e); + } + if (ce != null) { + // Caters to degenerate special case where we can't + // establish an actual certificate chain the usual way + // but have the peer certificate in our trust store. + if (!isDirectlyTrustedCert(chain)) { + throw ce; + } } } - // BEGIN android-added /** * Checks whether the given chain is just a certificate * that we have in our trust store. - * + * * @param chain The certificate chain. - * - * @return True if the certificate is in our trust store, false otherwise. + * + * @return True if the certificate is in our trust store, false otherwise. */ private boolean isDirectlyTrustedCert(X509Certificate[] chain) { byte[] questionable; if (chain.length == 1) { - try { - questionable = chain[0].getEncoded(); - Set anchors = params.getTrustAnchors(); - - for (TrustAnchor trustAnchor : anchors) { - byte[] trusted = trustAnchor.getTrustedCert().getEncoded(); - - if (Arrays.equals(questionable, trusted)) { - return true; - } + if (params instanceof IndexedPKIXParameters) { + IndexedPKIXParameters index = (IndexedPKIXParameters) params; + return index.isDirectlyTrusted(chain[0]); + } else { + try { + questionable = chain[0].getEncoded(); + Set anchors = params.getTrustAnchors(); + + for (TrustAnchor trustAnchor : anchors) { + byte[] trusted = trustAnchor.getTrustedCert() + .getEncoded(); + if (Arrays.equals(questionable, trusted)) { + return true; + } + } + } catch (CertificateEncodingException e) { + // Ignore. + } } - } catch (CertificateEncodingException e) { - // Ignore. - } + } return false; } - // END android-added - +// END android-changed + /** * @see javax.net.ssl.X509TrustManager#getAcceptedIssuers() */ @@ -222,11 +222,11 @@ public class TrustManagerImpl implements X509TrustManager { if (params == null) { return new X509Certificate[0]; } - Set anchors = params.getTrustAnchors(); + Set anchors = params.getTrustAnchors(); X509Certificate[] certs = new X509Certificate[anchors.size()]; int i = 0; - for (Iterator it = anchors.iterator(); it.hasNext();) { - certs[i++] = ((TrustAnchor) it.next()).getTrustedCert(); + for (Iterator it = anchors.iterator(); it.hasNext();) { + certs[i++] = it.next().getTrustedCert(); } return certs; } diff --git a/libcore/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl.cpp b/libcore/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl.cpp index 250cf8351..87f2af3ea 100644 --- a/libcore/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl.cpp +++ b/libcore/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl.cpp @@ -36,8 +36,6 @@ #include #include -#include - #include "org_apache_harmony_xnet_provider_jsse_common.h" /** @@ -423,7 +421,7 @@ typedef struct app_data { static int sslCreateAppData(SSL* ssl) { APP_DATA* data = (APP_DATA*) malloc(sizeof(APP_DATA)); - memset(data, sizeof(APP_DATA), 0); + memset(data, 0, sizeof(APP_DATA)); data->aliveAndKicking = 1; data->waitingThreads = 0; @@ -670,8 +668,11 @@ static int sslRead(SSL* ssl, char* buf, jint len, int* sslReturnCode, // LOGD("Doing SSL_Read()"); int result = SSL_read(ssl, buf, len); - int error = SSL_get_error(ssl, result); - freeSslErrorState(); + int error = SSL_ERROR_NONE; + if (result <= 0) { + error = SSL_get_error(ssl, result); + freeSslErrorState(); + } // LOGD("Returned from SSL_Read() with result %d, error code %d", result, error); // If we have been successful in moving data around, check whether it @@ -693,7 +694,6 @@ static int sslRead(SSL* ssl, char* buf, jint len, int* sslReturnCode, switch (error) { // Sucessfully read at least one byte. case SSL_ERROR_NONE: { - add_recv_stats(fd, result); return result; } @@ -786,8 +786,11 @@ static int sslWrite(SSL* ssl, const char* buf, jint len, int* sslReturnCode, // LOGD("Doing SSL_write() with %d bytes to go", len); int result = SSL_write(ssl, buf, len); - int error = SSL_get_error(ssl, result); - freeSslErrorState(); + int error = SSL_ERROR_NONE; + if (result <= 0) { + error = SSL_get_error(ssl, result); + freeSslErrorState(); + } // LOGD("Returned from SSL_write() with result %d, error code %d", result, error); // If we have been successful in moving data around, check whether it @@ -861,7 +864,6 @@ static int sslWrite(SSL* ssl, const char* buf, jint len, int* sslReturnCode, } } } - add_send_stats(fd, count); // LOGD("Successfully wrote %d bytes", count); return count; diff --git a/libcore/xml/src/test/java/tests/api/javax/xml/parsers/DocumentBuilderTest.java b/libcore/xml/src/test/java/tests/api/javax/xml/parsers/DocumentBuilderTest.java index c515d2057..292c2f1a7 100644 --- a/libcore/xml/src/test/java/tests/api/javax/xml/parsers/DocumentBuilderTest.java +++ b/libcore/xml/src/test/java/tests/api/javax/xml/parsers/DocumentBuilderTest.java @@ -278,7 +278,6 @@ public class DocumentBuilderTest extends TestCase { method = "parse", args = {java.io.File.class} ) - @KnownFailure("d.getChildNodes returns an unexpected/strange #Text node") public void test_parseLjava_io_File() throws IOException { File f = resourceToTmpFile("/simple.xml"); diff --git a/libdex/DexFile.c b/libdex/DexFile.c index 2639d7bdb..99b38c916 100644 --- a/libdex/DexFile.c +++ b/libdex/DexFile.c @@ -13,12 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + /* * Access the contents of a .dex file. */ #include "DexFile.h" #include "DexProto.h" +#include "DexCatch.h" #include "Leb128.h" #include "sha1.h" #include "ZipArchive.h" @@ -643,6 +645,10 @@ static bool parseAuxData(const u1* data, DexFile* pDexFile) } indexMapType = *pAux; break; + case kDexChunkRegisterMaps: + LOGV("+++ found register maps, size=%u\n", size); + pDexFile->pRegisterMapPool = data; + break; default: LOGI("Unknown chunk 0x%08x (%c%c%c%c), size=%d in aux data area\n", *pAux, @@ -882,6 +888,43 @@ u4 dexComputeChecksum(const DexHeader* pHeader) } +/* + * Compute the size, in bytes, of a DexCode. + */ +size_t dexGetDexCodeSize(const DexCode* pCode) +{ + /* + * The catch handler data is the last entry. It has a variable number + * of variable-size pieces, so we need to create an iterator. + */ + u4 handlersSize; + u4 offset; + u4 ui; + + if (pCode->triesSize != 0) { + handlersSize = dexGetHandlersSize(pCode); + offset = dexGetFirstHandlerOffset(pCode); + } else { + handlersSize = 0; + offset = 0; + } + + for (ui = 0; ui < handlersSize; ui++) { + DexCatchIterator iterator; + dexCatchIteratorInit(&iterator, pCode, offset); + offset = dexCatchIteratorGetEndOffset(&iterator, pCode); + } + + const u1* handlerData = dexGetCatchHandlerData(pCode); + + //LOGD("+++ pCode=%p handlerData=%p last offset=%d\n", + // pCode, handlerData, offset); + + /* return the size of the catch handler + everything before it */ + return (handlerData - (u1*) pCode) + offset; +} + + /* * =========================================================================== * Debug info @@ -1181,3 +1224,4 @@ invalid_stream: free(methodDescriptor); } } + diff --git a/libdex/DexFile.h b/libdex/DexFile.h index d1ea5ebb2..4b5fe7c1d 100644 --- a/libdex/DexFile.h +++ b/libdex/DexFile.h @@ -163,6 +163,7 @@ enum { /* auxillary data section chunk codes */ enum { kDexChunkClassLookup = 0x434c4b50, /* CLKP */ + kDexChunkRegisterMaps = 0x524d4150, /* RMAP */ kDexChunkReducingIndexMap = 0x5249584d, /* RIXM */ kDexChunkExpandingIndexMap = 0x4549584d, /* EIXM */ @@ -514,11 +515,13 @@ typedef struct DexFile { const DexClassDef* pClassDefs; const DexLink* pLinkData; - /* mapped in "auxillary" section */ + /* + * These are mapped out of the "auxillary" section, and may not be + * included in the file. + */ const DexClassLookup* pClassLookup; - - /* mapped in "auxillary" section */ DexIndexMap indexMap; + const void* pRegisterMapPool; // RegisterMapClassPool /* points to start of DEX file data */ const u1* baseAddr; @@ -672,6 +675,15 @@ DEX_INLINE const DexClassDef* dexGetClassDef(const DexFile* pDexFile, u4 idx) { return &pDexFile->pClassDefs[idx]; } +/* given a ClassDef pointer, recover its index */ +DEX_INLINE u4 dexGetIndexForClassDef(const DexFile* pDexFile, + const DexClassDef* pClassDef) +{ + assert(pClassDef >= pDexFile->pClassDefs && + pClassDef < pDexFile->pClassDefs + pDexFile->pHeader->classDefsSize); + return pClassDef - pDexFile->pClassDefs; +} + /* get the interface list for a DexClass */ DEX_INLINE const DexTypeList* dexGetInterfacesList(const DexFile* pDexFile, const DexClassDef* pClassDef) @@ -723,6 +735,9 @@ DEX_INLINE const char* dexGetSourceFile( return dexStringById(pDexFile, pClassDef->sourceFileIdx); } +/* get the size, in bytes, of a DexCode */ +size_t dexGetDexCodeSize(const DexCode* pCode); + /* Get the list of "tries" for the given DexCode. */ DEX_INLINE const DexTry* dexGetTries(const DexCode* pCode) { const u2* insnsEnd = &pCode->insns[pCode->insnsSize]; @@ -741,6 +756,7 @@ DEX_INLINE const u1* dexGetCatchHandlerData(const DexCode* pCode) { return (const u1*) &pTries[pCode->triesSize]; } +/* get a pointer to the start of the debugging data */ DEX_INLINE const u1* dexGetDebugInfoStream(const DexFile* pDexFile, const DexCode* pCode) { diff --git a/libdex/DexSwapVerify.c b/libdex/DexSwapVerify.c index 5ecda9ff0..bc6f51fe2 100644 --- a/libdex/DexSwapVerify.c +++ b/libdex/DexSwapVerify.c @@ -362,12 +362,15 @@ static bool isDataSectionType(int mapType) { static bool swapMap(CheckState* state, DexMapList* pMap) { DexMapItem* item = pMap->list; - u4 count = pMap->size; + u4 count; u4 dataItemCount = 0; // Total count of items in the data section. u4 dataItemsLeft = state->pHeader->dataSize; // See use below. u4 usedBits = 0; // Bit set: one bit per section bool first = true; u4 lastOffset = 0; + + SWAP_FIELD4(pMap->size); + count = pMap->size; CHECK_LIST_SIZE(item, count, sizeof(DexMapItem)); @@ -392,21 +395,21 @@ static bool swapMap(CheckState* state, DexMapList* pMap) } if (isDataSectionType(item->type)) { - u4 count = item->size; + u4 icount = item->size; /* * This sanity check on the data section items ensures that * there are no more items than the number of bytes in * the data section. */ - if (count > dataItemsLeft) { + if (icount > dataItemsLeft) { LOGE("Unrealistically many items in the data section: " - "at least %d\n", dataItemCount + count); + "at least %d\n", dataItemCount + icount); return false; } - dataItemsLeft -= count; - dataItemCount += count; + dataItemsLeft -= icount; + dataItemCount += icount; } u4 bit = mapTypeToBitMask(item->type); @@ -2077,6 +2080,7 @@ static const u1* verifyEncodedArray(const CheckState* state, while (size--) { data = verifyEncodedValue(state, data, crossVerify); if (data == NULL) { + LOGE("Bogus encoded_array value\n"); return NULL; } } diff --git a/libdex/InstrUtils.c b/libdex/InstrUtils.c index b0718f386..93e1f004a 100644 --- a/libdex/InstrUtils.c +++ b/libdex/InstrUtils.c @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + /* * Dalvik instruction utility functions. */ @@ -37,6 +38,10 @@ */ InstructionWidth* dexCreateInstrWidthTable(void) { +#ifdef __ARM_ARCH_7A__ + /* hack to work around mysterious problem on emulator */ + LOGD("creating instr width table\n"); +#endif InstructionWidth* instrWidth; int i; @@ -49,7 +54,7 @@ InstructionWidth* dexCreateInstrWidthTable(void) int width = 0; switch (opc) { - case OP_NOP: /* switch-statement data is a special case of NOP */ + case OP_NOP: /* note data for e.g. switch-* encoded "inside" a NOP */ case OP_MOVE: case OP_MOVE_WIDE: case OP_MOVE_OBJECT: @@ -289,6 +294,7 @@ InstructionWidth* dexCreateInstrWidthTable(void) case OP_IPUT_QUICK: case OP_IPUT_WIDE_QUICK: case OP_IPUT_OBJECT_QUICK: + case OP_THROW_VERIFICATION_ERROR: width = -2; break; case OP_INVOKE_VIRTUAL_QUICK: @@ -320,7 +326,6 @@ InstructionWidth* dexCreateInstrWidthTable(void) case OP_UNUSED_EA: case OP_UNUSED_EB: case OP_UNUSED_EC: - case OP_UNUSED_ED: case OP_UNUSED_EF: case OP_UNUSED_F1: case OP_UNUSED_FC: @@ -538,16 +543,6 @@ InstructionFlags* dexCreateInstrFlagsTable(void) case OP_SPUT_SHORT: case OP_SPUT_WIDE: case OP_SPUT_OBJECT: - case OP_INVOKE_VIRTUAL: - case OP_INVOKE_VIRTUAL_RANGE: - case OP_INVOKE_SUPER: - case OP_INVOKE_SUPER_RANGE: - case OP_INVOKE_DIRECT: - case OP_INVOKE_DIRECT_RANGE: - case OP_INVOKE_STATIC: - case OP_INVOKE_STATIC_RANGE: - case OP_INVOKE_INTERFACE: - case OP_INVOKE_INTERFACE_RANGE: case OP_DIV_INT: case OP_REM_INT: case OP_DIV_LONG: @@ -563,6 +558,19 @@ InstructionFlags* dexCreateInstrFlagsTable(void) flags = kInstrCanContinue | kInstrCanThrow; break; + case OP_INVOKE_VIRTUAL: + case OP_INVOKE_VIRTUAL_RANGE: + case OP_INVOKE_SUPER: + case OP_INVOKE_SUPER_RANGE: + case OP_INVOKE_DIRECT: + case OP_INVOKE_DIRECT_RANGE: + case OP_INVOKE_STATIC: + case OP_INVOKE_STATIC_RANGE: + case OP_INVOKE_INTERFACE: + case OP_INVOKE_INTERFACE_RANGE: + flags = kInstrCanContinue | kInstrCanThrow | kInstrInvoke; + break; + case OP_RETURN_VOID: case OP_RETURN: case OP_RETURN_WIDE: @@ -578,7 +586,7 @@ InstructionFlags* dexCreateInstrFlagsTable(void) case OP_GOTO: case OP_GOTO_16: case OP_GOTO_32: - flags = kInstrCanBranch; + flags = kInstrCanBranch | kInstrUnconditional; break; /* conditional branches */ @@ -603,7 +611,10 @@ InstructionFlags* dexCreateInstrFlagsTable(void) flags = kInstrCanSwitch | kInstrCanContinue; break; - /* optimizer-generated instructions */ + /* verifier/optimizer-generated instructions */ + case OP_THROW_VERIFICATION_ERROR: + flags = kInstrCanThrow; + break; case OP_EXECUTE_INLINE: flags = kInstrCanContinue; break; @@ -613,12 +624,15 @@ InstructionFlags* dexCreateInstrFlagsTable(void) case OP_IPUT_QUICK: case OP_IPUT_WIDE_QUICK: case OP_IPUT_OBJECT_QUICK: + flags = kInstrCanContinue | kInstrCanThrow; + break; + case OP_INVOKE_VIRTUAL_QUICK: case OP_INVOKE_VIRTUAL_QUICK_RANGE: case OP_INVOKE_SUPER_QUICK: case OP_INVOKE_SUPER_QUICK_RANGE: case OP_INVOKE_DIRECT_EMPTY: - flags = kInstrCanContinue | kInstrCanThrow; + flags = kInstrCanContinue | kInstrCanThrow | kInstrInvoke; break; /* these should never appear */ @@ -641,7 +655,6 @@ InstructionFlags* dexCreateInstrFlagsTable(void) case OP_UNUSED_EA: case OP_UNUSED_EB: case OP_UNUSED_EC: - case OP_UNUSED_ED: case OP_UNUSED_EF: case OP_UNUSED_F1: case OP_UNUSED_FC: @@ -950,6 +963,9 @@ InstructionFormat* dexCreateInstrFormatTable(void) /* * Optimized instructions. */ + case OP_THROW_VERIFICATION_ERROR: + fmt = kFmt20bc; + break; case OP_IGET_QUICK: case OP_IGET_WIDE_QUICK: case OP_IGET_OBJECT_QUICK: @@ -993,7 +1009,6 @@ InstructionFormat* dexCreateInstrFormatTable(void) case OP_UNUSED_EA: case OP_UNUSED_EB: case OP_UNUSED_EC: - case OP_UNUSED_ED: case OP_UNUSED_EF: case OP_UNUSED_F1: case OP_UNUSED_FC: @@ -1038,38 +1053,39 @@ void dexDecodeInstruction(const InstructionFormat* fmts, const u2* insns, pDec->opCode = (OpCode) INST_INST(inst); switch (dexGetInstrFormat(fmts, pDec->opCode)) { - case kFmt10x: // op + case kFmt10x: // op /* nothing to do; copy the AA bits out for the verifier */ pDec->vA = INST_AA(inst); break; - case kFmt12x: // op vA, vB + case kFmt12x: // op vA, vB pDec->vA = INST_A(inst); pDec->vB = INST_B(inst); break; - case kFmt11n: // op vA, #+B + case kFmt11n: // op vA, #+B pDec->vA = INST_A(inst); pDec->vB = (s4) (INST_B(inst) << 28) >> 28; // sign extend 4-bit value break; - case kFmt11x: // op vAA + case kFmt11x: // op vAA pDec->vA = INST_AA(inst); break; - case kFmt10t: // op +AA + case kFmt10t: // op +AA pDec->vA = (s1) INST_AA(inst); // sign-extend 8-bit value break; - case kFmt20t: // op +AAAA + case kFmt20t: // op +AAAA pDec->vA = (s2) FETCH(1); // sign-extend 16-bit value break; - case kFmt21c: // op vAA, thing@BBBB - case kFmt22x: // op vAA, vBBBB + case kFmt20bc: // op AA, thing@BBBB + case kFmt21c: // op vAA, thing@BBBB + case kFmt22x: // op vAA, vBBBB pDec->vA = INST_AA(inst); pDec->vB = FETCH(1); break; - case kFmt21s: // op vAA, #+BBBB - case kFmt21t: // op vAA, +BBBB + case kFmt21s: // op vAA, #+BBBB + case kFmt21t: // op vAA, +BBBB pDec->vA = INST_AA(inst); pDec->vB = (s2) FETCH(1); // sign-extend 16-bit value break; - case kFmt21h: // op vAA, #+BBBB0000[00000000] + case kFmt21h: // op vAA, #+BBBB0000[00000000] pDec->vA = INST_AA(inst); /* * The value should be treated as right-zero-extended, but we don't @@ -1078,24 +1094,24 @@ void dexDecodeInstruction(const InstructionFormat* fmts, const u2* insns, */ pDec->vB = FETCH(1); break; - case kFmt23x: // op vAA, vBB, vCC + case kFmt23x: // op vAA, vBB, vCC pDec->vA = INST_AA(inst); pDec->vB = FETCH(1) & 0xff; pDec->vC = FETCH(1) >> 8; break; - case kFmt22b: // op vAA, vBB, #+CC + case kFmt22b: // op vAA, vBB, #+CC pDec->vA = INST_AA(inst); pDec->vB = FETCH(1) & 0xff; pDec->vC = (s1) (FETCH(1) >> 8); // sign-extend 8-bit value break; - case kFmt22s: // op vA, vB, #+CCCC - case kFmt22t: // op vA, vB, +CCCC + case kFmt22s: // op vA, vB, #+CCCC + case kFmt22t: // op vA, vB, +CCCC pDec->vA = INST_A(inst); pDec->vB = INST_B(inst); pDec->vC = (s2) FETCH(1); // sign-extend 16-bit value break; - case kFmt22c: // op vA, vB, thing@CCCC - case kFmt22cs: // [opt] op vA, vB, field offset CCCC + case kFmt22c: // op vA, vB, thing@CCCC + case kFmt22cs: // [opt] op vA, vB, field offset CCCC pDec->vA = INST_A(inst); pDec->vB = INST_B(inst); pDec->vC = FETCH(1); @@ -1103,21 +1119,21 @@ void dexDecodeInstruction(const InstructionFormat* fmts, const u2* insns, case kFmt30t: // op +AAAAAAAA pDec->vA = FETCH(1) | ((u4) FETCH(2) << 16); // signed 32-bit value break; - case kFmt31t: // op vAA, +BBBBBBBB - case kFmt31c: // op vAA, thing@BBBBBBBB + case kFmt31t: // op vAA, +BBBBBBBB + case kFmt31c: // op vAA, thing@BBBBBBBB pDec->vA = INST_AA(inst); pDec->vB = FETCH(1) | ((u4) FETCH(2) << 16); // 32-bit value break; - case kFmt32x: // op vAAAA, vBBBB + case kFmt32x: // op vAAAA, vBBBB pDec->vA = FETCH(1); pDec->vB = FETCH(2); break; - case kFmt31i: // op vAA, #+BBBBBBBB + case kFmt31i: // op vAA, #+BBBBBBBB pDec->vA = INST_AA(inst); pDec->vB = FETCH(1) | ((u4) FETCH(2) << 16); break; - case kFmt35c: // op vB, {vD..vG,vA}, thing@CCCC - case kFmt35ms: // [opt] invoke-virtual+super + case kFmt35c: // op vB, {vD..vG,vA}, thing@CCCC + case kFmt35ms: // [opt] invoke-virtual+super { /* * The lettering changes that came about when we went from 4 args @@ -1158,7 +1174,7 @@ void dexDecodeInstruction(const InstructionFormat* fmts, const u2* insns, pDec->vC = pDec->arg[0]; } break; - case kFmt3inline: // [opt] inline invoke + case kFmt3inline: // [opt] inline invoke { u2 regList; int i; diff --git a/libdex/InstrUtils.h b/libdex/InstrUtils.h index 9d8e5c37a..9728cd453 100644 --- a/libdex/InstrUtils.h +++ b/libdex/InstrUtils.h @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + /* * Dalvik instruction utility functions. */ @@ -42,6 +43,7 @@ enum InstructionFormat { kFmt11n, // op vA, #+B kFmt11x, // op vAA kFmt10t, // op +AA + kFmt20bc, // op AA, thing@BBBB kFmt20t, // op +AAAA kFmt22x, // op vAA, vBBBB kFmt21t, // op vAA, +BBBB @@ -97,6 +99,8 @@ enum InstructionFlags { kInstrCanSwitch = 1 << 2, // switch statement kInstrCanThrow = 1 << 3, // could cause an exception to be thrown kInstrCanReturn = 1 << 4, // returns, no additional statements + kInstrInvoke = 1 << 5, // a flavor of invoke + kInstrUnconditional = 1 << 6, // unconditional branch }; diff --git a/libdex/Leb128.h b/libdex/Leb128.h index 215ae3025..41799fe5e 100644 --- a/libdex/Leb128.h +++ b/libdex/Leb128.h @@ -124,4 +124,41 @@ int readAndVerifyUnsignedLeb128(const u1** pStream, const u1* limit, */ int readAndVerifySignedLeb128(const u1** pStream, const u1* limit, bool* okay); + +/* + * Writes a 32-bit value in unsigned ULEB128 format. + * + * Returns the updated pointer. + */ +DEX_INLINE u1* writeUnsignedLeb128(u1* ptr, u4 data) +{ + while (true) { + u1 out = data & 0x7f; + if (out != data) { + *ptr++ = out | 0x80; + data >>= 7; + } else { + *ptr++ = out; + break; + } + } + + return ptr; +} + +/* + * Returns the number of bytes needed to encode "val" in ULEB128 form. + */ +DEX_INLINE int unsignedLeb128Size(u4 data) +{ + int count = 0; + + do { + data >>= 7; + count++; + } while (data != 0); + + return count; +} + #endif diff --git a/libdex/OpCode.h b/libdex/OpCode.h index d38947290..127223104 100644 --- a/libdex/OpCode.h +++ b/libdex/OpCode.h @@ -331,9 +331,9 @@ typedef enum OpCode { OP_UNUSED_EA = 0xea, OP_UNUSED_EB = 0xeb, OP_UNUSED_EC = 0xec, - OP_UNUSED_ED = 0xed, /* optimizer output -- these are never generated by "dx" */ + OP_THROW_VERIFICATION_ERROR = 0xed, OP_EXECUTE_INLINE = 0xee, OP_UNUSED_EF = 0xef, /* OP_EXECUTE_INLINE_RANGE? */ @@ -628,7 +628,7 @@ typedef enum OpCode { H(OP_UNUSED_EA), \ H(OP_UNUSED_EB), \ H(OP_UNUSED_EC), \ - H(OP_UNUSED_ED), \ + H(OP_THROW_VERIFICATION_ERROR), \ H(OP_EXECUTE_INLINE), \ H(OP_UNUSED_EF), \ /* f0..ff */ \ diff --git a/libdex/SysUtil.c b/libdex/SysUtil.c index 530ac2eeb..bf1be8853 100644 --- a/libdex/SysUtil.c +++ b/libdex/SysUtil.c @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + /* * System utilities. */ @@ -64,6 +65,25 @@ static void* sysCreateAnonShmem(size_t length) #endif } +/* + * Create a private anonymous storage area. + */ +int sysCreatePrivateMap(size_t length, MemMapping* pMap) +{ + void* memPtr; + + memPtr = sysCreateAnonShmem(length); + if (memPtr == NULL) + return -1; + + pMap->addr = pMap->baseAddr = memPtr; + pMap->length = pMap->baseLength = length; + return 0; +} + +/* + * Determine the current offset and remaining length of the open file. + */ static int getFileStartAndLength(int fd, off_t *start_, size_t *length_) { off_t start, end; diff --git a/libdex/SysUtil.h b/libdex/SysUtil.h index 8d85efa8a..8b80503ac 100644 --- a/libdex/SysUtil.h +++ b/libdex/SysUtil.h @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + /* * System utilities. */ @@ -62,6 +63,13 @@ int sysMapFileInShmem(int fd, MemMapping* pMap); int sysMapFileSegmentInShmem(int fd, off_t start, long length, MemMapping* pMap); +/* + * Create a private anonymous mapping, useful for large allocations. + * + * On success, "pMap" is filled in, and zero is returned. + */ +int sysCreatePrivateMap(size_t length, MemMapping* pMap); + /* * Release the pages associated with a shared memory segment. * diff --git a/libdex/ZipArchive.c b/libdex/ZipArchive.c index a75a85b84..3f88e7d4b 100644 --- a/libdex/ZipArchive.c +++ b/libdex/ZipArchive.c @@ -285,6 +285,8 @@ int dexZipOpenArchive(const char* fileName, ZipArchive* pArchive) LOGV("Opening archive '%s' %p\n", fileName, pArchive); + memset(pArchive, 0, sizeof(ZipArchive)); + fd = open(fileName, O_RDONLY, 0); if (fd < 0) { err = errno ? errno : -1; diff --git a/libnativehelper/Android.mk b/libnativehelper/Android.mk index 5f553c5aa..aa4be86bd 100644 --- a/libnativehelper/Android.mk +++ b/libnativehelper/Android.mk @@ -1,20 +1,36 @@ +# Copyright (C) 2009 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) -LOCAL_SRC_FILES := \ +# +# Common definitions for host and device. +# + +src_files := \ JNIHelp.c \ Register.c -LOCAL_C_INCLUDES += \ +c_includes := \ $(JNI_H_INCLUDE) # Any shared/static libs required by libjavacore # need to be mentioned here as well. # TODO: fix this requirement -LOCAL_SHARED_LIBRARIES := \ - liblog \ - libcutils \ +shared_libraries := \ libexpat \ libssl \ libutils \ @@ -25,10 +41,51 @@ LOCAL_SHARED_LIBRARIES := \ libicui18n \ libsqlite -LOCAL_STATIC_LIBRARIES := \ +static_libraries := \ libjavacore \ libfdlibm + + +# +# Build for the target (device). +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(src_files) +LOCAL_C_INCLUDES := $(c_includes) +LOCAL_SHARED_LIBRARIES := $(shared_libraries) +LOCAL_STATIC_LIBRARIES := $(static_libraries) + +# liblog and libcutils are shared for target. +LOCAL_SHARED_LIBRARIES += \ + liblog libcutils + LOCAL_MODULE := libnativehelper include $(BUILD_SHARED_LIBRARY) + + +# +# Build for the host. +# + +ifeq ($(WITH_HOST_DALVIK),true) + + include $(CLEAR_VARS) + + LOCAL_SRC_FILES := $(src_files) + LOCAL_C_INCLUDES := $(c_includes) + LOCAL_SHARED_LIBRARIES := $(shared_libraries) + LOCAL_STATIC_LIBRARIES := $(static_libraries) + + # liblog and libcutils are static for host. + LOCAL_STATIC_LIBRARIES += \ + liblog libcutils + + LOCAL_MODULE := libnativehelper-host + + include $(BUILD_HOST_SHARED_LIBRARY) + +endif diff --git a/libnativehelper/include/nativehelper/JNIHelp.h b/libnativehelper/include/nativehelper/JNIHelp.h index eec7af8a9..3982797c8 100644 --- a/libnativehelper/include/nativehelper/JNIHelp.h +++ b/libnativehelper/include/nativehelper/JNIHelp.h @@ -42,6 +42,8 @@ int jniRegisterNativeMethods(C_JNIEnv* env, const char* className, /* * Throw an exception with the specified class and an optional message. + * The "className" argument will be passed directly to FindClass, which + * takes strings with slashes (e.g. "java/lang/Object"). * * Returns 0 on success, nonzero if something failed (e.g. the exception * class couldn't be found). diff --git a/tests/003-omnibus-opcodes/expected.txt b/tests/003-omnibus-opcodes/expected.txt index 81a8879c3..4895dc3e0 100644 --- a/tests/003-omnibus-opcodes/expected.txt +++ b/tests/003-omnibus-opcodes/expected.txt @@ -23,6 +23,7 @@ IntMath.truncateTest IntMath.divideByZero IntMath.bigDivideOverflow IntMath.checkConsts +IntMath.jlmTests FloatMath.convTest FloatMath.floatOperTest FloatMath.doubleOperTest @@ -39,6 +40,7 @@ FloatMath.checkConvD 2: 123.45600128173828 -2.005440939E9, -8.6133032459203287E18, 123.4560012817382 FloatMath.checkConsts +FloatMath.jlmTests IntMath.testIntCompare IntMath.testLongCompare IntMath.testFloatCompare @@ -64,8 +66,9 @@ Throw.one Throw.twoA Throw.twoN Throw.rethrow -Caught: java.lang.VerifyError: UnresTest1 -Caught (retry): java.lang.VerifyError: UnresTest1 -Caught: java.lang.VerifyError: UnresTest2 +UnresTest1... +UnresTest1... +UnresTest2... +UnresTest2 done InternedString.run Done! diff --git a/tests/003-omnibus-opcodes/src/FloatMath.java b/tests/003-omnibus-opcodes/src/FloatMath.java index 0c1fe1ba7..cf4869c26 100644 --- a/tests/003-omnibus-opcodes/src/FloatMath.java +++ b/tests/003-omnibus-opcodes/src/FloatMath.java @@ -262,6 +262,50 @@ public class FloatMath { assert(d > 9.9 && d < 10.1); } + /* + * Determine if two floating point numbers are approximately equal. + * + * (Assumes that floating point is generally working, so we can't use + * this for the first set of tests.) + */ + static boolean approxEqual(float a, float b, float maxDelta) { + if (a > b) + return (a - b) < maxDelta; + else + return (b - a) < maxDelta; + } + static boolean approxEqual(double a, double b, double maxDelta) { + if (a > b) + return (a - b) < maxDelta; + else + return (b - a) < maxDelta; + } + + /* + * Test some java.lang.Math functions. + * + * The method arguments are positive values. + */ + static void jlmTests(float ff, double dd) { + System.out.println("FloatMath.jlmTests"); + + assert(approxEqual(Math.abs(ff), ff, 0.001f)); + assert(approxEqual(Math.abs(-ff), ff, 0.001f)); + assert(approxEqual(Math.min(ff, -5.0f), -5.0f, 0.001f)); + assert(approxEqual(Math.max(ff, -5.0f), ff, 0.001f)); + + assert(approxEqual(Math.abs(dd), dd, 0.001)); + assert(approxEqual(Math.abs(-dd), dd, 0.001)); + assert(approxEqual(Math.min(dd, -5.0), -5.0, 0.001)); + assert(approxEqual(Math.max(dd, -5.0), dd, 0.001)); + + double sq = Math.sqrt(dd); + assert(approxEqual(sq*sq, dd, 0.001)); + + assert(approxEqual(0.5403023058681398, Math.cos(1.0), 0.00000001)); + assert(approxEqual(0.8414709848078965, Math.sin(1.0), 0.00000001)); + } + public static void run() { convTest(); @@ -287,6 +331,8 @@ public class FloatMath { unopTest(123.456f); checkConsts(); + + jlmTests(3.14159f, 123456.78987654321); } } diff --git a/tests/003-omnibus-opcodes/src/IntMath.java b/tests/003-omnibus-opcodes/src/IntMath.java index 126bec6cd..d5ac744b8 100644 --- a/tests/003-omnibus-opcodes/src/IntMath.java +++ b/tests/003-omnibus-opcodes/src/IntMath.java @@ -432,6 +432,25 @@ public class IntMath { assert(huge == 0x9922334455667788L); // const-wide } + /* + * Test some java.lang.Math functions. + * + * The method arguments are positive values. + */ + static void jlmTests(int ii, long ll) { + System.out.println("IntMath.jlmTests"); + + assert(Math.abs(ii) == ii); + assert(Math.abs(-ii) == ii); + assert(Math.min(ii, -5) == -5); + assert(Math.max(ii, -5) == ii); + + assert(Math.abs(ll) == ll); + assert(Math.abs(-ll) == ll); + assert(Math.min(ll, -5L) == -5L); + assert(Math.max(ll, -5L) == ll); + } + public static void run() { shiftTest1(); shiftTest2(); @@ -467,6 +486,8 @@ public class IntMath { checkConsts((byte) 1, (short) -256, -88888, 0x9922334455667788L); unopCheck(unopTest(38)); + + jlmTests(12345, 0x1122334455667788L); } } diff --git a/tests/003-omnibus-opcodes/src/UnresTest2.java b/tests/003-omnibus-opcodes/src/UnresTest2.java index 43a92ac41..b458bfe59 100644 --- a/tests/003-omnibus-opcodes/src/UnresTest2.java +++ b/tests/003-omnibus-opcodes/src/UnresTest2.java @@ -44,6 +44,7 @@ class UnresTest2 { } checkCasts(stuff); + System.out.println("UnresTest2 done"); } } diff --git a/tests/030-bad-finalizer/info.txt b/tests/030-bad-finalizer/info.txt index 08127da23..0f76ad6a8 100644 --- a/tests/030-bad-finalizer/info.txt +++ b/tests/030-bad-finalizer/info.txt @@ -1,6 +1,3 @@ -This is a miscellaneous test that was imported into the new-at-the-time -runtime test framework. The test is intended to exercise basic features, -and as such cannot be build on top of junit, since failure of such basic -features might disrupt junit. - -TODO: Real description goes here. +The finalizer for this class never finishes. Dalvik is expected to detect +this situation and abort the VM (so you will likely see a "deadd00d" +crash in the log output). diff --git a/tests/042-new-instance/src/Main.java b/tests/042-new-instance/src/Main.java index 49894fee3..037aa2ae2 100644 --- a/tests/042-new-instance/src/Main.java +++ b/tests/042-new-instance/src/Main.java @@ -1,4 +1,20 @@ -// Copyright 2007 The Android Open Source Project +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.reflect.Constructor; import java.lang.reflect.Constructor; @@ -29,7 +45,7 @@ public class Main { try { Class c = Class.forName("otherpackage.PackageAccess"); Object obj = c.newInstance(); - System.out.println("ERROR: PackageAccess succeeded unexpectedly"); + System.err.println("ERROR: PackageAccess succeeded unexpectedly"); } catch (IllegalAccessException iae) { System.out.println("Got expected PackageAccess complaint"); } catch (Exception ex) { diff --git a/tests/044-proxy/expected.txt b/tests/044-proxy/expected.txt index 255cf996a..69a94f280 100644 --- a/tests/044-proxy/expected.txt +++ b/tests/044-proxy/expected.txt @@ -45,10 +45,10 @@ Invoke public abstract void Shapes.upCheck() throws java.lang.InterruptedExcepti (no args) Got expected ie -Proxy methods: [public native boolean .$Proxy0.equals(java.lang.Object), public native int .$Proxy0.hashCode(), public native java.lang.String .$Proxy0.toString(), public native int .$Proxy0.rectangle(int,int), public native int .$Proxy0.square(int,int), public native int .$Proxy0.trapezoid(int,double,int), public native java.lang.String .$Proxy0.blob(), public native void .$Proxy0.circle(int), public native void .$Proxy0.upCheck(), public native void .$Proxy0.upChuck(), public native double .$Proxy0.blue(int), public native R0aa .$Proxy0.checkMe(), public native int .$Proxy0.green(double), public native int .$Proxy0.mauve(java.lang.String), public native int .$Proxy0.red(float)] +Proxy methods: [public native boolean $Proxy0.equals(java.lang.Object), public native int $Proxy0.hashCode(), public native java.lang.String $Proxy0.toString(), public native int $Proxy0.rectangle(int,int), public native int $Proxy0.square(int,int), public native int $Proxy0.trapezoid(int,double,int), public native java.lang.String $Proxy0.blob(), public native void $Proxy0.circle(int), public native void $Proxy0.upCheck(), public native void $Proxy0.upChuck(), public native double $Proxy0.blue(int), public native R0aa $Proxy0.checkMe(), public native int $Proxy0.green(double), public native int $Proxy0.mauve(java.lang.String), public native int $Proxy0.red(float)] Decl annos: [] Param annos (1) : [[]] -Proxy fields: [private static [[Ljava.lang.Throwable; .$Proxy0.throws] +Proxy fields: [private static [[Ljava.lang.Throwable; $Proxy0.throws] Dupe threw expected exception Clash threw expected exception Clash2 threw expected exception diff --git a/tests/065-mismatched-implements/expected.txt b/tests/065-mismatched-implements/expected.txt index 36ebe5e6a..09c05967d 100644 --- a/tests/065-mismatched-implements/expected.txt +++ b/tests/065-mismatched-implements/expected.txt @@ -1,3 +1 @@ -Dalvik VM unable to find static main(String[]) in 'Main' -java.lang.VerifyError: Main - at dalvik.system.NativeStart.main(Native Method) +Got expected ICCE diff --git a/tests/065-mismatched-implements/info.txt b/tests/065-mismatched-implements/info.txt index 58d9b69d5..74c3ff377 100644 --- a/tests/065-mismatched-implements/info.txt +++ b/tests/065-mismatched-implements/info.txt @@ -1,19 +1,2 @@ This tests what happens when class A implements interface B, but somebody turns B into an abstract class without rebuilding A. - -If you run this with --no-verify you get reasonable results: - -java.lang.NoClassDefFoundError: Base - at Main.main(Main.java:8) - at dalvik.system.NativeStart.main(Native Method) -Caused by: java.lang.IncompatibleClassChangeError: Base - at dalvik.system.DexFile.defineClass(Native Method) - at dalvik.system.DexFile.loadClass(DexFile.java:91) - at dalvik.system.PathClassLoader.findClass(PathClassLoader.java:175) - at java.lang.ClassLoader.loadClass(ClassLoader.java:453) - at java.lang.ClassLoader.loadClass(ClassLoader.java:421) - ... 2 more - -With the verifier enabled, you get a relatively content-free VerifyError -with the detail only appearing in the log file. - diff --git a/tests/065-mismatched-implements/src/Indirect.java b/tests/065-mismatched-implements/src/Indirect.java new file mode 100644 index 000000000..dd87a6552 --- /dev/null +++ b/tests/065-mismatched-implements/src/Indirect.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Error indirection class. + * + * Some VMs will load this class and fail on the "new" call, others will + * refuse to load this class at all. + */ +public class Indirect { + public static void main() { + Base base = new Base(); + } +} + diff --git a/tests/065-mismatched-implements/src/Main.java b/tests/065-mismatched-implements/src/Main.java index 4a25232c7..6993b17ba 100644 --- a/tests/065-mismatched-implements/src/Main.java +++ b/tests/065-mismatched-implements/src/Main.java @@ -1,11 +1,30 @@ -// Copyright 2008 The Android Open Source Project +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ /* * Test field access through reflection. */ public class Main { public static void main(String[] args) { - Base base = new Base(); + try { + Indirect.main(); + System.err.println("Succeeded unexpectedly"); + } catch (IncompatibleClassChangeError icce) { + System.out.println("Got expected ICCE"); + } } } diff --git a/tests/066-mismatched-super/expected.txt b/tests/066-mismatched-super/expected.txt index 36ebe5e6a..09c05967d 100644 --- a/tests/066-mismatched-super/expected.txt +++ b/tests/066-mismatched-super/expected.txt @@ -1,3 +1 @@ -Dalvik VM unable to find static main(String[]) in 'Main' -java.lang.VerifyError: Main - at dalvik.system.NativeStart.main(Native Method) +Got expected ICCE diff --git a/tests/066-mismatched-super/info.txt b/tests/066-mismatched-super/info.txt index 2158899da..7865ffc4a 100644 --- a/tests/066-mismatched-super/info.txt +++ b/tests/066-mismatched-super/info.txt @@ -1,19 +1,2 @@ This tests what happens when class A extends abstract class B, but somebody turns B into an interface without rebuilding A. - -If you run this with --no-verify you get reasonable results: - -java.lang.NoClassDefFoundError: Base - at Main.main(Main.java:8) - at dalvik.system.NativeStart.main(Native Method) -Caused by: java.lang.IncompatibleClassChangeError: superclass is an interface - at dalvik.system.DexFile.defineClass(Native Method) - at dalvik.system.DexFile.loadClass(DexFile.java:91) - at dalvik.system.PathClassLoader.findClass(PathClassLoader.java:175) - at java.lang.ClassLoader.loadClass(ClassLoader.java:453) - at java.lang.ClassLoader.loadClass(ClassLoader.java:421) - ... 2 more - -With the verifier enabled, you get a relatively content-free VerifyError -with the detail only appearing in the log file. - diff --git a/tests/066-mismatched-super/src/Indirect.java b/tests/066-mismatched-super/src/Indirect.java new file mode 100644 index 000000000..dd87a6552 --- /dev/null +++ b/tests/066-mismatched-super/src/Indirect.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Error indirection class. + * + * Some VMs will load this class and fail on the "new" call, others will + * refuse to load this class at all. + */ +public class Indirect { + public static void main() { + Base base = new Base(); + } +} + diff --git a/tests/066-mismatched-super/src/Main.java b/tests/066-mismatched-super/src/Main.java index 4a25232c7..6993b17ba 100644 --- a/tests/066-mismatched-super/src/Main.java +++ b/tests/066-mismatched-super/src/Main.java @@ -1,11 +1,30 @@ -// Copyright 2008 The Android Open Source Project +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ /* * Test field access through reflection. */ public class Main { public static void main(String[] args) { - Base base = new Base(); + try { + Indirect.main(); + System.err.println("Succeeded unexpectedly"); + } catch (IncompatibleClassChangeError icce) { + System.out.println("Got expected ICCE"); + } } } diff --git a/tests/068-classloader/expected.txt b/tests/068-classloader/expected.txt index 0ca886270..bf131eee6 100644 --- a/tests/068-classloader/expected.txt +++ b/tests/068-classloader/expected.txt @@ -4,6 +4,9 @@ Got expected access exception #1 Got expected CNFE/IAE #2 Got expected CNFE/IAE #3 Got expected LinkageError on DE +Got DEO result DoubledExtendOkay 1 +Got LinkageError on GD +Got LinkageError on TA Ctor: doubled implement, type 1 DoubledImplement one Got LinkageError on DI (early) diff --git a/tests/068-classloader/src-ex/AbstractGet.java b/tests/068-classloader/src-ex/AbstractGet.java new file mode 100644 index 000000000..b62aa8f3b --- /dev/null +++ b/tests/068-classloader/src-ex/AbstractGet.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Verify that we don't reject this with a LinkageError. + */ +public class AbstractGet extends AbstractBase { + public DoubledExtendOkay getExtended() { + return new DoubledExtendOkay(); + } +} + +/** + * Abstract class, does not declare getAbstract. This cause the VM to + * generate a "miranda" method. + */ +abstract class AbstractBase extends BaseOkay { + public abstract DoubledExtendOkay getExtended(); +} + diff --git a/tests/068-classloader/src-ex/DoubledExtendOkay.java b/tests/068-classloader/src-ex/DoubledExtendOkay.java new file mode 100644 index 000000000..766fa8ead --- /dev/null +++ b/tests/068-classloader/src-ex/DoubledExtendOkay.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * "Okay" doubled sub-class, form #2. + */ +public class DoubledExtendOkay extends BaseOkay { + public DoubledExtendOkay() { + //System.out.println("Ctor: doubled extend okay, type 2"); + } + + /* + @Override + public DoubledExtendOkay getExtended() { + //System.out.println("getExtended 2"); + return new DoubledExtendOkay(); + } + */ + + public String getStr() { + return "DoubledExtendOkay 2"; + } +} + diff --git a/tests/068-classloader/src-ex/GetDoubled.java b/tests/068-classloader/src-ex/GetDoubled.java new file mode 100644 index 000000000..5e20441a3 --- /dev/null +++ b/tests/068-classloader/src-ex/GetDoubled.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * The interface we implement was declared in a different class loader, + * which means the DoubledExtend we return is not the one it was declared + * to return. + */ +public class GetDoubled implements IGetDoubled { + public DoubledExtendOkay getDoubled() { + return new DoubledExtendOkay(); + } +} + diff --git a/tests/068-classloader/src/BaseOkay.java b/tests/068-classloader/src/BaseOkay.java new file mode 100644 index 000000000..c5c3f7aa7 --- /dev/null +++ b/tests/068-classloader/src/BaseOkay.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Common base class. + */ +public class BaseOkay implements IDoubledExtendOkay { + public BaseOkay() {} + + public DoubledExtendOkay getExtended() { + return new DoubledExtendOkay(); + } + + public static String doStuff(DoubledExtendOkay dt) { + return dt.getStr(); + } +} + +/** + * Interface that declares the not-overridden method. This exists to ensure + * that the existence of an interface doesn't trip the check. + */ +interface IDoubledExtendOkay { + public DoubledExtendOkay getExtended(); +} + diff --git a/tests/068-classloader/src/DoubledExtendOkay.java b/tests/068-classloader/src/DoubledExtendOkay.java new file mode 100644 index 000000000..e8ff40477 --- /dev/null +++ b/tests/068-classloader/src/DoubledExtendOkay.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * "Okay" doubled sub-class, form #1. + */ +public class DoubledExtendOkay extends BaseOkay { + public DoubledExtendOkay() { + //System.out.println("Ctor: doubled extend okay, type 1"); + } + + /* + @Override + public DoubledExtendOkay getExtended() { + System.out.println("getExtended 1"); + return new DoubledExtendOkay(); + } + */ + + public String getStr() { + return "DoubledExtendOkay 1"; + } +} + diff --git a/tests/068-classloader/src/FancyLoader.java b/tests/068-classloader/src/FancyLoader.java index 491fd5c70..1daf155e2 100644 --- a/tests/068-classloader/src/FancyLoader.java +++ b/tests/068-classloader/src/FancyLoader.java @@ -1,4 +1,18 @@ -// Copyright 2008 The Android Open Source Project +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ import java.io.File; import java.io.FileNotFoundException; @@ -29,6 +43,8 @@ public class FancyLoader extends ClassLoader { /* on Dalvik, this is a DexFile; otherwise, it's null */ private Class mDexClass; + private Object mDexFile; + /** * Construct FancyLoader, grabbing a reference to the DexFile class * if we're running under Dalvik. @@ -64,26 +80,29 @@ public class FancyLoader extends ClassLoader { private Class findClassDalvik(String name) throws ClassNotFoundException { - Constructor ctor; - Object dexFile; - - /* - * Construct a DexFile object through reflection. - */ - try { - ctor = mDexClass.getConstructor(new Class[] { String.class }); - } catch (NoSuchMethodException nsme) { - throw new ClassNotFoundException("getConstructor failed", nsme); - } - - try { - dexFile = ctor.newInstance(DEX_FILE); - } catch (InstantiationException ie) { - throw new ClassNotFoundException("newInstance failed", ie); - } catch (IllegalAccessException iae) { - throw new ClassNotFoundException("newInstance failed", iae); - } catch (InvocationTargetException ite) { - throw new ClassNotFoundException("newInstance failed", ite); + if (mDexFile == null) { + synchronized (FancyLoader.class) { + Constructor ctor; + /* + * Construct a DexFile object through reflection. + */ + try { + ctor = mDexClass.getConstructor(new Class[] {String.class}); + } catch (NoSuchMethodException nsme) { + throw new ClassNotFoundException("getConstructor failed", + nsme); + } + + try { + mDexFile = ctor.newInstance(DEX_FILE); + } catch (InstantiationException ie) { + throw new ClassNotFoundException("newInstance failed", ie); + } catch (IllegalAccessException iae) { + throw new ClassNotFoundException("newInstance failed", iae); + } catch (InvocationTargetException ite) { + throw new ClassNotFoundException("newInstance failed", ite); + } + } } /* @@ -99,7 +118,7 @@ public class FancyLoader extends ClassLoader { } try { - meth.invoke(dexFile, name, this); + meth.invoke(mDexFile, name, this); } catch (IllegalAccessException iae) { throw new ClassNotFoundException("loadClass failed", iae); } catch (InvocationTargetException ite) { diff --git a/tests/068-classloader/src/IGetDoubled.java b/tests/068-classloader/src/IGetDoubled.java new file mode 100644 index 000000000..0a4ac915b --- /dev/null +++ b/tests/068-classloader/src/IGetDoubled.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Interface, loaded from one loader, used from another. + */ +public interface IGetDoubled { + public DoubledExtendOkay getDoubled(); +} + diff --git a/tests/068-classloader/src/Main.java b/tests/068-classloader/src/Main.java index f8698be19..0788b52f7 100644 --- a/tests/068-classloader/src/Main.java +++ b/tests/068-classloader/src/Main.java @@ -25,6 +25,8 @@ public class Main { FancyLoader loader; loader = new FancyLoader(ClassLoader.getSystemClassLoader()); + //System.out.println("SYSTEM: " + ClassLoader.getSystemClassLoader()); + //System.out.println("ALTERN: " + loader); /* * This statement has no effect on this program, but it can @@ -51,6 +53,9 @@ public class Main { testAccess3(loader); testExtend(loader); + testExtendOkay(loader); + testInterface(loader); + testAbstract(loader); testImplement(loader); testIfaceImplement(loader); } @@ -174,6 +179,146 @@ public class Main { } } + /** + * Test a doubled class that extends the base class, but is okay since + * it doesn't override the base class method. + */ + static void testExtendOkay(ClassLoader loader) { + Class doubledExtendOkayClass; + Object obj; + + /* get the "alternate" version of DoubledExtendOkay */ + try { + doubledExtendOkayClass = loader.loadClass("DoubledExtendOkay"); + } catch (ClassNotFoundException cnfe) { + System.err.println("loadClass failed: " + cnfe); + return; + } + + /* instantiate */ + try { + obj = doubledExtendOkayClass.newInstance(); + } catch (InstantiationException ie) { + System.err.println("newInstance failed: " + ie); + return; + } catch (IllegalAccessException iae) { + System.err.println("newInstance failed: " + iae); + return; + } catch (LinkageError le) { + System.err.println("Got unexpected LinkageError on DEO"); + le.printStackTrace(); + return; + } + + /* use the base class reference to get a CL-specific instance */ + BaseOkay baseRef = (BaseOkay) obj; + DoubledExtendOkay de = baseRef.getExtended(); + + /* try to call through it */ + try { + String result; + + result = BaseOkay.doStuff(de); + System.out.println("Got DEO result " + result); + } catch (LinkageError le) { + System.err.println("Got unexpected LinkageError on DEO"); + le.printStackTrace(); + return; + } + } + + /** + * Try to access a doubled class through a class that implements + * an interface declared in a different class. + */ + static void testInterface(ClassLoader loader) { + Class getDoubledClass; + Object obj; + + /* get GetDoubled from the "alternate" class loader */ + try { + getDoubledClass = loader.loadClass("GetDoubled"); + } catch (ClassNotFoundException cnfe) { + System.err.println("loadClass failed: " + cnfe); + return; + } + + /* instantiate */ + try { + obj = getDoubledClass.newInstance(); + } catch (InstantiationException ie) { + System.err.println("newInstance failed: " + ie); + return; + } catch (IllegalAccessException iae) { + System.err.println("newInstance failed: " + iae); + return; + } catch (LinkageError le) { + // Dalvik bails here + System.out.println("Got LinkageError on GD"); + return; + } + + /* + * Cast the object to the interface, and try to use it. + */ + IGetDoubled iface = (IGetDoubled) obj; + try { + /* "de" will be the wrong variety of DoubledExtendOkay */ + DoubledExtendOkay de = iface.getDoubled(); + // reference impl bails here + String str = de.getStr(); + } catch (LinkageError le) { + System.out.println("Got LinkageError on GD"); + return; + } + System.err.println("Should have failed by now on GetDoubled"); + } + + /** + * Throw an abstract class into the middle and see what happens. + */ + static void testAbstract(ClassLoader loader) { + Class abstractGetClass; + Object obj; + + /* get AbstractGet from the "alternate" loader */ + try { + abstractGetClass = loader.loadClass("AbstractGet"); + } catch (ClassNotFoundException cnfe) { + System.err.println("loadClass ta failed: " + cnfe); + return; + } + + /* instantiate */ + try { + obj = abstractGetClass.newInstance(); + } catch (InstantiationException ie) { + System.err.println("newInstance failed: " + ie); + return; + } catch (IllegalAccessException iae) { + System.err.println("newInstance failed: " + iae); + return; + } catch (LinkageError le) { + System.out.println("Got LinkageError on TA"); + return; + } + + /* use the base class reference to get a CL-specific instance */ + BaseOkay baseRef = (BaseOkay) obj; + DoubledExtendOkay de = baseRef.getExtended(); + + /* try to call through it */ + try { + String result; + + result = BaseOkay.doStuff(de); + } catch (LinkageError le) { + System.out.println("Got LinkageError on TA"); + return; + } + System.err.println("Should have failed by now in testAbstract"); + } + /** * Test a doubled class that implements a common interface. */ diff --git a/tests/071-dexfile/info.txt b/tests/071-dexfile/info.txt index 5eb04895f..54d9ed0e8 100644 --- a/tests/071-dexfile/info.txt +++ b/tests/071-dexfile/info.txt @@ -1,2 +1,4 @@ Exercise some Dalvik-specific DEX file features. This is not expected to work on other VMs. + +NOTE: the test requires that /sdcard exists and is writable. diff --git a/tests/071-dexfile/src/Main.java b/tests/071-dexfile/src/Main.java index 0e6cce760..42c841d75 100644 --- a/tests/071-dexfile/src/Main.java +++ b/tests/071-dexfile/src/Main.java @@ -15,6 +15,7 @@ */ import java.io.File; +import java.io.IOException; import java.lang.reflect.Constructor; /** @@ -27,11 +28,50 @@ public class Main { private static final String ODEX_ALT = "/tmp"; private static final String LIB_DIR = "/nowhere/nothing/"; + /** + * Prep the environment then run the test. + */ + public static void main(String[] args) { + Process p; + try { + /* + * Create a sub-process to see if the ProcessManager wait + * interferes with the dexopt invocation wait. + * + * /dev/random never hits EOF, so we're sure that we'll still + * be waiting for the process to complete. On the device it + * stops pretty quickly (which means the child won't be + * spinning). + */ + ProcessBuilder pb = new ProcessBuilder("cat", "/dev/random"); + p = pb.start(); + } catch (IOException ioe) { + System.err.println("cmd failed: " + ioe.getMessage()); + p = null; + } + + try { + testDexClassLoader(); + } finally { + // shouldn't be necessary, but it's good to be tidy + if (p != null) + p.destroy(); + + // let the ProcessManager's daemon thread finish before we shut down + // (avoids the occasional segmentation fault) + try { + Thread.sleep(500); + } catch (Exception ex) {} + } + + System.out.println("done"); + } + /** * Create a class loader, explicitly specifying the source DEX and * the location for the optimized DEX. */ - public static void main(String[] args) { + private static void testDexClassLoader() { ClassLoader dexClassLoader = getDexClassLoader(); Class anotherClass; @@ -50,10 +90,8 @@ public class Main { throw new RuntimeException("new another", ie); } - /* not expected to work; just exercises the call */ + // not expected to work; just exercises the call dexClassLoader.getResource("nonexistent"); - - System.out.println("done"); } /* @@ -94,6 +132,7 @@ public class Main { throw new RuntimeException("DCL ctor", nsme); } + // create an instance, using the path we found Object dclObj; try { dclObj = ctor.newInstance(CLASS_PATH, odexDir, LIB_DIR, myLoader); diff --git a/tests/072-precise-gc/expected.txt b/tests/072-precise-gc/expected.txt new file mode 100644 index 000000000..18ec087af --- /dev/null +++ b/tests/072-precise-gc/expected.txt @@ -0,0 +1,2 @@ +Valid refs: 0 +String0String1String2String3String4String5String6String7String8String9 diff --git a/tests/072-precise-gc/info.txt b/tests/072-precise-gc/info.txt new file mode 100644 index 000000000..b0b2cea9f --- /dev/null +++ b/tests/072-precise-gc/info.txt @@ -0,0 +1 @@ +Try to detect whether precise GC is working. diff --git a/tests/072-precise-gc/src/Main.java b/tests/072-precise-gc/src/Main.java new file mode 100644 index 000000000..9b2315d08 --- /dev/null +++ b/tests/072-precise-gc/src/Main.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.ref.WeakReference; + +public class Main { + public static void main(String[] args) { + staleStackTest(); + } + + public static void staleStackTest() { + WeakReference wrefs[] = new WeakReference[10]; + + populate(wrefs); + + check(wrefs); + } + + static void populate(WeakReference[] wrefs) { + /* + * Get a bunch of non-constant String objects into registers. These + * should be the first locals declared. + */ + String str0 = generateString("String", 0); + String str1 = generateString("String", 1); + String str2 = generateString("String", 2); + String str3 = generateString("String", 3); + String str4 = generateString("String", 4); + String str5 = generateString("String", 5); + String str6 = generateString("String", 6); + String str7 = generateString("String", 7); + String str8 = generateString("String", 8); + String str9 = generateString("String", 9); + + /* stuff them into the weak references array */ + wrefs[0] = new WeakReference(str0); + wrefs[1] = new WeakReference(str1); + wrefs[2] = new WeakReference(str2); + wrefs[3] = new WeakReference(str3); + wrefs[4] = new WeakReference(str4); + wrefs[5] = new WeakReference(str5); + wrefs[6] = new WeakReference(str6); + wrefs[7] = new WeakReference(str7); + wrefs[8] = new WeakReference(str8); + wrefs[9] = new WeakReference(str9); + } + + static String generateString(String base, int num) { + return base + num; + } + + static void check(WeakReference[] wrefs) { + /* + * Declare locals so that our stack overlaps the same region + * that populate() did. + */ + String str0; + String str1; + String str2; + String str3; + String str4; + String str5; + String str6; + String str7; + String str8; + String str9; + int numValid = 0; + + /* + * This *should* blow out all the weakly-reference objects. If + * we still have stale copies of references on the stack, a + * conservative GC will try to hold on to those objects and the + * count will be nonzero. + * + * Getting a zero result here isn't conclusive, but it's a strong + * indicator that precise GC is having an impact. + */ + System.gc(); + + for (int i = 0; i < wrefs.length; i++) { + if (wrefs[i].get() != null) + numValid++; + } + + System.out.println("Valid refs: " + numValid); + + /* use the locals in case the compiler gets smart */ + str0 = generateString("String", 0); + str1 = generateString("String", 1); + str2 = generateString("String", 2); + str3 = generateString("String", 3); + str4 = generateString("String", 4); + str5 = generateString("String", 5); + str6 = generateString("String", 6); + str7 = generateString("String", 7); + str8 = generateString("String", 8); + str9 = generateString("String", 9); + System.out.println(str0+str1+str2+str3+str4+str5+str6+str7+str8+str9); + } +} + diff --git a/tests/073-mismatched-field/expected.txt b/tests/073-mismatched-field/expected.txt new file mode 100644 index 000000000..90fbab87a --- /dev/null +++ b/tests/073-mismatched-field/expected.txt @@ -0,0 +1 @@ +Got expected failure diff --git a/tests/073-mismatched-field/info.txt b/tests/073-mismatched-field/info.txt new file mode 100644 index 000000000..4a15263e4 --- /dev/null +++ b/tests/073-mismatched-field/info.txt @@ -0,0 +1,3 @@ +Test behavior when an instance field is overlapped (through separate +compilation) by a static field. The VM is expected to detect the conflict +and throw an IncompatibleClassChangeError when the field is accessed. diff --git a/tests/073-mismatched-field/src/IMain.java b/tests/073-mismatched-field/src/IMain.java new file mode 100644 index 000000000..301dd2157 --- /dev/null +++ b/tests/073-mismatched-field/src/IMain.java @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public interface IMain { + //static int f = 123; +} + diff --git a/tests/073-mismatched-field/src/Main.java b/tests/073-mismatched-field/src/Main.java new file mode 100644 index 000000000..fb9f32a8e --- /dev/null +++ b/tests/073-mismatched-field/src/Main.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main extends SuperMain implements IMain { + public static void main(String[] args) { + Main main = new Main(); + main.doit(); + } + + void doit() { + try { + System.out.println("value=" + this.f); + System.err.println("Succeeded unexpectedly"); + } catch (IncompatibleClassChangeError icce) { + System.out.println("Got expected failure"); + } + } +} + diff --git a/tests/073-mismatched-field/src/SuperMain.java b/tests/073-mismatched-field/src/SuperMain.java new file mode 100644 index 000000000..7447739e5 --- /dev/null +++ b/tests/073-mismatched-field/src/SuperMain.java @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class SuperMain { + public int f = 456; +} + diff --git a/tests/073-mismatched-field/src2/IMain.java b/tests/073-mismatched-field/src2/IMain.java new file mode 100644 index 000000000..585c738cc --- /dev/null +++ b/tests/073-mismatched-field/src2/IMain.java @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public interface IMain { + static int f = 123; +} + diff --git a/tests/074-gc-thrash/expected.txt b/tests/074-gc-thrash/expected.txt new file mode 100644 index 000000000..26691659b --- /dev/null +++ b/tests/074-gc-thrash/expected.txt @@ -0,0 +1,2 @@ +Running (10 seconds) ... +Done. diff --git a/tests/074-gc-thrash/info.txt b/tests/074-gc-thrash/info.txt new file mode 100644 index 000000000..c195adbc8 --- /dev/null +++ b/tests/074-gc-thrash/info.txt @@ -0,0 +1,2 @@ +This thrashes the memory allocator and garbage collector for a brief period. + diff --git a/tests/074-gc-thrash/src/Main.java b/tests/074-gc-thrash/src/Main.java new file mode 100644 index 000000000..f79e5ce38 --- /dev/null +++ b/tests/074-gc-thrash/src/Main.java @@ -0,0 +1,338 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.io.File; +import java.lang.ref.WeakReference; +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; + +public class Main { + public static volatile boolean quit = false; + public static final boolean DEBUG = false; + + private static final boolean WRITE_HPROF_DATA = false; + private static final int TEST_TIME = 10; + private static final String OUTPUT_FILE = "gc-thrash.hprof"; + + public static void main(String[] args) { + // dump heap before + + System.out.println("Running (" + TEST_TIME + " seconds) ..."); + runTests(); + + Method dumpHprofDataMethod = null; + String dumpFile = null; + + if (WRITE_HPROF_DATA) { + dumpHprofDataMethod = getDumpHprofDataMethod(); + if (dumpHprofDataMethod != null) { + dumpFile = getDumpFileName(); + System.out.println("Sending output to " + dumpFile); + } + } + + System.gc(); + System.runFinalization(); + System.gc(); + + if (WRITE_HPROF_DATA && dumpHprofDataMethod != null) { + try { + dumpHprofDataMethod.invoke(null, dumpFile); + } catch (IllegalAccessException iae) { + System.err.println(iae); + } catch (InvocationTargetException ite) { + System.err.println(ite); + } + } + + System.out.println("Done."); + } + + /** + * Finds VMDebug.dumpHprofData() through reflection. In the reference + * implementation this will not be available. + * + * @return the reflection object, or null if the method can't be found + */ + private static Method getDumpHprofDataMethod() { + ClassLoader myLoader = Main.class.getClassLoader(); + Class vmdClass; + try { + vmdClass = myLoader.loadClass("dalvik.system.VMDebug"); + } catch (ClassNotFoundException cnfe) { + return null; + } + + Method meth; + try { + meth = vmdClass.getMethod("dumpHprofData", + new Class[] { String.class }); + } catch (NoSuchMethodException nsme) { + System.err.println("Found VMDebug but not dumpHprofData method"); + return null; + } + + return meth; + } + + private static String getDumpFileName() { + File tmpDir = new File("/tmp"); + if (tmpDir.exists() && tmpDir.isDirectory()) { + return "/tmp/" + OUTPUT_FILE; + } + + File sdcard = new File("/sdcard"); + if (sdcard.exists() && sdcard.isDirectory()) { + return "/sdcard/" + OUTPUT_FILE; + } + + return null; + } + + + /** + * Run the various tests for a set period. + */ + public static void runTests() { + Robin robin = new Robin(); + Deep deep = new Deep(); + Large large = new Large(); + + /* start all threads */ + robin.start(); + deep.start(); + large.start(); + + /* let everybody run for 10 seconds */ + sleep(TEST_TIME * 1000); + + quit = true; + + try { + /* wait for all threads to stop */ + robin.join(); + deep.join(); + large.join(); + } catch (InterruptedException ie) { + System.err.println("join was interrupted"); + } + } + + /** + * Sleeps for the "ms" milliseconds. + */ + public static void sleep(int ms) { + try { + Thread.sleep(ms); + } catch (InterruptedException ie) { + System.err.println("sleep was interrupted"); + } + } + + /** + * Sleeps briefly, allowing other threads some CPU time to get started. + */ + public static void startupDelay() { + sleep(500); + } +} + + +/** + * Allocates useless objects and holds on to several of them. + * + * Uses a single large array of references, replaced repeatedly in round-robin + * order. + */ +class Robin extends Thread { + private static final int ARRAY_SIZE = 40960; + int sleepCount = 0; + + public void run() { + Main.startupDelay(); + + String strings[] = new String[ARRAY_SIZE]; + int idx = 0; + + while (!Main.quit) { + strings[idx] = makeString(idx); + + if (idx % (ARRAY_SIZE / 4) == 0) { + Main.sleep(400); + sleepCount++; + } + + idx = (idx + 1) % ARRAY_SIZE; + } + + if (Main.DEBUG) + System.out.println("Robin: sleepCount=" + sleepCount); + } + + private String makeString(int val) { + return new String("Robin" + val); + } +} + + +/** + * Allocates useless objects in recursive calls. + */ +class Deep extends Thread { + private static final int MAX_DEPTH = 61; + + private static String strong[] = new String[MAX_DEPTH]; + private static WeakReference weak[] = new WeakReference[MAX_DEPTH]; + + public void run() { + int iter = 0; + boolean once = false; + + Main.startupDelay(); + + while (!Main.quit) { + dive(0, iter); + once = true; + iter += MAX_DEPTH; + } + + if (!once) { + System.err.println("not even once?"); + return; + } + + /* + * Check the results of the last trip through. Everything in + * "weak" should be matched in "strong", and the two should be + * equivalent (object-wise, not just string-equality-wise). + */ + for (int i = 0; i < MAX_DEPTH; i++) { + if (strong[i] != weak[i].get()) { + System.err.println("Deep: " + i + " strong=" + strong[i] + + ", weak=" + weak[i].get()); + } + } + + /* + * Wipe "strong", do a GC, see if "weak" got collected. + */ + for (int i = 0; i < MAX_DEPTH; i++) + strong[i] = null; + + System.gc(); + + for (int i = 0; i < MAX_DEPTH; i++) { + if (weak[i].get() != null) { + System.err.println("Deep: weak still has " + i); + } + } + + if (Main.DEBUG) + System.out.println("Deep: iters=" + iter / MAX_DEPTH); + } + + /** + * Recursively dive down, setting one or more local variables. + * + * We pad the stack out with locals, attempting to create a mix of + * valid and invalid references on the stack. + */ + private String dive(int depth, int iteration) { + String str0; + String str1; + String str2; + String str3; + String str4; + String str5; + String str6; + String str7; + String funStr; + + funStr = ""; + + switch (iteration % 8) { + case 0: + funStr = str0 = makeString(iteration); + break; + case 1: + funStr = str1 = makeString(iteration); + break; + case 2: + funStr = str2 = makeString(iteration); + break; + case 3: + funStr = str3 = makeString(iteration); + break; + case 4: + funStr = str4 = makeString(iteration); + break; + case 5: + funStr = str5 = makeString(iteration); + break; + case 6: + funStr = str6 = makeString(iteration); + break; + case 7: + funStr = str7 = makeString(iteration); + break; + } + + strong[depth] = funStr; + weak[depth] = new WeakReference(funStr); + + if (depth+1 < MAX_DEPTH) + dive(depth+1, iteration+1); + else + Main.sleep(100); + + return funStr; + } + + private String makeString(int val) { + return new String("Deep" + val); + } +} + + +/** + * Allocates large useless objects. + */ +class Large extends Thread { + public void run() { + byte[] chunk; + int count = 0; + int sleepCount = 0; + + Main.startupDelay(); + + while (!Main.quit) { + chunk = new byte[100000]; + pretendToUse(chunk); + + count++; + if ((count % 500) == 0) { + Main.sleep(400); + sleepCount++; + } + } + + if (Main.DEBUG) + System.out.println("Large: sleepCount=" + sleepCount); + } + + public void pretendToUse(byte[] chunk) {} +} + diff --git a/tests/075-verification-error/expected.txt b/tests/075-verification-error/expected.txt new file mode 100644 index 000000000..6e4f584d3 --- /dev/null +++ b/tests/075-verification-error/expected.txt @@ -0,0 +1,12 @@ +Got expected InstantationError +Got expected NoSuchFieldError +Got expected NoSuchFieldError +Got expected NoSuchMethodError +Got expected NoSuchMethodError +Got expected IllegalAccessError (ifield) +Got expected IllegalAccessError (sfield) +Got expected IllegalAccessError (method) +Got expected IllegalAccessError (smethod) +Got expected IllegalAccessError (meth-class) +Got expected IllegalAccessError (field-class) +Got expected IllegalAccessError (meth-meth) diff --git a/tests/075-verification-error/info.txt b/tests/075-verification-error/info.txt new file mode 100644 index 000000000..be688ff2b --- /dev/null +++ b/tests/075-verification-error/info.txt @@ -0,0 +1 @@ +Exercise deferred verification error reporting. diff --git a/tests/075-verification-error/src/Main.java b/tests/075-verification-error/src/Main.java new file mode 100644 index 000000000..f3572422b --- /dev/null +++ b/tests/075-verification-error/src/Main.java @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import other.Mutant; +import other.InaccessibleClass; +import other.InaccessibleMethod; + +/** + * Test some problematic situations that the verifier detects. + */ +public class Main { + public static final boolean VERBOSE = false; + + public static void main(String[] args) { + testClassNewInstance(); + testMissingStuff(); + testBadAccess(); + } + + /** + * Try to create a new instance of an abstract class. + */ + static void testClassNewInstance() { + try { + MaybeAbstract ma = new MaybeAbstract(); + System.err.println("ERROR: MaybeAbstract succeeded unexpectedly"); + } catch (InstantiationError ie) { + System.out.println("Got expected InstantationError"); + if (VERBOSE) System.out.println("--- " + ie); + } catch (Exception ex) { + System.err.println("Got unexpected MaybeAbstract failure"); + } + } + + /** + * Test stuff that disappears. + */ + static void testMissingStuff() { + Mutant mutant = new Mutant(); + + try { + int x = mutant.disappearingField; + } catch (NoSuchFieldError nsfe) { + System.out.println("Got expected NoSuchFieldError"); + if (VERBOSE) System.out.println("--- " + nsfe); + } + + try { + int y = Mutant.disappearingStaticField; + } catch (NoSuchFieldError nsfe) { + System.out.println("Got expected NoSuchFieldError"); + if (VERBOSE) System.out.println("--- " + nsfe); + } + + try { + mutant.disappearingMethod(); + } catch (NoSuchMethodError nsme) { + System.out.println("Got expected NoSuchMethodError"); + if (VERBOSE) System.out.println("--- " + nsme); + } + + try { + Mutant.disappearingStaticMethod(); + } catch (NoSuchMethodError nsme) { + System.out.println("Got expected NoSuchMethodError"); + if (VERBOSE) System.out.println("--- " + nsme); + } + } + + /** + * Test stuff that becomes inaccessible. + */ + static void testBadAccess() { + Mutant mutant = new Mutant(); + + try { + int x = mutant.inaccessibleField; + System.err.println("ERROR: bad access succeeded\n"); + } catch (IllegalAccessError iae) { + System.out.println("Got expected IllegalAccessError (ifield)"); + if (VERBOSE) System.out.println("--- " + iae); + } + + try { + int y = Mutant.inaccessibleStaticField; + System.err.println("ERROR: bad access succeeded\n"); + } catch (IllegalAccessError iae) { + System.out.println("Got expected IllegalAccessError (sfield)"); + if (VERBOSE) System.out.println("--- " + iae); + } + + try { + mutant.inaccessibleMethod(); + System.err.println("ERROR: bad access succeeded\n"); + } catch (IllegalAccessError iae) { + System.out.println("Got expected IllegalAccessError (method)"); + if (VERBOSE) System.out.println("--- " + iae); + } + + try { + Mutant.inaccessibleStaticMethod(); + System.err.println("ERROR: bad access succeeded\n"); + } catch (IllegalAccessError iae) { + System.out.println("Got expected IllegalAccessError (smethod)"); + if (VERBOSE) System.out.println("--- " + iae); + } + + try { + /* accessible static method in an inaccessible class */ + InaccessibleClass.test(); + System.err.println("ERROR: bad meth-class access succeeded\n"); + } catch (IllegalAccessError iae) { + System.out.println("Got expected IllegalAccessError (meth-class)"); + if (VERBOSE) System.out.println("--- " + iae); + } + + try { + /* accessible static field in an inaccessible class */ + int blah = InaccessibleClass.blah; + System.err.println("ERROR: bad field-class access succeeded\n"); + } catch (IllegalAccessError iae) { + System.out.println("Got expected IllegalAccessError (field-class)"); + if (VERBOSE) System.out.println("--- " + iae); + } + + try { + /* inaccessible static method in an accessible class */ + InaccessibleMethod.test(); + System.err.println("ERROR: bad access succeeded\n"); + } catch (IllegalAccessError iae) { + System.out.println("Got expected IllegalAccessError (meth-meth)"); + if (VERBOSE) System.out.println("--- " + iae); + } + } +} + diff --git a/tests/075-verification-error/src/MaybeAbstract.java b/tests/075-verification-error/src/MaybeAbstract.java new file mode 100644 index 000000000..43c002b60 --- /dev/null +++ b/tests/075-verification-error/src/MaybeAbstract.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public /*abstract*/ class MaybeAbstract { + public MaybeAbstract() {} + int foo() { return 0; } +} + diff --git a/tests/075-verification-error/src/other/InaccessibleClass.java b/tests/075-verification-error/src/other/InaccessibleClass.java new file mode 100644 index 000000000..254eaa321 --- /dev/null +++ b/tests/075-verification-error/src/other/InaccessibleClass.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package other; + +public class InaccessibleClass { + public static void test() {} + + public static int blah = 5; +} + diff --git a/tests/075-verification-error/src/other/InaccessibleMethod.java b/tests/075-verification-error/src/other/InaccessibleMethod.java new file mode 100644 index 000000000..49a0b29f1 --- /dev/null +++ b/tests/075-verification-error/src/other/InaccessibleMethod.java @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package other; + +public class InaccessibleMethod { + public static void test() {} +} + diff --git a/tests/075-verification-error/src/other/Mutant.java b/tests/075-verification-error/src/other/Mutant.java new file mode 100644 index 000000000..6c869c006 --- /dev/null +++ b/tests/075-verification-error/src/other/Mutant.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package other; + +/** + * Parts of this class will disappear or change form. + */ +public class Mutant { + public int disappearingField = 3; + public static int disappearingStaticField = 4; + + public void disappearingMethod() { + System.out.println("bye"); + } + public static void disappearingStaticMethod() { + System.out.println("kthxbai"); + } + + public int inaccessibleField = 5; + public static int inaccessibleStaticField = 6; + + public void inaccessibleMethod() { + System.out.println("no"); + } + + public static void inaccessibleStaticMethod() { + System.out.println("nay"); + } +} + diff --git a/tests/075-verification-error/src2/MaybeAbstract.java b/tests/075-verification-error/src2/MaybeAbstract.java new file mode 100644 index 000000000..bfbfd453f --- /dev/null +++ b/tests/075-verification-error/src2/MaybeAbstract.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public abstract class MaybeAbstract { + public MaybeAbstract() {} + int foo() { return 0; } +} + diff --git a/tests/075-verification-error/src2/other/InaccessibleClass.java b/tests/075-verification-error/src2/other/InaccessibleClass.java new file mode 100644 index 000000000..a3cfbff76 --- /dev/null +++ b/tests/075-verification-error/src2/other/InaccessibleClass.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package other; + +/*package*/ class InaccessibleClass { + public static void test() {} + + public static int blah = 5; +} + diff --git a/tests/075-verification-error/src2/other/InaccessibleMethod.java b/tests/075-verification-error/src2/other/InaccessibleMethod.java new file mode 100644 index 000000000..6e2738ebf --- /dev/null +++ b/tests/075-verification-error/src2/other/InaccessibleMethod.java @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package other; + +public class InaccessibleMethod { + /*package*/ static void test() {} +} + diff --git a/tests/075-verification-error/src2/other/Mutant.java b/tests/075-verification-error/src2/other/Mutant.java new file mode 100644 index 000000000..220fda076 --- /dev/null +++ b/tests/075-verification-error/src2/other/Mutant.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package other; + +/** + * Parts of this class will disappear or change form. + */ +public class Mutant { + //public int disappearingField = 3; + //public static int disappearingStaticField = 4; + + //public static void disappearingMethod() { + // System.out.println("bye"); + //} + //public static void disappearingStaticMethod() { + // System.out.println("kthxbai"); + //} + + protected int inaccessibleField = 5; + protected static int inaccessibleStaticField = 6; + + protected void inaccessibleMethod() { + System.out.println("no"); + } + + protected static void inaccessibleStaticMethod() { + System.out.println("nay"); + } +} + + diff --git a/tests/076-boolean-put/expected.txt b/tests/076-boolean-put/expected.txt new file mode 100644 index 000000000..a965a70ed --- /dev/null +++ b/tests/076-boolean-put/expected.txt @@ -0,0 +1 @@ +Done diff --git a/tests/076-boolean-put/info.txt b/tests/076-boolean-put/info.txt new file mode 100644 index 000000000..5b3ef4dba --- /dev/null +++ b/tests/076-boolean-put/info.txt @@ -0,0 +1,3 @@ +This checks a case where javac generates code that stores a byte into a +boolean field. The code as generated should not pass the verifier, so the +verifier had to be "loosened" to allow this case. diff --git a/tests/076-boolean-put/src/Main.java b/tests/076-boolean-put/src/Main.java new file mode 100644 index 000000000..b32542250 --- /dev/null +++ b/tests/076-boolean-put/src/Main.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Test access to private boolean fields. + * + * Accessing private boolean fields from an inner class causes the compiler + * to generate an accessor method that performs the boolean operation. + * Unfortunately the generated method takes an integer as an argument, + * not a boolean, which makes the verifier upset when the result of the + * operation is written back to a boolean field. + */ +public class Main { + private boolean mInstance; + private static boolean mStatic; + + public static void main(String[] args) { + Main foo = new Main(); + foo.test(); + + System.out.println("Done"); + } + + void test() { + Innard innard = new Innard(); + innard.doStuff(); + } + + class Innard { + void doStuff() { + mInstance |= true; + mStatic |= true; + } + } +} diff --git a/tests/077-method-override/expected.txt b/tests/077-method-override/expected.txt new file mode 100644 index 000000000..2e9bda33f --- /dev/null +++ b/tests/077-method-override/expected.txt @@ -0,0 +1,15 @@ +declaredInBase: Base +notDeclaredInBase: Derived +wasOverridden: Derived +overrideWithPublic: Derived +overrideProtectedWithPublic: Derived +overridePublicWithProtected: Derived +overridePublicWithPrivate: Base +overridePrivateWithPublic: Base +overridePrivateWithPublic: Derived +overrideVirtualWithStatic: Base +overrideVirtualWithStatic: Derived +overrideStaticWithVirtual: Base +overrideStaticWithVirtual: Derived +Got expected exception - ovws +Got expected exception - oswv diff --git a/tests/077-method-override/info.txt b/tests/077-method-override/info.txt new file mode 100644 index 000000000..914b4f253 --- /dev/null +++ b/tests/077-method-override/info.txt @@ -0,0 +1,2 @@ +Test various forms of method overrides, including some not allowed by the +compiler but possible with separate compilation. diff --git a/tests/077-method-override/src/Base.java b/tests/077-method-override/src/Base.java new file mode 100644 index 000000000..eed6f2258 --- /dev/null +++ b/tests/077-method-override/src/Base.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Base { + public void declaredInBase() { + System.out.println("declaredInBase: Base"); + } + + public void overridden() { + System.out.println("overridden: Base"); + } + + /* src2: removed */ + public void wasOverridden() { + System.out.println("wasOverridden: Base"); + } + + public void callOverrideWithPublic() { + overrideWithPublic(); + } + public void overrideWithPublic() { + System.out.println("overrideWithPublic: Base"); + } + + public void callOverridePublicWithProtected() { + overridePublicWithProtected(); + } + /* src2: public */ + protected void overridePublicWithProtected() { + System.out.println("overridePublicWithProtected: Base"); + } + + public void callOverrideProtectedWithPublic() { + overrideProtectedWithPublic(); + } + protected void overrideProtectedWithPublic() { + System.out.println("overrideProtectedWithPublic: Base"); + } + + public void callOverridePublicWithPrivate() { + overridePublicWithPrivate(); + } + /* src2: public */ + private void overridePublicWithPrivate() { + System.out.println("overridePublicWithPrivate: Base"); + } + + public void callOverridePrivateWithPublic() { + overridePrivateWithPublic(); + } + private void overridePrivateWithPublic() { + System.out.println("overridePrivateWithPublic: Base"); + } + + public void callOverrideVirtualWithStatic() { + overrideVirtualWithStatic(); + } + /* src2: non-static */ + public static void overrideVirtualWithStatic() { + System.out.println("overrideVirtualWithStatic: Base"); + } + + public void callOverrideStaticWithVirtual() { + overrideStaticWithVirtual(); + } + /* src2: static */ + public void overrideStaticWithVirtual() { + System.out.println("overrideStaticWithVirtual: Base"); + } +} + diff --git a/tests/077-method-override/src/Derived.java b/tests/077-method-override/src/Derived.java new file mode 100644 index 000000000..09ffdf648 --- /dev/null +++ b/tests/077-method-override/src/Derived.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Derived extends Base { + public static void notDeclaredInBase() { + System.out.println("notDeclaredInBase: Derived"); + } + + public void overridden() { + System.out.println("overridden: Derived"); + } + + public void wasOverridden() { + System.out.println("wasOverridden: Derived"); + } + + public void overrideWithPublic() { + System.out.println("overrideWithPublic: Derived"); + } + + protected void overridePublicWithProtected() { + System.out.println("overridePublicWithProtected: Derived"); + } + + public void overrideProtectedWithPublic() { + System.out.println("overrideProtectedWithPublic: Derived"); + } + + private void overridePublicWithPrivate() { + System.out.println("overridePublicWithPrivate: Derived"); + } + + public void overridePrivateWithPublic() { + System.out.println("overridePrivateWithPublic: Derived"); + } + + /* not really an "override"; just has same method signature */ + public static void overrideVirtualWithStatic() { + System.out.println("overrideVirtualWithStatic: Derived"); + } + + /* not really an "override"; just has same method signature */ + public void overrideStaticWithVirtual() { + System.out.println("overrideStaticWithVirtual: Derived"); + } +} + diff --git a/tests/077-method-override/src/Main.java b/tests/077-method-override/src/Main.java new file mode 100644 index 000000000..fa401cd26 --- /dev/null +++ b/tests/077-method-override/src/Main.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + public static void main(String args[]) { + Derived derived = new Derived(); + + derived.declaredInBase(); + derived.notDeclaredInBase(); + derived.wasOverridden(); + + derived.callOverrideWithPublic(); + derived.callOverrideProtectedWithPublic(); + derived.callOverridePublicWithProtected(); + derived.callOverridePublicWithPrivate(); + derived.callOverridePrivateWithPublic(); + derived.overridePrivateWithPublic(); + derived.callOverrideVirtualWithStatic(); + derived.overrideVirtualWithStatic(); + derived.callOverrideStaticWithVirtual(); + derived.overrideStaticWithVirtual(); + + try { + ((Base)derived).overrideVirtualWithStatic(); + } catch (NoSuchMethodError nsme) { + /* NSME is subclass of ICCE, so check it explicitly */ + System.err.println("Got NSME - ovws"); + } catch (IncompatibleClassChangeError icce) { + System.out.println("Got expected exception - ovws"); + } + + try { + ((Base)derived).overrideStaticWithVirtual(); + } catch (NoSuchMethodError nsme) { + System.err.println("Got NSME - ovws"); + } catch (IncompatibleClassChangeError icce) { + System.out.println("Got expected exception - oswv"); + } + } +} + diff --git a/tests/077-method-override/src2/Base.java b/tests/077-method-override/src2/Base.java new file mode 100644 index 000000000..8be42bc9a --- /dev/null +++ b/tests/077-method-override/src2/Base.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Base { + public void declaredInBase() { + System.out.println("declaredInBase: Base"); + } + + public void overridden() { + System.out.println("overridden: Base"); + } + + /* src2: removed */ + //public void wasOverridden() { + // System.out.println("wasOverridden: Base"); + //} + + public void callOverrideWithPublic() { + overrideWithPublic(); + } + public void overrideWithPublic() { + System.out.println("overrideWithPublic: Base"); + } + + public void callOverridePublicWithProtected() { + overridePublicWithProtected(); + } + /* src2: public */ + public void overridePublicWithProtected() { + System.out.println("overridePublicWithProtected: Base"); + } + + public void callOverrideProtectedWithPublic() { + overrideProtectedWithPublic(); + } + protected void overrideProtectedWithPublic() { + System.out.println("overrideProtectedWithPublic: Base"); + } + + public void callOverridePublicWithPrivate() { + overridePublicWithPrivate(); + } + /* src2: public */ + public void overridePublicWithPrivate() { + System.out.println("overridePublicWithPrivate: Base"); + } + + public void callOverridePrivateWithPublic() { + overridePrivateWithPublic(); + } + private void overridePrivateWithPublic() { + System.out.println("overridePrivateWithPublic: Base"); + } + + public void callOverrideVirtualWithStatic() { + overrideVirtualWithStatic(); + } + /* src2: non-static */ + public void overrideVirtualWithStatic() { + System.out.println("overrideVirtualWithStatic: Base"); + } + + public void callOverrideStaticWithVirtual() { + overrideStaticWithVirtual(); + } + public static void overrideStaticWithVirtual() { + System.out.println("overrideStaticWithVirtual: Base"); + } +} + diff --git a/tests/078-polymorphic-virtual/expected.txt b/tests/078-polymorphic-virtual/expected.txt new file mode 100644 index 000000000..0d2972823 --- /dev/null +++ b/tests/078-polymorphic-virtual/expected.txt @@ -0,0 +1,3 @@ +10000000 +20000000 +30000000 diff --git a/tests/078-polymorphic-virtual/info.txt b/tests/078-polymorphic-virtual/info.txt new file mode 100644 index 000000000..7c8a561b1 --- /dev/null +++ b/tests/078-polymorphic-virtual/info.txt @@ -0,0 +1,2 @@ +Stress test predicted chaining for overloaded virtual callsite with 3 resolved +calless invoked 10,000,000 times each in three threads. diff --git a/tests/078-polymorphic-virtual/src/Base.java b/tests/078-polymorphic-virtual/src/Base.java new file mode 100644 index 000000000..ec3aaddea --- /dev/null +++ b/tests/078-polymorphic-virtual/src/Base.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Base extends Thread { + int value; + + public void run() { + for (int i = 0; i < 10000000; i++) { + incrimentValue(); + } + } + + public void incrimentValue() { + } + + public int getValue() { + return value; + } +} diff --git a/tests/078-polymorphic-virtual/src/Derived1.java b/tests/078-polymorphic-virtual/src/Derived1.java new file mode 100644 index 000000000..57bd3b0c4 --- /dev/null +++ b/tests/078-polymorphic-virtual/src/Derived1.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Derived1 extends Base { + public void incrimentValue() { + value += 1; + } +} diff --git a/tests/078-polymorphic-virtual/src/Derived2.java b/tests/078-polymorphic-virtual/src/Derived2.java new file mode 100644 index 000000000..1d7de575e --- /dev/null +++ b/tests/078-polymorphic-virtual/src/Derived2.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Derived2 extends Base { + public void incrimentValue() { + value += 2; + } +} diff --git a/tests/078-polymorphic-virtual/src/Derived3.java b/tests/078-polymorphic-virtual/src/Derived3.java new file mode 100644 index 000000000..c2594d276 --- /dev/null +++ b/tests/078-polymorphic-virtual/src/Derived3.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Derived3 extends Base { + public void incrimentValue() { + value += 3; + } +} diff --git a/tests/078-polymorphic-virtual/src/Main.java b/tests/078-polymorphic-virtual/src/Main.java new file mode 100644 index 000000000..0514e53d7 --- /dev/null +++ b/tests/078-polymorphic-virtual/src/Main.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + public static void main(String args[]) { + Derived1 derived1 = new Derived1(); + Derived2 derived2 = new Derived2(); + Derived3 derived3 = new Derived3(); + + derived1.start(); + derived2.start(); + derived3.start(); + + try { + derived1.join(); + derived2.join(); + derived3.join(); + } catch (Exception e) { + System.out.println(e); + return; + } + + System.out.println(derived1.getValue()); + System.out.println(derived2.getValue()); + System.out.println(derived3.getValue()); + } +} diff --git a/tests/etc/local-run-test-jar b/tests/etc/local-run-test-jar index 0c802ba37..6155e3f0d 100755 --- a/tests/etc/local-run-test-jar +++ b/tests/etc/local-run-test-jar @@ -6,6 +6,7 @@ # Options: # --quiet -- don't chatter # --fast -- use the fast interpreter (the default) +# --jit -- use the jit # --portable -- use the portable interpreter # --debug -- wait for debugger to attach # --valgrind -- use valgrind @@ -26,11 +27,16 @@ OPTIMIZE="y" VALGRIND="n" DEV_MODE="n" QUIET="n" +PRECISE="y" while true; do if [ "x$1" = "x--quiet" ]; then QUIET="y" shift + elif [ "x$1" = "x--jit" ]; then + INTERP="jit" + msg "Using jit" + shift elif [ "x$1" = "x--fast" ]; then INTERP="fast" msg "Using fast interpreter" @@ -57,6 +63,9 @@ while true; do elif [ "x$1" = "x--no-optimize" ]; then OPTIMIZE="n" shift + elif [ "x$1" = "x--no-precise" ]; then + PRECISE="n" + shift elif [ "x$1" = "x--" ]; then shift break @@ -96,11 +105,17 @@ fi if [ "$VALGRIND" = "y" ]; then msg "Running with valgrind" valgrind_cmd="valgrind" - #valgrind_cmd="$valgrind_cmd --leak-check=full" + #valgrind_cmd="valgrind --leak-check=full" else valgrind_cmd="" fi +if [ "$PRECISE" = "y" ]; then + GC_OPTS="-Xgc:precise -Xgenregmap" +else + GC_OPTS="-Xgc:noprecise" +fi + msg "------------------------------" BASE="$OUT" # from build environment @@ -109,9 +124,9 @@ DEBUG_OPTS="-Xcheck:jni -Xrunjdwp:transport=dt_socket,address=8000,server=y,susp export ANDROID_PRINTF_LOG=brief if [ "$DEV_MODE" = "y" ]; then - export ANDROID_LOG_TAGS='*:d' + export ANDROID_LOG_TAGS='*:d' else - export ANDROID_LOG_TAGS='*:s' + export ANDROID_LOG_TAGS='*:s' fi export ANDROID_DATA="$DATA_DIR" export ANDROID_ROOT="${BASE}/system" @@ -134,5 +149,5 @@ if [ "$GDB" = "y" ]; then fi $valgrind_cmd $gdb $exe $gdbargs "-Xbootclasspath:${bpath}" \ - $DEX_VERIFY $DEX_OPTIMIZE $DEX_DEBUG "-Xint:${INTERP}" -ea \ + $DEX_VERIFY $DEX_OPTIMIZE $DEX_DEBUG $GC_OPTS "-Xint:${INTERP}" -ea \ -cp test.jar Main "$@" diff --git a/tests/etc/push-and-run-test-jar b/tests/etc/push-and-run-test-jar index c9bcfb272..df66a8eed 100755 --- a/tests/etc/push-and-run-test-jar +++ b/tests/etc/push-and-run-test-jar @@ -6,12 +6,14 @@ # Options: # --quiet -- don't chatter # --fast -- use the fast interpreter (the default) +# --jit -- use the jit # --portable -- use the portable interpreter # --debug -- wait for debugger to attach # --zygote -- use the zygote (if so, all other options are ignored) # --dev -- development mode # --no-verify -- turn off verification (on by default) # --no-optimize -- turn off optimization (on by default) +# --no-precise -- turn off precise GC (on by default) msg() { if [ "$QUIET" = "n" ]; then @@ -25,6 +27,7 @@ VERIFY="y" OPTIMIZE="y" ZYGOTE="n" QUIET="n" +PRECISE="y" while true; do if [ "x$1" = "x--quiet" ]; then @@ -34,6 +37,10 @@ while true; do INTERP="fast" msg "Using fast interpreter" shift + elif [ "x$1" = "x--jit" ]; then + INTERP="jit" + msg "Using jit" + shift elif [ "x$1" = "x--portable" ]; then INTERP="portable" msg "Using portable interpreter" @@ -54,6 +61,9 @@ while true; do elif [ "x$1" = "x--no-optimize" ]; then OPTIMIZE="n" shift + elif [ "x$1" = "x--no-precise" ]; then + PRECISE="n" + shift elif [ "x$1" = "x--" ]; then shift break @@ -106,9 +116,15 @@ if [ "$DEBUG" = "y" ]; then DEX_DEBUG="-agentlib:jdwp=transport=dt_android_adb,server=y,suspend=y" fi +if [ "$PRECISE" = "y" ]; then + GC_OPTS="-Xgc:precise -Xgenregmap" +else + GC_OPTS="-Xgc:noprecise" +fi + if [ "$ZYGOTE" = "y" ]; then adb shell cd /data \; dvz -classpath test.jar Main "$@" else adb shell cd /data \; dalvikvm $DEX_VERIFY $DEX_OPTIMIZE $DEX_DEBUG \ - -cp test.jar "-Xint:${INTERP}" -ea Main "$@" + $GC_OPTS -cp test.jar "-Xint:${INTERP}" -ea Main "$@" fi diff --git a/tests/run-test b/tests/run-test index b50390561..c5e209040 100755 --- a/tests/run-test +++ b/tests/run-test @@ -57,6 +57,9 @@ while true; do elif [ "x$1" = "x--reference" ]; then RUN="${progdir}/etc/reference-run-test-classes" shift + elif [ "x$1" = "x--jit" ]; then + run_args="${run_args} --jit" + shift elif [ "x$1" = "x--fast" ]; then run_args="${run_args} --fast" shift @@ -79,6 +82,9 @@ while true; do elif [ "x$1" = "x--no-optimize" ]; then run_args="${run_args} --no-optimize" shift + elif [ "x$1" = "x--no-precise" ]; then + run_args="${run_args} --no-precise" + shift elif [ "x$1" = "x--valgrind" ]; then run_args="${run_args} --valgrind" shift @@ -141,11 +147,13 @@ if [ "$usage" = "yes" ]; then "current directory." echo " Runtime Options:" echo " --fast Use the fast interpreter (the default)." + echo " --jit Use the jit." echo " --portable Use the portable interpreter." echo " --debug Wait for a debugger to attach." #echo " --gdb Run under gdb; incompatible with some tests." echo " --no-verify Turn off verification (on by default)." echo " --no-optimize Turn off optimization (on by default)." + echo " --no-precise Turn off precise GC (on by default)." echo " --zygote Spawn the process from the Zygote." \ "If used, then the" echo " other runtime options are ignored." diff --git a/tools/dexcheck b/tools/dexcheck new file mode 100755 index 000000000..76662e86d --- /dev/null +++ b/tools/dexcheck @@ -0,0 +1,32 @@ +#!/bin/bash +# +# This requires read permission on /data/dalvik-cache. On an eng build this +# works, on userdebug you will need to "adb root", or "su" followed by +# "chmod 777 /data/dalvik-cache". + +# Get the list of files. Use "sed" to drop the trailing carriage return. +files=`adb shell "cd /data/dalvik-cache; echo *" | sed -e s/.$//` +if [ "$files" = "*" ]; then + echo 'ERROR: commands must run as root on device (try "adb root" first?)' + exit 1 +fi + +failure=0 + +# Check each file in turn. This is much faster with "dexdump -c", but that +# was added post-cupcake. +# +# The dexdump found in older builds does not stop on checksum failures and +# will likely crash. +for file in $files; do + echo $file + errout=`adb shell "dexdump /data/dalvik-cache/$file > dev/null"` + errcount=`echo $errout | wc -w` > /dev/null + if [ $errcount != "0" ]; then + echo " Failure in $file: $errout" + failure=1 + fi +done + +exit $failure + diff --git a/tools/dexdeps/Android.mk b/tools/dexdeps/Android.mk new file mode 100644 index 000000000..9c2cec700 --- /dev/null +++ b/tools/dexdeps/Android.mk @@ -0,0 +1,44 @@ +# Copyright (C) 2009 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +LOCAL_PATH := $(call my-dir) + +# We use copy-file-to-new-target so that the installed +# script files' timestamps are at least as new as the +# .jar files they wrap. + +# the dexdeps script +# ============================================================ +include $(CLEAR_VARS) +LOCAL_IS_HOST_MODULE := true +LOCAL_MODULE_CLASS := EXECUTABLES +LOCAL_MODULE := dexdeps + +include $(BUILD_SYSTEM)/base_rules.mk + +$(LOCAL_BUILT_MODULE): $(HOST_OUT_JAVA_LIBRARIES)/dexdeps$(COMMON_JAVA_PACKAGE_SUFFIX) +$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/etc/dexdeps | $(ACP) + @echo "Copy: $(PRIVATE_MODULE) ($@)" + $(copy-file-to-new-target) + $(hide) chmod 755 $@ + +INTERNAL_DALVIK_MODULES += $(LOCAL_INSTALLED_MODULE) + +# the other stuff +# ============================================================ +subdirs := $(addprefix $(LOCAL_PATH)/,$(addsuffix /Android.mk, \ + src \ + )) + +include $(subdirs) diff --git a/tools/dexdeps/README.txt b/tools/dexdeps/README.txt new file mode 100644 index 000000000..14d65b0d8 --- /dev/null +++ b/tools/dexdeps/README.txt @@ -0,0 +1,28 @@ +dexdeps -- DEX external dependency dump + + +This tool dumps a list of fields and methods that a DEX file uses but does +not define. When combined with a list of public APIs, it can be used to +determine whether an APK is accessing fields and calling methods that it +shouldn't be. It may also be useful in determining whether an application +requires a certain minimum API level to execute. + +Basic usage: + + dexdeps [options] + +For zip archives (including .jar and .apk), dexdeps will look for a +"classes.dex" entry. + +Supported options are: + + --format={brief,xml} + + Specifies the output format. + + "brief" produces one line of output for each field and method. Field + and argument types are shown as descriptor strings. + + "xml" produces a larger output file, readable with an XML browser. Types + are shown in a more human-readable form (e.g. "[I" becomes "int[]"). + diff --git a/tools/dexdeps/etc/dexdeps b/tools/dexdeps/etc/dexdeps new file mode 100644 index 000000000..dc628bd14 --- /dev/null +++ b/tools/dexdeps/etc/dexdeps @@ -0,0 +1,69 @@ +#!/bin/bash +# +# Copyright (C) 2009 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Set up prog to be the path of this script, including following symlinks, +# and set up progdir to be the fully-qualified pathname of its directory. +prog="$0" +while [ -h "${prog}" ]; do + newProg=`/bin/ls -ld "${prog}"` + newProg=`expr "${newProg}" : ".* -> \(.*\)$"` + if expr "x${newProg}" : 'x/' >/dev/null; then + prog="${newProg}" + else + progdir=`dirname "${prog}"` + prog="${progdir}/${newProg}" + fi +done +oldwd=`pwd` +progdir=`dirname "${prog}"` +cd "${progdir}" +progdir=`pwd` +prog="${progdir}"/`basename "${prog}"` +cd "${oldwd}" + +jarfile=dexdeps.jar +libdir="$progdir" +if [ ! -r "$libdir/$jarfile" ] +then + libdir=`dirname "$progdir"`/tools/lib +fi +if [ ! -r "$libdir/$jarfile" ] +then + libdir=`dirname "$progdir"`/framework +fi +if [ ! -r "$libdir/$jarfile" ] +then + echo `basename "$prog"`": can't find $jarfile" + exit 1 +fi + +javaOpts="" + +# Alternatively, this will extract any parameter "-Jxxx" from the command line +# and pass them to Java (instead of to dexdeps). +while expr "x$1" : 'x-J' >/dev/null; do + opt=`expr "$1" : '-J\(.*\)'` + javaOpts="${javaOpts} -${opt}" + shift +done + +if [ "$OSTYPE" = "cygwin" ] ; then + jarpath=`cygpath -w "$libdir/$jarfile"` +else + jarpath="$libdir/$jarfile" +fi + +exec java $javaOpts -jar "$jarpath" "$@" diff --git a/tools/dexdeps/etc/manifest.txt b/tools/dexdeps/etc/manifest.txt new file mode 100644 index 000000000..760674402 --- /dev/null +++ b/tools/dexdeps/etc/manifest.txt @@ -0,0 +1 @@ +Main-Class: com.android.dexdeps.Main diff --git a/tools/dexdeps/src/Android.mk b/tools/dexdeps/src/Android.mk new file mode 100644 index 000000000..756a0b370 --- /dev/null +++ b/tools/dexdeps/src/Android.mk @@ -0,0 +1,32 @@ +# Copyright (C) 2009 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +LOCAL_PATH := $(call my-dir) + + +# dexdeps java library +# ============================================================ +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(call all-subdir-java-files) +LOCAL_JAR_MANIFEST := ../etc/manifest.txt + +LOCAL_MODULE:= dexdeps + +include $(BUILD_HOST_JAVA_LIBRARY) + +INTERNAL_DALVIK_MODULES += $(LOCAL_INSTALLED_MODULE) + +include $(BUILD_DROIDDOC) + diff --git a/tools/dexdeps/src/com/android/dexdeps/DexData.java b/tools/dexdeps/src/com/android/dexdeps/DexData.java new file mode 100644 index 000000000..fa79d60b6 --- /dev/null +++ b/tools/dexdeps/src/com/android/dexdeps/DexData.java @@ -0,0 +1,585 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.dexdeps; + +import java.io.IOException; +import java.io.RandomAccessFile; +import java.util.Arrays; + +/** + * Data extracted from a DEX file. + */ +public class DexData { + private RandomAccessFile mDexFile; + private HeaderItem mHeaderItem; + private String[] mStrings; // strings from string_data_* + private TypeIdItem[] mTypeIds; + private ProtoIdItem[] mProtoIds; + private FieldIdItem[] mFieldIds; + private MethodIdItem[] mMethodIds; + private ClassDefItem[] mClassDefs; + + private byte tmpBuf[] = new byte[4]; + private boolean isBigEndian = false; + + /** + * Constructs a new DexData for this file. + */ + public DexData(RandomAccessFile raf) { + mDexFile = raf; + } + + /** + * Loads the contents of the DEX file into our data structures. + * + * @throws IOException if we encounter a problem while reading + * @throws DexDataException if the DEX contents look bad + */ + public void load() throws IOException { + parseHeaderItem(); + + loadStrings(); + loadTypeIds(); + loadProtoIds(); + loadFieldIds(); + loadMethodIds(); + loadClassDefs(); + + markInternalClasses(); + } + + + /** + * Parses the interesting bits out of the header. + */ + void parseHeaderItem() throws IOException { + mHeaderItem = new HeaderItem(); + + seek(0); + + byte[] magic = new byte[8]; + readBytes(magic); + if (!Arrays.equals(magic, HeaderItem.DEX_FILE_MAGIC)) { + System.err.println("Magic number is wrong -- are you sure " + + "this is a DEX file?"); + throw new DexDataException(); + } + + /* + * Read the endian tag, so we properly swap things as we read + * them from here on. + */ + seek(8+4+20+4+4); + mHeaderItem.endianTag = readInt(); + if (mHeaderItem.endianTag == HeaderItem.ENDIAN_CONSTANT) { + /* do nothing */ + } else if (mHeaderItem.endianTag == HeaderItem.REVERSE_ENDIAN_CONSTANT){ + /* file is big-endian (!), reverse future reads */ + isBigEndian = true; + } else { + System.err.println("Endian constant has unexpected value " + + Integer.toHexString(mHeaderItem.endianTag)); + throw new DexDataException(); + } + + seek(8+4+20); // magic, checksum, signature + mHeaderItem.fileSize = readInt(); + mHeaderItem.headerSize = readInt(); + /*mHeaderItem.endianTag =*/ readInt(); + /*mHeaderItem.linkSize =*/ readInt(); + /*mHeaderItem.linkOff =*/ readInt(); + /*mHeaderItem.mapOff =*/ readInt(); + mHeaderItem.stringIdsSize = readInt(); + mHeaderItem.stringIdsOff = readInt(); + mHeaderItem.typeIdsSize = readInt(); + mHeaderItem.typeIdsOff = readInt(); + mHeaderItem.protoIdsSize = readInt(); + mHeaderItem.protoIdsOff = readInt(); + mHeaderItem.fieldIdsSize = readInt(); + mHeaderItem.fieldIdsOff = readInt(); + mHeaderItem.methodIdsSize = readInt(); + mHeaderItem.methodIdsOff = readInt(); + mHeaderItem.classDefsSize = readInt(); + mHeaderItem.classDefsOff = readInt(); + /*mHeaderItem.dataSize =*/ readInt(); + /*mHeaderItem.dataOff =*/ readInt(); + } + + /** + * Loads the string table out of the DEX. + * + * First we read all of the string_id_items, then we read all of the + * string_data_item. Doing it this way should allow us to avoid + * seeking around in the file. + */ + void loadStrings() throws IOException { + int count = mHeaderItem.stringIdsSize; + int stringOffsets[] = new int[count]; + + //System.out.println("reading " + count + " strings"); + + seek(mHeaderItem.stringIdsOff); + for (int i = 0; i < count; i++) { + stringOffsets[i] = readInt(); + } + + mStrings = new String[count]; + + seek(stringOffsets[0]); + for (int i = 0; i < count; i++) { + seek(stringOffsets[i]); // should be a no-op + mStrings[i] = readString(); + //System.out.println("STR: " + i + ": " + mStrings[i]); + } + } + + /** + * Loads the type ID list. + */ + void loadTypeIds() throws IOException { + int count = mHeaderItem.typeIdsSize; + mTypeIds = new TypeIdItem[count]; + + //System.out.println("reading " + count + " typeIds"); + seek(mHeaderItem.typeIdsOff); + for (int i = 0; i < count; i++) { + mTypeIds[i] = new TypeIdItem(); + mTypeIds[i].descriptorIdx = readInt(); + + //System.out.println(i + ": " + mTypeIds[i].descriptorIdx + + // " " + mStrings[mTypeIds[i].descriptorIdx]); + } + } + + /** + * Loads the proto ID list. + */ + void loadProtoIds() throws IOException { + int count = mHeaderItem.protoIdsSize; + mProtoIds = new ProtoIdItem[count]; + + //System.out.println("reading " + count + " protoIds"); + seek(mHeaderItem.protoIdsOff); + + /* + * Read the proto ID items. + */ + for (int i = 0; i < count; i++) { + mProtoIds[i] = new ProtoIdItem(); + mProtoIds[i].shortyIdx = readInt(); + mProtoIds[i].returnTypeIdx = readInt(); + mProtoIds[i].parametersOff = readInt(); + + //System.out.println(i + ": " + mProtoIds[i].shortyIdx + + // " " + mStrings[mProtoIds[i].shortyIdx]); + } + + /* + * Go back through and read the type lists. + */ + for (int i = 0; i < count; i++) { + ProtoIdItem protoId = mProtoIds[i]; + + int offset = protoId.parametersOff; + + if (offset == 0) { + protoId.types = new int[0]; + continue; + } else { + seek(offset); + int size = readInt(); // #of entries in list + protoId.types = new int[size]; + + for (int j = 0; j < size; j++) { + protoId.types[j] = readShort() & 0xffff; + } + } + } + } + + /** + * Loads the field ID list. + */ + void loadFieldIds() throws IOException { + int count = mHeaderItem.fieldIdsSize; + mFieldIds = new FieldIdItem[count]; + + //System.out.println("reading " + count + " fieldIds"); + seek(mHeaderItem.fieldIdsOff); + for (int i = 0; i < count; i++) { + mFieldIds[i] = new FieldIdItem(); + mFieldIds[i].classIdx = readShort() & 0xffff; + mFieldIds[i].typeIdx = readShort() & 0xffff; + mFieldIds[i].nameIdx = readInt(); + + //System.out.println(i + ": " + mFieldIds[i].nameIdx + + // " " + mStrings[mFieldIds[i].nameIdx]); + } + } + + /** + * Loads the method ID list. + */ + void loadMethodIds() throws IOException { + int count = mHeaderItem.methodIdsSize; + mMethodIds = new MethodIdItem[count]; + + //System.out.println("reading " + count + " methodIds"); + seek(mHeaderItem.methodIdsOff); + for (int i = 0; i < count; i++) { + mMethodIds[i] = new MethodIdItem(); + mMethodIds[i].classIdx = readShort() & 0xffff; + mMethodIds[i].protoIdx = readShort() & 0xffff; + mMethodIds[i].nameIdx = readInt(); + + //System.out.println(i + ": " + mMethodIds[i].nameIdx + + // " " + mStrings[mMethodIds[i].nameIdx]); + } + } + + /** + * Loads the class defs list. + */ + void loadClassDefs() throws IOException { + int count = mHeaderItem.classDefsSize; + mClassDefs = new ClassDefItem[count]; + + //System.out.println("reading " + count + " classDefs"); + seek(mHeaderItem.classDefsOff); + for (int i = 0; i < count; i++) { + mClassDefs[i] = new ClassDefItem(); + mClassDefs[i].classIdx = readInt(); + + /* access_flags = */ readInt(); + /* superclass_idx = */ readInt(); + /* interfaces_off = */ readInt(); + /* source_file_idx = */ readInt(); + /* annotations_off = */ readInt(); + /* class_data_off = */ readInt(); + /* static_values_off = */ readInt(); + + //System.out.println(i + ": " + mClassDefs[i].classIdx + " " + + // mStrings[mTypeIds[mClassDefs[i].classIdx].descriptorIdx]); + } + } + + /** + * Sets the "internal" flag on type IDs which are defined in the + * DEX file or within the VM (e.g. primitive classes and arrays). + */ + void markInternalClasses() { + for (int i = mClassDefs.length -1; i >= 0; i--) { + mTypeIds[mClassDefs[i].classIdx].internal = true; + } + + for (int i = 0; i < mTypeIds.length; i++) { + String className = mStrings[mTypeIds[i].descriptorIdx]; + + if (className.length() == 1) { + // primitive class + mTypeIds[i].internal = true; + } else if (className.charAt(0) == '[') { + mTypeIds[i].internal = true; + } + + //System.out.println(i + " " + + // (mTypeIds[i].internal ? "INTERNAL" : "external") + " - " + + // mStrings[mTypeIds[i].descriptorIdx]); + } + } + + + /* + * ======================================================================= + * Queries + * ======================================================================= + */ + + /** + * Returns the class name, given an index into the type_ids table. + */ + private String classNameFromTypeIndex(int idx) { + return mStrings[mTypeIds[idx].descriptorIdx]; + } + + /** + * Returns an array of method argument type strings, given an index + * into the proto_ids table. + */ + private String[] argArrayFromProtoIndex(int idx) { + ProtoIdItem protoId = mProtoIds[idx]; + String[] result = new String[protoId.types.length]; + + for (int i = 0; i < protoId.types.length; i++) { + result[i] = mStrings[mTypeIds[protoId.types[i]].descriptorIdx]; + } + + return result; + } + + /** + * Returns a string representing the method's return type, given an + * index into the proto_ids table. + */ + private String returnTypeFromProtoIndex(int idx) { + ProtoIdItem protoId = mProtoIds[idx]; + return mStrings[mTypeIds[protoId.returnTypeIdx].descriptorIdx]; + } + + /** + * Returns an array with all of the field references that don't + * correspond to classes in the DEX file. + */ + public FieldRef[] getExternalFieldReferences() { + // get a count + int count = 0; + for (int i = 0; i < mFieldIds.length; i++) { + if (!mTypeIds[mFieldIds[i].classIdx].internal) + count++; + } + + //System.out.println("count is " + count + " of " + mFieldIds.length); + + FieldRef[] fieldRefs = new FieldRef[count]; + count = 0; + for (int i = 0; i < mFieldIds.length; i++) { + if (!mTypeIds[mFieldIds[i].classIdx].internal) { + FieldIdItem fieldId = mFieldIds[i]; + fieldRefs[count++] = + new FieldRef(classNameFromTypeIndex(fieldId.classIdx), + classNameFromTypeIndex(fieldId.typeIdx), + mStrings[fieldId.nameIdx]); + } + } + + assert count == fieldRefs.length; + + return fieldRefs; + } + + /** + * Returns an array with all of the method references that don't + * correspond to classes in the DEX file. + */ + public MethodRef[] getExternalMethodReferences() { + // get a count + int count = 0; + for (int i = 0; i < mMethodIds.length; i++) { + if (!mTypeIds[mMethodIds[i].classIdx].internal) + count++; + } + + //System.out.println("count is " + count + " of " + mMethodIds.length); + + MethodRef[] methodRefs = new MethodRef[count]; + count = 0; + for (int i = 0; i < mMethodIds.length; i++) { + if (!mTypeIds[mMethodIds[i].classIdx].internal) { + MethodIdItem methodId = mMethodIds[i]; + methodRefs[count++] = + new MethodRef(classNameFromTypeIndex(methodId.classIdx), + argArrayFromProtoIndex(methodId.protoIdx), + returnTypeFromProtoIndex(methodId.protoIdx), + mStrings[methodId.nameIdx]); + } + } + + assert count == methodRefs.length; + + return methodRefs; + } + + + /* + * ======================================================================= + * Basic I/O functions + * ======================================================================= + */ + + /** + * Seeks the DEX file to the specified absolute position. + */ + void seek(int position) throws IOException { + mDexFile.seek(position); + } + + /** + * Fills the buffer by reading bytes from the DEX file. + */ + void readBytes(byte[] buffer) throws IOException { + mDexFile.readFully(buffer); + } + + /** + * Reads a single signed byte value. + */ + byte readByte() throws IOException { + mDexFile.readFully(tmpBuf, 0, 1); + return tmpBuf[0]; + } + + /** + * Reads a signed 16-bit integer, byte-swapping if necessary. + */ + short readShort() throws IOException { + mDexFile.readFully(tmpBuf, 0, 2); + if (isBigEndian) { + return (short) ((tmpBuf[1] & 0xff) | ((tmpBuf[0] & 0xff) << 8)); + } else { + return (short) ((tmpBuf[0] & 0xff) | ((tmpBuf[1] & 0xff) << 8)); + } + } + + /** + * Reads a signed 32-bit integer, byte-swapping if necessary. + */ + int readInt() throws IOException { + mDexFile.readFully(tmpBuf, 0, 4); + + if (isBigEndian) { + return (tmpBuf[3] & 0xff) | ((tmpBuf[2] & 0xff) << 8) | + ((tmpBuf[1] & 0xff) << 16) | ((tmpBuf[0] & 0xff) << 24); + } else { + return (tmpBuf[0] & 0xff) | ((tmpBuf[1] & 0xff) << 8) | + ((tmpBuf[2] & 0xff) << 16) | ((tmpBuf[3] & 0xff) << 24); + } + } + + /** + * Reads a variable-length unsigned LEB128 value. Does not attempt to + * verify that the value is valid. + * + * @throws EOFException if we run off the end of the file + */ + int readUnsignedLeb128() throws IOException { + int result = 0; + byte val; + + do { + val = readByte(); + result = (result << 7) | (val & 0x7f); + } while (val < 0); + + return result; + } + + /** + * Reads a UTF-8 string. + * + * We don't know how long the UTF-8 string is, so we have to read one + * byte at a time. We could make an educated guess based on the + * utf16_size and seek back if we get it wrong, but seeking backward + * may cause the underlying implementation to reload I/O buffers. + */ + String readString() throws IOException { + int utf16len = readUnsignedLeb128(); + byte inBuf[] = new byte[utf16len * 3]; // worst case + int idx; + + for (idx = 0; idx < inBuf.length; idx++) { + byte val = readByte(); + if (val == 0) + break; + inBuf[idx] = val; + } + + return new String(inBuf, 0, idx, "UTF-8"); + } + + + /* + * ======================================================================= + * Internal "structure" declarations + * ======================================================================= + */ + + /** + * Holds the contents of a header_item. + */ + static class HeaderItem { + public int fileSize; + public int headerSize; + public int endianTag; + public int stringIdsSize, stringIdsOff; + public int typeIdsSize, typeIdsOff; + public int protoIdsSize, protoIdsOff; + public int fieldIdsSize, fieldIdsOff; + public int methodIdsSize, methodIdsOff; + public int classDefsSize, classDefsOff; + + /* expected magic values */ + public static final byte[] DEX_FILE_MAGIC = { + 0x64, 0x65, 0x78, 0x0a, 0x30, 0x33, 0x35, 0x00 }; + public static final int ENDIAN_CONSTANT = 0x12345678; + public static final int REVERSE_ENDIAN_CONSTANT = 0x78563412; + } + + /** + * Holds the contents of a type_id_item. + * + * This is chiefly a list of indices into the string table. We need + * some additional bits of data, such as whether or not the type ID + * represents a class defined in this DEX, so we use an object for + * each instead of a simple integer. (Could use a parallel array, but + * since this is a desktop app it's not essential.) + */ + static class TypeIdItem { + public int descriptorIdx; // index into string_ids + + public boolean internal; // defined within this DEX file? + } + + /** + * Holds the contents of a proto_id_item. + */ + static class ProtoIdItem { + public int shortyIdx; // index into string_ids + public int returnTypeIdx; // index into type_ids + public int parametersOff; // file offset to a type_list + + public int types[]; // contents of type list + } + + /** + * Holds the contents of a field_id_item. + */ + static class FieldIdItem { + public int classIdx; // index into type_ids (defining class) + public int typeIdx; // index into type_ids (field type) + public int nameIdx; // index into string_ids + } + + /** + * Holds the contents of a method_id_item. + */ + static class MethodIdItem { + public int classIdx; // index into type_ids + public int protoIdx; // index into proto_ids + public int nameIdx; // index into string_ids + } + + /** + * Holds the contents of a class_def_item. + * + * We don't really need a class for this, but there's some stuff in + * the class_def_item that we might want later. + */ + static class ClassDefItem { + public int classIdx; // index into type_ids + } +} + diff --git a/tools/dexdeps/src/com/android/dexdeps/DexDataException.java b/tools/dexdeps/src/com/android/dexdeps/DexDataException.java new file mode 100644 index 000000000..e51853f53 --- /dev/null +++ b/tools/dexdeps/src/com/android/dexdeps/DexDataException.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.dexdeps; + +/** + * Bad data found inside a DEX file. + */ +public class DexDataException extends RuntimeException { +} + diff --git a/tools/dexdeps/src/com/android/dexdeps/FieldRef.java b/tools/dexdeps/src/com/android/dexdeps/FieldRef.java new file mode 100644 index 000000000..2726a7a7d --- /dev/null +++ b/tools/dexdeps/src/com/android/dexdeps/FieldRef.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.dexdeps; + +public class FieldRef { + private String mDeclClass, mFieldType, mFieldName; + + /** + * Initializes a new field reference. + */ + public FieldRef(String declClass, String fieldType, String fieldName) { + mDeclClass = declClass; + mFieldType = fieldType; + mFieldName = fieldName; + } + + /** + * Gets the name of the field's declaring class. + */ + public String getDeclClassName() { + return mDeclClass; + } + + /** + * Gets the type name. Examples: "Ljava/lang/String;", "[I". + */ + public String getTypeName() { + return mFieldType; + } + + /** + * Gets the field name. + */ + public String getName() { + return mFieldName; + } +} + diff --git a/tools/dexdeps/src/com/android/dexdeps/Main.java b/tools/dexdeps/src/com/android/dexdeps/Main.java new file mode 100644 index 000000000..7eba3aa80 --- /dev/null +++ b/tools/dexdeps/src/com/android/dexdeps/Main.java @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.dexdeps; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.RandomAccessFile; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; +import java.util.zip.ZipInputStream; + +public class Main { + private static final String CLASSES_DEX = "classes.dex"; + + private String mInputFileName; + private String mOutputFormat = "brief"; + + /** + * Entry point. + */ + public static void main(String[] args) { + Main main = new Main(); + main.run(args); + } + + /** + * Start things up. + */ + void run(String[] args) { + try { + parseArgs(args); + RandomAccessFile raf = openInputFile(); + DexData dexData = new DexData(raf); + dexData.load(); + + Output.generate(dexData, mOutputFormat); + } catch (UsageException ue) { + usage(); + System.exit(2); + } catch (IOException ioe) { + if (ioe.getMessage() != null) + System.err.println("Failed: " + ioe); + System.exit(1); + } catch (DexDataException dde) { + /* a message was already reported, just bail quietly */ + System.exit(1); + } + } + + /** + * Opens the input file, which could be a .dex or a .jar/.apk with a + * classes.dex inside. If the latter, we extract the contents to a + * temporary file. + */ + RandomAccessFile openInputFile() throws IOException { + RandomAccessFile raf; + + raf = openInputFileAsZip(); + if (raf == null) { + File inputFile = new File(mInputFileName); + raf = new RandomAccessFile(inputFile, "r"); + } + + return raf; + } + + /** + * Tries to open the input file as a Zip archive (jar/apk) with a + * "classes.dex" inside. + * + * @return a RandomAccessFile for classes.dex, or null if the input file + * is not a zip archive + * @throws IOException if the file isn't found, or it's a zip and + * classes.dex isn't found inside + */ + RandomAccessFile openInputFileAsZip() throws IOException { + ZipFile zipFile; + + /* + * Try it as a zip file. + */ + try { + zipFile = new ZipFile(mInputFileName); + } catch (FileNotFoundException fnfe) { + /* not found, no point in retrying as non-zip */ + System.err.println("Unable to open '" + mInputFileName + "': " + + fnfe.getMessage()); + throw fnfe; + } catch (ZipException ze) { + /* not a zip */ + return null; + } + + /* + * We know it's a zip; see if there's anything useful inside. A + * failure here results in some type of IOException (of which + * ZipException is a subclass). + */ + ZipEntry entry = zipFile.getEntry(CLASSES_DEX); + if (entry == null) { + System.err.println("Unable to find '" + CLASSES_DEX + + "' in '" + mInputFileName + "'"); + zipFile.close(); + throw new ZipException(); + } + + InputStream zis = zipFile.getInputStream(entry); + + /* + * Create a temp file to hold the DEX data, open it, and delete it + * to ensure it doesn't hang around if we fail. + */ + File tempFile = File.createTempFile("dexdeps", ".dex"); + //System.out.println("+++ using temp " + tempFile); + RandomAccessFile raf = new RandomAccessFile(tempFile, "rw"); + tempFile.delete(); + + /* + * Copy all data from input stream to output file. + */ + byte copyBuf[] = new byte[32768]; + int actual; + + while (true) { + actual = zis.read(copyBuf); + if (actual == -1) + break; + + raf.write(copyBuf, 0, actual); + } + + zis.close(); + raf.seek(0); + + return raf; + } + + + /** + * Parses command-line arguments. + * + * @throws UsageException if arguments are missing or poorly formed + */ + void parseArgs(String[] args) { + int idx; + + for (idx = 0; idx < args.length; idx++) { + String arg = args[idx]; + + if (arg.equals("--") || !arg.startsWith("--")) { + break; + } else if (arg.startsWith("--format=")) { + mOutputFormat = arg.substring(arg.indexOf('=') + 1); + if (!mOutputFormat.equals("brief") && + !mOutputFormat.equals("xml")) + { + System.err.println("Unknown format '" + mOutputFormat +"'"); + throw new UsageException(); + } + //System.out.println("+++ using format " + mOutputFormat); + } else { + System.err.println("Unknown option '" + arg + "'"); + throw new UsageException(); + } + } + + // expecting one argument left + if (idx != args.length - 1) { + throw new UsageException(); + } + + mInputFileName = args[idx]; + } + + /** + * Prints command-line usage info. + */ + void usage() { + System.err.println("\nUsage: dexdeps [options] "); + System.err.println("Options:"); + System.err.println(" --format={brief,xml}"); + } +} + diff --git a/tools/dexdeps/src/com/android/dexdeps/MethodRef.java b/tools/dexdeps/src/com/android/dexdeps/MethodRef.java new file mode 100644 index 000000000..96522eb92 --- /dev/null +++ b/tools/dexdeps/src/com/android/dexdeps/MethodRef.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.dexdeps; + +public class MethodRef { + private String mDeclClass, mReturnType, mMethodName; + private String[] mArgTypes; + + /** + * Initializes a new field reference. + */ + public MethodRef(String declClass, String[] argTypes, String returnType, + String methodName) { + mDeclClass = declClass; + mArgTypes = argTypes; + mReturnType = returnType; + mMethodName = methodName; + } + + /** + * Gets the name of the method's declaring class. + */ + public String getDeclClassName() { + return mDeclClass; + } + + /** + * Gets the method's descriptor. + */ + public String getDescriptor() { + return descriptorFromProtoArray(mArgTypes, mReturnType); + } + + /** + * Gets the method's name. + */ + public String getName() { + return mMethodName; + } + + /** + * Gets an array of method argument types. + */ + public String[] getArgumentTypeNames() { + return mArgTypes; + } + + /** + * Gets the method's return type. Examples: "Ljava/lang/String;", "[I". + */ + public String getReturnTypeName() { + return mReturnType; + } + + /** + * Returns the method descriptor, given the argument and return type + * prototype strings. + */ + private static String descriptorFromProtoArray(String[] protos, + String returnType) { + StringBuilder builder = new StringBuilder(); + + builder.append("("); + for (int i = 0; i < protos.length; i++) { + builder.append(protos[i]); + } + + builder.append(")"); + builder.append(returnType); + + return builder.toString(); + } +} + diff --git a/tools/dexdeps/src/com/android/dexdeps/Output.java b/tools/dexdeps/src/com/android/dexdeps/Output.java new file mode 100644 index 000000000..0039b3302 --- /dev/null +++ b/tools/dexdeps/src/com/android/dexdeps/Output.java @@ -0,0 +1,264 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.dexdeps; + +/** + * Generate fancy output. + */ +public class Output { + public static void generate(DexData dexData, String format) { + if (format.equals("brief")) { + printBrief(dexData); + } else if (format.equals("xml")) { + printXml(dexData); + } else { + /* should've been trapped in arg handler */ + throw new RuntimeException("unknown output format"); + } + } + + /** + * Prints the data in a simple human-readable format. + */ + static void printBrief(DexData dexData) { + FieldRef[] externFieldRefs = dexData.getExternalFieldReferences(); + MethodRef[] externMethodRefs = dexData.getExternalMethodReferences(); + + printFieldRefs(externFieldRefs); + printMethodRefs(externMethodRefs); + } + + /** + * Prints the list of fields in a simple human-readable format. + */ + static void printFieldRefs(FieldRef[] fields) { + System.out.println("Fields:"); + for (int i = 0; i < fields.length; i++) { + FieldRef ref = fields[i]; + + System.out.println(descriptorToDot(ref.getDeclClassName()) + "." + + ref.getName() + " : " + ref.getTypeName()); + } + } + + /** + * Prints the list of methods in a simple human-readable format. + */ + static void printMethodRefs(MethodRef[] methods) { + System.out.println("Methods:"); + for (int i = 0; i < methods.length; i++) { + MethodRef ref = methods[i]; + + System.out.println(descriptorToDot(ref.getDeclClassName()) + + "." + ref.getName() + " : " + ref.getDescriptor()); + } + } + + + /** + * Prints the output in XML format. + * + * We shouldn't need to XML-escape the field/method info. + */ + static void printXml(DexData dexData) { + final String IN0 = ""; + final String IN1 = " "; + final String IN2 = " "; + final String IN3 = " "; + FieldRef[] externFieldRefs = dexData.getExternalFieldReferences(); + MethodRef[] externMethodRefs = dexData.getExternalMethodReferences(); + String prevClass = null; + + System.out.println(IN0 + ""); + + /* print fields */ + for (int i = 0; i < externFieldRefs.length; i++) { + FieldRef fref = externFieldRefs[i]; + String declClassName = fref.getDeclClassName(); + + if (prevClass != null && !prevClass.equals(declClassName)) { + System.out.println(IN1 + ""); + } + if (!declClassName.equals(prevClass)) { + String className = classNameOnly(declClassName); + String packageName = packageNameOnly(declClassName); + System.out.println(IN1 + ""); + prevClass = declClassName; + } + + System.out.println(IN2 + ""); + } + + /* print methods */ + for (int i = 0; i < externMethodRefs.length; i++) { + MethodRef mref = externMethodRefs[i]; + String declClassName = mref.getDeclClassName(); + boolean constructor; + + if (prevClass != null && !prevClass.equals(declClassName)) { + System.out.println(IN1 + ""); + } + if (!declClassName.equals(prevClass)) { + String className = classNameOnly(declClassName); + String packageName = packageNameOnly(declClassName); + System.out.println(IN1 + ""); + prevClass = declClassName; + } + + constructor = mref.getName().equals(""); + if (constructor) { + /* use class name instead of method name */ + System.out.println(IN2 + ""); + } else { + System.out.println(IN2 + ""); + } + String[] args = mref.getArgumentTypeNames(); + for (int j = 0; j < args.length; j++) { + System.out.println(IN3 + ""); + } + if (constructor) { + System.out.println(IN2 + ""); + } else { + System.out.println(IN2 + ""); + } + } + + if (prevClass != null) + System.out.println(IN1 + ""); + System.out.println(IN0 + ""); + } + + + /* + * ======================================================================= + * Utility functions + * ======================================================================= + */ + + /** + * Converts a single-character primitive type into its human-readable + * equivalent. + */ + static String primitiveTypeLabel(char typeChar) { + /* primitive type; substitute human-readable name in */ + switch (typeChar) { + case 'B': return "byte"; + case 'C': return "char"; + case 'D': return "double"; + case 'F': return "float"; + case 'I': return "int"; + case 'J': return "long"; + case 'S': return "short"; + case 'V': return "void"; + case 'Z': return "boolean"; + default: + /* huh? */ + System.err.println("Unexpected class char " + typeChar); + assert false; + return "UNKNOWN"; + } + } + + /** + * Converts a type descriptor to human-readable "dotted" form. For + * example, "Ljava/lang/String;" becomes "java.lang.String", and + * "[I" becomes "int[]. + */ + static String descriptorToDot(String descr) { + int targetLen = descr.length(); + int offset = 0; + int arrayDepth = 0; + + /* strip leading [s; will be added to end */ + while (targetLen > 1 && descr.charAt(offset) == '[') { + offset++; + targetLen--; + } + arrayDepth = offset; + + if (targetLen == 1) { + descr = primitiveTypeLabel(descr.charAt(offset)); + offset = 0; + targetLen = descr.length(); + } else { + /* account for leading 'L' and trailing ';' */ + if (targetLen >= 2 && descr.charAt(offset) == 'L' && + descr.charAt(offset+targetLen-1) == ';') + { + targetLen -= 2; /* two fewer chars to copy */ + offset++; /* skip the 'L' */ + } + } + + char[] buf = new char[targetLen + arrayDepth * 2]; + + /* copy class name over */ + int i; + for (i = 0; i < targetLen; i++) { + char ch = descr.charAt(offset + i); + buf[i] = (ch == '/') ? '.' : ch; + } + + /* add the appopriate number of brackets for arrays */ + while (arrayDepth-- > 0) { + buf[i++] = '['; + buf[i++] = ']'; + } + assert i == buf.length; + + return new String(buf); + } + + /** + * Extracts the class name from a type descriptor. + */ + static String classNameOnly(String typeName) { + String dotted = descriptorToDot(typeName); + + int start = dotted.lastIndexOf("."); + if (start < 0) { + return dotted; + } else { + return dotted.substring(start+1); + } + } + + /** + * Extracts the package name from a type descriptor, and returns it in + * dotted form. + */ + static String packageNameOnly(String typeName) { + String dotted = descriptorToDot(typeName); + + int end = dotted.lastIndexOf("."); + if (end < 0) { + /* lives in default package */ + return ""; + } else { + return dotted.substring(0, end); + } + } +} + diff --git a/tools/dexdeps/src/com/android/dexdeps/UsageException.java b/tools/dexdeps/src/com/android/dexdeps/UsageException.java new file mode 100644 index 000000000..f9f971b78 --- /dev/null +++ b/tools/dexdeps/src/com/android/dexdeps/UsageException.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.dexdeps; + +/** + * Tells the main entry point to show the usage information and bail. + */ +public class UsageException extends RuntimeException { +} + diff --git a/tools/dmtracedump/TraceDump.c b/tools/dmtracedump/TraceDump.c index ae56d4d8f..2308148cd 100644 --- a/tools/dmtracedump/TraceDump.c +++ b/tools/dmtracedump/TraceDump.c @@ -14,6 +14,7 @@ ** See the License for the specific language governing permissions and ** limitations under the License. */ + /* * Process dmtrace output. * @@ -127,6 +128,7 @@ typedef struct DataHeader { typedef struct ThreadEntry { int threadId; const char* threadName; + uint64_t elapsedTime; } ThreadEntry; struct MethodEntry; @@ -178,7 +180,7 @@ typedef struct MethodEntry { * The parsed contents of the key file. */ typedef struct DataKeys { - char* fileData; /* contents of the entire file */ + char* fileData; /* contents of the entire file */ long fileLen; int numThreads; ThreadEntry* threads; @@ -396,6 +398,37 @@ int compareElapsedInclusive(const void *a, const void *b) { return result; } +/* + * This comparison function is called from qsort() to sort + * threads into decreasing order of elapsed time. + */ +int compareElapsed(const void *a, const void *b) { + const ThreadEntry *threadA, *threadB; + uint64_t elapsed1, elapsed2; + int result = 0; + + threadA = (ThreadEntry const *)a; + threadB = (ThreadEntry const *)b; + elapsed1 = threadA->elapsedTime; + elapsed2 = threadB->elapsedTime; + if (elapsed1 < elapsed2) + return 1; + if (elapsed1 > elapsed2) + return -1; + + /* If the elapsed times of two threads are equal, then sort them + * by thread id. + */ + int idA = threadA->threadId; + int idB = threadB->threadId; + if (idA < idB) + result = -1; + if (idA > idB) + result = 1; + + return result; +} + /* * This comparison function is called from qsort() to sort * TimedMethods into decreasing order of inclusive elapsed time. @@ -1078,7 +1111,7 @@ int parseDataHeader(FILE *fp, DataHeader* pHeader) } /* - * Look up a method by it's method ID. + * Look up a method by its method ID (using binary search). * * Returns NULL if no matching method was found. */ @@ -1096,7 +1129,7 @@ MethodEntry* lookupMethod(DataKeys* pKeys, unsigned int methodId) id = pKeys->methods[mid].methodId; if (id == methodId) /* match */ return &pKeys->methods[mid]; - else if (id < methodId) /* too low */ + else if (id < methodId) /* too low */ lo = mid + 1; else /* too high */ hi = mid - 1; @@ -1490,6 +1523,7 @@ void outputTableOfContents() printf("\n\n"); @@ -1500,6 +1534,7 @@ void outputNavigationBar() printf("[Top]\n"); printf("[Exclusive]\n"); printf("[Inclusive]\n"); + printf("[Thread]\n"); printf("[Class]\n"); printf("[Method]\n"); printf("

                        \n"); @@ -1765,6 +1800,65 @@ void printInclusiveProfile(MethodEntry **pMethods, int numMethods, } } +void printThreadProfile(ThreadEntry *pThreads, int numThreads, uint64_t sumThreadTime) +{ + int ii; + ThreadEntry thread; + double total, per, sum_per; + uint64_t sum; + char threadBuf[HTML_BUFSIZE]; + char anchor_buf[80]; + char *anchor_close = ""; + + total = sumThreadTime; + anchor_buf[0] = 0; + if (gOptions.outputHtml) { + anchor_close = ""; + printf("\n"); + printf("
                        \n"); + outputNavigationBar(); + } else { + printf("\n%s\n", profileSeparator); + } + + /* Sort the threads into decreasing order of elapsed time. */ + qsort(pThreads, numThreads, sizeof(ThreadEntry), compareElapsed); + + printf("\nElapsed times for each thread, sorted by elapsed time.\n\n"); + + if (gOptions.outputHtml) { + printf("

                        \n
                        \n");
                        +    }
                        +
                        +    printf("    Usecs   self %%  sum %% tid   ThreadName\n");
                        +    sum = 0;
                        +
                        +    for (ii = 0; ii < numThreads; ++ii) {
                        +        int threadId;
                        +        char *threadName;
                        +        uint64_t time;
                        +
                        +        thread = pThreads[ii];
                        +
                        +        threadId = thread.threadId;
                        +        threadName = (char*)(thread.threadName);
                        +        time = thread.elapsedTime;
                        +
                        +        sum += time;
                        +        per = 100.0 * time / total;
                        +        sum_per = 100.0 * sum / total;
                        +
                        +        if (gOptions.outputHtml) {
                        +	    threadName = htmlEscape(threadName, threadBuf, HTML_BUFSIZE);
                        +        }
                        +	printf("%9llu  %6.2f %6.2f  %3d   %s\n", time, per, sum_per, threadId, threadName);
                        +    }
                        +
                        +    if (gOptions.outputHtml)
                        +        printf("
                        \n"); + +} + void createClassList(TraceData* traceData, MethodEntry **pMethods, int numMethods) { int ii; @@ -2416,16 +2510,25 @@ DataKeys* parseDataKeys(TraceData* traceData, const char* traceFileName, uint64_ */ CallStack *pStack; int threadId; + uint64_t elapsedTime = 0; uint64_t sumThreadTime = 0; for (threadId = 0; threadId < MAX_THREADS; ++threadId) { + pStack = traceData->stacks[threadId]; /* If this thread never existed, then continue with next thread */ if (pStack == NULL) continue; - /* Also, add up the time taken by all of the threads */ - sumThreadTime += pStack->lastEventTime - pStack->threadStartTime; + /* Calculate time spent in thread, and add it to total time */ + elapsedTime = pStack->lastEventTime - pStack->threadStartTime; + sumThreadTime += elapsedTime; + + /* Save the per-thread elapsed time in the DataKeys struct */ + for (ii = 0; ii < dataKeys->numThreads; ++ii) { + if (dataKeys->threads[ii].threadId == threadId) + dataKeys->threads[ii].elapsedTime = elapsedTime; + } for (ii = 0; ii < pStack->top; ++ii) { if (ii == 0) @@ -2479,7 +2582,8 @@ MethodEntry** parseMethodEntries(DataKeys* dataKeys) /* * Produce a function profile from the following methods */ -void profileTrace(TraceData* traceData, MethodEntry **pMethods, int numMethods, uint64_t sumThreadTime) +void profileTrace(TraceData* traceData, MethodEntry **pMethods, int numMethods, uint64_t sumThreadTime, + ThreadEntry *pThreads, int numThreads) { /* Print the html header, if necessary */ if (gOptions.outputHtml) { @@ -2490,6 +2594,8 @@ void profileTrace(TraceData* traceData, MethodEntry **pMethods, int numMethods, printExclusiveProfile(pMethods, numMethods, sumThreadTime); printInclusiveProfile(pMethods, numMethods, sumThreadTime); + printThreadProfile(pThreads, numThreads, sumThreadTime); + createClassList(traceData, pMethods, numMethods); printClassProfiles(traceData, sumThreadTime); @@ -2844,7 +2950,7 @@ int main(int argc, char** argv) if (gOptions.threshold < 0 || 100 <= gOptions.threshold) { gOptions.threshold = 20; } - + if (gOptions.dump) { dumpTrace(); return 0; @@ -2870,7 +2976,8 @@ int main(int argc, char** argv) freeDataKeys(d2); } else { MethodEntry** methods = parseMethodEntries(dataKeys); - profileTrace(&data1, methods, dataKeys->numMethods, sumThreadTime); + profileTrace(&data1, methods, dataKeys->numMethods, sumThreadTime, + dataKeys->threads, dataKeys->numThreads); if (gOptions.graphFileName != NULL) { createInclusiveProfileGraphNew(dataKeys); } diff --git a/tools/gclog.py b/tools/gclog.py new file mode 100755 index 000000000..4d5b7042f --- /dev/null +++ b/tools/gclog.py @@ -0,0 +1,235 @@ +#!/usr/bin/env python +# +# Copyright (C) 2009 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Parse event log output, looking for GC events. Format them for human +# consumption. +# +# ALL OUTPUT VALUES ARE APPROXIMATE. The event log data format uses a +# 12-bit floating-point representation, which means there aren't enough +# bits to accurately represent anything but small integers. Larger +# values will be rounded off. +# +# The data is generated by dalvik/vm/alloc/HeapDebug.c. +# + +import getopt +import sys +import os +import re +import time + +DEBUG = False # DEBUG is a global variable + + +def unfloat12(f12): + """Unpack a float12 value""" + if f12 < 0: + raise DataParseError, "bad float12 value %s" % f12 + return (f12 & 0x1ff) << ((f12 >> 9) * 4) + + +def parseGlobalInfo(value): + """Parse event0 (global info)""" + value = int(value) + + # Global information: + # + # [63 ] Must be zero + # [62-24] ASCII process identifier + # [23-12] GC time in ms + # [11- 0] Bytes freed + id = (value >> 24) & 0xffffffffff + gctime = unfloat12((value >> 12) & 0xfff) + bytes_freed = unfloat12(value & 0xfff) + + idstr = "%c%c%c%c%c" % ( \ + (id >> 32) & 0xff, \ + (id >> 24) & 0xff, \ + (id >> 16) & 0xff, \ + (id >> 8) & 0xff, \ + id & 0xff ) + + return ( idstr, gctime, bytes_freed ) + + +def parseAggHeapStats(value): + """Parse event1 (aggregated heap stats)""" + value = int(value) + + # Aggregated heap stats: + # + # [63-62] 10 + # [61-60] Reserved; must be zero + # [59-48] Objects freed + # [47-36] Actual size (current footprint) + # [35-24] Allowed size (current hard max) + # [23-12] Objects allocated + # [11- 0] Bytes allocated + freed = unfloat12((value >> 48) & 0xfff) + footprint = unfloat12((value >> 36) & 0xfff) + allowed = unfloat12((value >> 24) & 0xfff) + objs = unfloat12((value >> 12) & 0xfff) + bytes = unfloat12(value & 0xfff) + + return ( freed, footprint, allowed, objs, bytes ) + + +def parseZygoteStats(value): + """Parse event2 (zygote heap stats)""" + value = int(value) + + # Zygote heap stats (except for the soft limit, which belongs to the + # active heap): + # + # [63-62] 11 + # [61-60] Reserved; must be zero + # [59-48] Soft Limit (for the active heap) + # [47-36] Actual size (current footprint) + # [35-24] Allowed size (current hard max) + # [23-12] Objects allocated + # [11- 0] Bytes allocated + soft_limit = unfloat12((value >> 48) & 0xfff) + actual = unfloat12((value >> 36) & 0xfff) + allowed = unfloat12((value >> 24) & 0xfff) + objs = unfloat12((value >> 12) & 0xfff) + bytes = unfloat12(value & 0xfff) + + return ( soft_limit, actual, allowed, objs, bytes ) + + +def parseExternalStats(value): + """Parse event3 (external allocation stats)""" + value = int(value) + + # Report the current external allocation stats and the native heap + # summary. + # + # [63-48] Reserved; must be zero (TODO: put new data in these slots) + # [47-36] dlmalloc_footprint + # [35-24] mallinfo: total allocated space + # [23-12] External byte limit + # [11- 0] External bytes allocated + footprint = unfloat12((value >> 36) & 0xfff) # currently disabled + total = unfloat12((value >> 24) & 0xfff) # currently disabled + limit = unfloat12((value >> 12) & 0xfff) + bytes = unfloat12(value & 0xfff) + + return ( footprint, total, limit, bytes ) + + +def handleGcInfo(procFilter, timestamp, pid, values): + """Handle a single dvm_gc_info event""" + + pid = int(pid) + + global_info = parseGlobalInfo(values[0]) + + if len(procFilter) > 0: + if global_info[0] != procFilter: + return + + heap_stats = parseAggHeapStats(values[1]) + zygote = parseZygoteStats(values[2]) + external = parseExternalStats(values[3]) + + print "%s %s(%d) softlim=%dKB, extlim=%dKB, extalloc=%dKB" % \ + (timestamp, global_info[0], pid, zygote[0]/1024, external[2]/1024, external[3]/1024) + + if DEBUG: + # print "RAW: %s %s (%s,%s,%s,%s)" % \ + # (timestamp, pid, values[0], values[1], values[2], values[3]) + + print "+ id=\"%s\" time=%d freed=%d" % (global_info[0], global_info[1], global_info[2]) + print "+ freed=%d foot=%d allow=%d objs=%d bytes=%d" % \ + (heap_stats[0], heap_stats[1], heap_stats[2], heap_stats[3], heap_stats[4]) + print "+ soft=%d act=%d allow=%d objs=%d bytes=%d" % \ + (zygote[0], zygote[1], zygote[2], zygote[3], zygote[4]) + print "+ foot=%d total=%d limit=%d alloc=%d" % \ + (external[0], external[1], external[2], external[3]) + + print " freed %d objects / %d bytes in %dms" % \ + (heap_stats[0], global_info[2], global_info[1]) + + +def filterInput(logPipe, processFilter): + """Loop until EOF, pulling out GC events""" + + # 04-29 20:31:00.334 I/dvm_gc_info( 69): [8320808730292729543,-8916699241518090181,-4006371297196337158,8165229] + gc_info_re = re.compile(r""" + (\d+-\d+\ \d+:\d+:\d+)\.\d+ # extract the date (#1), ignoring ms + .* # filler, usually " I/" + dvm_gc_info # only interested in GC info lines + \(\s*(\d+)\) # extract the pid (#2) + :\ \[ # filler + ([0-9-]+),([0-9-]+),([0-9-]+),([0-9-]+) # four values, may be negative + \].* # junk to end of line + """, re.VERBOSE) + + while True: + line = logPipe.readline() + if not line: + print "EOF hit" + return + + match = gc_info_re.match(line) + if not match: + #print "no match on %s" % line.strip() + continue + else: + handleGcInfo(processFilter, match.group(1), match.group(2), ( match.group(3), \ + match.group(4), match.group(5), match.group(6) ) ) + +def PrintUsage(): + print "usage: %s [-p procFilter] [-d]" % sys.argv[0] + + +def start(): + """Entry point""" + + global DEBUG + + procFilter = "" + + opts, args = getopt.getopt(sys.argv[1:], "hdp:") + + for opt, val in opts: + if opt == "-h": + PrintUsage() + sys.exit(2) + elif opt == "-p": + procFilter = val + elif opt == "-d": + DEBUG = True + + print "procfilter = %s" % procFilter + print "DEBUG = %s" % DEBUG + + # launch a logcat and read from it + command = 'adb logcat -v time -b events' + logPipe = os.popen(command) + + + try: + filterInput(logPipe, procFilter) + except KeyboardInterrupt, err: + print "Stopping on keyboard interrupt." + + logPipe.close() + + +start() + diff --git a/vm/Android.mk b/vm/Android.mk index f3eed3f36..5c276aaec 100644 --- a/vm/Android.mk +++ b/vm/Android.mk @@ -12,262 +12,71 @@ # See the License for the specific language governing permissions and # limitations under the License. -# -# Android.mk for Dalvik VM. If you enable or disable optional features here, -# rebuild the VM with "make clean-libdvm && make -j4 libdvm". -# -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - # -# Compiler defines. +# Android.mk for Dalvik VM. # -LOCAL_CFLAGS += -fstrict-aliasing -Wstrict-aliasing=2 -fno-align-jumps - +# This makefile builds both for host and target, and so the very large +# swath of common definitions are factored out into a separate file to +# minimize duplication. # -# Optional features. These may impact the size or performance of the VM. +# Also, if you enable or disable optional features here (or Dvm.mk), +# rebuild the VM with "make clean-libdvm && make -j4 libdvm". # -LOCAL_CFLAGS += -DWITH_PROFILER -DWITH_DEBUGGER -# 0=full cache, 1/2=reduced, 3=no cache -LOCAL_CFLAGS += -DDVM_RESOLVER_CACHE=0 +LOCAL_PATH:= $(call my-dir) -ifeq ($(WITH_DEADLOCK_PREDICTION),true) - LOCAL_CFLAGS += -DWITH_DEADLOCK_PREDICTION - WITH_MONITOR_TRACKING := true -endif -ifeq ($(WITH_MONITOR_TRACKING),true) - LOCAL_CFLAGS += -DWITH_MONITOR_TRACKING -endif +# +# Build for the target (device). +# -# Make DEBUG_DALVIK_VM default to true when building the simulator. -ifeq ($(TARGET_SIMULATOR),true) - ifeq ($(strip $(DEBUG_DALVIK_VM)),) - DEBUG_DALVIK_VM := true - endif -endif +include $(CLEAR_VARS) -ifeq ($(strip $(DEBUG_DALVIK_VM)),true) - # - # "Debug" profile: - # - debugger enabled - # - profiling enabled - # - tracked-reference verification enabled - # - allocation limits enabled - # - GDB helpers enabled - # - LOGV - # - assert() (NDEBUG is handled in the build system) - # - LOCAL_CFLAGS += -DWITH_INSTR_CHECKS -DWITH_EXTRA_OBJECT_VALIDATION - LOCAL_CFLAGS += -DWITH_TRACKREF_CHECKS - LOCAL_CFLAGS += -DWITH_ALLOC_LIMITS - #LOCAL_CFLAGS += -DCHECK_MUTEX - #LOCAL_CFLAGS += -DPROFILE_FIELD_ACCESS - LOCAL_CFLAGS += -DDVM_SHOW_EXCEPTION=3 - # add some extra stuff to make it easier to examine with GDB - LOCAL_CFLAGS += -DEASY_GDB -else # !DALVIK_VM_DEBUG - # - # "Performance" profile: - # - all development features disabled - # - compiler optimizations enabled (redundant for "release" builds) - # - (debugging and profiling still enabled) - # - #LOCAL_CFLAGS += -DNDEBUG -DLOG_NDEBUG=1 - # "-O2" is redundant for device (release) but useful for sim (debug) - #LOCAL_CFLAGS += -O2 -Winline - LOCAL_CFLAGS += -DDVM_SHOW_EXCEPTION=1 - # if you want to try with assertions on the device, add: - #LOCAL_CFLAGS += -UNDEBUG -DDEBUG=1 -DLOG_NDEBUG=1 -DWITH_DALVIK_ASSERT -endif # !DALVIK_VM_DEBUG +# Variables used in the included Dvm.mk. +dvm_os := $(TARGET_OS) +dvm_arch := $(TARGET_ARCH) +dvm_arch_variant := $(TARGET_ARCH_VARIANT) +dvm_simulator := $(TARGET_SIMULATOR) -# bug hunting: checksum and verify interpreted stack when making JNI calls -#LOCAL_CFLAGS += -DWITH_JNI_STACK_CHECK +include $(LOCAL_PATH)/Dvm.mk -LOCAL_SRC_FILES := \ - AllocTracker.c \ - AtomicCache.c \ - CheckJni.c \ - Ddm.c \ - Debugger.c \ - DvmDex.c \ - Exception.c \ - Hash.c \ - Init.c \ - InlineNative.c.arm \ - Inlines.c \ - Intern.c \ - Jni.c \ - JarFile.c \ - LinearAlloc.c \ - Misc.c.arm \ - Native.c \ - PointerSet.c \ - Profile.c \ - Properties.c \ - RawDexFile.c \ - ReferenceTable.c \ - SignalCatcher.c \ - StdioConverter.c \ - Sync.c \ - Thread.c \ - UtfString.c \ - alloc/clz.c.arm \ - alloc/Alloc.c \ - alloc/HeapBitmap.c.arm \ - alloc/HeapDebug.c \ - alloc/HeapSource.c \ - alloc/HeapTable.c \ - alloc/HeapWorker.c \ - alloc/Heap.c.arm \ - alloc/MarkSweep.c.arm \ - alloc/DdmHeap.c \ - analysis/CodeVerify.c \ - analysis/DexOptimize.c \ - analysis/DexVerify.c \ - analysis/ReduceConstants.c \ - analysis/RegisterMap.c \ - analysis/VerifySubs.c \ - interp/Interp.c.arm \ - interp/Stack.c \ - jdwp/ExpandBuf.c \ - jdwp/JdwpAdb.c \ - jdwp/JdwpConstants.c \ - jdwp/JdwpEvent.c \ - jdwp/JdwpHandler.c \ - jdwp/JdwpMain.c \ - jdwp/JdwpSocket.c \ - mterp/Mterp.c.arm \ - mterp/out/InterpC-portstd.c.arm \ - mterp/out/InterpC-portdbg.c.arm \ - native/InternalNative.c \ - native/dalvik_system_DexFile.c \ - native/dalvik_system_VMDebug.c \ - native/dalvik_system_VMRuntime.c \ - native/dalvik_system_VMStack.c \ - native/dalvik_system_Zygote.c \ - native/java_lang_Class.c \ - native/java_lang_Object.c \ - native/java_lang_Runtime.c \ - native/java_lang_String.c \ - native/java_lang_System.c \ - native/java_lang_SystemProperties.c \ - native/java_lang_Throwable.c \ - native/java_lang_VMClassLoader.c \ - native/java_lang_VMThread.c \ - native/java_lang_reflect_AccessibleObject.c \ - native/java_lang_reflect_Array.c \ - native/java_lang_reflect_Constructor.c \ - native/java_lang_reflect_Field.c \ - native/java_lang_reflect_Method.c \ - native/java_lang_reflect_Proxy.c \ - native/java_security_AccessController.c \ - native/java_util_concurrent_atomic_AtomicLong.c \ - native/org_apache_harmony_dalvik_NativeTestTarget.c \ - native/org_apache_harmony_dalvik_ddmc_DdmServer.c \ - native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.c \ - native/sun_misc_Unsafe.c \ - oo/AccessCheck.c \ - oo/Array.c \ - oo/Class.c \ - oo/Object.c \ - oo/Resolve.c \ - oo/TypeCheck.c \ - reflect/Annotation.c \ - reflect/Proxy.c \ - reflect/Reflect.c \ - test/TestHash.c +# liblog and libcutils are shared for target. +LOCAL_SHARED_LIBRARIES += \ + liblog libcutils -WITH_HPROF := $(strip $(WITH_HPROF)) -ifeq ($(WITH_HPROF),) - WITH_HPROF := true -endif -ifeq ($(WITH_HPROF),true) - LOCAL_SRC_FILES += \ - hprof/Hprof.c \ - hprof/HprofClass.c \ - hprof/HprofHeap.c \ - hprof/HprofOutput.c \ - hprof/HprofString.c - LOCAL_CFLAGS += -DWITH_HPROF=1 +LOCAL_MODULE := libdvm - ifeq ($(strip $(WITH_HPROF_UNREACHABLE)),true) - LOCAL_CFLAGS += -DWITH_HPROF_UNREACHABLE=1 - endif +include $(BUILD_SHARED_LIBRARY) - ifeq ($(strip $(WITH_HPROF_STACK)),true) - LOCAL_SRC_FILES += \ - hprof/HprofStack.c \ - hprof/HprofStackFrame.c - LOCAL_CFLAGS += -DWITH_HPROF_STACK=1 - endif # WITH_HPROF_STACK -endif # WITH_HPROF -ifeq ($(strip $(DVM_TRACK_HEAP_MARKING)),true) - LOCAL_CFLAGS += -DDVM_TRACK_HEAP_MARKING=1 -endif +# +# Build for the host. +# -LOCAL_C_INCLUDES += \ - $(JNI_H_INCLUDE) \ - dalvik \ - dalvik/vm \ - external/zlib \ - $(KERNEL_HEADERS) +ifeq ($(WITH_HOST_DALVIK),true) + include $(CLEAR_VARS) -ifeq ($(TARGET_SIMULATOR),true) - LOCAL_LDLIBS += -lpthread -ldl - ifeq ($(HOST_OS),linux) - # need this for clock_gettime() in profiling - LOCAL_LDLIBS += -lrt - endif -else - LOCAL_SHARED_LIBRARIES += libdl -endif + # Variables used in the included Dvm.mk. + dvm_os := $(HOST_OS) + dvm_arch := $(HOST_ARCH) + dvm_arch_variant := $(HOST_ARCH_VARIANT) + dvm_simulator := false -ifeq ($(TARGET_ARCH),arm) - LOCAL_SRC_FILES += \ - arch/arm/CallOldABI.S \ - arch/arm/CallEABI.S \ - arch/arm/HintsEABI.c - # TODO: select sources for ARMv4 vs. ARMv5TE - LOCAL_SRC_FILES += \ - mterp/out/InterpC-armv5te.c.arm \ - mterp/out/InterpAsm-armv5te.S - LOCAL_SHARED_LIBRARIES += libdl -else - ifeq ($(TARGET_ARCH),x86) - LOCAL_SRC_FILES += \ - arch/x86/Call386ABI.S \ - arch/x86/Hints386ABI.c - LOCAL_SRC_FILES += \ - mterp/out/InterpC-x86.c \ - mterp/out/InterpAsm-x86.S - else - # unknown architecture, try to use FFI - LOCAL_C_INCLUDES += external/libffi/$(TARGET_OS)-$(TARGET_ARCH) - LOCAL_SRC_FILES += \ - arch/generic/Call.c \ - arch/generic/Hints.c - LOCAL_SHARED_LIBRARIES += libffi - - LOCAL_SRC_FILES += \ - mterp/out/InterpC-allstubs.c \ - mterp/out/InterpAsm-allstubs.S - endif -endif + include $(LOCAL_PATH)/Dvm.mk + # liblog and libcutils are static for host. + LOCAL_STATIC_LIBRARIES += \ + liblog libcutils -LOCAL_MODULE := libdvm + # libffi is called libffi-host on the host. Similarly libnativehelper. + LOCAL_SHARED_LIBRARIES := \ + $(patsubst libffi,libffi-host,$(LOCAL_SHARED_LIBRARIES)) + LOCAL_SHARED_LIBRARIES := \ + $(patsubst libnativehelper,libnativehelper-host,$(LOCAL_SHARED_LIBRARIES)) -LOCAL_SHARED_LIBRARIES += \ - liblog \ - libcutils \ - libnativehelper \ - libz + LOCAL_MODULE := libdvm-host -LOCAL_STATIC_LIBRARIES += \ - libdex + include $(BUILD_HOST_SHARED_LIBRARY) -include $(BUILD_SHARED_LIBRARY) +endif diff --git a/vm/CheckJni.c b/vm/CheckJni.c index b17d0deb6..bc905271b 100644 --- a/vm/CheckJni.c +++ b/vm/CheckJni.c @@ -35,13 +35,155 @@ #include +static void abortMaybe(void); // fwd + + +/* + * =========================================================================== + * JNI call bridge wrapper + * =========================================================================== + */ + +/* + * Check the result of a native method call that returns an object reference. + * + * The primary goal here is to verify that native code is returning the + * correct type of object. If it's declared to return a String but actually + * returns a byte array, things will fail in strange ways later on. + * + * This can be a fairly expensive operation, since we have to look up the + * return type class by name in method->clazz' class loader. We take a + * shortcut here and allow the call to succeed if the descriptor strings + * match. This will allow some false-positives when a class is redefined + * by a class loader, but that's rare enough that it doesn't seem worth + * testing for. + * + * At this point, pResult->l has already been converted to an object pointer. + */ +static void checkCallCommon(const u4* args, JValue* pResult, + const Method* method, Thread* self) +{ + assert(pResult->l != NULL); + Object* resultObj = (Object*) pResult->l; + ClassObject* objClazz = resultObj->clazz; + + /* + * Make sure that pResult->l is an instance of the type this + * method was expected to return. + */ + const char* declType = dexProtoGetReturnType(&method->prototype); + const char* objType = objClazz->descriptor; + if (strcmp(declType, objType) == 0) { + /* names match; ignore class loader issues and allow it */ + LOGV("Check %s.%s: %s io %s (FAST-OK)\n", + method->clazz->descriptor, method->name, objType, declType); + } else { + /* + * Names didn't match. We need to resolve declType in the context + * of method->clazz->classLoader, and compare the class objects + * for equality. + * + * Since we're returning an instance of declType, it's safe to + * assume that it has been loaded and initialized (or, for the case + * of an array, generated), so we can just look for it in the + * loaded-classes list. + */ + ClassObject* declClazz; + + declClazz = dvmLookupClass(declType, method->clazz->classLoader, false); + if (declClazz == NULL) { + LOGW("JNI WARNING: method declared to return '%s' returned '%s'\n", + declType, objType); + LOGW(" failed in %s.%s ('%s' not found)\n", + method->clazz->descriptor, method->name, declType); + abortMaybe(); + return; + } + if (!dvmInstanceof(objClazz, declClazz)) { + LOGW("JNI WARNING: method declared to return '%s' returned '%s'\n", + declType, objType); + LOGW(" failed in %s.%s\n", + method->clazz->descriptor, method->name); + abortMaybe(); + return; + } else { + LOGV("Check %s.%s: %s io %s (SLOW-OK)\n", + method->clazz->descriptor, method->name, objType, declType); + } + } +} + +/* + * Determine if we need to check the return type coming out of the call. + * + * (We don't do this at the top of checkCallCommon() because this is on + * the critical path for native method calls.) + */ +static inline bool callNeedsCheck(const u4* args, JValue* pResult, + const Method* method, Thread* self) +{ + return (method->shorty[0] == 'L' && !dvmCheckException(self) && + pResult->l != NULL); +} + +/* + * Check a call into native code. + */ +void dvmCheckCallJNIMethod_general(const u4* args, JValue* pResult, + const Method* method, Thread* self) +{ + dvmCallJNIMethod_general(args, pResult, method, self); + if (callNeedsCheck(args, pResult, method, self)) + checkCallCommon(args, pResult, method, self); +} + +/* + * Check a synchronized call into native code. + */ +void dvmCheckCallJNIMethod_synchronized(const u4* args, JValue* pResult, + const Method* method, Thread* self) +{ + dvmCallJNIMethod_synchronized(args, pResult, method, self); + if (callNeedsCheck(args, pResult, method, self)) + checkCallCommon(args, pResult, method, self); +} + +/* + * Check a virtual call with no reference arguments (other than "this"). + */ +void dvmCheckCallJNIMethod_virtualNoRef(const u4* args, JValue* pResult, + const Method* method, Thread* self) +{ + dvmCallJNIMethod_virtualNoRef(args, pResult, method, self); + if (callNeedsCheck(args, pResult, method, self)) + checkCallCommon(args, pResult, method, self); +} + +/* + * Check a static call with no reference arguments (other than "clazz"). + */ +void dvmCheckCallJNIMethod_staticNoRef(const u4* args, JValue* pResult, + const Method* method, Thread* self) +{ + dvmCallJNIMethod_staticNoRef(args, pResult, method, self); + if (callNeedsCheck(args, pResult, method, self)) + checkCallCommon(args, pResult, method, self); +} + + +/* + * =========================================================================== + * JNI function helpers + * =========================================================================== + */ + #define JNI_ENTER() dvmChangeStatus(NULL, THREAD_RUNNING) #define JNI_EXIT() dvmChangeStatus(NULL, THREAD_NATIVE) #define BASE_ENV(_env) (((JNIEnvExt*)_env)->baseFuncTable) #define BASE_VM(_vm) (((JavaVMExt*)_vm)->baseFuncTable) -#define kRedundantDirectBufferTest true +#define kRedundantDirectBufferTest false /* * Flags passed into checkThread(). @@ -84,8 +226,8 @@ #define CHECK_VMEXIT(_vm, _hasmeth) \ do { JNI_TRACE(false, _hasmeth); } while(false) -#define CHECK_FIELD_TYPE(_obj, _fieldid, _prim, _isstatic) \ - checkFieldType(_obj, _fieldid, _prim, _isstatic, __FUNCTION__) +#define CHECK_FIELD_TYPE(_env, _obj, _fieldid, _prim, _isstatic) \ + checkFieldType(_env, _obj, _fieldid, _prim, _isstatic, __FUNCTION__) #define CHECK_INST_FIELD_ID(_env, _obj, _fieldid) \ checkInstanceFieldID(_env, _obj, _fieldid, __FUNCTION__) #define CHECK_CLASS(_env, _clazz) \ @@ -145,7 +287,7 @@ static void showLocation(const Method* meth, const char* func) /* * Abort if we are configured to bail out on JNI warnings. */ -static inline void abortMaybe() +static void abortMaybe(void) { JavaVMExt* vm = (JavaVMExt*) gDvm.vmList; if (vm->warnError) { @@ -186,7 +328,8 @@ static void checkThread(JNIEnv* env, int flags, const char* func) */ if (threadEnv == NULL) { LOGE("JNI ERROR: non-VM thread making JNI calls\n"); - // don't set printWarn + // don't set printWarn -- it'll try to call showLocation() + dvmAbort(); } else if ((JNIEnvExt*) env != threadEnv) { if (dvmThreadSelf()->threadId != threadEnv->envThreadId) { LOGE("JNI: threadEnv != thread->env?\n"); @@ -202,7 +345,8 @@ static void checkThread(JNIEnv* env, int flags, const char* func) // "invalid use of JNI env ptr"); } else if (((JNIEnvExt*) env)->self != dvmThreadSelf()) { /* correct JNIEnv*; make sure the "self" pointer is correct */ - LOGE("JNI: env->self != thread-self\n"); + LOGE("JNI ERROR: env->self != thread-self (%p vs. %p)\n", + ((JNIEnvExt*) env)->self, dvmThreadSelf()); dvmAbort(); } @@ -244,12 +388,6 @@ static void checkThread(JNIEnv* env, int flags, const char* func) printException = true; } - if (false) { - Thread* self = dvmThreadSelf(); - LOGW("NOW: %d\n", - (int) dvmReferenceTableEntries(&self->internalLocalRefTable)); - } - if (printWarn) showLocation(dvmGetCurrentJNIMethod(), func); if (printException) { @@ -266,8 +404,8 @@ static void checkThread(JNIEnv* env, int flags, const char* func) * * Works for both static and instance fields. */ -static void checkFieldType(jobject obj, jfieldID fieldID, PrimitiveType prim, - bool isStatic, const char* func) +static void checkFieldType(JNIEnv* env, jobject jobj, jfieldID fieldID, + PrimitiveType prim, bool isStatic, const char* func) { static const char* primNameList[] = { "Object/Array", "boolean", "char", "float", "double", @@ -283,10 +421,11 @@ static void checkFieldType(jobject obj, jfieldID fieldID, PrimitiveType prim, } if (field->signature[0] == 'L' || field->signature[0] == '[') { + Object* obj = dvmDecodeIndirectRef(env, jobj); if (obj != NULL) { ClassObject* fieldClass = dvmFindLoadedClass(field->signature); - ClassObject* objClass = ((Object*)obj)->clazz; + ClassObject* objClass = obj->clazz; assert(fieldClass != NULL); assert(objClass != NULL); @@ -318,27 +457,32 @@ static void checkFieldType(jobject obj, jfieldID fieldID, PrimitiveType prim, } /* - * Verify that "obj" is a valid object, and that it's an object that JNI + * Verify that "jobj" is a valid object, and that it's an object that JNI * is allowed to know about. We allow NULL references. * * Switches to "running" mode before performing checks. */ -static void checkObject(JNIEnv* env, jobject obj, const char* func) +static void checkObject(JNIEnv* env, jobject jobj, const char* func) { UNUSED_PARAMETER(env); bool printWarn = false; - if (obj == NULL) + if (jobj == NULL) return; JNI_ENTER(); - if (!dvmIsValidObject(obj)) { - LOGW("JNI WARNING: native code passing in bad object %p (%s)\n", - obj, func); - printWarn = true; - } else if (dvmGetJNIRefType(obj) == JNIInvalidRefType) { - LOGW("JNI WARNING: ref %p should not be visible to native code\n", obj); + + if (dvmGetJNIRefType(env, jobj) == JNIInvalidRefType) { + LOGW("JNI WARNING: %p is not a valid JNI reference\n", jobj); printWarn = true; + } else { + Object* obj = dvmDecodeIndirectRef(env, jobj); + + if (obj == NULL || !dvmIsValidObject(obj)) { + LOGW("JNI WARNING: native code passing in bad object %p %p (%s)\n", + jobj, obj, func); + printWarn = true; + } } if (printWarn) { @@ -365,7 +509,7 @@ static void checkClass(JNIEnv* env, jclass jclazz, const char* func) JNI_ENTER(); bool printWarn = false; - ClassObject* clazz = (ClassObject*) jclazz; + ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); if (clazz == NULL) { LOGW("JNI WARNING: received null jclass\n"); @@ -391,22 +535,28 @@ static void checkClass(JNIEnv* env, jclass jclazz, const char* func) * * Since we're dealing with objects, switch to "running" mode. */ -static void checkString(JNIEnv* env, jstring str, const char* func) +static void checkString(JNIEnv* env, jstring jstr, const char* func) { JNI_ENTER(); bool printWarn = false; - Object* obj = (Object*) str; + Object* obj = dvmDecodeIndirectRef(env, jstr); if (obj == NULL) { LOGW("JNI WARNING: received null jstring (%s)\n", func); printWarn = true; } else if (obj->clazz != gDvm.classJavaLangString) { + /* + * TODO: we probably should test dvmIsValidObject first, because + * this will crash if "obj" is non-null but pointing to an invalid + * memory region. However, the "is valid" test is a little slow, + * we're doing it again over in checkObject(). + */ if (dvmIsValidObject(obj)) - LOGW("JNI WARNING: jstring points to non-string object (%s)\n", - func); + LOGW("JNI WARNING: jstring %p points to non-string object (%s)\n", + jstr, func); else - LOGW("JNI WARNING: jstring %p is bogus (%s)\n", str, func); + LOGW("JNI WARNING: jstring %p is bogus (%s)\n", jstr, func); printWarn = true; } JNI_EXIT(); @@ -414,7 +564,7 @@ static void checkString(JNIEnv* env, jstring str, const char* func) if (printWarn) abortMaybe(); else - checkObject(env, str, func); + checkObject(env, jstr, func); } /* @@ -531,21 +681,22 @@ fail: * * Since we're dealing with objects, switch to "running" mode. */ -static void checkArray(JNIEnv* env, jarray array, const char* func) +static void checkArray(JNIEnv* env, jarray jarr, const char* func) { JNI_ENTER(); bool printWarn = false; - Object* obj = (Object*) array; + Object* obj = dvmDecodeIndirectRef(env, jarr); if (obj == NULL) { LOGW("JNI WARNING: received null array (%s)\n", func); printWarn = true; } else if (obj->clazz->descriptor[0] != '[') { if (dvmIsValidObject(obj)) - LOGW("JNI WARNING: jarray points to non-array object\n"); + LOGW("JNI WARNING: jarray %p points to non-array object (%s)\n", + jarr, obj->clazz->descriptor); else - LOGW("JNI WARNING: jarray is bogus (%p)\n", array); + LOGW("JNI WARNING: jarray %p is bogus\n", jarr); printWarn = true; } @@ -554,7 +705,7 @@ static void checkArray(JNIEnv* env, jarray array, const char* func) if (printWarn) abortMaybe(); else - checkObject(env, array, func); + checkObject(env, jarr, func); } /* @@ -626,16 +777,17 @@ static void checkSig(JNIEnv* env, jmethodID methodID, char expectedSigByte, /* * Verify that this static field ID is valid for this class. */ -static void checkStaticFieldID(JNIEnv* env, jclass clazz, jfieldID fieldID) +static void checkStaticFieldID(JNIEnv* env, jclass jclazz, jfieldID fieldID) { - StaticField* base = ((ClassObject*) clazz)->sfields; - int fieldCount = ((ClassObject*) clazz)->sfieldCount; + ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); + StaticField* base = clazz->sfields; + int fieldCount = clazz->sfieldCount; if ((StaticField*) fieldID < base || (StaticField*) fieldID >= base + fieldCount) { LOGW("JNI WARNING: static fieldID %p not valid for class %s\n", - fieldID, ((ClassObject*) clazz)->descriptor); + fieldID, clazz->descriptor); LOGW(" base=%p count=%d\n", base, fieldCount); abortMaybe(); } @@ -644,18 +796,19 @@ static void checkStaticFieldID(JNIEnv* env, jclass clazz, jfieldID fieldID) /* * Verify that this instance field ID is valid for this object. */ -static void checkInstanceFieldID(JNIEnv* env, jobject obj, jfieldID fieldID, +static void checkInstanceFieldID(JNIEnv* env, jobject jobj, jfieldID fieldID, const char* func) { JNI_ENTER(); - if (obj == NULL) { + if (jobj == NULL) { LOGW("JNI WARNING: invalid null object (%s)\n", func); abortMaybe(); goto bail; } - ClassObject* clazz = ((Object*)obj)->clazz; + Object* obj = dvmDecodeIndirectRef(env, jobj); + ClassObject* clazz = obj->clazz; /* * Check this class and all of its superclasses for a matching field. @@ -672,7 +825,7 @@ static void checkInstanceFieldID(JNIEnv* env, jobject obj, jfieldID fieldID, } LOGW("JNI WARNING: inst fieldID %p not valid for class %s\n", - fieldID, ((Object*)obj)->clazz->descriptor); + fieldID, obj->clazz->descriptor); abortMaybe(); bail: @@ -845,7 +998,7 @@ static void* freeGuardedCopy(void* dataBuf) void* originalPtr = (void*) pExtra->originalPtr; size_t len = pExtra->originalLen; - memset(dataBuf, len, 0xdd); + memset(dataBuf, 0xdd, len); free(fullBuf); return originalPtr; } @@ -892,13 +1045,15 @@ static int dvmPrimitiveTypeWidth(PrimitiveType primType) * Create a guarded copy of a primitive array. Modifications to the copied * data are allowed. Returns a pointer to the copied data. */ -static void* createGuardedPACopy(const ArrayObject* array, jboolean* isCopy) +static void* createGuardedPACopy(JNIEnv* env, const jarray jarr, + jboolean* isCopy) { - PrimitiveType primType = array->obj.clazz->elementClass->primitiveType; - int len = array->length * dvmPrimitiveTypeWidth(primType); + ArrayObject* arrObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr); + PrimitiveType primType = arrObj->obj.clazz->elementClass->primitiveType; + int len = arrObj->length * dvmPrimitiveTypeWidth(primType); void* result; - result = createGuardedCopy(array->contents, len, true); + result = createGuardedCopy(arrObj->contents, len, true); if (isCopy != NULL) *isCopy = JNI_TRUE; @@ -910,9 +1065,11 @@ static void* createGuardedPACopy(const ArrayObject* array, jboolean* isCopy) * Perform the array "release" operation, which may or may not copy data * back into the VM, and may or may not release the underlying storage. */ -static void* releaseGuardedPACopy(ArrayObject* array, void* dataBuf, int mode) +static void* releaseGuardedPACopy(JNIEnv* env, jarray jarr, void* dataBuf, + int mode) { - PrimitiveType primType = array->obj.clazz->elementClass->primitiveType; + ArrayObject* arrObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr); + PrimitiveType primType = arrObj->obj.clazz->elementClass->primitiveType; //int len = array->length * dvmPrimitiveTypeWidth(primType); bool release, copyBack; u1* result; @@ -943,7 +1100,7 @@ static void* releaseGuardedPACopy(ArrayObject* array, void* dataBuf, int mode) if (copyBack) { size_t len = getGuardedCopyOriginalLen(dataBuf); - memcpy(array->contents, dataBuf, len); + memcpy(arrObj->contents, dataBuf, len); } if (release) { @@ -1143,11 +1300,22 @@ static jobject Check_NewGlobalRef(JNIEnv* env, jobject obj) return result; } -static void Check_DeleteGlobalRef(JNIEnv* env, jobject localRef) +static void Check_DeleteGlobalRef(JNIEnv* env, jobject globalRef) { CHECK_ENTER(env, kFlag_Default | kFlag_ExcepOkay); - CHECK_OBJECT(env, localRef); - BASE_ENV(env)->DeleteGlobalRef(env, localRef); + CHECK_OBJECT(env, globalRef); +#ifdef USE_INDIRECT_REF + if (globalRef != NULL && + dvmGetJNIRefType(env, globalRef) != JNIGlobalRefType) + { + LOGW("JNI WARNING: DeleteGlobalRef on non-global %p (type=%d)\n", + globalRef, dvmGetJNIRefType(env, globalRef)); + abortMaybe(); + } else +#endif + { + BASE_ENV(env)->DeleteGlobalRef(env, globalRef); + } CHECK_EXIT(env); } @@ -1161,11 +1329,22 @@ static jobject Check_NewLocalRef(JNIEnv* env, jobject ref) return result; } -static void Check_DeleteLocalRef(JNIEnv* env, jobject globalRef) +static void Check_DeleteLocalRef(JNIEnv* env, jobject localRef) { CHECK_ENTER(env, kFlag_Default | kFlag_ExcepOkay); - CHECK_OBJECT(env, globalRef); - BASE_ENV(env)->DeleteLocalRef(env, globalRef); + CHECK_OBJECT(env, localRef); +#ifdef USE_INDIRECT_REF + if (localRef != NULL && + dvmGetJNIRefType(env, localRef) != JNILocalRefType) + { + LOGW("JNI WARNING: DeleteLocalRef on non-local %p (type=%d)\n", + localRef, dvmGetJNIRefType(env, localRef)); + abortMaybe(); + } else +#endif + { + BASE_ENV(env)->DeleteLocalRef(env, localRef); + } CHECK_EXIT(env); } @@ -1308,7 +1487,7 @@ static jfieldID Check_GetStaticFieldID(JNIEnv* env, jclass clazz, return result; } -#define GET_STATIC_TYPE_FIELD(_ctype, _jname, _isref) \ +#define GET_STATIC_TYPE_FIELD(_ctype, _jname) \ static _ctype Check_GetStatic##_jname##Field(JNIEnv* env, jclass clazz, \ jfieldID fieldID) \ { \ @@ -1321,15 +1500,15 @@ static jfieldID Check_GetStaticFieldID(JNIEnv* env, jclass clazz, CHECK_EXIT(env); \ return result; \ } -GET_STATIC_TYPE_FIELD(jobject, Object, true); -GET_STATIC_TYPE_FIELD(jboolean, Boolean, false); -GET_STATIC_TYPE_FIELD(jbyte, Byte, false); -GET_STATIC_TYPE_FIELD(jchar, Char, false); -GET_STATIC_TYPE_FIELD(jshort, Short, false); -GET_STATIC_TYPE_FIELD(jint, Int, false); -GET_STATIC_TYPE_FIELD(jlong, Long, false); -GET_STATIC_TYPE_FIELD(jfloat, Float, false); -GET_STATIC_TYPE_FIELD(jdouble, Double, false); +GET_STATIC_TYPE_FIELD(jobject, Object); +GET_STATIC_TYPE_FIELD(jboolean, Boolean); +GET_STATIC_TYPE_FIELD(jbyte, Byte); +GET_STATIC_TYPE_FIELD(jchar, Char); +GET_STATIC_TYPE_FIELD(jshort, Short); +GET_STATIC_TYPE_FIELD(jint, Int); +GET_STATIC_TYPE_FIELD(jlong, Long); +GET_STATIC_TYPE_FIELD(jfloat, Float); +GET_STATIC_TYPE_FIELD(jdouble, Double); #define SET_STATIC_TYPE_FIELD(_ctype, _jname, _ftype) \ static void Check_SetStatic##_jname##Field(JNIEnv* env, jclass clazz, \ @@ -1338,7 +1517,8 @@ GET_STATIC_TYPE_FIELD(jdouble, Double, false); CHECK_ENTER(env, kFlag_Default); \ CHECK_CLASS(env, clazz); \ checkStaticFieldID(env, clazz, fieldID); \ - CHECK_FIELD_TYPE((jobject)(u4)value, fieldID, _ftype, true); \ + /* "value" arg only used when type == ref */ \ + CHECK_FIELD_TYPE(env, (jobject)(u4)value, fieldID, _ftype, true); \ BASE_ENV(env)->SetStatic##_jname##Field(env, clazz, fieldID, \ value); \ CHECK_EXIT(env); \ @@ -1353,7 +1533,7 @@ SET_STATIC_TYPE_FIELD(jlong, Long, PRIM_LONG); SET_STATIC_TYPE_FIELD(jfloat, Float, PRIM_FLOAT); SET_STATIC_TYPE_FIELD(jdouble, Double, PRIM_DOUBLE); -#define GET_TYPE_FIELD(_ctype, _jname, _isref) \ +#define GET_TYPE_FIELD(_ctype, _jname) \ static _ctype Check_Get##_jname##Field(JNIEnv* env, jobject obj, \ jfieldID fieldID) \ { \ @@ -1365,15 +1545,15 @@ SET_STATIC_TYPE_FIELD(jdouble, Double, PRIM_DOUBLE); CHECK_EXIT(env); \ return result; \ } -GET_TYPE_FIELD(jobject, Object, true); -GET_TYPE_FIELD(jboolean, Boolean, false); -GET_TYPE_FIELD(jbyte, Byte, false); -GET_TYPE_FIELD(jchar, Char, false); -GET_TYPE_FIELD(jshort, Short, false); -GET_TYPE_FIELD(jint, Int, false); -GET_TYPE_FIELD(jlong, Long, false); -GET_TYPE_FIELD(jfloat, Float, false); -GET_TYPE_FIELD(jdouble, Double, false); +GET_TYPE_FIELD(jobject, Object); +GET_TYPE_FIELD(jboolean, Boolean); +GET_TYPE_FIELD(jbyte, Byte); +GET_TYPE_FIELD(jchar, Char); +GET_TYPE_FIELD(jshort, Short); +GET_TYPE_FIELD(jint, Int); +GET_TYPE_FIELD(jlong, Long); +GET_TYPE_FIELD(jfloat, Float); +GET_TYPE_FIELD(jdouble, Double); #define SET_TYPE_FIELD(_ctype, _jname, _ftype) \ static void Check_Set##_jname##Field(JNIEnv* env, jobject obj, \ @@ -1382,7 +1562,8 @@ GET_TYPE_FIELD(jdouble, Double, false); CHECK_ENTER(env, kFlag_Default); \ CHECK_OBJECT(env, obj); \ CHECK_INST_FIELD_ID(env, obj, fieldID); \ - CHECK_FIELD_TYPE((jobject)(u4) value, fieldID, _ftype, false); \ + /* "value" arg only used when type == ref */ \ + CHECK_FIELD_TYPE(env, (jobject)(u4) value, fieldID, _ftype, false); \ BASE_ENV(env)->Set##_jname##Field(env, obj, fieldID, value); \ CHECK_EXIT(env); \ } @@ -1396,8 +1577,7 @@ SET_TYPE_FIELD(jlong, Long, PRIM_LONG); SET_TYPE_FIELD(jfloat, Float, PRIM_FLOAT); SET_TYPE_FIELD(jdouble, Double, PRIM_DOUBLE); -#define CALL_VIRTUAL(_ctype, _jname, _retfail, _retdecl, _retasgn, _retok, \ - _retsig) \ +#define CALL_VIRTUAL(_ctype, _jname, _retdecl, _retasgn, _retok, _retsig) \ static _ctype Check_Call##_jname##Method(JNIEnv* env, jobject obj, \ jmethodID methodID, ...) \ { \ @@ -1437,19 +1617,19 @@ SET_TYPE_FIELD(jdouble, Double, PRIM_DOUBLE); CHECK_EXIT(env); \ return _retok; \ } -CALL_VIRTUAL(jobject, Object, NULL, Object* result, result=, result, 'L'); -CALL_VIRTUAL(jboolean, Boolean, 0, jboolean result, result=, result, 'Z'); -CALL_VIRTUAL(jbyte, Byte, 0, jbyte result, result=, result, 'B'); -CALL_VIRTUAL(jchar, Char, 0, jchar result, result=, result, 'C'); -CALL_VIRTUAL(jshort, Short, 0, jshort result, result=, result, 'S'); -CALL_VIRTUAL(jint, Int, 0, jint result, result=, result, 'I'); -CALL_VIRTUAL(jlong, Long, 0, jlong result, result=, result, 'J'); -CALL_VIRTUAL(jfloat, Float, 0.0f, jfloat result, result=, *(float*)&result, 'F'); -CALL_VIRTUAL(jdouble, Double, 0.0, jdouble result, result=, *(double*)&result, 'D'); -CALL_VIRTUAL(void, Void, , , , , 'V'); - -#define CALL_NONVIRTUAL(_ctype, _jname, _retfail, _retdecl, _retasgn, \ - _retok, _retsig) \ +CALL_VIRTUAL(jobject, Object, Object* result, result=, result, 'L'); +CALL_VIRTUAL(jboolean, Boolean, jboolean result, result=, result, 'Z'); +CALL_VIRTUAL(jbyte, Byte, jbyte result, result=, result, 'B'); +CALL_VIRTUAL(jchar, Char, jchar result, result=, result, 'C'); +CALL_VIRTUAL(jshort, Short, jshort result, result=, result, 'S'); +CALL_VIRTUAL(jint, Int, jint result, result=, result, 'I'); +CALL_VIRTUAL(jlong, Long, jlong result, result=, result, 'J'); +CALL_VIRTUAL(jfloat, Float, jfloat result, result=, result, 'F'); +CALL_VIRTUAL(jdouble, Double, jdouble result, result=, result, 'D'); +CALL_VIRTUAL(void, Void, , , , 'V'); + +#define CALL_NONVIRTUAL(_ctype, _jname, _retdecl, _retasgn, _retok, \ + _retsig) \ static _ctype Check_CallNonvirtual##_jname##Method(JNIEnv* env, \ jobject obj, jclass clazz, jmethodID methodID, ...) \ { \ @@ -1492,20 +1672,19 @@ CALL_VIRTUAL(void, Void, , , , , 'V'); CHECK_EXIT(env); \ return _retok; \ } -CALL_NONVIRTUAL(jobject, Object, NULL, Object* result, result=, (Object*)(u4)result, 'L'); -CALL_NONVIRTUAL(jboolean, Boolean, 0, jboolean result, result=, result, 'Z'); -CALL_NONVIRTUAL(jbyte, Byte, 0, jbyte result, result=, result, 'B'); -CALL_NONVIRTUAL(jchar, Char, 0, jchar result, result=, result, 'C'); -CALL_NONVIRTUAL(jshort, Short, 0, jshort result, result=, result, 'S'); -CALL_NONVIRTUAL(jint, Int, 0, jint result, result=, result, 'I'); -CALL_NONVIRTUAL(jlong, Long, 0, jlong result, result=, result, 'J'); -CALL_NONVIRTUAL(jfloat, Float, 0.0f, jfloat result, result=, *(float*)&result, 'F'); -CALL_NONVIRTUAL(jdouble, Double, 0.0, jdouble result, result=, *(double*)&result, 'D'); -CALL_NONVIRTUAL(void, Void, , , , , 'V'); - - -#define CALL_STATIC(_ctype, _jname, _retfail, _retdecl, _retasgn, _retok, \ - _retsig) \ +CALL_NONVIRTUAL(jobject, Object, Object* result, result=, result, 'L'); +CALL_NONVIRTUAL(jboolean, Boolean, jboolean result, result=, result, 'Z'); +CALL_NONVIRTUAL(jbyte, Byte, jbyte result, result=, result, 'B'); +CALL_NONVIRTUAL(jchar, Char, jchar result, result=, result, 'C'); +CALL_NONVIRTUAL(jshort, Short, jshort result, result=, result, 'S'); +CALL_NONVIRTUAL(jint, Int, jint result, result=, result, 'I'); +CALL_NONVIRTUAL(jlong, Long, jlong result, result=, result, 'J'); +CALL_NONVIRTUAL(jfloat, Float, jfloat result, result=, result, 'F'); +CALL_NONVIRTUAL(jdouble, Double, jdouble result, result=, result, 'D'); +CALL_NONVIRTUAL(void, Void, , , , 'V'); + + +#define CALL_STATIC(_ctype, _jname, _retdecl, _retasgn, _retok, _retsig) \ static _ctype Check_CallStatic##_jname##Method(JNIEnv* env, \ jclass clazz, jmethodID methodID, ...) \ { \ @@ -1545,16 +1724,16 @@ CALL_NONVIRTUAL(void, Void, , , , , 'V'); CHECK_EXIT(env); \ return _retok; \ } -CALL_STATIC(jobject, Object, NULL, Object* result, result=, (Object*)(u4)result, 'L'); -CALL_STATIC(jboolean, Boolean, 0, jboolean result, result=, result, 'Z'); -CALL_STATIC(jbyte, Byte, 0, jbyte result, result=, result, 'B'); -CALL_STATIC(jchar, Char, 0, jchar result, result=, result, 'C'); -CALL_STATIC(jshort, Short, 0, jshort result, result=, result, 'S'); -CALL_STATIC(jint, Int, 0, jint result, result=, result, 'I'); -CALL_STATIC(jlong, Long, 0, jlong result, result=, result, 'J'); -CALL_STATIC(jfloat, Float, 0.0f, jfloat result, result=, *(float*)&result, 'F'); -CALL_STATIC(jdouble, Double, 0.0, jdouble result, result=, *(double*)&result, 'D'); -CALL_STATIC(void, Void, , , , , 'V'); +CALL_STATIC(jobject, Object, Object* result, result=, result, 'L'); +CALL_STATIC(jboolean, Boolean, jboolean result, result=, result, 'Z'); +CALL_STATIC(jbyte, Byte, jbyte result, result=, result, 'B'); +CALL_STATIC(jchar, Char, jchar result, result=, result, 'C'); +CALL_STATIC(jshort, Short, jshort result, result=, result, 'S'); +CALL_STATIC(jint, Int, jint result, result=, result, 'I'); +CALL_STATIC(jlong, Long, jlong result, result=, result, 'J'); +CALL_STATIC(jfloat, Float, jfloat result, result=, result, 'F'); +CALL_STATIC(jdouble, Double, jdouble result, result=, result, 'D'); +CALL_STATIC(void, Void, , , , 'V'); static jstring Check_NewString(JNIEnv* env, const jchar* unicodeChars, jsize len) @@ -1584,6 +1763,7 @@ static const jchar* Check_GetStringChars(JNIEnv* env, jstring string, const jchar* result; result = BASE_ENV(env)->GetStringChars(env, string, isCopy); if (((JNIEnvExt*)env)->forceDataCopy && result != NULL) { + // TODO: fix for indirect int len = dvmStringLen(string) * 2; result = (const jchar*) createGuardedCopy(result, len, false); if (isCopy != NULL) @@ -1639,6 +1819,7 @@ static const char* Check_GetStringUTFChars(JNIEnv* env, jstring string, const char* result; result = BASE_ENV(env)->GetStringUTFChars(env, string, isCopy); if (((JNIEnvExt*)env)->forceDataCopy && result != NULL) { + // TODO: fix for indirect int len = dvmStringUtf8ByteLen(string) + 1; result = (const char*) createGuardedCopy(result, len, false); if (isCopy != NULL) @@ -1741,8 +1922,7 @@ NEW_PRIMITIVE_ARRAY(jdoubleArray, Double); result = BASE_ENV(env)->Get##_jname##ArrayElements(env, \ array, isCopy); \ if (((JNIEnvExt*)env)->forceDataCopy && result != NULL) { \ - result = (_ctype*) \ - createGuardedPACopy((ArrayObject*) array, isCopy); \ + result = (_ctype*) createGuardedPACopy(env, array, isCopy); \ } \ CHECK_EXIT(env); \ return result; \ @@ -1757,8 +1937,7 @@ NEW_PRIMITIVE_ARRAY(jdoubleArray, Double); CHECK_NON_NULL(env, elems); \ CHECK_RELEASE_MODE(env, mode); \ if (((JNIEnvExt*)env)->forceDataCopy) { \ - elems = (_ctype*) releaseGuardedPACopy((ArrayObject*) array, \ - elems, mode); \ + elems = (_ctype*) releaseGuardedPACopy(env, array, elems, mode);\ } \ BASE_ENV(env)->Release##_jname##ArrayElements(env, \ array, elems, mode); \ @@ -1879,7 +2058,7 @@ static void* Check_GetPrimitiveArrayCritical(JNIEnv* env, jarray array, void* result; result = BASE_ENV(env)->GetPrimitiveArrayCritical(env, array, isCopy); if (((JNIEnvExt*)env)->forceDataCopy && result != NULL) { - result = createGuardedPACopy((ArrayObject*) array, isCopy); + result = createGuardedPACopy(env, array, isCopy); } CHECK_EXIT(env); return result; @@ -1893,7 +2072,7 @@ static void Check_ReleasePrimitiveArrayCritical(JNIEnv* env, jarray array, CHECK_NON_NULL(env, carray); CHECK_RELEASE_MODE(env, mode); if (((JNIEnvExt*)env)->forceDataCopy) { - carray = releaseGuardedPACopy((ArrayObject*) array, carray, mode); + carray = releaseGuardedPACopy(env, array, carray, mode); } BASE_ENV(env)->ReleasePrimitiveArrayCritical(env, array, carray, mode); CHECK_EXIT(env); @@ -1907,6 +2086,7 @@ static const jchar* Check_GetStringCritical(JNIEnv* env, jstring string, const jchar* result; result = BASE_ENV(env)->GetStringCritical(env, string, isCopy); if (((JNIEnvExt*)env)->forceDataCopy && result != NULL) { + // TODO: fix for indirect int len = dvmStringLen(string) * 2; result = (const jchar*) createGuardedCopy(result, len, false); if (isCopy != NULL) @@ -2004,7 +2184,7 @@ static void* Check_GetDirectBufferAddress(JNIEnv* env, jobject buf) * interfaces. Note this does not guarantee that it's a direct buffer. */ if (JNI_FALSE == (*env)->IsInstanceOf(env, buf, - (jclass) gDvm.classOrgApacheHarmonyNioInternalDirectBuffer)) + gDvm.jclassOrgApacheHarmonyNioInternalDirectBuffer)) { goto bail; } @@ -2027,10 +2207,12 @@ static void* Check_GetDirectBufferAddress(JNIEnv* env, jobject buf) goto bail; } - Method* toLong = ((Object*)platformAddr)->clazz->vtable[ - gDvm.voffOrgApacheHarmonyLuniPlatformPlatformAddress_toLong]; + jclass platformAddrClass = (*env)->FindClass(env, + "org/apache/harmony/luni/platform/PlatformAddress"); + jmethodID toLongMethod = (*env)->GetMethodID(env, platformAddrClass, + "toLong", "()J"); checkResult = (void*)(u4)(*env)->CallLongMethod(env, platformAddr, - (jmethodID)toLong); + toLongMethod); bail: if (platformAddr != NULL) @@ -2425,4 +2607,3 @@ void dvmUseCheckedJniVm(JavaVMExt* pVm) pVm->baseFuncTable = pVm->funcTable; pVm->funcTable = &gCheckInvokeInterface; } - diff --git a/vm/Common.h b/vm/Common.h index 8ca522464..d0c021d50 100644 --- a/vm/Common.h +++ b/vm/Common.h @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + /* * Common defines for all Dalvik code. */ @@ -29,8 +30,8 @@ #if !defined(NDEBUG) && defined(WITH_DALVIK_ASSERT) # undef assert # define assert(x) \ - ((x) ? ((void)0) : (LOGE("ASSERT FAILED (%s:%d): " #x "\n", \ - __FILE__, __LINE__), *(int*)39=39, 0) ) + ((x) ? ((void)0) : (LOGE("ASSERT FAILED (%s:%d): %s\n", \ + __FILE__, __LINE__, #x), *(int*)39=39, 0) ) #endif @@ -96,11 +97,15 @@ typedef union JValue { } JValue; /* - * Some systems might have this in . + * The definition uses _Bool, a type known to the compiler. */ -#ifndef __bool_true_false_are_defined +#ifdef HAVE_STDBOOL_H +# include /* C99 */ +#else +# ifndef __bool_true_false_are_defined typedef enum { false=0, true=!false } bool; -#define __bool_true_false_are_defined 1 +# define __bool_true_false_are_defined 1 +# endif #endif #define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0]))) diff --git a/vm/Dalvik.h b/vm/Dalvik.h index 2c7bd7c09..054b838e0 100644 --- a/vm/Dalvik.h +++ b/vm/Dalvik.h @@ -44,6 +44,7 @@ #include "UtfString.h" #include "Intern.h" #include "ReferenceTable.h" +#include "IndirectRefTable.h" #include "AtomicCache.h" #include "Thread.h" #include "Ddm.h" @@ -67,11 +68,15 @@ #include "LinearAlloc.h" #include "analysis/DexVerify.h" #include "analysis/DexOptimize.h" +#include "analysis/RegisterMap.h" #include "Init.h" #include "libdex/OpCode.h" #include "libdex/InstrUtils.h" #include "AllocTracker.h" #include "PointerSet.h" +#if defined(WITH_JIT) +#include "compiler/Compiler.h" +#endif #include "Globals.h" #include "reflect/Reflect.h" #include "oo/TypeCheck.h" diff --git a/vm/DalvikVersion.h b/vm/DalvikVersion.h index 4926fd060..efbb39327 100644 --- a/vm/DalvikVersion.h +++ b/vm/DalvikVersion.h @@ -24,14 +24,14 @@ * The version we show to tourists. */ #define DALVIK_MAJOR_VERSION 1 -#define DALVIK_MINOR_VERSION 0 -#define DALVIK_BUG_VERSION 1 +#define DALVIK_MINOR_VERSION 1 +#define DALVIK_BUG_VERSION 0 /* * VM build number. This must change whenever something that affects the * way classes load changes, e.g. field ordering or vtable layout. Changing * this guarantees that the optimized form of the DEX file is regenerated. */ -#define DALVIK_VM_BUILD 14 +#define DALVIK_VM_BUILD 17 #endif /*_DALVIK_VERSION*/ diff --git a/vm/Debugger.c b/vm/Debugger.c index 3affa976f..4ddf25c4e 100644 --- a/vm/Debugger.c +++ b/vm/Debugger.c @@ -2814,7 +2814,7 @@ void dvmDbgExecuteMethod(DebugInvokeReq* pReq) free(desc); } - dvmCallMethodA(self, meth, pReq->obj, &pReq->resultValue, + dvmCallMethodA(self, meth, pReq->obj, false, &pReq->resultValue, (jvalue*)pReq->argArray); pReq->exceptObj = objectToObjectId(dvmGetException(self)); pReq->resultTag = resultTagFromSignature(meth); diff --git a/vm/Dvm.mk b/vm/Dvm.mk new file mode 100644 index 000000000..96f6dacb8 --- /dev/null +++ b/vm/Dvm.mk @@ -0,0 +1,331 @@ +# Copyright (C) 2008 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Common definitions for host or target builds of libdvm. +# +# If you enable or disable optional features here, +# rebuild the VM with "make clean-libdvm && make -j4 libdvm". +# + + +# +# Compiler defines. +# +LOCAL_CFLAGS += -fstrict-aliasing -Wstrict-aliasing=2 -fno-align-jumps +#LOCAL_CFLAGS += -DUSE_INDIRECT_REF + +# +# Optional features. These may impact the size or performance of the VM. +# +LOCAL_CFLAGS += -DWITH_PROFILER -DWITH_DEBUGGER + +# 0=full cache, 1/2=reduced, 3=no cache +LOCAL_CFLAGS += -DDVM_RESOLVER_CACHE=0 + +ifeq ($(WITH_DEADLOCK_PREDICTION),true) + LOCAL_CFLAGS += -DWITH_DEADLOCK_PREDICTION + WITH_MONITOR_TRACKING := true +endif +ifeq ($(WITH_MONITOR_TRACKING),true) + LOCAL_CFLAGS += -DWITH_MONITOR_TRACKING +endif + +# Make a debugging version when building the simulator (if not told +# otherwise) and when explicitly asked. +dvm_make_debug_vm := false +ifeq ($(strip $(DEBUG_DALVIK_VM)),) + ifeq ($(dvm_simulator),true) + dvm_make_debug_vm := true + endif +else + dvm_make_debug_vm := $(DEBUG_DALVIK_VM) +endif + +ifeq ($(dvm_make_debug_vm),true) + # + # "Debug" profile: + # - debugger enabled + # - profiling enabled + # - tracked-reference verification enabled + # - allocation limits enabled + # - GDB helpers enabled + # - LOGV + # - assert() + # + LOCAL_CFLAGS += -DWITH_INSTR_CHECKS + LOCAL_CFLAGS += -DWITH_EXTRA_OBJECT_VALIDATION + LOCAL_CFLAGS += -DWITH_TRACKREF_CHECKS + LOCAL_CFLAGS += -DWITH_ALLOC_LIMITS + LOCAL_CFLAGS += -DWITH_EXTRA_GC_CHECKS=1 + #LOCAL_CFLAGS += -DCHECK_MUTEX + #LOCAL_CFLAGS += -DPROFILE_FIELD_ACCESS + LOCAL_CFLAGS += -DDVM_SHOW_EXCEPTION=3 + # add some extra stuff to make it easier to examine with GDB + LOCAL_CFLAGS += -DEASY_GDB + # overall config may be for a "release" build, so reconfigure these + LOCAL_CFLAGS += -UNDEBUG -DDEBUG=1 -DLOG_NDEBUG=1 -DWITH_DALVIK_ASSERT +else # !dvm_make_debug_vm + # + # "Performance" profile: + # - all development features disabled + # - compiler optimizations enabled (redundant for "release" builds) + # - (debugging and profiling still enabled) + # + #LOCAL_CFLAGS += -DNDEBUG -DLOG_NDEBUG=1 + # "-O2" is redundant for device (release) but useful for sim (debug) + #LOCAL_CFLAGS += -O2 -Winline + #LOCAL_CFLAGS += -DWITH_EXTRA_OBJECT_VALIDATION + LOCAL_CFLAGS += -DDVM_SHOW_EXCEPTION=1 + # if you want to try with assertions on the device, add: + #LOCAL_CFLAGS += -UNDEBUG -DDEBUG=1 -DLOG_NDEBUG=1 -DWITH_DALVIK_ASSERT +endif # !dvm_make_debug_vm + +# bug hunting: checksum and verify interpreted stack when making JNI calls +#LOCAL_CFLAGS += -DWITH_JNI_STACK_CHECK + +LOCAL_SRC_FILES := \ + AllocTracker.c \ + AtomicCache.c \ + CheckJni.c \ + Ddm.c \ + Debugger.c \ + DvmDex.c \ + Exception.c \ + Hash.c \ + IndirectRefTable.c.arm \ + Init.c \ + InlineNative.c.arm \ + Inlines.c \ + Intern.c \ + Jni.c \ + JarFile.c \ + LinearAlloc.c \ + Misc.c.arm \ + Native.c \ + PointerSet.c \ + Profile.c \ + Properties.c \ + RawDexFile.c \ + ReferenceTable.c \ + SignalCatcher.c \ + StdioConverter.c \ + Sync.c \ + Thread.c \ + UtfString.c \ + alloc/clz.c.arm \ + alloc/Alloc.c \ + alloc/HeapBitmap.c.arm \ + alloc/HeapDebug.c \ + alloc/HeapSource.c \ + alloc/HeapTable.c \ + alloc/HeapWorker.c \ + alloc/Heap.c.arm \ + alloc/MarkSweep.c.arm \ + alloc/DdmHeap.c \ + analysis/CodeVerify.c \ + analysis/DexOptimize.c \ + analysis/DexVerify.c \ + analysis/ReduceConstants.c \ + analysis/RegisterMap.c \ + analysis/VerifySubs.c \ + interp/Interp.c.arm \ + interp/Stack.c \ + jdwp/ExpandBuf.c \ + jdwp/JdwpAdb.c \ + jdwp/JdwpConstants.c \ + jdwp/JdwpEvent.c \ + jdwp/JdwpHandler.c \ + jdwp/JdwpMain.c \ + jdwp/JdwpSocket.c \ + mterp/Mterp.c.arm \ + mterp/out/InterpC-portstd.c.arm \ + mterp/out/InterpC-portdbg.c.arm \ + native/InternalNative.c \ + native/dalvik_system_DexFile.c \ + native/dalvik_system_SamplingProfiler.c \ + native/dalvik_system_VMDebug.c \ + native/dalvik_system_VMRuntime.c \ + native/dalvik_system_VMStack.c \ + native/dalvik_system_Zygote.c \ + native/java_lang_Class.c \ + native/java_lang_Object.c \ + native/java_lang_Runtime.c \ + native/java_lang_String.c \ + native/java_lang_System.c \ + native/java_lang_SystemProperties.c \ + native/java_lang_Throwable.c \ + native/java_lang_VMClassLoader.c \ + native/java_lang_VMThread.c \ + native/java_lang_reflect_AccessibleObject.c \ + native/java_lang_reflect_Array.c \ + native/java_lang_reflect_Constructor.c \ + native/java_lang_reflect_Field.c \ + native/java_lang_reflect_Method.c \ + native/java_lang_reflect_Proxy.c \ + native/java_security_AccessController.c \ + native/java_util_concurrent_atomic_AtomicLong.c \ + native/org_apache_harmony_dalvik_NativeTestTarget.c \ + native/org_apache_harmony_dalvik_ddmc_DdmServer.c \ + native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.c \ + native/sun_misc_Unsafe.c \ + native/SystemThread.c \ + oo/AccessCheck.c \ + oo/Array.c \ + oo/Class.c \ + oo/Object.c \ + oo/Resolve.c \ + oo/TypeCheck.c \ + reflect/Annotation.c \ + reflect/Proxy.c \ + reflect/Reflect.c \ + test/AtomicSpeed.c \ + test/TestHash.c \ + test/TestIndirectRefTable.c + +ifeq ($(WITH_JIT_TUNING),true) + LOCAL_CFLAGS += -DWITH_JIT_TUNING + # NOTE: Turn on assertion for JIT for now + LOCAL_CFLAGS += -UNDEBUG -DDEBUG=1 -DLOG_NDEBUG=1 -DWITH_DALVIK_ASSERT +endif + +ifeq ($(WITH_JIT),true) + LOCAL_CFLAGS += -DWITH_JIT + LOCAL_SRC_FILES += \ + ../dexdump/OpCodeNames.c \ + compiler/Compiler.c \ + compiler/Frontend.c \ + compiler/Utility.c \ + compiler/IntermediateRep.c \ + interp/Jit.c +endif + +WITH_HPROF := $(strip $(WITH_HPROF)) +ifeq ($(WITH_HPROF),) + WITH_HPROF := true +endif +ifeq ($(WITH_HPROF),true) + LOCAL_SRC_FILES += \ + hprof/Hprof.c \ + hprof/HprofClass.c \ + hprof/HprofHeap.c \ + hprof/HprofOutput.c \ + hprof/HprofString.c + LOCAL_CFLAGS += -DWITH_HPROF=1 + + ifeq ($(strip $(WITH_HPROF_UNREACHABLE)),true) + LOCAL_CFLAGS += -DWITH_HPROF_UNREACHABLE=1 + endif + + ifeq ($(strip $(WITH_HPROF_STACK)),true) + LOCAL_SRC_FILES += \ + hprof/HprofStack.c \ + hprof/HprofStackFrame.c + LOCAL_CFLAGS += -DWITH_HPROF_STACK=1 + endif # WITH_HPROF_STACK +endif # WITH_HPROF + +ifeq ($(strip $(DVM_TRACK_HEAP_MARKING)),true) + LOCAL_CFLAGS += -DDVM_TRACK_HEAP_MARKING=1 +endif + +LOCAL_C_INCLUDES += \ + $(JNI_H_INCLUDE) \ + dalvik \ + dalvik/vm \ + external/zlib \ + $(KERNEL_HEADERS) + + +ifeq ($(dvm_simulator),true) + LOCAL_LDLIBS += -lpthread -ldl + ifeq ($(HOST_OS),linux) + # need this for clock_gettime() in profiling + LOCAL_LDLIBS += -lrt + endif +else + ifeq ($(dvm_os),linux) + LOCAL_SHARED_LIBRARIES += libdl + endif +endif + +MTERP_ARCH_KNOWN := false + +ifeq ($(dvm_arch),arm) + #dvm_arch_variant := armv7-a + #LOCAL_CFLAGS += -march=armv7-a -mfloat-abi=softfp -mfpu=vfp + MTERP_ARCH_KNOWN := true + # Select architecture-specific sources (armv4t, armv5te etc.) + LOCAL_SRC_FILES += \ + arch/arm/CallOldABI.S \ + arch/arm/CallEABI.S \ + arch/arm/HintsEABI.c \ + mterp/out/InterpC-$(dvm_arch_variant).c.arm \ + mterp/out/InterpAsm-$(dvm_arch_variant).S + + ifeq ($(WITH_JIT),true) + LOCAL_SRC_FILES += \ + compiler/codegen/arm/Codegen-$(dvm_arch_variant).c \ + compiler/codegen/arm/Assemble.c \ + compiler/codegen/arm/ArchUtility.c \ + compiler/codegen/arm/LocalOptimizations.c \ + compiler/codegen/arm/GlobalOptimizations.c \ + compiler/template/out/CompilerTemplateAsm-$(dvm_arch_variant).S + endif +endif + +ifeq ($(dvm_arch),x86) + ifeq ($(dvm_os),linux) + MTERP_ARCH_KNOWN := true + LOCAL_SRC_FILES += \ + arch/x86/Call386ABI.S \ + arch/x86/Hints386ABI.c \ + mterp/out/InterpC-x86.c \ + mterp/out/InterpAsm-x86.S + endif +endif + +ifeq ($(dvm_arch),sh) + MTERP_ARCH_KNOWN := true + LOCAL_SRC_FILES += \ + arch/sh/CallSH4ABI.S \ + arch/generic/Hints.c \ + mterp/out/InterpC-allstubs.c \ + mterp/out/InterpAsm-allstubs.S +endif + +ifeq ($(MTERP_ARCH_KNOWN),false) + # unknown architecture, try to use FFI + LOCAL_C_INCLUDES += external/libffi/$(dvm_os)-$(dvm_arch) + LOCAL_SHARED_LIBRARIES += libffi + + LOCAL_SRC_FILES += \ + arch/generic/Call.c \ + arch/generic/Hints.c \ + mterp/out/InterpC-allstubs.c + + # The following symbols are usually defined in the asm file, but + # since we don't have an asm file in this case, we instead just + # peg them at 0 here, and we add an #ifdef'able define for good + # measure, too. + LOCAL_CFLAGS += -DdvmAsmInstructionStart=0 -DdvmAsmInstructionEnd=0 \ + -DdvmAsmSisterStart=0 -DdvmAsmSisterEnd=0 -DDVM_NO_ASM_INTERP=1 +endif + +LOCAL_SHARED_LIBRARIES += \ + libnativehelper \ + libz + +LOCAL_STATIC_LIBRARIES += \ + libdex diff --git a/vm/DvmDex.c b/vm/DvmDex.c index b5f8d02a3..6740632b3 100644 --- a/vm/DvmDex.c +++ b/vm/DvmDex.c @@ -171,8 +171,10 @@ int dvmDexFileOpenPartial(const void* addr, int len, DvmDex** ppDvmDex) int parseFlags = kDexParseDefault; int result = -1; + /* -- file is incomplete, new checksum has not yet been calculated if (gDvm.verifyDexChecksum) parseFlags |= kDexParseVerifyChecksum; + */ pDexFile = dexFileParse(addr, len, parseFlags); if (pDexFile == NULL) { diff --git a/vm/Exception.c b/vm/Exception.c index 488164048..3a56cc33a 100644 --- a/vm/Exception.c +++ b/vm/Exception.c @@ -102,6 +102,9 @@ static bool initException(Object* exception, const char* msg, Object* cause, /* * Cache pointers to some of the exception classes we use locally. + * + * Note this is NOT called during dexopt optimization. Some of the fields + * are initialized by the verifier (dvmVerifyCodeFlow). */ bool dvmExceptionStartup(void) { @@ -377,6 +380,14 @@ static bool initException(Object* exception, const char* msg, Object* cause, } } + if (cause != NULL) { + if (!dvmInstanceof(cause->clazz, gDvm.classJavaLangThrowable)) { + LOGE("Tried to init exception with cause '%s'\n", + cause->clazz->descriptor); + dvmAbort(); + } + } + /* * The Throwable class has four public constructors: * (1) Throwable() @@ -624,6 +635,28 @@ void dvmWrapException(const char* newExcepStr) dvmReleaseTrackedAlloc(origExcep, self); } +/* + * Get the "cause" field from an exception. + * + * The Throwable class initializes the "cause" field to "this" to + * differentiate between being initialized to null and never being + * initialized. We check for that here and convert it to NULL. + */ +Object* dvmGetExceptionCause(const Object* exception) +{ + if (!dvmInstanceof(exception->clazz, gDvm.classJavaLangThrowable)) { + LOGE("Tried to get cause from object of type '%s'\n", + exception->clazz->descriptor); + dvmAbort(); + } + Object* cause = + dvmGetFieldObject(exception, gDvm.offJavaLangThrowable_cause); + if (cause == exception) + return NULL; + else + return cause; +} + /* * Print the stack trace of the current exception on stderr. This is called * from the JNI ExceptionDescribe call. @@ -1208,9 +1241,8 @@ void dvmLogExceptionStackTrace(void) for (;;) { logStackTraceOf(exception); - cause = (Object*) dvmGetFieldObject(exception, - gDvm.offJavaLangThrowable_cause); - if ((cause == NULL) || (cause == exception)) { + cause = dvmGetExceptionCause(exception); + if (cause == NULL) { break; } LOGI("Caused by:\n"); diff --git a/vm/Exception.h b/vm/Exception.h index 9887929be..404434548 100644 --- a/vm/Exception.h +++ b/vm/Exception.h @@ -122,6 +122,13 @@ bool dvmIsCheckedException(const Object* exception); */ void dvmWrapException(const char* newExcepStr); +/* + * Get the "cause" field from an exception. + * + * Returns NULL if the field is null or uninitialized. + */ +Object* dvmGetExceptionCause(const Object* exception); + /* * Print the exception stack trace on stderr. Calls the exception's * print function. diff --git a/vm/Globals.h b/vm/Globals.h index acaebaab3..9b12d8478 100644 --- a/vm/Globals.h +++ b/vm/Globals.h @@ -54,6 +54,9 @@ typedef enum ExecutionMode { kExecutionModeUnknown = 0, kExecutionModeInterpPortable, kExecutionModeInterpFast, +#if defined(WITH_JIT) + kExecutionModeJit, +#endif } ExecutionMode; /* @@ -99,6 +102,7 @@ struct DvmGlobals { DexOptimizerMode dexOptMode; DexClassVerifyMode classVerifyMode; + bool preciseGc; bool generateRegisterMaps; int assertionCtrlCount; @@ -190,6 +194,7 @@ struct DvmGlobals { ClassObject* classOrgApacheHarmonyLangAnnotationAnnotationMember; ClassObject* classOrgApacheHarmonyLangAnnotationAnnotationMemberArray; ClassObject* classOrgApacheHarmonyNioInternalDirectBuffer; + jclass jclassOrgApacheHarmonyNioInternalDirectBuffer; /* synthetic classes for arrays of primitives */ ClassObject* classArrayBoolean; @@ -338,6 +343,8 @@ struct DvmGlobals { * (3) a thread has hit a breakpoint or exception that the debugger * has marked as a "suspend all" event; * (4) the SignalCatcher caught a signal that requires suspension. + * (5) (if implemented) the JIT needs to perform a heavyweight + * rearrangement of the translation cache or JitTable. * * Because we use "safe point" self-suspension, it is never safe to * do a blocking "lock" call on this mutex -- if it has been acquired, @@ -364,6 +371,12 @@ struct DvmGlobals { */ pthread_cond_t threadSuspendCountCond; + /* + * Sum of all threads' suspendCount fields. The JIT needs to know if any + * thread is suspended. Guarded by threadSuspendCountLock. + */ + int sumThreadSuspendCount; + /* * MUTEX ORDERING: when locking multiple mutexes, always grab them in * this order to avoid deadlock: @@ -397,11 +410,21 @@ struct DvmGlobals { /* * JNI global reference table. */ +#ifdef USE_INDIRECT_REF + IndirectRefTable jniGlobalRefTable; +#else ReferenceTable jniGlobalRefTable; +#endif pthread_mutex_t jniGlobalRefLock; int jniGlobalRefHiMark; int jniGlobalRefLoMark; + /* + * JNI pinned object table (used for primitive arrays). + */ + ReferenceTable jniPinRefTable; + pthread_mutex_t jniPinRefLock; + /* * Native shared library table. */ @@ -417,12 +440,11 @@ struct DvmGlobals { GcHeap* gcHeap; /* - * Pre-allocated object for out-of-memory errors. + * Pre-allocated throwables. */ Object* outOfMemoryObj; - - /* pre-allocated general failure exception */ Object* internalErrorObj; + Object* noClassDefFoundErrorObj; /* Monitor list, so we can free them */ /*volatile*/ Monitor* monitorList; @@ -487,9 +509,12 @@ struct DvmGlobals { /* * JDWP debugger support. + * + * Note "debuggerActive" is accessed from mterp, so its storage size and + * meaning must not be changed without updating the assembly sources. */ bool debuggerConnected; /* debugger or DDMS is connected */ - bool debuggerActive; /* debugger is making requests */ + u1 debuggerActive; /* debugger is making requests */ JdwpState* jdwpState; /* @@ -608,12 +633,127 @@ struct DvmGlobals { */ pid_t systemServerPid; + int kernelGroupScheduling; + //#define COUNT_PRECISE_METHODS #ifdef COUNT_PRECISE_METHODS PointerSet* preciseMethods; #endif + + /* some RegisterMap statistics, useful during development */ + void* registerMapStats; }; extern struct DvmGlobals gDvm; +#if defined(WITH_JIT) + +/* + * JIT-specific global state + */ +struct DvmJitGlobals { + /* + * Guards writes to Dalvik PC (dPC), translated code address (codeAddr) and + * chain fields within the JIT hash table. Note carefully the access + * mechanism. + * Only writes are guarded, and the guarded fields must be updated in a + * specific order using atomic operations. Further, once a field is + * written it cannot be changed without halting all threads. + * + * The write order is: + * 1) codeAddr + * 2) dPC + * 3) chain [if necessary] + * + * This mutex also guards both read and write of curJitTableEntries. + */ + pthread_mutex_t tableLock; + + /* The JIT hash table. Note that for access speed, copies of this pointer + * are stored in each thread. */ + struct JitEntry *pJitEntryTable; + + /* Array of profile threshold counters */ + unsigned char *pProfTable; + unsigned char *pProfTableCopy; + + /* Size of JIT hash table in entries. Must be a power of 2 */ + unsigned int jitTableSize; + + /* Mask used in hash function for JitTable. Should be jitTableSize-1 */ + unsigned int jitTableMask; + + /* How many entries in the JitEntryTable are in use */ + unsigned int jitTableEntriesUsed; + + /* Trigger for trace selection */ + unsigned short threshold; + + /* JIT Compiler Control */ + bool haltCompilerThread; + bool blockingMode; + pthread_t compilerHandle; + pthread_mutex_t compilerLock; + pthread_cond_t compilerQueueActivity; + pthread_cond_t compilerQueueEmpty; + int compilerQueueLength; + int compilerHighWater; + int compilerWorkEnqueueIndex; + int compilerWorkDequeueIndex; + CompilerWorkOrder compilerWorkQueue[COMPILER_WORK_QUEUE_SIZE]; + + /* JIT internal stats */ + int compilerMaxQueued; + int addrLookupsFound; + int addrLookupsNotFound; + int noChainExit; + int normalExit; + int puntExit; + int translationChains; + int invokeChain; + int invokePredictedChain; + int invokeNative; + int returnOp; + + /* Compiled code cache */ + void* codeCache; + + /* Bytes used by the code templates */ + unsigned int templateSize; + + /* Bytes already used in the code cache */ + unsigned int codeCacheByteUsed; + + /* Number of installed compilations in the cache */ + unsigned int numCompilations; + + /* Flag to indicate that the code cache is full */ + bool codeCacheFull; + + /* true/false: compile/reject opcodes specified in the -Xjitop list */ + bool includeSelectedOp; + + /* true/false: compile/reject methods specified in the -Xjitmethod list */ + bool includeSelectedMethod; + + /* Disable JIT for selected opcodes - one bit for each opcode */ + char opList[32]; + + /* Disable JIT for selected methods */ + HashTable *methodTable; + + /* Flag to dump all compiled code */ + bool printMe; + + /* Flag to count trace execution */ + bool profile; + + /* Table to track the overall and trace statistics of hot methods */ + HashTable* methodStatsTable; +}; + +extern struct DvmJitGlobals gDvmJit; + +#endif + #endif /*_DALVIK_GLOBALS*/ diff --git a/vm/IndirectRefTable.c b/vm/IndirectRefTable.c new file mode 100644 index 000000000..f7c764749 --- /dev/null +++ b/vm/IndirectRefTable.c @@ -0,0 +1,459 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Indirect reference table management. + */ +#include "Dalvik.h" + +/* + * Initialize an IndirectRefTable structure. + */ +bool dvmInitIndirectRefTable(IndirectRefTable* pRef, int initialCount, + int maxCount, IndirectRefKind kind) +{ + assert(initialCount > 0); + assert(initialCount <= maxCount); + assert(kind == kIndirectKindLocal || kind == kIndirectKindGlobal); + + pRef->table = (Object**) malloc(initialCount * sizeof(Object*)); + if (pRef->table == NULL) + return false; +#ifndef NDEBUG + memset(pRef->table, 0xd1, initialCount * sizeof(Object*)); +#endif + pRef->segmentState.all = IRT_FIRST_SEGMENT; + pRef->allocEntries = initialCount; + pRef->maxEntries = maxCount; + pRef->kind = kind; + + return true; +} + +/* + * Clears out the contents of a IndirectRefTable, freeing allocated storage. + */ +void dvmClearIndirectRefTable(IndirectRefTable* pRef) +{ + free(pRef->table); + pRef->table = NULL; + pRef->allocEntries = pRef->maxEntries = -1; +} + +/* + * Remove one or more segments from the top. The table entry identified + * by "cookie" becomes the new top-most entry. + * + * Returns false if "cookie" is invalid or the table has only one segment. + */ +bool dvmPopIndirectRefTableSegmentCheck(IndirectRefTable* pRef, u4 cookie) +{ + IRTSegmentState sst; + + /* + * The new value for "top" must be <= the current value. Otherwise + * this would represent an expansion of the table. + */ + sst.all = cookie; + if (sst.parts.topIndex > pRef->segmentState.parts.topIndex) { + LOGE("Attempt to expand table with segment pop (%d to %d)\n", + pRef->segmentState.parts.topIndex, sst.parts.topIndex); + return false; + } + if (sst.parts.numHoles >= sst.parts.topIndex) { + LOGE("Absurd numHoles in cookie (%d bi=%d)\n", + sst.parts.numHoles, sst.parts.topIndex); + return false; + } + + LOGV("--- after pop, top=%d holes=%d\n", + sst.parts.topIndex, sst.parts.numHoles); + + return true; +} + +/* + * Make sure that the entry at "idx" is correctly paired with "iref". + */ +static bool checkEntry(IndirectRefTable* pRef, IndirectRef iref, int idx) +{ + Object* obj = pRef->table[idx]; + IndirectRef checkRef = dvmObjectToIndirectRef(obj, idx, pRef->kind); + if (checkRef != iref) { + LOGW("iref mismatch: %p vs %p\n", iref, checkRef); + return false; + } + return true; +} + +/* + * Add "obj" to "pRef". + */ +IndirectRef dvmAddToIndirectRefTable(IndirectRefTable* pRef, u4 cookie, + Object* obj) +{ + IRTSegmentState prevState; + prevState.all = cookie; + int topIndex = pRef->segmentState.parts.topIndex; + int bottomIndex = prevState.parts.topIndex; + + assert(obj != NULL); + assert(dvmIsValidObject(obj)); + assert(pRef->table != NULL); + assert(pRef->allocEntries <= pRef->maxEntries); + assert(pRef->segmentState.parts.numHoles >= prevState.parts.numHoles); + + if (topIndex == pRef->allocEntries) { + /* reached end of allocated space; did we hit buffer max? */ + if (topIndex == pRef->maxEntries) { + LOGW("IndirectRefTable overflow (max=%d)\n", pRef->maxEntries); + return NULL; + } + + Object** newTable; + int newSize; + + newSize = pRef->allocEntries * 2; + if (newSize > pRef->maxEntries) + newSize = pRef->maxEntries; + assert(newSize > pRef->allocEntries); + + newTable = (Object**) realloc(pRef->table, newSize * sizeof(Object*)); + if (newTable == NULL) { + LOGE("Unable to expand iref table (from %d to %d, max=%d)\n", + pRef->allocEntries, newSize, pRef->maxEntries); + return false; + } + LOGI("Growing ireftab %p from %d to %d (max=%d)\n", + pRef, pRef->allocEntries, newSize, pRef->maxEntries); + + /* update entries; adjust "nextEntry" in case memory moved */ + pRef->table = newTable; + pRef->allocEntries = newSize; + } + + IndirectRef result; + + /* + * We know there's enough room in the table. Now we just need to find + * the right spot. If there's a hole, find it and fill it; otherwise, + * add to the end of the list. + */ + int numHoles = pRef->segmentState.parts.numHoles - prevState.parts.numHoles; + if (numHoles > 0) { + assert(topIndex > 1); + /* find the first hole; likely to be near the end of the list */ + Object** pScan = &pRef->table[topIndex - 1]; + assert(*pScan != NULL); + while (*--pScan != NULL) { + assert(pScan >= pRef->table + bottomIndex); + } + result = dvmObjectToIndirectRef(obj, pScan - pRef->table, pRef->kind); + *pScan = obj; + pRef->segmentState.parts.numHoles--; + } else { + /* add to the end */ + result = dvmObjectToIndirectRef(obj, topIndex, pRef->kind); + pRef->table[topIndex++] = obj; + pRef->segmentState.parts.topIndex = topIndex; + } + + assert(result != NULL); + return result; +} + +/* + * Verify that the indirect table lookup is valid. + * + * Returns "false" if something looks bad. + */ +bool dvmGetFromIndirectRefTableCheck(IndirectRefTable* pRef, IndirectRef iref) +{ + if (dvmGetIndirectRefType(iref) == kIndirectKindInvalid) { + LOGW("Invalid indirect reference 0x%08x\n", (u4) iref); + return false; + } + + int topIndex = pRef->segmentState.parts.topIndex; + int idx = dvmIndirectRefToIndex(iref); + + if (iref == NULL) { + LOGI("--- lookup on NULL iref\n"); + return false; + } + if (idx >= topIndex) { + /* bad -- stale reference? */ + LOGI("Attempt to access invalid index %d (top=%d)\n", + idx, topIndex); + return false; + } + + Object* obj = pRef->table[idx]; + if (obj == NULL) { + LOGI("Attempt to read from hole, iref=%p\n", iref); + return false; + } + if (!checkEntry(pRef, iref, idx)) + return false; + + return true; +} + +/* + * Remove "obj" from "pRef". We extract the table offset bits from "iref" + * and zap the corresponding entry, leaving a hole if it's not at the top. + * + * If the entry is not between the current top index and the bottom index + * specified by the cookie, we don't remove anything. This is the behavior + * required by JNI's DeleteLocalRef function. + * + * Returns "false" if nothing was removed. + */ +bool dvmRemoveFromIndirectRefTable(IndirectRefTable* pRef, u4 cookie, + IndirectRef iref) +{ + IRTSegmentState prevState; + prevState.all = cookie; + int topIndex = pRef->segmentState.parts.topIndex; + int bottomIndex = prevState.parts.topIndex; + + assert(pRef->table != NULL); + assert(pRef->allocEntries <= pRef->maxEntries); + assert(pRef->segmentState.parts.numHoles >= prevState.parts.numHoles); + + int idx = dvmIndirectRefToIndex(iref); + if (idx < bottomIndex) { + /* wrong segment */ + LOGV("Attempt to remove index outside index area (%d vs %d-%d)\n", + idx, bottomIndex, topIndex); + return false; + } + if (idx >= topIndex) { + /* bad -- stale reference? */ + LOGI("Attempt to remove invalid index %d (bottom=%d top=%d)\n", + idx, bottomIndex, topIndex); + return false; + } + + if (idx == topIndex-1) { + /* + * Top-most entry. Scan up and consume holes. No need to NULL + * out the entry, since the test vs. topIndex will catch it. + */ + if (!checkEntry(pRef, iref, idx)) + return false; + +#ifndef NDEBUG + pRef->table[idx] = (IndirectRef) 0xd3d3d3d3; +#endif + + int numHoles = + pRef->segmentState.parts.numHoles - prevState.parts.numHoles; + if (numHoles != 0) { + while (--topIndex > bottomIndex && numHoles != 0) { + LOGV("+++ checking for hole at %d (cookie=0x%08x) val=%p\n", + topIndex-1, cookie, pRef->table[topIndex-1]); + if (pRef->table[topIndex-1] != NULL) + break; + LOGV("+++ ate hole at %d\n", topIndex-1); + numHoles--; + } + pRef->segmentState.parts.numHoles = + numHoles + prevState.parts.numHoles; + pRef->segmentState.parts.topIndex = topIndex; + } else { + pRef->segmentState.parts.topIndex = topIndex-1; + LOGV("+++ ate last entry %d\n", topIndex-1); + } + } else { + /* + * Not the top-most entry. This creates a hole. We NULL out the + * entry to prevent somebody from deleting it twice and screwing up + * the hole count. + */ + if (pRef->table[idx] == NULL) { + LOGV("--- WEIRD: removing null entry %d\n", idx); + return false; + } + if (!checkEntry(pRef, iref, idx)) + return false; + + pRef->table[idx] = NULL; + pRef->segmentState.parts.numHoles++; + LOGV("+++ left hole at %d, holes=%d\n", + idx, pRef->segmentState.parts.numHoles); + } + + return true; +} + +/* + * This is a qsort() callback. We sort Object* by class, allocation size, + * and then by the Object* itself. + */ +static int compareObject(const void* vobj1, const void* vobj2) +{ + Object* obj1 = *((Object**) vobj1); + Object* obj2 = *((Object**) vobj2); + + /* ensure null references appear at the end */ + if (obj1 == NULL) { + if (obj2 == NULL) { + return 0; + } else { + return 1; + } + } else if (obj2 == NULL) { + return -1; + } + + if (obj1->clazz != obj2->clazz) { + return (u1*)obj1->clazz - (u1*)obj2->clazz; + } else { + int size1 = dvmObjectSizeInHeap(obj1); + int size2 = dvmObjectSizeInHeap(obj2); + if (size1 != size2) { + return size1 - size2; + } else { + return (u1*)obj1 - (u1*)obj2; + } + } +} + +/* + * Log an object with some additional info. + * + * Pass in the number of additional elements that are identical to or + * equivalent to the original. + */ +static void logObject(Object* obj, int size, int identical, int equiv) +{ + if (obj == NULL) { + LOGW(" NULL reference (count=%d)\n", equiv); + return; + } + + if (identical + equiv != 0) { + LOGW("%5d of %s %dB (%d unique)\n", identical + equiv +1, + obj->clazz->descriptor, size, equiv +1); + } else { + LOGW("%5d of %s %dB\n", identical + equiv +1, + obj->clazz->descriptor, size); + } +} + +/* + * Dump the contents of a IndirectRefTable to the log. + */ +void dvmDumpIndirectRefTable(const IndirectRefTable* pRef, const char* descr) +{ + const int kLast = 10; + int count = dvmIndirectRefTableEntries(pRef); + Object** refs; + int i; + + if (count == 0) { + LOGW("Reference table has no entries\n"); + return; + } + assert(count > 0); + + /* + * Dump the most recent N entries. If there are holes, we will show + * fewer than N. + */ + LOGW("Last %d entries in %s reference table:\n", kLast, descr); + refs = pRef->table; // use unsorted list + int size; + int start = count - kLast; + if (start < 0) + start = 0; + + for (i = start; i < count; i++) { + if (refs[i] == NULL) + continue; + size = dvmObjectSizeInHeap(refs[i]); + Object* ref = refs[i]; + if (ref->clazz == gDvm.classJavaLangClass) { + ClassObject* clazz = (ClassObject*) ref; + LOGW("%5d: %p cls=%s '%s' (%d bytes)\n", i, ref, + (refs[i] == NULL) ? "-" : ref->clazz->descriptor, + clazz->descriptor, size); + } else { + LOGW("%5d: %p cls=%s (%d bytes)\n", i, ref, + (refs[i] == NULL) ? "-" : ref->clazz->descriptor, size); + } + } + + /* + * Make a copy of the table, and sort it. + * + * The NULL "holes" wind up at the end, so we can strip them off easily. + */ + Object** tableCopy = (Object**)malloc(sizeof(Object*) * count); + memcpy(tableCopy, pRef->table, sizeof(Object*) * count); + qsort(tableCopy, count, sizeof(Object*), compareObject); + refs = tableCopy; // use sorted list + + { + int q; + for (q = 0; q < count; q++) + LOGI("%d %p\n", q, refs[q]); + } + + int holes = 0; + while (refs[count-1] == NULL) { + count--; + holes++; + } + + /* + * Dump uniquified table summary. While we're at it, generate a + * cumulative total amount of pinned memory based on the unique entries. + */ + LOGW("%s reference table summary (%d entries / %d holes):\n", + descr, count, holes); + int equiv, identical, total; + total = equiv = identical = 0; + for (i = 1; i < count; i++) { + size = dvmObjectSizeInHeap(refs[i-1]); + + if (refs[i] == refs[i-1]) { + /* same reference, added more than once */ + identical++; + } else if (refs[i]->clazz == refs[i-1]->clazz && + (int) dvmObjectSizeInHeap(refs[i]) == size) + { + /* same class / size, different object */ + total += size; + equiv++; + } else { + /* different class */ + total += size; + logObject(refs[i-1], size, identical, equiv); + equiv = identical = 0; + } + } + + /* handle the last entry (everything above outputs refs[i-1]) */ + size = (refs[count-1] == NULL) ? 0 : dvmObjectSizeInHeap(refs[count-1]); + total += size; + logObject(refs[count-1], size, identical, equiv); + + LOGW("Memory held directly by native code is %d bytes\n", total); + free(tableCopy); +} + diff --git a/vm/IndirectRefTable.h b/vm/IndirectRefTable.h new file mode 100644 index 000000000..c03353b44 --- /dev/null +++ b/vm/IndirectRefTable.h @@ -0,0 +1,368 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _DALVIK_INDIRECTREFTABLE +#define _DALVIK_INDIRECTREFTABLE +/* + * Maintain a table of indirect references. Used for local/global JNI + * references. + * + * The table contains object references that are part of the GC root set. + * When an object is added we return an IndirectRef that is not a valid + * pointer but can be used to find the original value in O(1) time. + * Conversions to and from indirect refs are performed on JNI method calls + * in and out of the VM, so they need to be very fast. + * + * To be efficient for JNI local variable storage, we need to provide + * operations that allow us to operate on segments of the table, where + * segments are pushed and popped as if on a stack. For example, deletion + * of an entry should only succeed if it appears in the current segment, + * and we want to be able to strip off the current segment quickly when + * a method returns. Additions to the table must be made in the current + * segment even if space is available in an earlier area. + * + * A new segment is created when we call into native code from interpreted + * code, or when we handle the JNI PushLocalFrame function. + * + * The GC must be able to scan the entire table quickly. + * + * In summary, these must be very fast: + * - adding or removing a segment + * - adding references to a new segment + * - converting an indirect reference back to an Object + * These can be a little slower, but must still be pretty quick: + * - adding references to a "mature" segment + * - removing individual references + * - scanning the entire table straight through + * + * If there's more than one segment, we don't guarantee that the table + * will fill completely before we fail due to lack of space. We do ensure + * that the current segment will pack tightly, which should satisfy JNI + * requirements (e.g. EnsureLocalCapacity). + * + * To make everything fit nicely in 32-bit integers, the maximum size of + * the table is capped at 64K. + * + * None of the table functions are synchronized. + */ + +/* + * Indirect reference definition. This must be interchangeable with JNI's + * jobject, and it's convenient to let null be null, so we use void*. + * + * We need a 16-bit table index and a 2-bit reference type (global, local, + * weak global). Real object pointers will have zeroes in the low 2 or 3 + * bits (4- or 8-byte alignment), so it's useful to put the ref type + * in the low bits and reserve zero as an invalid value. + * + * The remaining 14 bits can be used to detect stale indirect references. + * For example, if objects don't move, we can use a hash of the original + * Object* to make sure the entry hasn't been re-used. (If the Object* + * we find there doesn't match because of heap movement, we could do a + * secondary check on the preserved hash value; this implies that creating + * a global/local ref queries the hash value and forces it to be saved.) + * This is only done when CheckJNI is enabled. + * + * A more rigorous approach would be to put a serial number in the extra + * bits, and keep a copy of the serial number in a parallel table. This is + * easier when objects can move, but requires 2x the memory and additional + * memory accesses on add/get. It will catch additional problems, e.g.: + * create iref1 for obj, delete iref1, create iref2 for same obj, lookup + * iref1. A pattern based on object bits will miss this. + */ +typedef void* IndirectRef; + +/* + * Indirect reference kind, used as the two low bits of IndirectRef. + * + * For convenience these match up with enum jobjectRefType from jni.h. + */ +typedef enum IndirectRefKind { + kIndirectKindInvalid = 0, + kIndirectKindLocal = 1, + kIndirectKindGlobal = 2, + kIndirectKindWeakGlobal = 3 +} IndirectRefKind; + +/* + * Table definition. + * + * For the global reference table, the expected common operations are + * adding a new entry and removing a recently-added entry (usually the + * most-recently-added entry). For JNI local references, the common + * operations are adding a new entry and removing an entire table segment. + * + * If "allocEntries" is not equal to "maxEntries", the table may expand + * when entries are added, which means the memory may move. If you want + * to keep pointers into "table" rather than offsets, you must use a + * fixed-size table. + * + * If we delete entries from the middle of the list, we will be left with + * "holes". We track the number of holes so that, when adding new elements, + * we can quickly decide to do a trivial append or go slot-hunting. + * + * When the top-most entry is removed, any holes immediately below it are + * also removed. Thus, deletion of an entry may reduce "topIndex" by more + * than one. + * + * To get the desired behavior for JNI locals, we need to know the bottom + * and top of the current "segment". The top is managed internally, and + * the bottom is passed in as a function argument (the VM keeps it in a + * slot in the interpreted stack frame). When we call a native method or + * push a local frame, the current top index gets pushed on, and serves + * as the new bottom. When we pop a frame off, the value from the stack + * becomes the new top index, and the value stored in the previous frame + * becomes the new bottom. + * + * To avoid having to re-scan the table after a pop, we want to push the + * number of holes in the table onto the stack. Because of our 64K-entry + * cap, we can combine the two into a single unsigned 32-bit value. + * Instead of a "bottom" argument we take a "cookie", which includes the + * bottom index and the count of holes below the bottom. + * + * We need to minimize method call/return overhead. If we store the + * "cookie" externally, on the interpreted call stack, the VM can handle + * pushes and pops with a single 4-byte load and store. (We could also + * store it internally in a public structure, but the local JNI refs are + * logically tied to interpreted stack frames anyway.) + * + * Common alternative implementation: make IndirectRef a pointer to the + * actual reference slot. Instead of getting a table and doing a lookup, + * the lookup can be done instantly. Operations like determining the + * type and deleting the reference are more expensive because the table + * must be hunted for (i.e. you have to do a pointer comparison to see + * which table it's in), you can't move the table when expanding it (so + * realloc() is out), and tricks like serial number checking to detect + * stale references aren't possible (though we may be able to get similar + * benefits with other approaches). + * + * TODO: consider a "lastDeleteIndex" for quick hole-filling when an + * add immediately follows a delete; must invalidate after segment pop + * (which could increase the cost/complexity of method call/return). + * Might be worth only using it for JNI globals. + * + * TODO: may want completely different add/remove algorithms for global + * and local refs to improve performance. A large circular buffer might + * reduce the amortized cost of adding global references. + * + * TODO: if we can guarantee that the underlying storage doesn't move, + * e.g. by using oversized mmap regions to handle expanding tables, we may + * be able to avoid having to synchronize lookups. Might make sense to + * add a "synchronized lookup" call that takes the mutex as an argument, + * and either locks or doesn't lock based on internal details. + */ +typedef union IRTSegmentState { + u4 all; + struct { + u4 topIndex:16; /* index of first unused entry */ + u4 numHoles:16; /* #of holes in entire table */ + } parts; +} IRTSegmentState; +typedef struct IndirectRefTable { + /* semi-public - read/write by interpreter in native call handler */ + IRTSegmentState segmentState; + + /* semi-public - read-only during GC scan; pointer must not be kept */ + Object** table; /* bottom of the stack */ + + /* private */ + int allocEntries; /* #of entries we have space for */ + int maxEntries; /* max #of entries allowed */ + IndirectRefKind kind; /* bit mask, ORed into all irefs */ + + // TODO: want hole-filling stats (#of holes filled, total entries scanned) + // for performance evaluation. +} IndirectRefTable; + +/* use as initial value for "cookie", and when table has only one segment */ +#define IRT_FIRST_SEGMENT 0 + +/* + * (This is PRIVATE, but we want it inside other inlines in this header.) + * + * Indirectify the object. + * + * The object pointer itself is subject to relocation in some GC + * implementations, so we shouldn't really be using it here. + */ +INLINE IndirectRef dvmObjectToIndirectRef(Object* obj, u4 tableIndex, + IndirectRefKind kind) +{ + assert(tableIndex < 65536); + u4 objChunk = (((u4) obj >> 3) ^ ((u4) obj >> 19)) & 0x3fff; + u4 uref = objChunk << 18 | (tableIndex << 2) | kind; + return (IndirectRef) uref; +} + +/* + * (This is PRIVATE, but we want it inside other inlines in this header.) + * + * Extract the table index from an indirect reference. + */ +INLINE u4 dvmIndirectRefToIndex(IndirectRef iref) +{ + u4 uref = (u4) iref; + return (uref >> 2) & 0xffff; +} + +/* + * Determine what kind of indirect reference this is. + */ +INLINE IndirectRefKind dvmGetIndirectRefType(IndirectRef iref) +{ + return (u4) iref & 0x03; +} + +/* + * Initialize an IndirectRefTable. + * + * If "initialCount" != "maxCount", the table will expand as required. + * + * "kind" should be Local or Global. The Global table may also hold + * WeakGlobal refs. + * + * Returns "false" if table allocation fails. + */ +bool dvmInitIndirectRefTable(IndirectRefTable* pRef, int initialCount, + int maxCount, IndirectRefKind kind); + +/* + * Clear out the contents, freeing allocated storage. Does not free "pRef". + * + * You must call dvmInitReferenceTable() before you can re-use this table. + */ +void dvmClearIndirectRefTable(IndirectRefTable* pRef); + +/* + * Start a new segment at the top of the table. + * + * Returns an opaque 32-bit value that must be provided when the segment + * is to be removed. + * + * IMPORTANT: this is implemented as a single instruction in mterp, rather + * than a call here. You can add debugging aids for the C-language + * interpreters, but the basic implementation may not change. + */ +INLINE u4 dvmPushIndirectRefTableSegment(IndirectRefTable* pRef) +{ + return pRef->segmentState.all; +} + +/* extra debugging checks */ +bool dvmPopIndirectRefTableSegmentCheck(IndirectRefTable* pRef, u4 cookie); + +/* + * Remove one or more segments from the top. The table entry identified + * by "cookie" becomes the new top-most entry. + * + * IMPORTANT: this is implemented as a single instruction in mterp, rather + * than a call here. You can add debugging aids for the C-language + * interpreters, but the basic implementation may not change. + */ +INLINE void dvmPopIndirectRefTableSegment(IndirectRefTable* pRef, u4 cookie) +{ + dvmPopIndirectRefTableSegmentCheck(pRef, cookie); + pRef->segmentState.all = cookie; +} + +/* + * Return the #of entries in the entire table. This includes holes, and + * so may be larger than the actual number of "live" entries. + */ +INLINE size_t dvmIndirectRefTableEntries(const IndirectRefTable* pRef) +{ + return pRef->segmentState.parts.topIndex; +} + +/* + * Returns "true" if the table is full. The table is considered full if + * we would need to expand it to add another entry to the current segment. + */ +INLINE size_t dvmIsIndirectRefTableFull(const IndirectRefTable* pRef) +{ + return dvmIndirectRefTableEntries(pRef) == (size_t)pRef->allocEntries; +} + +/* + * Add a new entry. "obj" must be a valid non-NULL object reference + * (though it's okay if it's not fully-formed, e.g. the result from + * dvmMalloc doesn't have obj->clazz set). + * + * Returns NULL if the table is full (max entries reached, or alloc + * failed during expansion). + */ +IndirectRef dvmAddToIndirectRefTable(IndirectRefTable* pRef, u4 cookie, + Object* obj); + +/* + * Add a new entry at the end. Similar to Add but does not usually attempt + * to fill in holes. This is only appropriate to use right after a new + * segment has been pushed. + * + * (This is intended for use when calling into a native JNI method, so + * performance is critical.) + */ +INLINE IndirectRef dvmAppendToIndirectRefTable(IndirectRefTable* pRef, + u4 cookie, Object* obj) +{ + int topIndex = pRef->segmentState.parts.topIndex; + if (topIndex == pRef->allocEntries) { + /* up against alloc or max limit, call the fancy version */ + return dvmAddToIndirectRefTable(pRef, cookie, obj); + } else { + IndirectRef result = dvmObjectToIndirectRef(obj, topIndex, pRef->kind); + pRef->table[topIndex++] = obj; + pRef->segmentState.parts.topIndex = topIndex; + return result; + } +} + +/* extra debugging checks */ +bool dvmGetFromIndirectRefTableCheck(IndirectRefTable* pRef, IndirectRef iref); + +/* + * Given an IndirectRef in the table, return the Object it refers to. + * + * Returns NULL if iref is invalid. + */ +INLINE Object* dvmGetFromIndirectRefTable(IndirectRefTable* pRef, + IndirectRef iref) +{ + if (!dvmGetFromIndirectRefTableCheck(pRef, iref)) + return NULL; + + int idx = dvmIndirectRefToIndex(iref); + return pRef->table[idx]; +} + +/* + * Remove an existing entry. + * + * If the entry is not between the current top index and the bottom index + * specified by the cookie, we don't remove anything. This is the behavior + * required by JNI's DeleteLocalRef function. + * + * Returns "false" if nothing was removed. + */ +bool dvmRemoveFromIndirectRefTable(IndirectRefTable* pRef, u4 cookie, + IndirectRef iref); + +/* + * Dump the contents of a reference table to the log file. + */ +void dvmDumpIndirectRefTable(const IndirectRefTable* pRef, const char* descr); + +#endif /*_DALVIK_INDIRECTREFTABLE*/ diff --git a/vm/Init.c b/vm/Init.c index 176910c82..c46de253b 100644 --- a/vm/Init.c +++ b/vm/Init.c @@ -20,6 +20,7 @@ #include "Dalvik.h" #include "test/Test.h" #include "mterp/Mterp.h" +#include "Hash.h" #include #include @@ -49,6 +50,11 @@ static bool dvmInitZygote(void); /* global state */ struct DvmGlobals gDvm; +/* JIT-specific global state */ +#if defined(WITH_JIT) +struct DvmJitGlobals gDvmJit; +#endif + /* * Show usage. * @@ -83,8 +89,13 @@ static void dvmUsage(const char* progName) kMinStackSize / 1024, kMaxStackSize / 1024); dvmFprintf(stderr, " -Xverify:{none,remote,all}\n"); dvmFprintf(stderr, " -Xrs\n"); +#if defined(WITH_JIT) + dvmFprintf(stderr, + " -Xint (extended to accept ':portable', ':fast' and ':jit')\n"); +#else dvmFprintf(stderr, " -Xint (extended to accept ':portable' and ':fast')\n"); +#endif dvmFprintf(stderr, "\n"); dvmFprintf(stderr, "These are unique to Dalvik:\n"); dvmFprintf(stderr, " -Xzygote\n"); @@ -95,8 +106,21 @@ static void dvmUsage(const char* progName) dvmFprintf(stderr, " -Xjniopts:{warnonly,forcecopy}\n"); dvmFprintf(stderr, " -Xdeadlockpredict:{off,warn,err,abort}\n"); dvmFprintf(stderr, " -Xstacktracefile:\n"); + dvmFprintf(stderr, " -Xgc:[no]precise\n"); dvmFprintf(stderr, " -Xgenregmap\n"); dvmFprintf(stderr, " -Xcheckdexsum\n"); +#if defined(WITH_JIT) + dvmFprintf(stderr, " -Xincludeselectedop\n"); + dvmFprintf(stderr, " -Xjitop:hexopvalue[-endvalue]" + "[,hexopvalue[-endvalue]]*\n"); + dvmFprintf(stderr, " -Xincludeselectedmethod\n"); + dvmFprintf(stderr, " -Xthreshold:decimalvalue\n"); + dvmFprintf(stderr, " -Xblocking\n"); + dvmFprintf(stderr, " -Xjitmethod:signture[,signature]* " + "(eg Ljava/lang/String\\;replace)\n"); + dvmFprintf(stderr, " -Xjitverbose\n"); + dvmFprintf(stderr, " -Xjitprofile\n"); +#endif dvmFprintf(stderr, "\n"); dvmFprintf(stderr, "Configured with:" #ifdef WITH_DEBUGGER @@ -132,6 +156,9 @@ static void dvmUsage(const char* progName) #ifdef WITH_EXTRA_OBJECT_VALIDATION " extra_object_validation" #endif +#ifdef WITH_EXTRA_GC_CHECKS + " extra_gc_checks" +#endif #ifdef WITH_DALVIK_ASSERT " dalvik_assert" #endif @@ -156,6 +183,9 @@ static void dvmUsage(const char* progName) " resolver_cache_expanding" #elif DVM_RESOLVER_CACHE == DVM_RC_NO_CACHE " resolver_cache_disabled" +#endif +#if defined(WITH_JIT) + " with_jit" #endif ); #ifdef DVM_SHOW_EXCEPTION @@ -527,6 +557,97 @@ static void freeAssertionCtrl(void) free(gDvm.assertionCtrl); } +#if defined(WITH_JIT) +/* Parse -Xjitop to selectively turn on/off certain opcodes for JIT */ +static void processXjitop(const char *opt) +{ + if (opt[7] == ':') { + const char *startPtr = &opt[8]; + char *endPtr = NULL; + + do { + long startValue, endValue; + + startValue = strtol(startPtr, &endPtr, 16); + if (startPtr != endPtr) { + /* Just in case value is out of range */ + startValue &= 0xff; + + if (*endPtr == '-') { + endValue = strtol(endPtr+1, &endPtr, 16); + endValue &= 0xff; + } else { + endValue = startValue; + } + + for (; startValue <= endValue; startValue++) { + LOGW("Dalvik opcode %x is selected for debugging", + (unsigned int) startValue); + /* Mark the corresponding bit to 1 */ + gDvmJit.opList[startValue >> 3] |= + 1 << (startValue & 0x7); + } + + if (*endPtr == 0) { + break; + } + + startPtr = endPtr + 1; + + continue; + } else { + if (*endPtr != 0) { + dvmFprintf(stderr, + "Warning: Unrecognized opcode value substring " + "%s\n", endPtr); + } + break; + } + } while (1); + } else { + int i; + for (i = 0; i < 32; i++) { + gDvmJit.opList[i] = 0xff; + } + dvmFprintf(stderr, "Warning: select all opcodes\n"); + } +} + +/* Parse -Xjitmethod to selectively turn on/off certain methods for JIT */ +static void processXjitmethod(const char *opt) +{ + char *buf = strdup(&opt[12]); + char *start, *end; + + gDvmJit.methodTable = dvmHashTableCreate(8, NULL); + + start = buf; + /* + * Break comma-separated method signatures and enter them into the hash + * table individually. + */ + do { + int hashValue; + + end = strchr(start, ','); + if (end) { + *end = 0; + } + + hashValue = dvmComputeUtf8Hash(start); + + dvmHashTableLookup(gDvmJit.methodTable, hashValue, + strdup(start), + (HashCompareFunc) strcmp, true); + if (end) { + start = end + 1; + } else { + break; + } + } while (1); + free(buf); +} +#endif /* * Process an argument vector full of options. Unlike standard C programs, @@ -756,6 +877,10 @@ static int dvmProcessOptions(int argc, const char* const argv[], gDvm.executionMode = kExecutionModeInterpPortable; else if (strcmp(argv[i] + 6, "fast") == 0) gDvm.executionMode = kExecutionModeInterpFast; +#ifdef WITH_JIT + else if (strcmp(argv[i] + 6, "jit") == 0) + gDvm.executionMode = kExecutionModeJit; +#endif else { dvmFprintf(stderr, "Warning: Unrecognized interpreter mode %s\n",argv[i]); @@ -765,6 +890,25 @@ static int dvmProcessOptions(int argc, const char* const argv[], /* disable JIT -- nothing to do here for now */ } +#ifdef WITH_JIT + } else if (strncmp(argv[i], "-Xjitop", 7) == 0) { + processXjitop(argv[i]); + } else if (strncmp(argv[i], "-Xjitmethod", 11) == 0) { + processXjitmethod(argv[i]); + } else if (strncmp(argv[i], "-Xblocking", 10) == 0) { + gDvmJit.blockingMode = true; + } else if (strncmp(argv[i], "-Xthreshold:", 12) == 0) { + gDvmJit.threshold = atoi(argv[i] + 12); + } else if (strncmp(argv[i], "-Xincludeselectedop", 19) == 0) { + gDvmJit.includeSelectedOp = true; + } else if (strncmp(argv[i], "-Xincludeselectedmethod", 23) == 0) { + gDvmJit.includeSelectedMethod = true; + } else if (strncmp(argv[i], "-Xjitverbose", 12) == 0) { + gDvmJit.printMe = true; + } else if (strncmp(argv[i], "-Xjitprofile", 12) == 0) { + gDvmJit.profile = true; +#endif + } else if (strncmp(argv[i], "-Xdeadlockpredict:", 18) == 0) { #ifdef WITH_DEADLOCK_PREDICTION if (strcmp(argv[i] + 18, "off") == 0) @@ -788,6 +932,18 @@ static int dvmProcessOptions(int argc, const char* const argv[], } else if (strcmp(argv[i], "-Xgenregmap") == 0) { gDvm.generateRegisterMaps = true; + LOGV("Register maps will be generated during verification\n"); + + } else if (strncmp(argv[i], "-Xgc:", 5) == 0) { + if (strcmp(argv[i] + 5, "precise") == 0) + gDvm.preciseGc = true; + else if (strcmp(argv[i] + 5, "noprecise") == 0) + gDvm.preciseGc = false; + else { + dvmFprintf(stderr, "Bad value for -Xgc"); + return -1; + } + LOGD("Precise GC configured %s\n", gDvm.preciseGc ? "ON" : "OFF"); } else if (strcmp(argv[i], "-Xcheckdexsum") == 0) { gDvm.verifyDexChecksum = true; @@ -810,6 +966,8 @@ static int dvmProcessOptions(int argc, const char* const argv[], /* * Set defaults for fields altered or modified by arguments. + * + * Globals are initialized to 0 (a/k/a NULL or false). */ static void setCommandLineDefaults() { @@ -849,7 +1007,19 @@ static void setCommandLineDefaults() * we know we're using the "desktop" build we should probably be * using "portable" rather than "fast". */ +#if defined(WITH_JIT) + gDvm.executionMode = kExecutionModeJit; + /* + * TODO - check system property and insert command-line options in + * frameworks/base/core/jni/AndroidRuntime.cpp + */ + gDvmJit.blockingMode = false; + gDvmJit.jitTableSize = 512; + gDvmJit.jitTableMask = gDvmJit.jitTableSize - 1; + gDvmJit.threshold = 200; +#else gDvm.executionMode = kExecutionModeInterpFast; +#endif } @@ -886,6 +1056,9 @@ static void blockSignals() sigemptyset(&mask); sigaddset(&mask, SIGQUIT); sigaddset(&mask, SIGUSR1); // used to initiate heap dump +#if defined(WITH_JIT) && defined(WITH_JIT_TUNING) + sigaddset(&mask, SIGUSR2); // used to investigate JIT internals +#endif //sigaddset(&mask, SIGPIPE); cc = sigprocmask(SIG_BLOCK, &mask, NULL); assert(cc == 0); @@ -936,6 +1109,22 @@ int dvmStartup(int argc, const char* const argv[], bool ignoreUnrecognized, goto fail; } +#if WITH_EXTRA_GC_CHECKS > 1 + /* only "portable" interp has the extra goodies */ + if (gDvm.executionMode != kExecutionModeInterpPortable) { + LOGI("Switching to 'portable' interpreter for GC checks\n"); + gDvm.executionMode = kExecutionModeInterpPortable; + } +#endif + + /* Configure group scheduling capabilities */ + if (!access("/dev/cpuctl/tasks", F_OK)) { + LOGV("Using kernel group scheduling"); + gDvm.kernelGroupScheduling = 1; + } else { + LOGV("Using kernel scheduler policies"); + } + /* configure signal handling */ if (!gDvm.reduceSignals) blockSignals(); @@ -957,6 +1146,8 @@ int dvmStartup(int argc, const char* const argv[], bool ignoreUnrecognized, goto fail; if (!dvmVerificationStartup()) goto fail; + if (!dvmRegisterMapStartup()) + goto fail; if (!dvmInstanceofStartup()) goto fail; if (!dvmClassStartup()) @@ -1028,7 +1219,7 @@ int dvmStartup(int argc, const char* const argv[], bool ignoreUnrecognized, * Do some "late" initialization for the memory allocator. This may * allocate storage and initialize classes. */ - if (!dvmGcLateInit()) + if (!dvmCreateStockExceptions()) goto fail; /* @@ -1058,7 +1249,10 @@ int dvmStartup(int argc, const char* const argv[], bool ignoreUnrecognized, #ifndef NDEBUG - dvmTestHash(); + if (!dvmTestHash()) + LOGE("dmvTestHash FAILED\n"); + if (false /*noisy!*/ && !dvmTestIndirectRefTable()) + LOGE("dvmTestIndirectRefTable FAILED\n"); #endif assert(!dvmCheckException(dvmThreadSelf())); @@ -1167,6 +1361,11 @@ bool dvmInitAfterZygote(void) (int)(endHeap-startHeap), (int)(endQuit-startQuit), (int)(endJdwp-startJdwp), (int)(endJdwp-startHeap)); +#ifdef WITH_JIT + if (!dvmJitStartup()) + return false; +#endif + return true; } @@ -1290,6 +1489,8 @@ int dvmPrepForDexOpt(const char* bootClassPath, DexOptimizerMode dexOptMode, goto fail; if (!dvmVerificationStartup()) goto fail; + if (!dvmRegisterMapStartup()) + goto fail; if (!dvmInstanceofStartup()) goto fail; if (!dvmClassStartup()) @@ -1350,6 +1551,11 @@ void dvmShutdown(void) /* shut down stdout/stderr conversion */ dvmStdioConverterShutdown(); +#ifdef WITH_JIT + /* tell the compiler to shut down if it was started */ + dvmJitShutdown(); +#endif + /* * Kill any daemon threads that still exist. Actively-running threads * are likely to crash the process if they continue to execute while @@ -1370,6 +1576,7 @@ void dvmShutdown(void) dvmThreadShutdown(); dvmClassShutdown(); dvmVerificationShutdown(); + dvmRegisterMapShutdown(); dvmInstanceofShutdown(); dvmInlineNativeShutdown(); dvmGcShutdown(); diff --git a/vm/Init.h b/vm/Init.h index 854933834..63051a25d 100644 --- a/vm/Init.h +++ b/vm/Init.h @@ -41,9 +41,14 @@ int dvmPrepForDexOpt(const char* bootClassPath, DexOptimizerMode dexOptMode, DexClassVerifyMode verifyMode, int dexoptFlags); /* - * Unconditionally abort the entire VM. Try not to use this. + * Replacement for fprintf() when we want to send a message to the console. + * This defaults to fprintf(), but will use the JNI fprintf callback if + * one was provided. */ -int dvmFprintf(FILE* fp, const char* format, ...); -void dvmAbort(void); +int dvmFprintf(FILE* fp, const char* format, ...) +#if defined(__GNUC__) + __attribute__ ((format(printf, 2, 3))) +#endif + ; #endif /*_DALVIK_INIT*/ diff --git a/vm/InlineNative.c b/vm/InlineNative.c index 49025a6f2..f8293608f 100644 --- a/vm/InlineNative.c +++ b/vm/InlineNative.c @@ -20,6 +20,8 @@ */ #include "Dalvik.h" +#include + #ifdef HAVE__MEMCMP16 /* hand-coded assembly implementation, available on some platforms */ //#warning "trying memcmp16" @@ -386,6 +388,154 @@ static bool javaLangString_length(u4 arg0, u4 arg1, u4 arg2, u4 arg3, } +/* + * =========================================================================== + * java.lang.Math + * =========================================================================== + */ + +/* + * public static int abs(int) + */ +static bool javaLangMath_abs_int(u4 arg0, u4 arg1, u4 arg2, u4 arg3, + JValue* pResult) +{ + s4 val = (s4) arg0; + pResult->i = (val >= 0) ? val : -val; + return true; +} + +/* + * public static long abs(long) + */ +static bool javaLangMath_abs_long(u4 arg0, u4 arg1, u4 arg2, u4 arg3, + JValue* pResult) +{ + union { + u4 arg[2]; + s8 ll; + } convert; + + convert.arg[0] = arg0; + convert.arg[1] = arg1; + s8 val = convert.ll; + pResult->j = (val >= 0) ? val : -val; + return true; +} + +/* + * public static float abs(float) + */ +static bool javaLangMath_abs_float(u4 arg0, u4 arg1, u4 arg2, u4 arg3, + JValue* pResult) +{ + union { + u4 arg; + float ff; + } convert; + + /* clear the sign bit; assumes a fairly common fp representation */ + convert.arg = arg0 & 0x7fffffff; + pResult->f = convert.ff; + return true; +} + +/* + * public static double abs(double) + */ +static bool javaLangMath_abs_double(u4 arg0, u4 arg1, u4 arg2, u4 arg3, + JValue* pResult) +{ + union { + u4 arg[2]; + s8 ll; + double dd; + } convert; + + /* clear the sign bit in the (endian-dependent) high word */ + convert.arg[0] = arg0; + convert.arg[1] = arg1; + convert.ll &= 0x7fffffffffffffffULL; + pResult->d = convert.dd; + return true; +} + +/* + * public static int min(int) + */ +static bool javaLangMath_min_int(u4 arg0, u4 arg1, u4 arg2, u4 arg3, + JValue* pResult) +{ + pResult->i = ((s4) arg0 < (s4) arg1) ? arg0 : arg1; + return true; +} + +/* + * public static int max(int) + */ +static bool javaLangMath_max_int(u4 arg0, u4 arg1, u4 arg2, u4 arg3, + JValue* pResult) +{ + pResult->i = ((s4) arg0 > (s4) arg1) ? arg0 : arg1; + return true; +} + +/* + * public static double sqrt(double) + * + * With ARM VFP enabled, gcc turns this into an fsqrtd instruction, followed + * by an fcmpd of the result against itself. If it doesn't match (i.e. + * it's NaN), the libm sqrt() is invoked. + */ +static bool javaLangMath_sqrt(u4 arg0, u4 arg1, u4 arg2, u4 arg3, + JValue* pResult) +{ + union { + u4 arg[2]; + double dd; + } convert; + + convert.arg[0] = arg0; + convert.arg[1] = arg1; + pResult->d = sqrt(convert.dd); + return true; +} + +/* + * public static double cos(double) + */ +static bool javaLangMath_cos(u4 arg0, u4 arg1, u4 arg2, u4 arg3, + JValue* pResult) +{ + union { + u4 arg[2]; + double dd; + } convert; + + convert.arg[0] = arg0; + convert.arg[1] = arg1; + pResult->d = cos(convert.dd); + return true; +} + +/* + * public static double sin(double) + */ +static bool javaLangMath_sin(u4 arg0, u4 arg1, u4 arg2, u4 arg3, + JValue* pResult) +{ + union { + u4 arg[2]; + double dd; + } convert; + + convert.arg[0] = arg0; + convert.arg[1] = arg1; + pResult->d = sin(convert.dd); + return true; +} + + /* * =========================================================================== * Infrastructure @@ -400,12 +550,14 @@ static bool javaLangString_length(u4 arg0, u4 arg1, u4 arg2, u4 arg3, * pointer field. * * IMPORTANT: you must update DALVIK_VM_BUILD in DalvikVersion.h if you make - * changes to this table. + * changes to this table. Must also be kept in sync with NativeInlineOps + * enum in InlineNative.h. */ const InlineOperation gDvmInlineOpsTable[] = { { org_apache_harmony_dalvik_NativeTestTarget_emptyInlineMethod, "Lorg/apache/harmony/dalvik/NativeTestTarget;", "emptyInlineMethod", "()V" }, + { javaLangString_charAt, "Ljava/lang/String;", "charAt", "(I)C" }, { javaLangString_compareTo, @@ -414,6 +566,25 @@ const InlineOperation gDvmInlineOpsTable[] = { "Ljava/lang/String;", "equals", "(Ljava/lang/Object;)Z" }, { javaLangString_length, "Ljava/lang/String;", "length", "()I" }, + + { javaLangMath_abs_int, + "Ljava/lang/Math;", "abs", "(I)I" }, + { javaLangMath_abs_long, + "Ljava/lang/Math;", "abs", "(J)J" }, + { javaLangMath_abs_float, + "Ljava/lang/Math;", "abs", "(F)F" }, + { javaLangMath_abs_double, + "Ljava/lang/Math;", "abs", "(D)D" }, + { javaLangMath_min_int, + "Ljava/lang/Math;", "min", "(II)I" }, + { javaLangMath_max_int, + "Ljava/lang/Math;", "max", "(II)I" }, + { javaLangMath_sqrt, + "Ljava/lang/Math;", "sqrt", "(D)D" }, + { javaLangMath_cos, + "Ljava/lang/Math;", "cos", "(D)D" }, + { javaLangMath_sin, + "Ljava/lang/Math;", "sin", "(D)D" }, }; @@ -525,4 +696,3 @@ skip_prof: #endif return (*gDvmInlineOpsTable[opIndex].func)(arg0, arg1, arg2, arg3, pResult); } - diff --git a/vm/InlineNative.h b/vm/InlineNative.h index a6177a6a1..98398da28 100644 --- a/vm/InlineNative.h +++ b/vm/InlineNative.h @@ -48,6 +48,24 @@ typedef struct InlineOperation { const char* methodSignature; } InlineOperation; +/* Must be kept in sync w/ gDvmInlineOpsTable in InlineNative.c */ +typedef enum NativeInlineOps { + INLINE_EMPTYINLINEMETHOD = 0, + INLINE_STRING_CHARAT = 1, + INLINE_STRING_COMPARETO = 2, + INLINE_STRING_EQUALS = 3, + INLINE_STRING_LENGTH = 4, + INLINE_MATH_ABS_INT = 5, + INLINE_MATH_ABS_LONG = 6, + INLINE_MATH_ABS_FLOAT = 7, + INLINE_MATH_ABS_DOUBLE = 8, + INLINE_MATH_MIN_INT = 9, + INLINE_MATH_MAX_INT = 10, + INLINE_MATH_SQRT = 11, + INLINE_MATH_COS = 12, + INLINE_MATH_SIN = 13, +} NativeInlineOps; + /* * Get the inlineops table. */ diff --git a/vm/Inlines.c b/vm/Inlines.c index 57405e3c9..e31444896 100644 --- a/vm/Inlines.c +++ b/vm/Inlines.c @@ -21,6 +21,7 @@ #define _DALVIK_GEN_INLINES #include "Dalvik.h" #include "analysis/CodeVerify.h" +#include "analysis/RegisterMap.h" #include "mterp/c/header.c" #undef LOG_TAG diff --git a/vm/JarFile.c b/vm/JarFile.c index 4a4eb622e..0a58390a5 100644 --- a/vm/JarFile.c +++ b/vm/JarFile.c @@ -261,7 +261,8 @@ tryArchive: dexGetZipEntryCrc32(&archive, entry), isBootstrap, &newFile, /*createIfMissing=*/true); if (fd < 0) { - LOGI("Unable to open or create cache for %s\n", fileName); + LOGI("Unable to open or create cache for %s (%s)\n", + fileName, cachedName); goto bail; } locked = true; diff --git a/vm/Jni.c b/vm/Jni.c index 0248e8fc1..10afcaa33 100644 --- a/vm/Jni.c +++ b/vm/Jni.c @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + /* * Dalvik implementation of JNI interfaces. */ @@ -47,19 +48,21 @@ checked at runtime, such as array bounds checks, we do the tests here. General notes on local/global reference tracking -JNI provides explicit control over natively-held references that the VM GC +JNI provides explicit control over natively-held references that the GC needs to know about. These can be local, in which case they're released -when the native method returns, or global, which are held until explicitly -released. - -The references can be created and deleted with JNI NewLocalRef / -NewGlobalRef calls, but this is unusual except perhaps for holding on -to a Class reference. Most often they are created transparently by the -JNI functions. For example, the paired Get/Release calls guarantee that -objects survive until explicitly released, so a simple way to implement -this is to create a global reference on "Get" and delete it on "Release". -The AllocObject/NewObject functions must create local references, because -nothing else in the GC root set has a reference to the new objects. +when the native method returns into the VM, or global, which are held +until explicitly released. (There are also weak-global references, +which have the lifespan and visibility of global references, but the +object they refer to may be collected.) + +The references can be created with explicit JNI NewLocalRef / NewGlobalRef +calls. The former is very unusual, the latter is reasonably common +(e.g. for caching references to class objects). + +Local references are most often created as a side-effect of JNI functions. +For example, the AllocObject/NewObject functions must create local +references to the objects returned, because nothing else in the GC root +set has a reference to the new objects. The most common mode of operation is for a method to create zero or more local references and return. Explicit "local delete" operations @@ -70,38 +73,52 @@ with a simple store/increment operation; to avoid infinite growth in pathological situations, we need to reclaim the space used by deleted entries. -The simplest implementation is an expanding append-only array that compacts -when objects are deleted. In typical situations, e.g. running through -an array of objects, we will be deleting one of the most recently added -entries, so we can minimize the number of elements moved (or avoid having -to move any). +If we just want to maintain a list for the GC root set, we can use an +expanding append-only array that compacts when objects are deleted. +In typical situations, e.g. running through an array of objects, we will +be deleting one of the most recently added entries, so we can minimize +the number of elements moved (or avoid having to move any). + +If we want to conceal the pointer values from native code, which is +necessary to allow the GC to move JNI-referenced objects around, then we +have to use a more complicated indirection mechanism. The spec says, "Local references are only valid in the thread in which they are created. The native code must not pass local references from -one thread to another." It should also be noted that, while some calls -will *create* global references as a side-effect, only the NewGlobalRef -and NewWeakGlobalRef calls actually *return* global references. +one thread to another." + + +Pinned objects + +For some large chunks of data, notably primitive arrays and String data, +JNI allows the VM to choose whether it wants to pin the array object or +make a copy. We currently pin the memory for better execution performance. + +TODO: we're using simple root set references to pin primitive array data, +because they have the property we need (i.e. the pointer we return is +guaranteed valid until we explicitly release it). However, if we have a +compacting GC and don't want to pin all memory held by all global refs, +we need to treat these differently. Global reference tracking There should be a small "active" set centered around the most-recently -added items. We can use an append-only, compacting array like we do for -local refs. +added items. -Because it's global, access to it has to be synchronized. +Because it's global, access to it has to be synchronized. Additions and +removals require grabbing a mutex. If the table serves as an indirection +mechanism (i.e. it's not just a list for the benefit of the garbage +collector), reference lookups may also require grabbing a mutex. The JNI spec does not define any sort of limit, so the list must be able -to expand. It may be useful to log significant increases in usage to -help identify resource leaks. +to expand to a reasonable size. It may be useful to log significant +increases in usage to help identify resource leaks. + -TODO: we currently use global references on strings and primitive array -data, because they have the property we need (i.e. the pointer we return -is guaranteed valid until we explicitly release it). However, if we have -a compacting GC and don't want to pin all memory held by all global refs, -we actually want to treat these differently. Either we need a way to -tell the GC that specific global references are pinned, or we have to -make a copy of the data and return that instead (something JNI supports). +Weak-global reference tracking + +[TBD] Local reference tracking @@ -174,14 +191,14 @@ all references. An exact GC will need to understand the actual layout. *** Approach #2: use a parallel stack -Each Thread/JNIEnv points to a ReferenceTable struct. The struct -has a system-heap-allocated array of references and a pointer to the +Each Thread/JNIEnv points to a reference table. The struct has +a system-heap-allocated array of references and a pointer to the next-available entry ("nextEntry"). -Each stack frame has a pointer to what it sees as the "top" element in the -array (we can double-up the "currentPc" field). This is set to "nextEntry" -when the frame is pushed on. As local references are added or removed, -"nextEntry" is updated. +Each stack frame has a pointer to what it sees as the "bottom" element +in the array (we can double-up the "currentPc" field). This is set to +"nextEntry" when the frame is pushed on. As local references are added +or removed, "nextEntry" is updated. We implement Push/PopLocalFrame with actual stack frames. Before a JNI frame gets popped, we set "nextEntry" to the "top" pointer of the current @@ -219,99 +236,19 @@ Compared to #1, approach #2: /* fwd */ static const struct JNINativeInterface gNativeInterface; -static jobject addGlobalReference(jobject obj); - +static jobject addGlobalReference(Object* obj); #ifdef WITH_JNI_STACK_CHECK # define COMPUTE_STACK_SUM(_self) computeStackSum(_self); # define CHECK_STACK_SUM(_self) checkStackSum(_self); -static void computeStackSum(Thread* self); -static void checkStackSum(Thread* self); +//static void computeStackSum(Thread* self); +//static void checkStackSum(Thread* self); #else # define COMPUTE_STACK_SUM(_self) ((void)0) # define CHECK_STACK_SUM(_self) ((void)0) #endif -/* - * =========================================================================== - * JNI call bridge - * =========================================================================== - */ - -/* - * Bridge to calling a JNI function. This ideally gets some help from - * assembly language code in dvmPlatformInvoke, because the arguments - * must be pushed into the native stack as if we were calling a - * function. - * - * The number of values in "args" must match method->insSize. - * - * This is generally just set up by the resolver and then called through. - * We don't call here explicitly. This takes the same arguments as all - * of the "internal native" methods. - */ -void dvmCallJNIMethod(const u4* args, JValue* pResult, const Method* method, - Thread* self) -{ - int oldStatus; - - assert(method->insns != NULL); - - //int i; - //LOGI("JNI calling %p (%s.%s %s):\n", method->insns, - // method->clazz->descriptor, method->name, method->signature); - //for (i = 0; i < method->insSize; i++) - // LOGI(" %d: 0x%08x\n", i, args[i]); - - oldStatus = dvmChangeStatus(self, THREAD_NATIVE); - - COMPUTE_STACK_SUM(self); - // TODO: should we be converting 'this' to a local ref? - dvmPlatformInvoke(self->jniEnv, - dvmIsStaticMethod(method) ? method->clazz : NULL, - method->jniArgInfo, method->insSize, args, method->shorty, - (void*)method->insns, pResult); - CHECK_STACK_SUM(self); - - dvmChangeStatus(self, oldStatus); -} - -/* - * Alternate call bridge for the unusual case of a synchronized native method. - * - * Lock the object, then call through the usual function. - */ -void dvmCallSynchronizedJNIMethod(const u4* args, JValue* pResult, - const Method* method, Thread* self) -{ - Object* lockObj; - - assert(dvmIsSynchronizedMethod(method)); - - if (dvmIsStaticMethod(method)) - lockObj = (Object*) method->clazz; - else - lockObj = (Object*) args[0]; - - LOGVV("Calling %s.%s: locking %p (%s)\n", - method->clazz->descriptor, method->name, - lockObj, lockObj->clazz->descriptor); - - dvmLockObject(self, lockObj); - dvmCallJNIMethod(args, pResult, method, self); - dvmUnlockObject(self, lockObj); -} - -/* - * Extract the return type enum from the "jniArgInfo" field. - */ -DalvikJniReturnType dvmGetArgInfoReturnType(int jniArgInfo) -{ - return (jniArgInfo & DALVIK_JNI_RETURN_MASK) >> DALVIK_JNI_RETURN_SHIFT; -} - - /* * =========================================================================== * Utility functions @@ -346,26 +283,41 @@ DalvikJniReturnType dvmGetArgInfoReturnType(int jniArgInfo) COMPUTE_STACK_SUM(_self) #define kGlobalRefsTableInitialSize 512 -#define kGlobalRefsTableMaxSize 51200 /* arbitrary */ +#define kGlobalRefsTableMaxSize 51200 /* arbitrary, must be < 64K */ #define kGrefWaterInterval 100 - #define kTrackGrefUsage true +#define kPinTableInitialSize 16 +#define kPinTableMaxSize 1024 +#define kPinComplainThreshold 10 + /* * Allocate the global references table, and look up some classes for * the benefit of direct buffer access. */ bool dvmJniStartup(void) { +#ifdef USE_INDIRECT_REF + if (!dvmInitIndirectRefTable(&gDvm.jniGlobalRefTable, + kGlobalRefsTableInitialSize, kGlobalRefsTableMaxSize, + kIndirectKindGlobal)) + return false; +#else if (!dvmInitReferenceTable(&gDvm.jniGlobalRefTable, kGlobalRefsTableInitialSize, kGlobalRefsTableMaxSize)) return false; +#endif dvmInitMutex(&gDvm.jniGlobalRefLock); - gDvm.jniGlobalRefLoMark = 0; gDvm.jniGlobalRefHiMark = kGrefWaterInterval * 2; + if (!dvmInitReferenceTable(&gDvm.jniPinRefTable, + kPinTableInitialSize, kPinTableMaxSize)) + return false; + + dvmInitMutex(&gDvm.jniPinRefLock); + /* * Look up and cache pointers to some direct buffer classes, fields, * and methods. @@ -390,10 +342,11 @@ bool dvmJniStartup(void) LOGE("Unable to find internal direct buffer classes\n"); return false; } - /* needs to be a global ref so CheckJNI thinks we're allowed to see it */ - gDvm.classOrgApacheHarmonyNioInternalDirectBuffer = - addGlobalReference((Object*) directBufferClass); gDvm.classJavaNioReadWriteDirectByteBuffer = readWriteBufferClass; + gDvm.classOrgApacheHarmonyNioInternalDirectBuffer = directBufferClass; + /* need a global reference for extended CheckJNI tests */ + gDvm.jclassOrgApacheHarmonyNioInternalDirectBuffer = + addGlobalReference((Object*) directBufferClass); /* * We need a Method* here rather than a vtable offset, because @@ -465,7 +418,11 @@ bool dvmJniStartup(void) */ void dvmJniShutdown(void) { +#ifdef USE_INDIRECT_REF + dvmClearIndirectRefTable(&gDvm.jniGlobalRefTable); +#else dvmClearReferenceTable(&gDvm.jniGlobalRefTable); +#endif } @@ -571,13 +528,69 @@ void dvmDestroyJNIEnv(JNIEnv* env) /* * Retrieve the ReferenceTable struct for the current thread. * - * If we know the code isn't sharing JNIEnv pointers between threads, we - * could put this into env and skip the TLS lookup. + * Going through "env" rather than dvmThreadSelf() is faster but will + * get weird if the JNI code is passing the wrong JNIEnv around. + */ +#ifdef USE_INDIRECT_REF +static inline IndirectRefTable* getLocalRefTable(JNIEnv* env) +#else +static inline ReferenceTable* getLocalRefTable(JNIEnv* env) +#endif +{ + //return &dvmThreadSelf()->jniLocalRefTable; + return &((JNIEnvExt*)env)->self->jniLocalRefTable; +} + +#ifdef USE_INDIRECT_REF +/* + * Convert an indirect reference to an Object reference. The indirect + * reference may be local, global, or weak-global. + * + * If "jobj" is NULL or an invalid indirect reference, this returns NULL. */ -static inline ReferenceTable* getLocalRefTable(void) +Object* dvmDecodeIndirectRef(JNIEnv* env, jobject jobj) { - return &dvmThreadSelf()->jniLocalRefTable; + if (jobj == NULL) + return NULL; + + Object* result; + + switch (dvmGetIndirectRefType(jobj)) { + case kIndirectKindLocal: + { + IndirectRefTable* pRefTable = getLocalRefTable(env); + result = dvmGetFromIndirectRefTable(pRefTable, jobj); + } + break; + case kIndirectKindGlobal: + { + // TODO: find a way to avoid the mutex activity here + IndirectRefTable* pRefTable = &gDvm.jniGlobalRefTable; + dvmLockMutex(&gDvm.jniGlobalRefLock); + result = dvmGetFromIndirectRefTable(pRefTable, jobj); + dvmUnlockMutex(&gDvm.jniGlobalRefLock); + } + break; + case kIndirectKindWeakGlobal: + { + LOGE("weak-global not yet supported\n"); + result = NULL; + dvmAbort(); + } + break; + case kIndirectKindInvalid: + default: + LOGW("Invalid indirect reference %p in decodeIndirectRef\n", jobj); + dvmAbort(); + result = NULL; + break; + } + + return result; } +#else + /* use trivial inline in JniInternal.h for performance */ +#endif /* * Add a local reference for an object to the current stack frame. When @@ -591,52 +604,100 @@ static inline ReferenceTable* getLocalRefTable(void) * Returns the local reference (currently just the same pointer that was * passed in), or NULL on failure. */ -static jobject addLocalReference(jobject obj) +static jobject addLocalReference(JNIEnv* env, Object* obj) { if (obj == NULL) return NULL; - ReferenceTable* pRef = getLocalRefTable(); + jobject jobj; + +#ifdef USE_INDIRECT_REF + IndirectRefTable* pRefTable = getLocalRefTable(env); + void* curFrame = ((JNIEnvExt*)env)->self->curFrame; + u4 cookie = SAVEAREA_FROM_FP(curFrame)->xtra.localRefCookie; + + jobj = (jobject) dvmAddToIndirectRefTable(pRefTable, cookie, obj); + if (jobj == NULL) { + dvmDumpIndirectRefTable(pRefTable, "JNI local"); + LOGE("Failed adding to JNI local ref table (has %d entries)\n", + (int) dvmIndirectRefTableEntries(pRefTable)); + dvmDumpThread(dvmThreadSelf(), false); + dvmAbort(); // spec says call FatalError; this is equivalent + } else { + LOGVV("LREF add %p (%s.%s) (ent=%d)\n", obj, + dvmGetCurrentJNIMethod()->clazz->descriptor, + dvmGetCurrentJNIMethod()->name, + (int) dvmReferenceTableEntries(pRefTable)); + } +#else + ReferenceTable* pRefTable = getLocalRefTable(env); - if (!dvmAddToReferenceTable(pRef, (Object*)obj)) { - dvmDumpReferenceTable(pRef, "JNI local"); + if (!dvmAddToReferenceTable(pRefTable, obj)) { + dvmDumpReferenceTable(pRefTable, "JNI local"); LOGE("Failed adding to JNI local ref table (has %d entries)\n", - (int) dvmReferenceTableEntries(pRef)); + (int) dvmReferenceTableEntries(pRefTable)); dvmDumpThread(dvmThreadSelf(), false); dvmAbort(); // spec says call FatalError; this is equivalent } else { - LOGVV("LREF add %p (%s.%s)\n", obj, + LOGVV("LREF add %p (%s.%s) (ent=%d)\n", obj, dvmGetCurrentJNIMethod()->clazz->descriptor, - dvmGetCurrentJNIMethod()->name); + dvmGetCurrentJNIMethod()->name, + (int) dvmReferenceTableEntries(pRefTable)); } - return obj; + jobj = (jobject) obj; +#endif + + return jobj; } /* * Ensure that at least "capacity" references can be held in the local * refs table of the current thread. */ -static bool ensureLocalCapacity(int capacity) +static bool ensureLocalCapacity(JNIEnv* env, int capacity) { - ReferenceTable* pRef = getLocalRefTable(); +#ifdef USE_INDIRECT_REF + IndirectRefTable* pRefTable = getLocalRefTable(env); + int numEntries = dvmIndirectRefTableEntries(pRefTable); + // TODO: this isn't quite right, since "numEntries" includes holes + return ((kJniLocalRefMax - numEntries) >= capacity); +#else + ReferenceTable* pRefTable = getLocalRefTable(env); - return (kJniLocalRefMax - (pRef->nextEntry - pRef->table) >= capacity); + return (kJniLocalRefMax - (pRefTable->nextEntry - pRefTable->table) >= capacity); +#endif } /* * Explicitly delete a reference from the local list. */ -static void deleteLocalReference(jobject obj) +static void deleteLocalReference(JNIEnv* env, jobject jobj) { - if (obj == NULL) + if (jobj == NULL) return; - ReferenceTable* pRef = getLocalRefTable(); - Thread* self = dvmThreadSelf(); - Object** top = SAVEAREA_FROM_FP(self->curFrame)->xtra.localRefTop; +#ifdef USE_INDIRECT_REF + IndirectRefTable* pRefTable = getLocalRefTable(env); + Thread* self = ((JNIEnvExt*)env)->self; + u4 cookie = SAVEAREA_FROM_FP(self->curFrame)->xtra.localRefCookie; + + if (!dvmRemoveFromIndirectRefTable(pRefTable, cookie, jobj)) { + /* + * Attempting to delete a local reference that is not in the + * topmost local reference frame is a no-op. DeleteLocalRef returns + * void and doesn't throw any exceptions, but we should probably + * complain about it so the user will notice that things aren't + * going quite the way they expect. + */ + LOGW("JNI WARNING: DeleteLocalRef(%p) failed to find entry\n", jobj); + } +#else + ReferenceTable* pRefTable = getLocalRefTable(env); + Thread* self = ((JNIEnvExt*)env)->self; + Object** bottom = SAVEAREA_FROM_FP(self->curFrame)->xtra.localRefCookie; - if (!dvmRemoveFromReferenceTable(pRef, top, (Object*) obj)) { + if (!dvmRemoveFromReferenceTable(pRefTable, bottom, (Object*) jobj)) { /* * Attempting to delete a local reference that is not in the * topmost local reference frame is a no-op. DeleteLocalRef returns @@ -645,8 +706,9 @@ static void deleteLocalReference(jobject obj) * going quite the way they expect. */ LOGW("JNI WARNING: DeleteLocalRef(%p) failed to find entry (valid=%d)\n", - obj, dvmIsValidObject((Object*) obj)); + jobj, dvmIsValidObject((Object*) jobj)); } +#endif } /* @@ -655,7 +717,7 @@ static void deleteLocalReference(jobject obj) * We may add the same object more than once. Add/remove calls are paired, * so it needs to appear on the list multiple times. */ -static jobject addGlobalReference(jobject obj) +static jobject addGlobalReference(Object* obj) { if (obj == NULL) return NULL; @@ -682,8 +744,8 @@ static jobject addGlobalReference(jobject obj) } if (false && ((Object*)obj)->clazz == gDvm.classArrayByte) { ArrayObject* arrayObj = (ArrayObject*) obj; - if (arrayObj->length == 8192 && - dvmReferenceTableEntries(&gDvm.jniGlobalRefTable) > 400) + if (arrayObj->length == 8192 /*&& + dvmReferenceTableEntries(&gDvm.jniGlobalRefTable) > 400*/) { LOGI("Adding global ref on byte array %p (len=%d)\n", arrayObj, arrayObj->length); @@ -691,14 +753,11 @@ static jobject addGlobalReference(jobject obj) } } + jobject jobj; + dvmLockMutex(&gDvm.jniGlobalRefLock); /* - * Expanding the table should happen rarely, so I'm not overly - * concerned about the performance impact of copying the old list - * over. We shouldn't see one-time activity spikes, so freeing - * up storage shouldn't be required. - * * Throwing an exception on failure is problematic, because JNI code * may not be expecting an exception, and things sort of cascade. We * want to have a hard limit to catch leaks during debugging, but this @@ -707,12 +766,51 @@ static jobject addGlobalReference(jobject obj) * we're either leaking global ref table entries or we're going to * run out of space in the GC heap. */ - if (!dvmAddToReferenceTable(&gDvm.jniGlobalRefTable, (Object*)obj)) { +#ifdef USE_INDIRECT_REF + jobj = dvmAddToIndirectRefTable(&gDvm.jniGlobalRefTable, IRT_FIRST_SEGMENT, + obj); + if (jobj == NULL) { + dvmDumpIndirectRefTable(&gDvm.jniGlobalRefTable, "JNI global"); + LOGE("Failed adding to JNI global ref table (%d entries)\n", + (int) dvmIndirectRefTableEntries(&gDvm.jniGlobalRefTable)); + dvmAbort(); + } + + LOGVV("GREF add %p (%s.%s)\n", obj, + dvmGetCurrentJNIMethod()->clazz->descriptor, + dvmGetCurrentJNIMethod()->name); + + /* GREF usage tracking; should probably be disabled for production env */ + if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) { + int count = dvmIndirectRefTableEntries(&gDvm.jniGlobalRefTable); + // TODO: adjust for "holes" + if (count > gDvm.jniGlobalRefHiMark) { + LOGD("GREF has increased to %d\n", count); + gDvm.jniGlobalRefHiMark += kGrefWaterInterval; + gDvm.jniGlobalRefLoMark += kGrefWaterInterval; + + /* watch for "excessive" use; not generally appropriate */ + if (count >= gDvm.jniGrefLimit) { + JavaVMExt* vm = (JavaVMExt*) gDvm.vmList; + if (vm->warnError) { + dvmDumpIndirectRefTable(&gDvm.jniGlobalRefTable, + "JNI global"); + LOGE("Excessive JNI global references (%d)\n", count); + dvmAbort(); + } else { + LOGW("Excessive JNI global references (%d)\n", count); + } + } + } + } +#else + if (!dvmAddToReferenceTable(&gDvm.jniGlobalRefTable, obj)) { dvmDumpReferenceTable(&gDvm.jniGlobalRefTable, "JNI global"); LOGE("Failed adding to JNI global ref table (%d entries)\n", (int) dvmReferenceTableEntries(&gDvm.jniGlobalRefTable)); dvmAbort(); } + jobj = (jobject) obj; LOGVV("GREF add %p (%s.%s)\n", obj, dvmGetCurrentJNIMethod()->clazz->descriptor, @@ -739,10 +837,11 @@ static jobject addGlobalReference(jobject obj) } } } +#endif bail: dvmUnlockMutex(&gDvm.jniGlobalRefLock); - return obj; + return jobj; } /* @@ -752,18 +851,36 @@ bail: * Thought: if it's not the most recent entry, just null it out. When we * fill up, do a compaction pass before we expand the list. */ -static void deleteGlobalReference(jobject obj) +static void deleteGlobalReference(jobject jobj) { - if (obj == NULL) + if (jobj == NULL) return; dvmLockMutex(&gDvm.jniGlobalRefLock); +#ifdef USE_INDIRECT_REF + if (!dvmRemoveFromIndirectRefTable(&gDvm.jniGlobalRefTable, + IRT_FIRST_SEGMENT, jobj)) + { + LOGW("JNI: DeleteGlobalRef(%p) failed to find entry\n", jobj); + goto bail; + } + + if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) { + int count = dvmIndirectRefTableEntries(&gDvm.jniGlobalRefTable); + // TODO: not quite right, need to subtract holes + if (count < gDvm.jniGlobalRefLoMark) { + LOGD("GREF has decreased to %d\n", count); + gDvm.jniGlobalRefHiMark -= kGrefWaterInterval; + gDvm.jniGlobalRefLoMark -= kGrefWaterInterval; + } + } +#else if (!dvmRemoveFromReferenceTable(&gDvm.jniGlobalRefTable, - gDvm.jniGlobalRefTable.table, obj)) + gDvm.jniGlobalRefTable.table, jobj)) { LOGW("JNI: DeleteGlobalRef(%p) failed to find entry (valid=%d)\n", - obj, dvmIsValidObject((Object*) obj)); + jobj, dvmIsValidObject((Object*) jobj)); goto bail; } @@ -775,29 +892,125 @@ static void deleteGlobalReference(jobject obj) gDvm.jniGlobalRefLoMark -= kGrefWaterInterval; } } +#endif bail: dvmUnlockMutex(&gDvm.jniGlobalRefLock); } +/* + * Objects don't currently move, so we just need to create a reference + * that will ensure the array object isn't collected. + * + * We use a separate reference table, which is part of the GC root set. + */ +static void pinPrimitiveArray(ArrayObject* arrayObj) +{ + if (arrayObj == NULL) + return; + + dvmLockMutex(&gDvm.jniPinRefLock); + if (!dvmAddToReferenceTable(&gDvm.jniPinRefTable, (Object*)arrayObj)) { + dvmDumpReferenceTable(&gDvm.jniPinRefTable, "JNI pinned array"); + LOGE("Failed adding to JNI pinned array ref table (%d entries)\n", + (int) dvmReferenceTableEntries(&gDvm.jniPinRefTable)); + dvmDumpThread(dvmThreadSelf(), false); + dvmAbort(); + } + + /* + * If we're watching global ref usage, also keep an eye on these. + * + * The total number of pinned primitive arrays should be pretty small. + * A single array should not be pinned more than once or twice; any + * more than that is a strong indicator that a Release function is + * not being called. + */ + if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) { + int count = 0; + Object** ppObj = gDvm.jniPinRefTable.table; + while (ppObj < gDvm.jniPinRefTable.nextEntry) { + if (*ppObj++ == (Object*) arrayObj) + count++; + } + + if (count > kPinComplainThreshold) { + LOGW("JNI: pin count on array %p (%s) is now %d\n", + arrayObj, arrayObj->obj.clazz->descriptor, count); + /* keep going */ + } + } + + dvmUnlockMutex(&gDvm.jniPinRefLock); +} + +/* + * Un-pin the array object. If an object was pinned twice, it must be + * unpinned twice before it's free to move. + */ +static void unpinPrimitiveArray(ArrayObject* arrayObj) +{ + if (arrayObj == NULL) + return; + + dvmLockMutex(&gDvm.jniPinRefLock); + if (!dvmRemoveFromReferenceTable(&gDvm.jniPinRefTable, + gDvm.jniPinRefTable.table, (Object*) arrayObj)) + { + LOGW("JNI: unpinPrimitiveArray(%p) failed to find entry (valid=%d)\n", + arrayObj, dvmIsValidObject((Object*) arrayObj)); + goto bail; + } + +bail: + dvmUnlockMutex(&gDvm.jniPinRefLock); +} + /* * GC helper function to mark all JNI global references. + * + * We're currently handling the "pin" table here too. */ void dvmGcMarkJniGlobalRefs() { - Object **op; + Object** op; dvmLockMutex(&gDvm.jniGlobalRefLock); +#ifdef USE_INDIRECT_REF + IndirectRefTable* pRefTable = &gDvm.jniGlobalRefTable; + op = pRefTable->table; + int numEntries = dvmIndirectRefTableEntries(pRefTable); + int i; + + for (i = 0; i < numEntries; i++) { + Object* obj = *op; + if (obj != NULL) + dvmMarkObjectNonNull(obj); + op++; + } +#else op = gDvm.jniGlobalRefTable.table; while ((uintptr_t)op < (uintptr_t)gDvm.jniGlobalRefTable.nextEntry) { dvmMarkObjectNonNull(*(op++)); } +#endif dvmUnlockMutex(&gDvm.jniGlobalRefLock); + + + dvmLockMutex(&gDvm.jniPinRefLock); + + op = gDvm.jniPinRefTable.table; + while ((uintptr_t)op < (uintptr_t)gDvm.jniPinRefTable.nextEntry) { + dvmMarkObjectNonNull(*(op++)); + } + + dvmUnlockMutex(&gDvm.jniPinRefLock); } +#ifndef USE_INDIRECT_REF /* * Determine if "obj" appears in the argument list for the native method. * @@ -868,16 +1081,17 @@ static bool findInArgList(Thread* self, Object* obj) } return false; } +#endif /* * Verify that a reference passed in from native code is one that the * code is allowed to have. * * It's okay for native code to pass us a reference that: - * - was just passed in as an argument when invoked by native code - * - was returned to it from JNI (and is now in the JNI local refs table) + * - was passed in as an argument when invoked by native code (and hence + * is in the JNI local refs table) + * - was returned to it from JNI (and is now in the local refs table) * - is present in the JNI global refs table - * The first one is a little awkward. The latter two are just table lookups. * * Used by -Xcheck:jni and GetObjectRefType. * @@ -885,32 +1099,46 @@ static bool findInArgList(Thread* self, Object* obj) * something is both global and local, we can't tell them apart, and always * return "local". */ -jobjectRefType dvmGetJNIRefType(Object* obj) +jobjectRefType dvmGetJNIRefType(JNIEnv* env, jobject jobj) { - ReferenceTable* pRef = getLocalRefTable(); +#ifdef USE_INDIRECT_REF + /* + * IndirectRefKind is currently defined as an exact match of + * jobjectRefType, so this is easy. We have to decode it to determine + * if it's a valid reference and not merely valid-looking. + */ + Object* obj = dvmDecodeIndirectRef(env, jobj); + + if (obj == NULL) { + /* invalid ref, or jobj was NULL */ + return JNIInvalidRefType; + } else { + return (jobjectRefType) dvmGetIndirectRefType(jobj); + } +#else + ReferenceTable* pRefTable = getLocalRefTable(env); Thread* self = dvmThreadSelf(); //Object** top; Object** ptr; /* check args */ - if (findInArgList(self, obj)) { - //LOGI("--- REF found %p on stack\n", obj); + if (findInArgList(self, jobj)) { + //LOGI("--- REF found %p on stack\n", jobj); return JNILocalRefType; } /* check locals */ - //top = SAVEAREA_FROM_FP(self->curFrame)->xtra.localRefTop; - if (dvmFindInReferenceTable(pRef, pRef->table, obj) != NULL) { - //LOGI("--- REF found %p in locals\n", obj); + if (dvmFindInReferenceTable(pRefTable, pRefTable->table, jobj) != NULL) { + //LOGI("--- REF found %p in locals\n", jobj); return JNILocalRefType; } /* check globals */ dvmLockMutex(&gDvm.jniGlobalRefLock); if (dvmFindInReferenceTable(&gDvm.jniGlobalRefTable, - gDvm.jniGlobalRefTable.table, obj)) + gDvm.jniGlobalRefTable.table, jobj)) { - //LOGI("--- REF found %p in globals\n", obj); + //LOGI("--- REF found %p in globals\n", jobj); dvmUnlockMutex(&gDvm.jniGlobalRefLock); return JNIGlobalRefType; } @@ -918,6 +1146,7 @@ jobjectRefType dvmGetJNIRefType(Object* obj) /* not found! */ return JNIInvalidRefType; +#endif } /* @@ -953,14 +1182,7 @@ static bool dvmRegisterJNIMethod(ClassObject* clazz, const char* methodName, /* keep going, I guess */ } - /* - * Point "nativeFunc" at the JNI bridge, and overload "insns" to - * point at the actual function. - */ - if (dvmIsSynchronizedMethod(method)) - dvmSetNativeFunc(method, dvmCallSynchronizedJNIMethod, fnPtr); - else - dvmSetNativeFunc(method, dvmCallJNIMethod, fnPtr); + dvmUseJNIBridge(method, fnPtr); LOGV("JNI-registered %s.%s %s\n", clazz->descriptor, methodName, signature); @@ -970,6 +1192,78 @@ bail: return result; } +/* + * Returns "true" if CheckJNI is enabled in the VM. + */ +static bool dvmIsCheckJNIEnabled(void) +{ + JavaVMExt* vm = (JavaVMExt*) gDvm.vmList; + return vm->useChecked; +} + +/* + * Point "method->nativeFunc" at the JNI bridge, and overload "method->insns" + * to point at the actual function. + */ +void dvmUseJNIBridge(Method* method, void* func) +{ + enum { + kJNIGeneral = 0, + kJNISync = 1, + kJNIVirtualNoRef = 2, + kJNIStaticNoRef = 3, + } kind; + static const DalvikBridgeFunc stdFunc[] = { + dvmCallJNIMethod_general, + dvmCallJNIMethod_synchronized, + dvmCallJNIMethod_virtualNoRef, + dvmCallJNIMethod_staticNoRef + }; + static const DalvikBridgeFunc checkFunc[] = { + dvmCheckCallJNIMethod_general, + dvmCheckCallJNIMethod_synchronized, + dvmCheckCallJNIMethod_virtualNoRef, + dvmCheckCallJNIMethod_staticNoRef + }; + + bool hasRefArg = false; + + if (dvmIsSynchronizedMethod(method)) { + /* use version with synchronization; calls into general handler */ + kind = kJNISync; + } else { + /* + * Do a quick scan through the "shorty" signature to see if the method + * takes any reference arguments. + */ + const char* cp = method->shorty; + while (*++cp != '\0') { /* pre-incr to skip return type */ + if (*cp == 'L') { + /* 'L' used for both object and array references */ + hasRefArg = true; + break; + } + } + + if (hasRefArg) { + /* use general handler to slurp up reference args */ + kind = kJNIGeneral; + } else { + /* virtual methods have a ref in args[0] (not in signature) */ + if (dvmIsStaticMethod(method)) + kind = kJNIStaticNoRef; + else + kind = kJNIVirtualNoRef; + } + } + + if (dvmIsCheckJNIEnabled()) { + dvmSetNativeFunc(method, checkFunc[kind], func); + } else { + dvmSetNativeFunc(method, stdFunc[kind], func); + } +} + /* * Get the method currently being executed by examining the interp stack. */ @@ -1020,14 +1314,15 @@ static void trackMonitorEnter(Thread* self, Object* obj) } } + /* * Track a JNI MonitorExit in the current thread. */ static void trackMonitorExit(Thread* self, Object* obj) { - ReferenceTable* refTable = &self->jniMonitorRefTable; + ReferenceTable* pRefTable = &self->jniMonitorRefTable; - if (!dvmRemoveFromReferenceTable(refTable, refTable->table, obj)) { + if (!dvmRemoveFromReferenceTable(pRefTable, pRefTable->table, obj)) { LOGE("JNI monitor %p not found in tracking list\n", obj); /* keep going? */ } else { @@ -1040,13 +1335,13 @@ static void trackMonitorExit(Thread* self, Object* obj) */ void dvmReleaseJniMonitors(Thread* self) { - ReferenceTable* refTable = &self->jniMonitorRefTable; - Object** top = refTable->table; + ReferenceTable* pRefTable = &self->jniMonitorRefTable; + Object** top = pRefTable->table; if (top == NULL) return; - Object** ptr = refTable->nextEntry; + Object** ptr = pRefTable->nextEntry; while (--ptr >= top) { if (!dvmUnlockObject(self, *ptr)) { LOGW("Unable to unlock monitor %p at thread detach\n", *ptr); @@ -1056,7 +1351,7 @@ void dvmReleaseJniMonitors(Thread* self) } /* zap it */ - refTable->nextEntry = refTable->table; + pRefTable->nextEntry = pRefTable->table; } #ifdef WITH_JNI_STACK_CHECK @@ -1114,6 +1409,250 @@ static void checkStackSum(Thread* self) #endif +/* + * =========================================================================== + * JNI call bridge + * =========================================================================== + */ + +/* + * The functions here form a bridge between interpreted code and JNI native + * functions. The basic task is to convert an array of primitives and + * references into C-style function arguments. This is architecture-specific + * and usually requires help from assembly code. + * + * The bridge takes four arguments: the array of parameters, a place to + * store the function result (if any), the method to call, and a pointer + * to the current thread. + * + * These functions aren't called directly from elsewhere in the VM. + * A pointer in the Method struct points to one of these, and when a native + * method is invoked the interpreter jumps to it. + * + * (The "internal native" methods are invoked the same way, but instead + * of calling through a bridge, the target method is called directly.) + * + * The "args" array should not be modified, but we do so anyway for + * performance reasons. We know that it points to the "outs" area on + * the current method's interpreted stack. This area is ignored by the + * precise GC, because there is no register map for a native method (for + * an interpreted method the args would be listed in the argument set). + * We know all of the values exist elsewhere on the interpreted stack, + * because the method call setup copies them right before making the call, + * so we don't have to worry about concealing stuff from the GC. + * + * If we don't want to modify "args", we either have to create a local + * copy and modify it before calling dvmPlatformInvoke, or we have to do + * the local reference replacement within dvmPlatformInvoke. The latter + * has some performance advantages, though if we can inline the local + * reference adds we may win when there's a lot of reference args (unless + * we want to code up some local ref table manipulation in assembly. + */ + +/* + * If necessary, convert the value in pResult from a local/global reference + * to an object pointer. + */ +static inline void convertReferenceResult(JNIEnv* env, JValue* pResult, + const Method* method, Thread* self) +{ +#ifdef USE_INDIRECT_REF + if (method->shorty[0] == 'L' && !dvmCheckException(self) && + pResult->l != NULL) + { + pResult->l = dvmDecodeIndirectRef(env, pResult->l); + } +#endif +} + +/* + * General form, handles all cases. + */ +void dvmCallJNIMethod_general(const u4* args, JValue* pResult, + const Method* method, Thread* self) +{ + int oldStatus; + u4* modArgs = (u4*) args; + jclass staticMethodClass; + JNIEnv* env = self->jniEnv; + + assert(method->insns != NULL); + + //LOGI("JNI calling %p (%s.%s:%s):\n", method->insns, + // method->clazz->descriptor, method->name, method->shorty); + +#ifdef USE_INDIRECT_REF + /* + * Walk the argument list, creating local references for appropriate + * arguments. + */ + int idx = 0; + if (dvmIsStaticMethod(method)) { + /* add the class object we pass in */ + staticMethodClass = addLocalReference(env, (Object*) method->clazz); + if (staticMethodClass == NULL) { + assert(dvmCheckException(self)); + return; + } + } else { + /* add "this" */ + staticMethodClass = NULL; + jobject thisObj = addLocalReference(env, (Object*) modArgs[0]); + if (thisObj == NULL) { + assert(dvmCheckException(self)); + return; + } + modArgs[idx] = (u4) thisObj; + idx = 1; + } + + const char* shorty = &method->shorty[1]; /* skip return type */ + while (*shorty != '\0') { + switch (*shorty++) { + case 'L': + //LOGI(" local %d: 0x%08x\n", idx, modArgs[idx]); + if (modArgs[idx] != 0) { + //if (!dvmIsValidObject((Object*) modArgs[idx])) + // dvmAbort(); + jobject argObj = addLocalReference(env, (Object*) modArgs[idx]); + if (argObj == NULL) { + assert(dvmCheckException(self)); + return; + } + modArgs[idx] = (u4) argObj; + } + break; + case 'D': + case 'J': + idx++; + break; + default: + /* Z B C S I -- do nothing */ + break; + } + + idx++; + } +#else + staticMethodClass = dvmIsStaticMethod(method) ? + (jclass) method->clazz : NULL; +#endif + + oldStatus = dvmChangeStatus(self, THREAD_NATIVE); + + COMPUTE_STACK_SUM(self); + dvmPlatformInvoke(env, staticMethodClass, + method->jniArgInfo, method->insSize, modArgs, method->shorty, + (void*)method->insns, pResult); + CHECK_STACK_SUM(self); + + dvmChangeStatus(self, oldStatus); + + convertReferenceResult(env, pResult, method, self); +} + +/* + * Handler for the unusual case of a synchronized native method. + * + * Lock the object, then call through the general function. + */ +void dvmCallJNIMethod_synchronized(const u4* args, JValue* pResult, + const Method* method, Thread* self) +{ + Object* lockObj; + + assert(dvmIsSynchronizedMethod(method)); + + if (dvmIsStaticMethod(method)) + lockObj = (Object*) method->clazz; + else + lockObj = (Object*) args[0]; + + LOGVV("Calling %s.%s: locking %p (%s)\n", + method->clazz->descriptor, method->name, + lockObj, lockObj->clazz->descriptor); + + dvmLockObject(self, lockObj); + dvmCallJNIMethod_general(args, pResult, method, self); + dvmUnlockObject(self, lockObj); +} + +/* + * Virtual method call, no reference arguments. + * + * We need to local-ref the "this" argument, found in args[0]. + */ +void dvmCallJNIMethod_virtualNoRef(const u4* args, JValue* pResult, + const Method* method, Thread* self) +{ + u4* modArgs = (u4*) args; + int oldStatus; + +#ifdef USE_INDIRECT_REF + jobject thisObj = addLocalReference(self->jniEnv, (Object*) args[0]); + if (thisObj == NULL) { + assert(dvmCheckException(self)); + return; + } + modArgs[0] = (u4) thisObj; +#endif + + oldStatus = dvmChangeStatus(self, THREAD_NATIVE); + + COMPUTE_STACK_SUM(self); + dvmPlatformInvoke(self->jniEnv, NULL, + method->jniArgInfo, method->insSize, modArgs, method->shorty, + (void*)method->insns, pResult); + CHECK_STACK_SUM(self); + + dvmChangeStatus(self, oldStatus); + + convertReferenceResult(self->jniEnv, pResult, method, self); +} + +/* + * Static method call, no reference arguments. + * + * We need to local-ref the class reference. + */ +void dvmCallJNIMethod_staticNoRef(const u4* args, JValue* pResult, + const Method* method, Thread* self) +{ + jclass staticMethodClass; + int oldStatus; + +#ifdef USE_INDIRECT_REF + staticMethodClass = addLocalReference(self->jniEnv, (Object*)method->clazz); + if (staticMethodClass == NULL) { + assert(dvmCheckException(self)); + return; + } +#else + staticMethodClass = (jobject) method->clazz; +#endif + + oldStatus = dvmChangeStatus(self, THREAD_NATIVE); + + COMPUTE_STACK_SUM(self); + dvmPlatformInvoke(self->jniEnv, staticMethodClass, + method->jniArgInfo, method->insSize, args, method->shorty, + (void*)method->insns, pResult); + CHECK_STACK_SUM(self); + + dvmChangeStatus(self, oldStatus); + + convertReferenceResult(self->jniEnv, pResult, method, self); +} + +/* + * Extract the return type enum from the "jniArgInfo" field. + */ +DalvikJniReturnType dvmGetArgInfoReturnType(int jniArgInfo) +{ + return (jniArgInfo & DALVIK_JNI_RETURN_MASK) >> DALVIK_JNI_RETURN_SHIFT; +} + + /* * =========================================================================== * JNI implementation @@ -1149,7 +1688,7 @@ static jclass DefineClass(JNIEnv* env, const char *name, jobject loader, UNUSED_PARAMETER(bufLen); JNI_ENTER(); - LOGW("Rejecting JNI DefineClass request\n"); + LOGW("JNI DefineClass is not supported\n"); JNI_EXIT(); return NULL; } @@ -1174,6 +1713,7 @@ static jclass FindClass(JNIEnv* env, const char* name) const Method* thisMethod; ClassObject* clazz; + jclass jclazz = NULL; Object* loader; char* descriptor = NULL; @@ -1202,25 +1742,28 @@ static jclass FindClass(JNIEnv* env, const char* name) } clazz = dvmFindClassNoInit(descriptor, loader); - clazz = addLocalReference(clazz); + jclazz = addLocalReference(env, (Object*) clazz); bail: free(descriptor); - + JNI_EXIT(); - return (jclass)clazz; + return jclazz; } /* * Return the superclass of a class. */ -static jclass GetSuperclass(JNIEnv* env, jclass clazz) +static jclass GetSuperclass(JNIEnv* env, jclass jclazz) { JNI_ENTER(); - jclass super = (jclass) ((ClassObject*) clazz)->super; - super = addLocalReference(super); + jclass jsuper = NULL; + + ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); + if (clazz != NULL) + jsuper = addLocalReference(env, (Object*)clazz->super); JNI_EXIT(); - return super; + return jsuper; } /* @@ -1228,12 +1771,14 @@ static jclass GetSuperclass(JNIEnv* env, jclass clazz) * * Like IsInstanceOf, but with a pair of class objects instead of obj+class. */ -static jboolean IsAssignableFrom(JNIEnv* env, jclass clazz1, jclass clazz2) +static jboolean IsAssignableFrom(JNIEnv* env, jclass jclazz1, jclass jclazz2) { JNI_ENTER(); - jboolean result; - result = dvmInstanceof((ClassObject*) clazz1, (ClassObject*) clazz2); + ClassObject* clazz1 = (ClassObject*) dvmDecodeIndirectRef(env, jclazz1); + ClassObject* clazz2 = (ClassObject*) dvmDecodeIndirectRef(env, jclazz2); + + jboolean result = dvmInstanceof(clazz1, clazz2); JNI_EXIT(); return result; @@ -1242,11 +1787,12 @@ static jboolean IsAssignableFrom(JNIEnv* env, jclass clazz1, jclass clazz2) /* * Given a java.lang.reflect.Method or .Constructor, return a methodID. */ -static jmethodID FromReflectedMethod(JNIEnv* env, jobject method) +static jmethodID FromReflectedMethod(JNIEnv* env, jobject jmethod) { JNI_ENTER(); jmethodID methodID; - methodID = (jmethodID) dvmGetMethodFromReflectObj((Object*)method); + Object* method = dvmDecodeIndirectRef(env, jmethod); + methodID = (jmethodID) dvmGetMethodFromReflectObj(method); JNI_EXIT(); return methodID; } @@ -1254,10 +1800,12 @@ static jmethodID FromReflectedMethod(JNIEnv* env, jobject method) /* * Given a java.lang.reflect.Field, return a fieldID. */ -static jfieldID FromReflectedField(JNIEnv* env, jobject field) +static jfieldID FromReflectedField(JNIEnv* env, jobject jfield) { JNI_ENTER(); - jfieldID fieldID = (jfieldID) dvmGetFieldFromReflectObj((Object*)field); + jfieldID fieldID; + Object* field = dvmDecodeIndirectRef(env, jfield); + fieldID = (jfieldID) dvmGetFieldFromReflectObj(field); JNI_EXIT(); return fieldID; } @@ -1269,17 +1817,16 @@ static jfieldID FromReflectedField(JNIEnv* env, jobject field) * * Throws OutOfMemory and returns NULL on failure. */ -static jobject ToReflectedMethod(JNIEnv* env, jclass cls, jmethodID methodID, +static jobject ToReflectedMethod(JNIEnv* env, jclass jcls, jmethodID methodID, jboolean isStatic) { JNI_ENTER(); - jobject obj; - obj = (jobject) dvmCreateReflectObjForMethod((ClassObject*) cls, - (Method*) methodID); + ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jcls); + Object* obj = dvmCreateReflectObjForMethod(clazz, (Method*) methodID); dvmReleaseTrackedAlloc(obj, NULL); - obj = addLocalReference(obj); + jobject jobj = addLocalReference(env, obj); JNI_EXIT(); - return obj; + return jobj; } /* @@ -1289,50 +1836,50 @@ static jobject ToReflectedMethod(JNIEnv* env, jclass cls, jmethodID methodID, * * Throws OutOfMemory and returns NULL on failure. */ -static jobject ToReflectedField(JNIEnv* env, jclass cls, jfieldID fieldID, +static jobject ToReflectedField(JNIEnv* env, jclass jcls, jfieldID fieldID, jboolean isStatic) { JNI_ENTER(); - jobject obj; - obj = (jobject) dvmCreateReflectObjForField((ClassObject*) cls, - (Field*) fieldID); + ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jcls); + Object* obj = dvmCreateReflectObjForField(jcls, (Field*) fieldID); dvmReleaseTrackedAlloc(obj, NULL); - obj = addLocalReference(obj); + jobject jobj = addLocalReference(env, obj); JNI_EXIT(); - return obj; + return jobj; } - /* * Take this exception and throw it. */ -static jint Throw(JNIEnv* env, jthrowable obj) +static jint Throw(JNIEnv* env, jthrowable jobj) { JNI_ENTER(); jint retval; - if (obj != NULL) { + if (jobj != NULL) { + Object* obj = dvmDecodeIndirectRef(env, jobj); dvmSetException(_self, obj); retval = JNI_OK; - } else + } else { retval = JNI_ERR; + } JNI_EXIT(); return retval; } /* - * Constructs an exeption object from the specified class with the message + * Constructs an exception object from the specified class with the message * specified by "message", and throws it. */ -static jint ThrowNew(JNIEnv* env, jclass clazz, const char* message) +static jint ThrowNew(JNIEnv* env, jclass jclazz, const char* message) { JNI_ENTER(); - ClassObject* classObj = (ClassObject*) clazz; - - dvmThrowExceptionByClass(classObj, message); + ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); + dvmThrowExceptionByClass(clazz, message); + // TODO: should return failure if this didn't work (e.g. OOM) JNI_EXIT(); return JNI_OK; @@ -1351,10 +1898,10 @@ static jthrowable ExceptionOccurred(JNIEnv* env) JNI_ENTER(); Object* exception; - Object* localException; + jobject localException; - exception = (Object*) dvmGetException(_self); - localException = addLocalReference(exception); + exception = dvmGetException(_self); + localException = addLocalReference(env, exception); if (localException == NULL && exception != NULL) { /* * We were unable to add a new local reference, and threw a new @@ -1419,7 +1966,7 @@ static jint PushLocalFrame(JNIEnv* env, jint capacity) { JNI_ENTER(); int result = JNI_OK; - if (!ensureLocalCapacity(capacity) || + if (!ensureLocalCapacity(env, capacity) || !dvmPushLocalFrame(_self /*dvmThreadSelf()*/, dvmGetCurrentJNIMethod())) { /* yes, OutOfMemoryError, not StackOverflowError */ @@ -1436,16 +1983,17 @@ static jint PushLocalFrame(JNIEnv* env, jint capacity) * Pop the local frame off. If "result" is not null, add it as a * local reference on the now-current frame. */ -static jobject PopLocalFrame(JNIEnv* env, jobject result) +static jobject PopLocalFrame(JNIEnv* env, jobject jresult) { JNI_ENTER(); + Object* result = dvmDecodeIndirectRef(env, jresult); if (!dvmPopLocalFrame(_self /*dvmThreadSelf()*/)) { LOGW("JNI WARNING: too many PopLocalFrame calls\n"); dvmClearException(_self); dvmThrowException("Ljava/lang/RuntimeException;", "too many PopLocalFrame calls"); } - result = addLocalReference(result); + jresult = addLocalReference(env, result); JNI_EXIT(); return result; } @@ -1453,9 +2001,10 @@ static jobject PopLocalFrame(JNIEnv* env, jobject result) /* * Add a reference to the global list. */ -static jobject NewGlobalRef(JNIEnv* env, jobject obj) +static jobject NewGlobalRef(JNIEnv* env, jobject jobj) { JNI_ENTER(); + Object* obj = dvmDecodeIndirectRef(env, jobj); jobject retval = addGlobalReference(obj); JNI_EXIT(); return retval; @@ -1464,10 +2013,10 @@ static jobject NewGlobalRef(JNIEnv* env, jobject obj) /* * Delete a reference from the global list. */ -static void DeleteGlobalRef(JNIEnv* env, jobject globalRef) +static void DeleteGlobalRef(JNIEnv* env, jobject jglobalRef) { JNI_ENTER(); - deleteGlobalReference(globalRef); + deleteGlobalReference(jglobalRef); JNI_EXIT(); } @@ -1475,12 +2024,11 @@ static void DeleteGlobalRef(JNIEnv* env, jobject globalRef) /* * Add a reference to the local list. */ -static jobject NewLocalRef(JNIEnv* env, jobject ref) +static jobject NewLocalRef(JNIEnv* env, jobject jref) { JNI_ENTER(); - - jobject retval = addLocalReference(ref); - + Object* obj = dvmDecodeIndirectRef(env, jref); + jobject retval = addLocalReference(env, obj); JNI_EXIT(); return retval; } @@ -1488,10 +2036,10 @@ static jobject NewLocalRef(JNIEnv* env, jobject ref) /* * Delete a reference from the local list. */ -static void DeleteLocalRef(JNIEnv* env, jobject localRef) +static void DeleteLocalRef(JNIEnv* env, jobject jlocalRef) { JNI_ENTER(); - deleteLocalReference(localRef); + deleteLocalReference(env, jlocalRef); JNI_EXIT(); } @@ -1499,10 +2047,10 @@ static void DeleteLocalRef(JNIEnv* env, jobject localRef) * Ensure that the local references table can hold at least this many * references. */ -static jint EnsureLocalCapacity(JNIEnv *env, jint capacity) +static jint EnsureLocalCapacity(JNIEnv* env, jint capacity) { JNI_ENTER(); - bool okay = ensureLocalCapacity(capacity); + bool okay = ensureLocalCapacity(env, capacity); if (!okay) { dvmThrowException("Ljava/lang/OutOfMemoryError;", "can't ensure local reference capacity"); @@ -1518,10 +2066,12 @@ static jint EnsureLocalCapacity(JNIEnv *env, jint capacity) /* * Determine whether two Object references refer to the same underlying object. */ -static jboolean IsSameObject(JNIEnv* env, jobject ref1, jobject ref2) +static jboolean IsSameObject(JNIEnv* env, jobject jref1, jobject jref2) { JNI_ENTER(); - jboolean result = (ref1 == ref2); + Object* obj1 = dvmDecodeIndirectRef(env, jref1); + Object* obj2 = dvmDecodeIndirectRef(env, jref2); + jboolean result = (obj1 == obj2); JNI_EXIT(); return result; } @@ -1533,83 +2083,82 @@ static jobject AllocObject(JNIEnv* env, jclass jclazz) { JNI_ENTER(); - ClassObject* clazz = (ClassObject*) jclazz; - jobject newObj; + ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); + jobject result; if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) { assert(dvmCheckException(_self)); - newObj = NULL; + result = NULL; } else { - newObj = (jobject) dvmAllocObject(clazz, ALLOC_DONT_TRACK); - newObj = addLocalReference(newObj); + Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK); + result = addLocalReference(env, newObj); } JNI_EXIT(); - return newObj; + return result; } /* - * Construct a new object. + * Allocate a new object and invoke the supplied constructor. */ static jobject NewObject(JNIEnv* env, jclass jclazz, jmethodID methodID, ...) { JNI_ENTER(); - - ClassObject* clazz = (ClassObject*) jclazz; - jobject newObj; + ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); + jobject result; if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) { assert(dvmCheckException(_self)); - newObj = NULL; + result = NULL; } else { - newObj = (jobject) dvmAllocObject(clazz, ALLOC_DONT_TRACK); - newObj = addLocalReference(newObj); + Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK); + result = addLocalReference(env, newObj); if (newObj != NULL) { JValue unused; va_list args; va_start(args, methodID); - dvmCallMethodV(_self, (Method*) methodID, (Object*)newObj, &unused, + dvmCallMethodV(_self, (Method*) methodID, newObj, true, &unused, args); va_end(args); } } JNI_EXIT(); - return newObj; + return result; } -static jobject NewObjectV(JNIEnv* env, jclass clazz, jmethodID methodID, +static jobject NewObjectV(JNIEnv* env, jclass jclazz, jmethodID methodID, va_list args) { JNI_ENTER(); + ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); + jobject result; - jobject newObj; - newObj = (jobject) dvmAllocObject((ClassObject*) clazz, ALLOC_DONT_TRACK); - newObj = addLocalReference(newObj); + Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK); + result = addLocalReference(env, newObj); if (newObj != NULL) { JValue unused; - dvmCallMethodV(_self, (Method*) methodID, (Object*)newObj, &unused, - args); + dvmCallMethodV(_self, (Method*) methodID, newObj, true, &unused, args); } JNI_EXIT(); - return newObj; + return result; } -static jobject NewObjectA(JNIEnv* env, jclass clazz, jmethodID methodID, +static jobject NewObjectA(JNIEnv* env, jclass jclazz, jmethodID methodID, jvalue* args) { JNI_ENTER(); + ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); + jobject result; - jobject newObj; - newObj = (jobject) dvmAllocObject((ClassObject*) clazz, ALLOC_DONT_TRACK); - newObj = addLocalReference(newObj); + Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK); + result = addLocalReference(env, newObj); if (newObj != NULL) { JValue unused; - dvmCallMethodA(_self, (Method*) methodID, (Object*)newObj, &unused, - args); + dvmCallMethodA(_self, (Method*) methodID, newObj, true, &unused, args); } JNI_EXIT(); - return newObj; + return result; } /* @@ -1617,33 +2166,37 @@ static jobject NewObjectA(JNIEnv* env, jclass clazz, jmethodID methodID, * * JNI spec says: obj must not be NULL. */ -static jclass GetObjectClass(JNIEnv* env, jobject obj) +static jclass GetObjectClass(JNIEnv* env, jobject jobj) { JNI_ENTER(); - assert(obj != NULL); + assert(jobj != NULL); - jclass clazz; - clazz = (jclass) ((Object*)obj)->clazz; - clazz = addLocalReference(clazz); + Object* obj = dvmDecodeIndirectRef(env, jobj); + jclass jclazz = addLocalReference(env, (Object*) obj->clazz); JNI_EXIT(); - return clazz; + return jclazz; } /* * Determine whether "obj" is an instance of "clazz". */ -static jboolean IsInstanceOf(JNIEnv* env, jobject obj, jclass clazz) +static jboolean IsInstanceOf(JNIEnv* env, jobject jobj, jclass jclazz) { JNI_ENTER(); + assert(jclazz != NULL); + jboolean result; - if (obj == NULL) + if (jobj == NULL) { result = true; - else - result = dvmInstanceof(((Object*)obj)->clazz, (ClassObject*) clazz); + } else { + Object* obj = dvmDecodeIndirectRef(env, jobj); + ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); + result = dvmInstanceof(obj->clazz, clazz); + } JNI_EXIT(); return result; @@ -1663,7 +2216,7 @@ static jmethodID GetMethodID(JNIEnv* env, jclass jclazz, const char* name, { JNI_ENTER(); - ClassObject* clazz = (ClassObject*) jclazz; + ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); jmethodID id = NULL; if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) { @@ -1686,8 +2239,8 @@ static jmethodID GetMethodID(JNIEnv* env, jclass jclazz, const char* name, meth = NULL; } if (meth == NULL) { - LOGI("Method not found: '%s' '%s' in %s\n", - name, sig, clazz->descriptor); + LOGD("GetMethodID: method not found: %s.%s:%s\n", + clazz->descriptor, name, sig); dvmThrowException("Ljava/lang/NoSuchMethodError;", name); } @@ -1714,7 +2267,7 @@ static jfieldID GetFieldID(JNIEnv* env, jclass jclazz, { JNI_ENTER(); - ClassObject* clazz = (ClassObject*) jclazz; + ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); jfieldID id; if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) { @@ -1722,8 +2275,11 @@ static jfieldID GetFieldID(JNIEnv* env, jclass jclazz, id = NULL; } else { id = (jfieldID) dvmFindInstanceFieldHier(clazz, name, sig); - if (id == NULL) + if (id == NULL) { + LOGD("GetFieldID: unable to find field %s.%s:%s\n", + clazz->descriptor, name, sig); dvmThrowException("Ljava/lang/NoSuchFieldError;", name); + } } JNI_EXIT(); return id; @@ -1737,7 +2293,7 @@ static jmethodID GetStaticMethodID(JNIEnv* env, jclass jclazz, { JNI_ENTER(); - ClassObject* clazz = (ClassObject*) jclazz; + ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); jmethodID id; if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) { @@ -1777,7 +2333,7 @@ static jfieldID GetStaticFieldID(JNIEnv* env, jclass jclazz, { JNI_ENTER(); - ClassObject* clazz = (ClassObject*) jclazz; + ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); jfieldID id; if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) { @@ -1798,15 +2354,19 @@ static jfieldID GetStaticFieldID(JNIEnv* env, jclass jclazz, * If we get an object reference, add it to the local refs list. */ #define GET_STATIC_TYPE_FIELD(_ctype, _jname, _isref) \ - static _ctype GetStatic##_jname##Field(JNIEnv* env, jclass clazz, \ + static _ctype GetStatic##_jname##Field(JNIEnv* env, jclass jclazz, \ jfieldID fieldID) \ { \ - UNUSED_PARAMETER(clazz); \ + UNUSED_PARAMETER(jclazz); \ JNI_ENTER(); \ StaticField* sfield = (StaticField*) fieldID; \ - _ctype value = dvmGetStaticField##_jname(sfield); \ - if (_isref) /* only when _ctype==jobject */ \ - value = (_ctype)(u4)addLocalReference((jobject)(u4)value); \ + _ctype value; \ + if (_isref) { /* only when _ctype==jobject */ \ + Object* obj = dvmGetStaticFieldObject(sfield); \ + value = (_ctype)(u4)addLocalReference(env, obj); \ + } else { \ + value = dvmGetStaticField##_jname(sfield); \ + } \ JNI_EXIT(); \ return value; \ } @@ -1823,25 +2383,30 @@ GET_STATIC_TYPE_FIELD(jdouble, Double, false); /* * Set a static field. */ -#define SET_STATIC_TYPE_FIELD(_ctype, _jname, _jvfld) \ - static void SetStatic##_jname##Field(JNIEnv* env, jclass clazz, \ +#define SET_STATIC_TYPE_FIELD(_ctype, _jname, _isref) \ + static void SetStatic##_jname##Field(JNIEnv* env, jclass jclazz, \ jfieldID fieldID, _ctype value) \ { \ - UNUSED_PARAMETER(clazz); \ + UNUSED_PARAMETER(jclazz); \ JNI_ENTER(); \ StaticField* sfield = (StaticField*) fieldID; \ - dvmSetStaticField##_jname(sfield, value); \ + if (_isref) { /* only when _ctype==jobject */ \ + Object* valObj = dvmDecodeIndirectRef(env, (jobject)(u4)value); \ + dvmSetStaticFieldObject(sfield, valObj); \ + } else { \ + dvmSetStaticField##_jname(sfield, value); \ + } \ JNI_EXIT(); \ } -SET_STATIC_TYPE_FIELD(jobject, Object, l); -SET_STATIC_TYPE_FIELD(jboolean, Boolean, z); -SET_STATIC_TYPE_FIELD(jbyte, Byte, b); -SET_STATIC_TYPE_FIELD(jchar, Char, c); -SET_STATIC_TYPE_FIELD(jshort, Short, s); -SET_STATIC_TYPE_FIELD(jint, Int, i); -SET_STATIC_TYPE_FIELD(jlong, Long, j); -SET_STATIC_TYPE_FIELD(jfloat, Float, f); -SET_STATIC_TYPE_FIELD(jdouble, Double, d); +SET_STATIC_TYPE_FIELD(jobject, Object, true); +SET_STATIC_TYPE_FIELD(jboolean, Boolean, false); +SET_STATIC_TYPE_FIELD(jbyte, Byte, false); +SET_STATIC_TYPE_FIELD(jchar, Char, false); +SET_STATIC_TYPE_FIELD(jshort, Short, false); +SET_STATIC_TYPE_FIELD(jint, Int, false); +SET_STATIC_TYPE_FIELD(jlong, Long, false); +SET_STATIC_TYPE_FIELD(jfloat, Float, false); +SET_STATIC_TYPE_FIELD(jdouble, Double, false); /* * Get an instance field. @@ -1849,14 +2414,19 @@ SET_STATIC_TYPE_FIELD(jdouble, Double, d); * If we get an object reference, add it to the local refs list. */ #define GET_TYPE_FIELD(_ctype, _jname, _isref) \ - static _ctype Get##_jname##Field(JNIEnv* env, jobject obj, \ + static _ctype Get##_jname##Field(JNIEnv* env, jobject jobj, \ jfieldID fieldID) \ { \ JNI_ENTER(); \ + Object* obj = dvmDecodeIndirectRef(env, jobj); \ InstField* field = (InstField*) fieldID; \ - _ctype value = dvmGetField##_jname((Object*) obj,field->byteOffset);\ - if (_isref) /* only when _ctype==jobject */ \ - value = (_ctype)(u4)addLocalReference((jobject)(u4)value); \ + _ctype value; \ + if (_isref) { /* only when _ctype==jobject */ \ + Object* valObj = dvmGetFieldObject(obj, field->byteOffset); \ + value = (_ctype)(u4)addLocalReference(env, valObj); \ + } else { \ + value = dvmGetField##_jname(obj, field->byteOffset); \ + } \ JNI_EXIT(); \ return value; \ } @@ -1873,24 +2443,30 @@ GET_TYPE_FIELD(jdouble, Double, false); /* * Set an instance field. */ -#define SET_TYPE_FIELD(_ctype, _jname) \ - static void Set##_jname##Field(JNIEnv* env, jobject obj, \ +#define SET_TYPE_FIELD(_ctype, _jname, _isref) \ + static void Set##_jname##Field(JNIEnv* env, jobject jobj, \ jfieldID fieldID, _ctype value) \ { \ JNI_ENTER(); \ + Object* obj = dvmDecodeIndirectRef(env, jobj); \ InstField* field = (InstField*) fieldID; \ - dvmSetField##_jname((Object*) obj, field->byteOffset, value); \ + if (_isref) { /* only when _ctype==jobject */ \ + Object* valObj = dvmDecodeIndirectRef(env, (jobject)(u4)value); \ + dvmSetFieldObject(obj, field->byteOffset, valObj); \ + } else { \ + dvmSetField##_jname(obj, field->byteOffset, value); \ + } \ JNI_EXIT(); \ } -SET_TYPE_FIELD(jobject, Object); -SET_TYPE_FIELD(jboolean, Boolean); -SET_TYPE_FIELD(jbyte, Byte); -SET_TYPE_FIELD(jchar, Char); -SET_TYPE_FIELD(jshort, Short); -SET_TYPE_FIELD(jint, Int); -SET_TYPE_FIELD(jlong, Long); -SET_TYPE_FIELD(jfloat, Float); -SET_TYPE_FIELD(jdouble, Double); +SET_TYPE_FIELD(jobject, Object, true); +SET_TYPE_FIELD(jboolean, Boolean, false); +SET_TYPE_FIELD(jbyte, Byte, false); +SET_TYPE_FIELD(jchar, Char, false); +SET_TYPE_FIELD(jshort, Short, false); +SET_TYPE_FIELD(jint, Int, false); +SET_TYPE_FIELD(jlong, Long, false); +SET_TYPE_FIELD(jfloat, Float, false); +SET_TYPE_FIELD(jdouble, Double, false); /* * Make a virtual method call. @@ -1899,60 +2475,60 @@ SET_TYPE_FIELD(jdouble, Double); * returning an Object, we have to add it to the local references table. */ #define CALL_VIRTUAL(_ctype, _jname, _retfail, _retok, _isref) \ - static _ctype Call##_jname##Method(JNIEnv* env, jobject obj, \ + static _ctype Call##_jname##Method(JNIEnv* env, jobject jobj, \ jmethodID methodID, ...) \ { \ JNI_ENTER(); \ - Object* dobj = (Object*) obj; \ + Object* obj = dvmDecodeIndirectRef(env, jobj); \ const Method* meth; \ va_list args; \ JValue result; \ - meth = dvmGetVirtualizedMethod(dobj->clazz, (Method*)methodID); \ + meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \ if (meth == NULL) { \ JNI_EXIT(); \ return _retfail; \ } \ va_start(args, methodID); \ - dvmCallMethodV(_self, meth, dobj, &result, args); \ + dvmCallMethodV(_self, meth, obj, true, &result, args); \ va_end(args); \ - if (_isref) \ - result.l = addLocalReference(result.l); \ + if (_isref && !dvmCheckException(_self)) \ + result.l = addLocalReference(env, result.l); \ JNI_EXIT(); \ return _retok; \ } \ - static _ctype Call##_jname##MethodV(JNIEnv* env, jobject obj, \ + static _ctype Call##_jname##MethodV(JNIEnv* env, jobject jobj, \ jmethodID methodID, va_list args) \ { \ JNI_ENTER(); \ - Object* dobj = (Object*) obj; \ + Object* obj = dvmDecodeIndirectRef(env, jobj); \ const Method* meth; \ JValue result; \ - meth = dvmGetVirtualizedMethod(dobj->clazz, (Method*)methodID); \ + meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \ if (meth == NULL) { \ JNI_EXIT(); \ return _retfail; \ } \ - dvmCallMethodV(_self, meth, dobj, &result, args); \ - if (_isref) \ - result.l = addLocalReference(result.l); \ + dvmCallMethodV(_self, meth, obj, true, &result, args); \ + if (_isref && !dvmCheckException(_self)) \ + result.l = addLocalReference(env, result.l); \ JNI_EXIT(); \ return _retok; \ } \ - static _ctype Call##_jname##MethodA(JNIEnv* env, jobject obj, \ + static _ctype Call##_jname##MethodA(JNIEnv* env, jobject jobj, \ jmethodID methodID, jvalue* args) \ { \ JNI_ENTER(); \ - Object* dobj = (Object*) obj; \ + Object* obj = dvmDecodeIndirectRef(env, jobj); \ const Method* meth; \ JValue result; \ - meth = dvmGetVirtualizedMethod(dobj->clazz, (Method*)methodID); \ + meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \ if (meth == NULL) { \ JNI_EXIT(); \ return _retfail; \ } \ - dvmCallMethodA(_self, meth, dobj, &result, args); \ - if (_isref) \ - result.l = addLocalReference(result.l); \ + dvmCallMethodA(_self, meth, obj, true, &result, args); \ + if (_isref && !dvmCheckException(_self)) \ + result.l = addLocalReference(env, result.l); \ JNI_EXIT(); \ return _retok; \ } @@ -1975,63 +2551,66 @@ CALL_VIRTUAL(void, Void, , , false); * Three versions (..., va_list, jvalue[]) for each return type. */ #define CALL_NONVIRTUAL(_ctype, _jname, _retfail, _retok, _isref) \ - static _ctype CallNonvirtual##_jname##Method(JNIEnv* env, jobject obj, \ - jclass clazz, jmethodID methodID, ...) \ + static _ctype CallNonvirtual##_jname##Method(JNIEnv* env, jobject jobj, \ + jclass jclazz, jmethodID methodID, ...) \ { \ JNI_ENTER(); \ - Object* dobj = (Object*) obj; \ + Object* obj = dvmDecodeIndirectRef(env, jobj); \ + ClassObject* clazz = \ + (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \ const Method* meth; \ va_list args; \ JValue result; \ - meth = dvmGetVirtualizedMethod((ClassObject*)clazz, \ - (Method*)methodID); \ + meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \ if (meth == NULL) { \ JNI_EXIT(); \ return _retfail; \ } \ va_start(args, methodID); \ - dvmCallMethodV(_self, meth, dobj, &result, args); \ - if (_isref) \ - result.l = addLocalReference(result.l); \ + dvmCallMethodV(_self, meth, obj, true, &result, args); \ + if (_isref && !dvmCheckException(_self)) \ + result.l = addLocalReference(env, result.l); \ va_end(args); \ JNI_EXIT(); \ return _retok; \ } \ - static _ctype CallNonvirtual##_jname##MethodV(JNIEnv* env, jobject obj, \ - jclass clazz, jmethodID methodID, va_list args) \ + static _ctype CallNonvirtual##_jname##MethodV(JNIEnv* env, jobject jobj,\ + jclass jclazz, jmethodID methodID, va_list args) \ { \ JNI_ENTER(); \ - Object* dobj = (Object*) obj; \ + Object* obj = dvmDecodeIndirectRef(env, jobj); \ + ClassObject* clazz = \ + (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \ const Method* meth; \ JValue result; \ - meth = dvmGetVirtualizedMethod((ClassObject*)clazz, \ - (Method*)methodID); \ + meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \ if (meth == NULL) { \ JNI_EXIT(); \ return _retfail; \ } \ - dvmCallMethodV(_self, meth, dobj, &result, args); \ - if (_isref) \ - result.l = addLocalReference(result.l); \ + dvmCallMethodV(_self, meth, obj, true, &result, args); \ + if (_isref && !dvmCheckException(_self)) \ + result.l = addLocalReference(env, result.l); \ JNI_EXIT(); \ return _retok; \ } \ - static _ctype CallNonvirtual##_jname##MethodA(JNIEnv* env, jobject obj, \ - jclass clazz, jmethodID methodID, jvalue* args) \ + static _ctype CallNonvirtual##_jname##MethodA(JNIEnv* env, jobject jobj,\ + jclass jclazz, jmethodID methodID, jvalue* args) \ { \ JNI_ENTER(); \ - Object* dobj = (Object*) obj; \ + Object* obj = dvmDecodeIndirectRef(env, jobj); \ + ClassObject* clazz = \ + (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \ const Method* meth; \ JValue result; \ - meth = dvmGetVirtualizedMethod((ClassObject*)clazz, \ - (Method*)methodID); \ + meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \ if (meth == NULL) { \ JNI_EXIT(); \ return _retfail; \ } \ - dvmCallMethodA(_self, meth, dobj, &result, args); \ - if (_isref) \ - result.l = addLocalReference(result.l); \ + dvmCallMethodA(_self, meth, obj, true, &result, args); \ + if (_isref && !dvmCheckException(_self)) \ + result.l = addLocalReference(env, result.l); \ JNI_EXIT(); \ return _retok; \ } @@ -2051,42 +2630,42 @@ CALL_NONVIRTUAL(void, Void, , , false); * Call a static method. */ #define CALL_STATIC(_ctype, _jname, _retfail, _retok, _isref) \ - static _ctype CallStatic##_jname##Method(JNIEnv* env, jclass clazz, \ + static _ctype CallStatic##_jname##Method(JNIEnv* env, jclass jclazz, \ jmethodID methodID, ...) \ { \ + UNUSED_PARAMETER(jclazz); \ JNI_ENTER(); \ JValue result; \ va_list args; \ - assert((ClassObject*) clazz == ((Method*)methodID)->clazz); \ va_start(args, methodID); \ - dvmCallMethodV(_self, (Method*) methodID, NULL, &result, args); \ + dvmCallMethodV(_self, (Method*)methodID, NULL, true, &result, args);\ va_end(args); \ - if (_isref) \ - result.l = addLocalReference(result.l); \ + if (_isref && !dvmCheckException(_self)) \ + result.l = addLocalReference(env, result.l); \ JNI_EXIT(); \ return _retok; \ } \ - static _ctype CallStatic##_jname##MethodV(JNIEnv* env, jclass clazz, \ + static _ctype CallStatic##_jname##MethodV(JNIEnv* env, jclass jclazz, \ jmethodID methodID, va_list args) \ { \ + UNUSED_PARAMETER(jclazz); \ JNI_ENTER(); \ JValue result; \ - assert((ClassObject*) clazz == ((Method*)methodID)->clazz); \ - dvmCallMethodV(_self, (Method*) methodID, NULL, &result, args); \ - if (_isref) \ - result.l = addLocalReference(result.l); \ + dvmCallMethodV(_self, (Method*)methodID, NULL, true, &result, args);\ + if (_isref && !dvmCheckException(_self)) \ + result.l = addLocalReference(env, result.l); \ JNI_EXIT(); \ return _retok; \ } \ - static _ctype CallStatic##_jname##MethodA(JNIEnv* env, jclass clazz, \ + static _ctype CallStatic##_jname##MethodA(JNIEnv* env, jclass jclazz, \ jmethodID methodID, jvalue* args) \ { \ + UNUSED_PARAMETER(jclazz); \ JNI_ENTER(); \ JValue result; \ - assert((ClassObject*) clazz == ((Method*)methodID)->clazz); \ - dvmCallMethodA(_self, (Method*) methodID, NULL, &result, args); \ - if (_isref) \ - result.l = addLocalReference(result.l); \ + dvmCallMethodA(_self, (Method*)methodID, NULL, true, &result, args);\ + if (_isref && !dvmCheckException(_self)) \ + result.l = addLocalReference(env, result.l); \ JNI_EXIT(); \ return _retok; \ } @@ -2110,53 +2689,51 @@ CALL_STATIC(void, Void, , , false); static jstring NewString(JNIEnv* env, const jchar* unicodeChars, jsize len) { JNI_ENTER(); + jobject retval; - StringObject* jstr; - jstr = dvmCreateStringFromUnicode(unicodeChars, len); - if (jstr != NULL) { + StringObject* jstr = dvmCreateStringFromUnicode(unicodeChars, len); + if (jstr == NULL) { + retval = NULL; + } else { dvmReleaseTrackedAlloc((Object*) jstr, NULL); - jstr = addLocalReference((jstring) jstr); + retval = addLocalReference(env, (Object*) jstr); } JNI_EXIT(); - return jstr; + return retval; } /* * Return the length of a String in Unicode character units. */ -static jsize GetStringLength(JNIEnv* env, jstring string) +static jsize GetStringLength(JNIEnv* env, jstring jstr) { JNI_ENTER(); - jsize len = dvmStringLen((StringObject*) string); + StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr); + jsize len = dvmStringLen(strObj); JNI_EXIT(); return len; } + /* - * Get a pointer to the string's character data. + * Get a string's character data. * * The result is guaranteed to be valid until ReleaseStringChars is - * called, which means we can't just hold a reference to it in the local - * refs table. We have to add it to the global refs. - * - * Technically, we don't need to hold a reference to the String, but rather - * to the Char[] object within the String. - * - * We could also just allocate some storage and copy the data into it, - * but it's a choice between our synchronized global reference table and - * libc's synchronized heap allocator. + * called, which means we have to pin it or return a copy. */ -static const jchar* GetStringChars(JNIEnv* env, jstring string, - jboolean* isCopy) +static const jchar* GetStringChars(JNIEnv* env, jstring jstr, jboolean* isCopy) { JNI_ENTER(); - const u2* data = dvmStringChars((StringObject*) string); - addGlobalReference(string); + StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr); + ArrayObject* strChars = dvmStringCharArray(strObj); + + pinPrimitiveArray(strChars); + const u2* data = dvmStringChars(strObj); if (isCopy != NULL) *isCopy = JNI_FALSE; @@ -2167,10 +2744,12 @@ static const jchar* GetStringChars(JNIEnv* env, jstring string, /* * Release our grip on some characters from a string. */ -static void ReleaseStringChars(JNIEnv* env, jstring string, const jchar* chars) +static void ReleaseStringChars(JNIEnv* env, jstring jstr, const jchar* chars) { JNI_ENTER(); - deleteGlobalReference(string); + StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr); + ArrayObject* strChars = dvmStringCharArray(strObj); + unpinPrimitiveArray(strChars); JNI_EXIT(); } @@ -2184,30 +2763,30 @@ static jstring NewStringUTF(JNIEnv* env, const char* bytes) { JNI_ENTER(); - StringObject* newStr; - + jstring result; + if (bytes == NULL) { - newStr = NULL; + result = NULL; } else { - newStr = dvmCreateStringFromCstr(bytes, ALLOC_DEFAULT); - if (newStr != NULL) { - dvmReleaseTrackedAlloc((Object*)newStr, NULL); - newStr = addLocalReference((jstring) newStr); - } + /* note newStr could come back NULL on OOM */ + StringObject* newStr = dvmCreateStringFromCstr(bytes, ALLOC_DEFAULT); + result = addLocalReference(env, (Object*) newStr); + dvmReleaseTrackedAlloc((Object*)newStr, NULL); } JNI_EXIT(); - return (jstring)newStr; + return result; } /* * Return the length in bytes of the modified UTF-8 form of the string. */ -static jsize GetStringUTFLength(JNIEnv* env, jstring string) +static jsize GetStringUTFLength(JNIEnv* env, jstring jstr) { JNI_ENTER(); - jsize len = dvmStringUtf8ByteLen((StringObject*) string); + StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr); + jsize len = dvmStringUtf8ByteLen(strObj); JNI_EXIT(); return len; @@ -2227,20 +2806,21 @@ static jsize GetStringUTFLength(JNIEnv* env, jstring string) * which should catch this sort of thing during development.) Certain other * VMs will crash with a segmentation fault. */ -static const char* GetStringUTFChars(JNIEnv* env, jstring string, +static const char* GetStringUTFChars(JNIEnv* env, jstring jstr, jboolean* isCopy) { JNI_ENTER(); char* newStr; - if (string == NULL) { + if (jstr == NULL) { /* this shouldn't happen; throw NPE? */ newStr = NULL; } else { if (isCopy != NULL) *isCopy = JNI_TRUE; - newStr = dvmCreateCstrFromString((StringObject*) string); + StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr); + newStr = dvmCreateCstrFromString(strObj); if (newStr == NULL) { /* assume memory failure */ dvmThrowException("Ljava/lang/OutOfMemoryError;", @@ -2255,7 +2835,7 @@ static const char* GetStringUTFChars(JNIEnv* env, jstring string, /* * Release a string created by GetStringUTFChars(). */ -static void ReleaseStringUTFChars(JNIEnv* env, jstring string, const char* utf) +static void ReleaseStringUTFChars(JNIEnv* env, jstring jstr, const char* utf) { JNI_ENTER(); free((char*)utf); @@ -2265,11 +2845,12 @@ static void ReleaseStringUTFChars(JNIEnv* env, jstring string, const char* utf) /* * Return the capacity of the array. */ -static jsize GetArrayLength(JNIEnv* env, jarray array) +static jsize GetArrayLength(JNIEnv* env, jarray jarr) { JNI_ENTER(); - jsize length = ((ArrayObject*) array)->length; + ArrayObject* arrObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr); + jsize length = arrObj->length; JNI_EXIT(); return length; @@ -2279,12 +2860,13 @@ static jsize GetArrayLength(JNIEnv* env, jarray array) * Construct a new array that holds objects from class "elementClass". */ static jobjectArray NewObjectArray(JNIEnv* env, jsize length, - jclass elementClass, jobject initialElement) + jclass jelementClass, jobject jinitialElement) { JNI_ENTER(); - ClassObject* elemClassObj = (ClassObject*) elementClass; - ArrayObject* newObj = NULL; + jobjectArray newArray = NULL; + ClassObject* elemClassObj = + (ClassObject*) dvmDecodeIndirectRef(env, jelementClass); if (elemClassObj == NULL) { dvmThrowException("Ljava/lang/NullPointerException;", @@ -2292,28 +2874,30 @@ static jobjectArray NewObjectArray(JNIEnv* env, jsize length, goto bail; } - newObj = dvmAllocObjectArray(elemClassObj, length, ALLOC_DEFAULT); + ArrayObject* newObj = + dvmAllocObjectArray(elemClassObj, length, ALLOC_DEFAULT); if (newObj == NULL) { assert(dvmCheckException(_self)); goto bail; } + newArray = addLocalReference(env, (Object*) newObj); dvmReleaseTrackedAlloc((Object*) newObj, NULL); /* * Initialize the array. Trashes "length". */ - if (initialElement != NULL) { + if (jinitialElement != NULL) { + Object* initialElement = dvmDecodeIndirectRef(env, jinitialElement); Object** arrayData = (Object**) newObj->contents; while (length--) - *arrayData++ = (Object*) initialElement; + *arrayData++ = initialElement; } - newObj = addLocalReference((jobjectArray) newObj); bail: JNI_EXIT(); - return (jobjectArray) newObj; + return newArray; } /* @@ -2321,15 +2905,15 @@ bail: * * Add the object to the local references table in case the array goes away. */ -static jobject GetObjectArrayElement(JNIEnv* env, jobjectArray array, +static jobject GetObjectArrayElement(JNIEnv* env, jobjectArray jarr, jsize index) { JNI_ENTER(); - ArrayObject* arrayObj = (ArrayObject*) array; - Object* value = NULL; + ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr); + jobject retval = NULL; - assert(array != NULL); + assert(arrayObj != NULL); /* check the array bounds */ if (index < 0 || index >= (int) arrayObj->length) { @@ -2338,25 +2922,25 @@ static jobject GetObjectArrayElement(JNIEnv* env, jobjectArray array, goto bail; } - value = ((Object**) arrayObj->contents)[index]; - value = addLocalReference(value); + Object* value = ((Object**) arrayObj->contents)[index]; + retval = addLocalReference(env, value); bail: JNI_EXIT(); - return (jobject) value; + return retval; } /* * Set one element of an Object array. */ -static void SetObjectArrayElement(JNIEnv* env, jobjectArray array, - jsize index, jobject value) +static void SetObjectArrayElement(JNIEnv* env, jobjectArray jarr, + jsize index, jobject jobj) { JNI_ENTER(); - ArrayObject* arrayObj = (ArrayObject*) array; + ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr); - assert(array != NULL); + assert(arrayObj != NULL); /* check the array bounds */ if (index < 0 || index >= (int) arrayObj->length) { @@ -2367,7 +2951,8 @@ static void SetObjectArrayElement(JNIEnv* env, jobjectArray array, //LOGV("JNI: set element %d in array %p to %p\n", index, array, value); - ((Object**) arrayObj->contents)[index] = (Object*) value; + Object* obj = dvmDecodeIndirectRef(env, jobj); + ((Object**) arrayObj->contents)[index] = obj; bail: JNI_EXIT(); @@ -2383,12 +2968,13 @@ bail: ArrayObject* arrayObj; \ arrayObj = dvmAllocPrimitiveArray(_typechar, length, \ ALLOC_DEFAULT); \ + jarray jarr = NULL; \ if (arrayObj != NULL) { \ + jarr = addLocalReference(env, (Object*) arrayObj); \ dvmReleaseTrackedAlloc((Object*) arrayObj, NULL); \ - arrayObj = addLocalReference(arrayObj); \ } \ JNI_EXIT(); \ - return (_artype)arrayObj; \ + return (_artype)jarr; \ } NEW_PRIMITIVE_ARRAY(jbooleanArray, Boolean, 'Z'); NEW_PRIMITIVE_ARRAY(jbyteArray, Byte, 'B'); @@ -2403,22 +2989,20 @@ NEW_PRIMITIVE_ARRAY(jdoubleArray, Double, 'D'); * Get a pointer to a C array of primitive elements from an array object * of the matching type. * - * We guarantee availability until Release is called, so we have to add - * the array object to the global refs table. - * - * In a compacting GC, we either need to return a copy of the elements - * or "pin" the memory. Otherwise we run the risk of native code using - * the buffer as the destination of a blocking read() call that wakes up + * In a compacting GC, we either need to return a copy of the elements or + * "pin" the memory. Otherwise we run the risk of native code using the + * buffer as the destination of e.g. a blocking read() call that wakes up * during a GC. */ #define GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \ static _ctype* Get##_jname##ArrayElements(JNIEnv* env, \ - _ctype##Array array, jboolean* isCopy) \ + _ctype##Array jarr, jboolean* isCopy) \ { \ JNI_ENTER(); \ _ctype* data; \ - ArrayObject* arrayObj = (ArrayObject*)array; \ - addGlobalReference(arrayObj); \ + ArrayObject* arrayObj = \ + (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \ + pinPrimitiveArray(arrayObj); \ data = (_ctype*) arrayObj->contents; \ if (isCopy != NULL) \ *isCopy = JNI_FALSE; \ @@ -2429,21 +3013,21 @@ NEW_PRIMITIVE_ARRAY(jdoubleArray, Double, 'D'); /* * Release the storage locked down by the "get" function. * - * The API says, ""'mode' has no effect if 'elems' is not a copy of the + * The spec says, "'mode' has no effect if 'elems' is not a copy of the * elements in 'array'." They apparently did not anticipate the need to - * create a global reference to avoid GC race conditions. We actually - * want to delete the global reference in all circumstances that would - * result in a copied array being freed. This means anything but a - * JNI_COMMIT release. + * un-pin memory. */ #define RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \ static void Release##_jname##ArrayElements(JNIEnv* env, \ - _ctype##Array array, _ctype* elems, jint mode) \ + _ctype##Array jarr, _ctype* elems, jint mode) \ { \ UNUSED_PARAMETER(elems); \ JNI_ENTER(); \ - if (mode != JNI_COMMIT) \ - deleteGlobalReference(array); \ + if (mode != JNI_COMMIT) { \ + ArrayObject* arrayObj = \ + (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \ + unpinPrimitiveArray(arrayObj); \ + } \ JNI_EXIT(); \ } @@ -2452,10 +3036,11 @@ NEW_PRIMITIVE_ARRAY(jdoubleArray, Double, 'D'); */ #define GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \ static void Get##_jname##ArrayRegion(JNIEnv* env, \ - _ctype##Array array, jsize start, jsize len, _ctype* buf) \ + _ctype##Array jarr, jsize start, jsize len, _ctype* buf) \ { \ JNI_ENTER(); \ - ArrayObject* arrayObj = (ArrayObject*)array; \ + ArrayObject* arrayObj = \ + (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \ _ctype* data = (_ctype*) arrayObj->contents; \ if (start < 0 || len < 0 || start + len > (int) arrayObj->length) { \ dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \ @@ -2471,10 +3056,11 @@ NEW_PRIMITIVE_ARRAY(jdoubleArray, Double, 'D'); */ #define SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \ static void Set##_jname##ArrayRegion(JNIEnv* env, \ - _ctype##Array array, jsize start, jsize len, const _ctype* buf) \ + _ctype##Array jarr, jsize start, jsize len, const _ctype* buf) \ { \ JNI_ENTER(); \ - ArrayObject* arrayObj = (ArrayObject*)array; \ + ArrayObject* arrayObj = \ + (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \ _ctype* data = (_ctype*) arrayObj->contents; \ if (start < 0 || len < 0 || start + len > (int) arrayObj->length) { \ dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \ @@ -2510,22 +3096,23 @@ PRIMITIVE_ARRAY_FUNCTIONS(jdouble, Double); /* * Register one or more native functions in one class. */ -static jint RegisterNatives(JNIEnv* env, jclass clazz, +static jint RegisterNatives(JNIEnv* env, jclass jclazz, const JNINativeMethod* methods, jint nMethods) { JNI_ENTER(); + ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); jint retval; int i; if (gDvm.verboseJni) { LOGI("[Registering JNI native methods for class %s]\n", - ((ClassObject*) clazz)->descriptor); + clazz->descriptor); } for (i = 0; i < nMethods; i++) { - if (!dvmRegisterJNIMethod((ClassObject*) clazz, - methods[i].name, methods[i].signature, methods[i].fnPtr)) + if (!dvmRegisterJNIMethod(clazz, methods[i].name, + methods[i].signature, methods[i].fnPtr)) { retval = JNI_ERR; goto bail; @@ -2541,7 +3128,7 @@ bail: /* * Un-register a native function. */ -static jint UnregisterNatives(JNIEnv* env, jclass clazz) +static jint UnregisterNatives(JNIEnv* env, jclass jclazz) { JNI_ENTER(); /* @@ -2560,11 +3147,12 @@ static jint UnregisterNatives(JNIEnv* env, jclass clazz) * We have to track all monitor enters and exits, so that we can undo any * outstanding synchronization before the thread exits. */ -static jint MonitorEnter(JNIEnv* env, jobject obj) +static jint MonitorEnter(JNIEnv* env, jobject jobj) { JNI_ENTER(); - dvmLockObject(_self, (Object*) obj); - trackMonitorEnter(_self, (Object*) obj); + Object* obj = dvmDecodeIndirectRef(env, jobj); + dvmLockObject(_self, obj); + trackMonitorEnter(_self, obj); JNI_EXIT(); return JNI_OK; } @@ -2573,17 +3161,18 @@ static jint MonitorEnter(JNIEnv* env, jobject obj) * Unlock the monitor. * * Throws an IllegalMonitorStateException if the current thread - * doesn't own the monitor. (dvmUnlockObject() takes care of the throw.) + * doesn't own the monitor. (dvmUnlockObject() takes care of the throw.) * * According to the 1.6 spec, it's legal to call here with an exception * pending. If this fails, we'll stomp the original exception. */ -static jint MonitorExit(JNIEnv* env, jobject obj) +static jint MonitorExit(JNIEnv* env, jobject jobj) { JNI_ENTER(); - bool success = dvmUnlockObject(_self, (Object*) obj); + Object* obj = dvmDecodeIndirectRef(env, jobj); + bool success = dvmUnlockObject(_self, obj); if (success) - trackMonitorExit(_self, (Object*) obj); + trackMonitorExit(_self, obj); JNI_EXIT(); return success ? JNI_OK : JNI_ERR; } @@ -2606,11 +3195,11 @@ static jint GetJavaVM(JNIEnv* env, JavaVM** vm) /* * Copies "len" Unicode characters, from offset "start". */ -static void GetStringRegion(JNIEnv* env, jstring str, jsize start, jsize len, +static void GetStringRegion(JNIEnv* env, jstring jstr, jsize start, jsize len, jchar* buf) { JNI_ENTER(); - StringObject* strObj = (StringObject*) str; + StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr); if (start + len > dvmStringLen(strObj)) dvmThrowException("Ljava/lang/StringIndexOutOfBoundsException;", NULL); else @@ -2622,11 +3211,11 @@ static void GetStringRegion(JNIEnv* env, jstring str, jsize start, jsize len, * Translates "len" Unicode characters, from offset "start", into * modified UTF-8 encoding. */ -static void GetStringUTFRegion(JNIEnv* env, jstring str, jsize start, +static void GetStringUTFRegion(JNIEnv* env, jstring jstr, jsize start, jsize len, char* buf) { JNI_ENTER(); - StringObject* strObj = (StringObject*) str; + StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr); if (start + len > dvmStringLen(strObj)) dvmThrowException("Ljava/lang/StringIndexOutOfBoundsException;", NULL); else @@ -2640,15 +3229,15 @@ static void GetStringUTFRegion(JNIEnv* env, jstring str, jsize start, * The caller is expected to call "release" before doing any JNI calls * or blocking I/O operations. * - * In a compacting GC, we need to pin the memory or block GC. + * We need to pin the memory or block GC. */ -static void* GetPrimitiveArrayCritical(JNIEnv* env, jarray array, +static void* GetPrimitiveArrayCritical(JNIEnv* env, jarray jarr, jboolean* isCopy) { JNI_ENTER(); void* data; - ArrayObject* arrayObj = (ArrayObject*)array; - addGlobalReference(arrayObj); + ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr); + pinPrimitiveArray(arrayObj); data = arrayObj->contents; if (isCopy != NULL) *isCopy = JNI_FALSE; @@ -2659,25 +3248,30 @@ static void* GetPrimitiveArrayCritical(JNIEnv* env, jarray array, /* * Release an array obtained with GetPrimitiveArrayCritical. */ -static void ReleasePrimitiveArrayCritical(JNIEnv* env, jarray array, +static void ReleasePrimitiveArrayCritical(JNIEnv* env, jarray jarr, void* carray, jint mode) { JNI_ENTER(); - if (mode != JNI_COMMIT) - deleteGlobalReference(array); + if (mode != JNI_COMMIT) { + ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr); + unpinPrimitiveArray(arrayObj); + } JNI_EXIT(); } /* * Like GetStringChars, but with restricted use. */ -static const jchar* GetStringCritical(JNIEnv* env, jstring string, +static const jchar* GetStringCritical(JNIEnv* env, jstring jstr, jboolean* isCopy) { JNI_ENTER(); - const u2* data = dvmStringChars((StringObject*) string); - addGlobalReference(string); + StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr); + ArrayObject* strChars = dvmStringCharArray(strObj); + pinPrimitiveArray(strChars); + + const u2* data = dvmStringChars(strObj); if (isCopy != NULL) *isCopy = JNI_FALSE; @@ -2688,11 +3282,13 @@ static const jchar* GetStringCritical(JNIEnv* env, jstring string, /* * Like ReleaseStringChars, but with restricted use. */ -static void ReleaseStringCritical(JNIEnv* env, jstring string, +static void ReleaseStringCritical(JNIEnv* env, jstring jstr, const jchar* carray) { JNI_ENTER(); - deleteGlobalReference(string); + StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr); + ArrayObject* strChars = dvmStringCharArray(strObj); + unpinPrimitiveArray(strChars); JNI_EXIT(); } @@ -2743,15 +3339,10 @@ static jboolean ExceptionCheck(JNIEnv* env) * the same time, so while the return value is accurate it may not tell * the whole story. */ -static jobjectRefType GetObjectRefType(JNIEnv* env, jobject obj) +static jobjectRefType GetObjectRefType(JNIEnv* env, jobject jobj) { JNI_ENTER(); - jobjectRefType type; - - if (obj == NULL) - type = JNIInvalidRefType; - else - type = dvmGetJNIRefType(obj); + jobjectRefType type = dvmGetJNIRefType(env, jobj); JNI_EXIT(); return type; } @@ -2790,15 +3381,16 @@ static jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity) Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK); if (newObj != NULL) { /* call the (PlatformAddress, int, int) constructor */ - newObj = addLocalReference(newObj); + result = addLocalReference(env, newObj); dvmCallMethod(self, gDvm.methJavaNioReadWriteDirectByteBuffer_init, newObj, &callResult, platformAddress, (jint) capacity, (jint) 0); - if (dvmGetException(self) != NULL) + if (dvmGetException(self) != NULL) { + deleteLocalReference(env, result); + result = NULL; goto bail; + } } - result = (jobject) newObj; - bail: if (platformAddress != NULL) dvmReleaseTrackedAlloc(platformAddress, self); @@ -2811,11 +3403,11 @@ bail: * * If this is not a "direct" buffer, we return NULL. */ -static void* GetDirectBufferAddress(JNIEnv* env, jobject buf) +static void* GetDirectBufferAddress(JNIEnv* env, jobject jbuf) { JNI_ENTER(); - Object* bufObj = (Object*) buf; + Object* bufObj = dvmDecodeIndirectRef(env, jbuf); Thread* self = _self /*dvmThreadSelf()*/; void* result; @@ -2852,7 +3444,7 @@ static void* GetDirectBufferAddress(JNIEnv* env, jobject buf) JValue callResult; const Method* meth = dvmGetVirtualizedMethod(bufObj->clazz, gDvm.methOrgApacheHarmonyNioInternalDirectBuffer_getEffectiveAddress); - dvmCallMethodA(self, meth, bufObj, &callResult, NULL); + dvmCallMethodA(self, meth, bufObj, false, &callResult, NULL); if (dvmGetException(self) != NULL) { dvmClearException(self); callResult.l = NULL; @@ -2886,7 +3478,7 @@ bail: * this check, since it's expensive to determine, and just return the * capacity regardless.) */ -static jlong GetDirectBufferCapacity(JNIEnv* env, jobject buf) +static jlong GetDirectBufferCapacity(JNIEnv* env, jobject jbuf) { JNI_ENTER(); @@ -2896,7 +3488,8 @@ static jlong GetDirectBufferCapacity(JNIEnv* env, jobject buf) * (The "check" version should verify that this is actually a Buffer, * but we're not required to do so here.) */ - jlong result = dvmGetFieldInt((Object*)buf, gDvm.offJavaNioBuffer_capacity); + Object* buf = dvmDecodeIndirectRef(env, jbuf); + jlong result = dvmGetFieldInt(buf, gDvm.offJavaNioBuffer_capacity); JNI_EXIT(); return result; @@ -3438,12 +4031,16 @@ static const struct JNIInvokeInterface gInvokeInterface = { /* * Enable "checked JNI" after the VM has partially started. This must * only be called in "zygote" mode, when we have one thread running. + * + * This doesn't attempt to rewrite the JNI call bridge associated with + * native methods, so we won't get those checks for any methods that have + * already been resolved. */ void dvmLateEnableCheckedJni(void) { JNIEnvExt* extEnv; JavaVMExt* extVm; - + extEnv = dvmGetJNIEnvForThread(); if (extEnv == NULL) { LOGE("dvmLateEnableCheckedJni: thread has no JNIEnv\n"); diff --git a/vm/JniInternal.h b/vm/JniInternal.h index 79d16ecab..37920caf6 100644 --- a/vm/JniInternal.h +++ b/vm/JniInternal.h @@ -74,13 +74,18 @@ typedef struct JavaVMExt { * Native function return type; used by dvmPlatformInvoke(). * * This is part of Method.jniArgInfo, and must fit in 3 bits. + * Note: Assembly code in arch//Call.S relies on + * the enum values defined here. */ typedef enum DalvikJniReturnType { DALVIK_JNI_RETURN_VOID = 0, /* must be zero */ - DALVIK_JNI_RETURN_FLOAT, - DALVIK_JNI_RETURN_DOUBLE, - DALVIK_JNI_RETURN_S8, - DALVIK_JNI_RETURN_S4 + DALVIK_JNI_RETURN_FLOAT = 1, + DALVIK_JNI_RETURN_DOUBLE = 2, + DALVIK_JNI_RETURN_S8 = 3, + DALVIK_JNI_RETURN_S4 = 4, + DALVIK_JNI_RETURN_S2 = 5, + DALVIK_JNI_RETURN_U2 = 6, + DALVIK_JNI_RETURN_S1 = 7 } DalvikJniReturnType; #define DALVIK_JNI_NO_ARG_INFO 0x80000000 @@ -93,15 +98,17 @@ typedef enum DalvikJniReturnType { /* * Pop the JNI local stack when we return from a native method. "saveArea" * points to the StackSaveArea for the method we're leaving. + * + * (This may be implemented directly in assembly in mterp, so changes here + * may only affect the portable interpreter.) */ INLINE void dvmPopJniLocals(Thread* self, StackSaveArea* saveArea) { - if (saveArea->xtra.localRefTop != self->jniLocalRefTable.nextEntry) { - LOGVV("LREF: popped %d entries (%d remain)\n", - (int)(self->jniLocalRefTable.nextEntry-saveArea->xtra.localRefTop), - (int)(saveArea->xtra.localRefTop - self->jniLocalRefTable.table)); - } - self->jniLocalRefTable.nextEntry = saveArea->xtra.localRefTop; +#ifdef USE_INDIRECT_REF + self->jniLocalRefTable.segmentState.all = saveArea->xtra.localRefCookie; +#else + self->jniLocalRefTable.nextEntry = saveArea->xtra.localRefCookie; +#endif } /* @@ -114,13 +121,33 @@ INLINE void dvmSetJniEnvThreadId(JNIEnv* pEnv, Thread* self) } /* - * JNI call bridges. Not usually called directly. + * JNI call bridges. Not called directly. + * + * The "Check" versions are used when CheckJNI is enabled. */ -void dvmCallJNIMethod(const u4* args, JValue* pResult, const Method* method, - Thread* self); -void dvmCallSynchronizedJNIMethod(const u4* args, JValue* pResult, +void dvmCallJNIMethod_general(const u4* args, JValue* pResult, + const Method* method, Thread* self); +void dvmCallJNIMethod_synchronized(const u4* args, JValue* pResult, + const Method* method, Thread* self); +void dvmCallJNIMethod_virtualNoRef(const u4* args, JValue* pResult, + const Method* method, Thread* self); +void dvmCallJNIMethod_staticNoRef(const u4* args, JValue* pResult, + const Method* method, Thread* self); +void dvmCheckCallJNIMethod_general(const u4* args, JValue* pResult, + const Method* method, Thread* self); +void dvmCheckCallJNIMethod_synchronized(const u4* args, JValue* pResult, + const Method* method, Thread* self); +void dvmCheckCallJNIMethod_virtualNoRef(const u4* args, JValue* pResult, + const Method* method, Thread* self); +void dvmCheckCallJNIMethod_staticNoRef(const u4* args, JValue* pResult, const Method* method, Thread* self); +/* + * Configure "method" to use the JNI bridge to call "func". + */ +void dvmUseJNIBridge(Method* method, void* func); + + /* * Enable the "checked" versions. */ @@ -128,11 +155,23 @@ void dvmUseCheckedJniEnv(JNIEnvExt* pEnv); void dvmUseCheckedJniVm(JavaVMExt* pVm); void dvmLateEnableCheckedJni(void); +/* + * Decode a local, global, or weak-global reference. + */ +#ifdef USE_INDIRECT_REF +Object* dvmDecodeIndirectRef(JNIEnv* env, jobject jobj); +#else +/* use an inline to ensure this is a no-op */ +INLINE Object* dvmDecodeIndirectRef(JNIEnv* env, jobject jobj) { + return (Object*) jobj; +} +#endif + /* * Verify that a reference passed in from native code is valid. Returns * an indication of local/global/invalid. */ -jobjectRefType dvmGetJNIRefType(Object* obj); +jobjectRefType dvmGetJNIRefType(JNIEnv* env, jobject jobj); /* * Get the last method called on the interp stack. This is the method diff --git a/vm/LinearAlloc.c b/vm/LinearAlloc.c index 77802ee51..8a18af3c2 100644 --- a/vm/LinearAlloc.c +++ b/vm/LinearAlloc.c @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + /* * Linear memory allocation, tied to class loaders. */ @@ -68,7 +69,7 @@ guard the pages on debug builds. Handy when tracking down corruption. #define BLOCK_ALIGN 8 /* default length of memory segment (worst case is probably "dexopt") */ -#define DEFAULT_MAX_LENGTH (4*1024*1024) +#define DEFAULT_MAX_LENGTH (5*1024*1024) /* leave enough space for a length word */ #define HEADER_EXTRA 4 @@ -137,7 +138,7 @@ LinearAllocHdr* dvmLinearAllocCreate(Object* classLoader) fd = ashmem_create_region("dalvik-LinearAlloc", DEFAULT_MAX_LENGTH); if (fd < 0) { - LOGE("ashmem LinearAlloc failed %s", strerror(errno)); + LOGE("ashmem LinearAlloc failed %s", strerror(errno)); free(pHdr); return NULL; } @@ -148,13 +149,13 @@ LinearAllocHdr* dvmLinearAllocCreate(Object* classLoader) LOGE("LinearAlloc mmap(%d) failed: %s\n", pHdr->mapLength, strerror(errno)); free(pHdr); - close(fd); + close(fd); return NULL; } close(fd); #else /*USE_ASHMEM*/ - // MAP_ANON is listed as "deprecated" on Linux, + // MAP_ANON is listed as "deprecated" on Linux, // but MAP_ANONYMOUS is not defined under Mac OS X. pHdr->mapAddr = mmap(NULL, pHdr->mapLength, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); @@ -313,7 +314,8 @@ void* dvmLinearAlloc(Object* classLoader, size_t size) * works if the users of these functions actually free everything * they allocate. */ - LOGE("LinearAlloc exceeded capacity, last=%d\n", (int) size); + LOGE("LinearAlloc exceeded capacity (%d), last=%d\n", + pHdr->mapLength, (int) size); dvmAbort(); } @@ -686,3 +688,21 @@ static void checkAllFree(Object* classLoader) dvmUnlockMutex(&pHdr->lock); } +/* + * Determine if [start, start+length) is contained in the in-use area of + * a single LinearAlloc. The full set of linear allocators is scanned. + * + * [ Since we currently only have one region, this is pretty simple. In + * the future we'll need to traverse a table of class loaders. ] + */ +bool dvmLinearAllocContains(const void* start, size_t length) +{ + LinearAllocHdr* pHdr = getHeader(NULL); + + if (pHdr == NULL) + return false; + + return (char*) start >= pHdr->mapAddr && + ((char*)start + length) <= (pHdr->mapAddr + pHdr->curOffset); +} + diff --git a/vm/LinearAlloc.h b/vm/LinearAlloc.h index 9c1d0962e..a390ee37e 100644 --- a/vm/LinearAlloc.h +++ b/vm/LinearAlloc.h @@ -112,4 +112,10 @@ char* dvmLinearStrdup(Object* classLoader, const char* str); */ void dvmLinearAllocDump(Object* classLoader); +/* + * Determine if [start, start+length) is contained in the in-use area of + * a single LinearAlloc. The full set of linear allocators is scanned. + */ +bool dvmLinearAllocContains(const void* start, size_t length); + #endif /*_DALVIK_LINEARALLOC*/ diff --git a/vm/Misc.c b/vm/Misc.c index ef9c4afa6..f8f72567f 100644 --- a/vm/Misc.c +++ b/vm/Misc.c @@ -280,6 +280,7 @@ bool dvmSetBit(BitVector* pBits, int num) pBits->storage = realloc(pBits->storage, newSize * sizeof(u4)); memset(&pBits->storage[pBits->storageSize], 0x00, (newSize - pBits->storageSize) * sizeof(u4)); + pBits->storageSize = newSize; } pBits->storage[num >> 5] |= 1 << (num & 0x1f); @@ -296,6 +297,15 @@ void dvmClearBit(BitVector* pBits, int num) pBits->storage[num >> 5] &= ~(1 << (num & 0x1f)); } +/* + * Mark all bits bit as "clear". + */ +void dvmClearAllBits(BitVector* pBits) +{ + int count = pBits->storageSize; + memset(pBits->storage, 0, count * sizeof(u4)); +} + /* * Determine whether or not the specified bit is set. */ @@ -334,7 +344,6 @@ int dvmCountSetBits(const BitVector* pBits) return count; } - /* * Return a newly-allocated string in which all occurrences of '.' have * been changed to '/'. If we find a '/' in the original string, NULL diff --git a/vm/Misc.h b/vm/Misc.h index 5f3af7b68..14635a363 100644 --- a/vm/Misc.h +++ b/vm/Misc.h @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + /* * Miscellaneous utility functions. */ @@ -51,28 +52,6 @@ INLINE u4 dvmFloatToU4(float val) { conv.in = val; return conv.out; } -#if 0 -INLINE float dvmU8ToFloat(u8 val) { - union { u8 in; float out; } conv; - conv.in = val; - return conv.out; -} -INLINE u8 dvmFloatToU8(float val) { - union { float in; u8 out; } conv; - conv.in = val; - return conv.out; -} -INLINE double dvmU8ToDouble(u8 val) { - union { u8 in; double out; } conv; - conv.in = val; - return conv.out; -} -INLINE u8 dvmDoubleToU8(double val) { - union { double in; u8 out; } conv; - conv.in = val; - return conv.out; -} -#endif /* * Print a hex dump to the log file. @@ -143,7 +122,11 @@ void dvmCreateFileOutputTarget(DebugOutputTarget* target, FILE* fp); * Print a debug message. */ void dvmPrintDebugMessage(const DebugOutputTarget* target, const char* format, - ...); + ...) +#if defined(__GNUC__) + __attribute__ ((format(printf, 2, 3))) +#endif + ; /* @@ -175,6 +158,7 @@ void dvmFreeBitVector(BitVector* pBits); int dvmAllocBit(BitVector* pBits); bool dvmSetBit(BitVector* pBits, int num); void dvmClearBit(BitVector* pBits, int num); +void dvmClearAllBits(BitVector* pBits); bool dvmIsBitSet(const BitVector* pBits, int num); /* count the number of bits that have been set */ @@ -277,6 +261,17 @@ bool dvmIterativeSleep(int iteration, int maxTotalSleep, u8 relStartTime); */ bool dvmSetCloseOnExec(int fd); +/* + * Unconditionally abort the entire VM. Try not to use this. + * + * NOTE: if this is marked ((noreturn)), gcc will merge multiple dvmAbort() + * calls in a single function together. This is good, in that it reduces + * code size slightly, but also bad, because the native stack trace we + * get from the abort may point at the wrong call site. Best to leave + * it undecorated. + */ +void dvmAbort(void); + #if (!HAVE_STRLCPY) /* Implementation of strlcpy() for platforms that don't already have it. */ size_t strlcpy(char *dst, const char *src, size_t size); diff --git a/vm/Native.c b/vm/Native.c index 618fb9997..31832c250 100644 --- a/vm/Native.c +++ b/vm/Native.c @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + /* * Native method resolution. * @@ -61,6 +62,10 @@ void dvmNativeShutdown(void) * Initializes method's class if necessary. * * An exception is thrown on resolution failure. + * + * (This should not be taking "const Method*", because it modifies the + * structure, but the declaration needs to match the DalvikBridgeFunc + * type definition.) */ void dvmResolveNativeMethod(const u4* args, JValue* pResult, const Method* method, Thread* self) @@ -107,11 +112,9 @@ void dvmResolveNativeMethod(const u4* args, JValue* pResult, /* now scan any DLLs we have loaded for JNI signatures */ func = lookupSharedLibMethod(method); if (func != NULL) { - if (dvmIsSynchronizedMethod(method)) - dvmSetNativeFunc(method, dvmCallSynchronizedJNIMethod, func); - else - dvmSetNativeFunc(method, dvmCallJNIMethod, func); - dvmCallJNIMethod(args, pResult, method, self); + /* found it, point it at the JNI bridge and then call it */ + dvmUseJNIBridge((Method*) method, func); + (*method->nativeFunc)(args, pResult, method, self); return; } @@ -467,16 +470,18 @@ bool dvmLoadNativeCode(const char* pathName, Object* classLoader) * doesn't have to search through LD_LIBRARY_PATH. (It may do so to * resolve this library's dependencies though.) * - * Failures here are expected when java.library.path has several entries. + * Failures here are expected when java.library.path has several entries + * and we have to hunt for the lib. * * The current android-arm dynamic linker implementation tends to * return "Cannot find library" from dlerror() regardless of the actual - * problem. A more useful diagnostic may be sent to stdout/stderr, - * but often that's not visible. Some things to try: + * problem. A more useful diagnostic may be sent to stdout/stderr if + * linker diagnostics are enabled, but that's not usually visible in + * Android apps. Some things to try: * - make sure the library exists on the device * - verify that the right path is being opened (the debug log message * above can help with that) - * - check to see if the library is valid + * - check to see if the library is valid (e.g. not zero bytes long) * - check config/prelink-linux-arm.map to ensure that the library * is listed and is not being overrun by the previous entry (if * loading suddenly stops working on a prelinked library, this is @@ -646,7 +651,7 @@ static char* mangleString(const char* str, int len) for (i = 0; i < charLen; i++) { u2 ch = utf16[i]; - if (ch > 127) { + if (ch == '$' || ch > 127) { mangleLen += 6; } else { switch (ch) { @@ -671,7 +676,7 @@ static char* mangleString(const char* str, int len) for (i = 0, cp = mangle; i < charLen; i++) { u2 ch = utf16[i]; - if (ch > 127) { + if (ch == '$' || ch > 127) { sprintf(cp, "_0%04x", ch); cp += 6; } else { diff --git a/vm/Profile.c b/vm/Profile.c index 8e19b3459..7f39f1adc 100644 --- a/vm/Profile.c +++ b/vm/Profile.c @@ -64,6 +64,8 @@ #define TRACE_MAGIC 0x574f4c53 #define TRACE_HEADER_LEN 32 +#define FILL_PATTERN 0xeeeeeeee + /* * Get the wall-clock date/time, in usec. @@ -364,7 +366,7 @@ void dvmMethodTraceStart(const char* traceFileName, int traceFd, int bufferSize, dvmThrowException("Ljava/lang/RuntimeException;", "file open failed"); goto fail; } - memset(state->buf, 0xee, bufferSize); + memset(state->buf, (char)FILL_PATTERN, bufferSize); state->bufferSize = bufferSize; state->overflow = false; @@ -418,10 +420,10 @@ fail: * Run through the data buffer and pull out the methods that were visited. * Set a mark so that we know which ones to output. */ -static void markTouchedMethods(void) +static void markTouchedMethods(int endOffset) { u1* ptr = gDvm.methodTrace.buf + TRACE_HEADER_LEN; - u1* end = gDvm.methodTrace.buf + gDvm.methodTrace.curOffset; + u1* end = gDvm.methodTrace.buf + endOffset; unsigned int methodVal; Method* method; @@ -508,13 +510,56 @@ void dvmMethodTraceStop(void) state->traceEnabled = false; MEM_BARRIER(); sched_yield(); + usleep(250 * 1000); if ((state->flags & TRACE_ALLOC_COUNTS) != 0) dvmStopAllocCounting(); + /* + * It's possible under some circumstances for a thread to have advanced + * the data pointer but not written the method value. It's possible + * (though less likely) for the data pointer to be advanced, or partial + * data written, while we're doing work here. + * + * To avoid seeing partially-written data, we grab state->curOffset here, + * and use our local copy from here on. We then scan through what's + * already written. If we see the fill pattern in what should be the + * method pointer, we cut things off early. (If we don't, we'll fail + * when we dereference the pointer.) + * + * There's a theoretical possibility of interrupting another thread + * after it has partially written the method pointer, in which case + * we'll likely crash when we dereference it. The possibility of + * this actually happening should be at or near zero. Fixing it + * completely could be done by writing the thread number last and + * using a sentinel value to indicate a partially-written record, + * but that requires memory barriers. + */ + int finalCurOffset = state->curOffset; + + if (finalCurOffset > TRACE_HEADER_LEN) { + u4 fillVal = METHOD_ID(FILL_PATTERN); + u1* scanPtr = state->buf + TRACE_HEADER_LEN; + + while (scanPtr < state->buf + finalCurOffset) { + u4 methodVal = scanPtr[1] | (scanPtr[2] << 8) | (scanPtr[3] << 16) + | (scanPtr[4] << 24); + if (METHOD_ID(methodVal) == fillVal) { + u1* scanBase = state->buf + TRACE_HEADER_LEN; + LOGW("Found unfilled record at %d (of %d)\n", + (scanPtr - scanBase) / TRACE_REC_SIZE, + (finalCurOffset - TRACE_HEADER_LEN) / TRACE_REC_SIZE); + finalCurOffset = scanPtr - state->buf; + break; + } + + scanPtr += TRACE_REC_SIZE; + } + } + LOGI("TRACE STOPPED%s: writing %d records\n", state->overflow ? " (NOTE: overflowed buffer)" : "", - (state->curOffset - TRACE_HEADER_LEN) / TRACE_REC_SIZE); + (finalCurOffset - TRACE_HEADER_LEN) / TRACE_REC_SIZE); if (gDvm.debuggerActive) { LOGW("WARNING: a debugger is active; method-tracing results " "will be skewed\n"); @@ -525,7 +570,7 @@ void dvmMethodTraceStop(void) */ u4 clockNsec = getClockOverhead(); - markTouchedMethods(); + markTouchedMethods(finalCurOffset); fprintf(state->traceFile, "%cversion\n", TOKEN_CHAR); fprintf(state->traceFile, "%d\n", TRACE_VERSION); @@ -538,7 +583,7 @@ void dvmMethodTraceStop(void) #endif fprintf(state->traceFile, "elapsed-time-usec=%llu\n", elapsed); fprintf(state->traceFile, "num-method-calls=%d\n", - (state->curOffset - TRACE_HEADER_LEN) / TRACE_REC_SIZE); + (finalCurOffset - TRACE_HEADER_LEN) / TRACE_REC_SIZE); fprintf(state->traceFile, "clock-call-overhead-nsec=%d\n", clockNsec); fprintf(state->traceFile, "vm=dalvik\n"); if ((state->flags & TRACE_ALLOC_COUNTS) != 0) { @@ -555,8 +600,8 @@ void dvmMethodTraceStop(void) dumpMethodList(state->traceFile); fprintf(state->traceFile, "%cend\n", TOKEN_CHAR); - if (fwrite(state->buf, state->curOffset, 1, state->traceFile) != 1) { - LOGE("trace fwrite(%d) failed, errno=%d\n", state->curOffset, errno); + if (fwrite(state->buf, finalCurOffset, 1, state->traceFile) != 1) { + LOGE("trace fwrite(%d) failed, errno=%d\n", finalCurOffset, errno); dvmThrowException("Ljava/lang/RuntimeException;", "data write failed"); goto bail; } @@ -674,7 +719,7 @@ void dvmEmitEmulatorTrace(const Method* method, int action) * Fortunately, the trace tools can get by without the address, but * it would be nice to fix this. */ - addr = method->nativeFunc; + addr = (u4) method->nativeFunc; } else { /* * The dexlist output shows the &DexCode.insns offset value, which diff --git a/vm/Properties.c b/vm/Properties.c index a9fe5e1fa..91288c27f 100644 --- a/vm/Properties.c +++ b/vm/Properties.c @@ -165,6 +165,8 @@ void dvmCreateDefaultProperties(Object* propObj) setProperty(propObj, put, "java.io.tmpdir", "/tmp"); setProperty(propObj, put, "java.library.path", getenv("LD_LIBRARY_PATH")); + setProperty(propObj, put, "java.net.preferIPv6Addresses", "true"); + setProperty(propObj, put, "java.vendor", projectName); setProperty(propObj, put, "java.vendor.url", projectUrl); setProperty(propObj, put, "java.version", "0"); @@ -185,12 +187,7 @@ void dvmCreateDefaultProperties(Object* propObj) setProperty(propObj, put, "java.specification.vendor", projectName); setProperty(propObj, put, "java.specification.version", "0.9"); - #define OS_ARCH generic /* TODO: Use an "arch" header. */ - #define OS_ARCH_QUOTE(x) #x - setProperty(propObj, put, "os.arch", OS_ARCH_QUOTE(OS_ARCH)); - #undef OS_ARCH - #undef OS_ARCH_QUOTE - + setProperty(propObj, put, "os.arch", info.machine); setProperty(propObj, put, "os.name", info.sysname); setProperty(propObj, put, "os.version", info.release); setProperty(propObj, put, "user.home", getenv("HOME")); @@ -285,4 +282,3 @@ bail: dvmReleaseTrackedAlloc((Object*)keyObj, NULL); return result; } - diff --git a/vm/ReferenceTable.c b/vm/ReferenceTable.c index c748222a7..a8010a251 100644 --- a/vm/ReferenceTable.c +++ b/vm/ReferenceTable.c @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + /* * Reference table management. */ @@ -58,11 +59,15 @@ bool dvmAddToReferenceTable(ReferenceTable* pRef, Object* obj) assert(dvmIsValidObject(obj)); assert(obj != NULL); assert(pRef->table != NULL); + assert(pRef->allocEntries <= pRef->maxEntries); + + if (pRef->nextEntry == pRef->table + pRef->allocEntries) { + /* reached end of allocated space; did we hit buffer max? */ + if (pRef->nextEntry == pRef->table + pRef->maxEntries) { + LOGW("ReferenceTable overflow (max=%d)\n", pRef->maxEntries); + return false; + } - if (pRef->nextEntry == pRef->table + pRef->maxEntries) { - LOGW("ReferenceTable overflow (max=%d)\n", pRef->maxEntries); - return false; - } else if (pRef->nextEntry == pRef->table + pRef->allocEntries) { Object** newTable; int newSize; @@ -92,13 +97,13 @@ bool dvmAddToReferenceTable(ReferenceTable* pRef, Object* obj) /* * Returns NULL if not found. */ -Object** dvmFindInReferenceTable(const ReferenceTable* pRef, Object** top, +Object** dvmFindInReferenceTable(const ReferenceTable* pRef, Object** bottom, Object* obj) { Object** ptr; ptr = pRef->nextEntry; - while (--ptr >= top) { + while (--ptr >= bottom) { if (*ptr == obj) return ptr; } @@ -108,12 +113,12 @@ Object** dvmFindInReferenceTable(const ReferenceTable* pRef, Object** top, /* * Remove "obj" from "pRef". We start at the end of the list (where the * most-recently-added element is), and stop searching for a match after - * examining the element at "top". + * examining the element at "bottom". * * Most of the time "obj" is at or near the end of the list. If not, we * compact it down. */ -bool dvmRemoveFromReferenceTable(ReferenceTable* pRef, Object** top, +bool dvmRemoveFromReferenceTable(ReferenceTable* pRef, Object** bottom, Object* obj) { Object** ptr; @@ -121,10 +126,10 @@ bool dvmRemoveFromReferenceTable(ReferenceTable* pRef, Object** top, assert(pRef->table != NULL); /* - * Scan from the most-recently-added entry up to the top entry for + * Scan from the most-recently-added entry up to the bottom entry for * this frame. */ - ptr = dvmFindInReferenceTable(pRef, top, obj); + ptr = dvmFindInReferenceTable(pRef, bottom, obj); if (ptr == NULL) return false; diff --git a/vm/ReferenceTable.h b/vm/ReferenceTable.h index f8f24610e..d6e2d705a 100644 --- a/vm/ReferenceTable.h +++ b/vm/ReferenceTable.h @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + /* * Maintain a table of references. Used for internal local references, * JNI locals, JNI globals, and GC heap references. @@ -36,8 +37,8 @@ * table/nextEntry is allowed.) */ typedef struct ReferenceTable { - Object** table; /* top of the list */ - Object** nextEntry; /* bottom of the list */ + Object** nextEntry; /* top of the list */ + Object** table; /* bottom of the list */ int allocEntries; /* #of entries we have space for */ int maxEntries; /* max #of entries allowed */ @@ -88,23 +89,24 @@ INLINE size_t dvmIsReferenceTableFull(const ReferenceTable* pRef) bool dvmAddToReferenceTable(ReferenceTable* pRef, Object* obj); /* - * Determine if "obj" is present in "pRef". Stops searching when we hit "top". - * To include the entire table, pass in "pRef->table" as the top. + * Determine if "obj" is present in "pRef". Stops searching when we hit + * "bottom". To include the entire table, pass in "pRef->table" as the + * bottom. * * Returns NULL if "obj" was not found. */ -Object** dvmFindInReferenceTable(const ReferenceTable* pRef, Object** top, +Object** dvmFindInReferenceTable(const ReferenceTable* pRef, Object** bottom, Object* obj); /* * Remove an existing entry. * - * We stop searching for a match after examining the element at "top". This - * is useful when entries are associated with a stack frame. + * We stop searching for a match after examining the element at "bottom". + * This is useful when entries are associated with a stack frame. * * Returns "false" if the entry was not found. */ -bool dvmRemoveFromReferenceTable(ReferenceTable* pRef, Object** top, +bool dvmRemoveFromReferenceTable(ReferenceTable* pRef, Object** bottom, Object* obj); /* diff --git a/vm/SignalCatcher.c b/vm/SignalCatcher.c index 550f77796..e187aeb3c 100644 --- a/vm/SignalCatcher.c +++ b/vm/SignalCatcher.c @@ -192,6 +192,9 @@ static void* signalCatcherThreadStart(void* arg) sigemptyset(&mask); sigaddset(&mask, SIGQUIT); sigaddset(&mask, SIGUSR1); +#if defined(WITH_JIT) && defined(WITH_JIT_TUNING) + sigaddset(&mask, SIGUSR2); +#endif while (true) { int rcvd; @@ -237,9 +240,13 @@ loop: logThreadStacks(); +#if defined(WITH_JIT) && defined(WITH_JIT_TUNING) + dvmCompilerDumpStats(); +#endif + if (false) { dvmLockMutex(&gDvm.jniGlobalRefLock); - dvmDumpReferenceTable(&gDvm.jniGlobalRefTable, "JNI global"); + //dvmDumpReferenceTable(&gDvm.jniGlobalRefTable, "JNI global"); dvmUnlockMutex(&gDvm.jniGlobalRefLock); } @@ -252,6 +259,13 @@ loop: #else LOGI("SIGUSR1 forcing GC (no HPROF)\n"); dvmCollectGarbage(false); +#endif +#if defined(WITH_JIT) && defined(WITH_JIT_TUNING) + } else if (rcvd == SIGUSR2) { + gDvmJit.printMe ^= true; + dvmCompilerDumpStats(); + /* Stress-test unchain all */ + dvmJitUnchainAll(); #endif } else { LOGE("unexpected signal %d\n", rcvd); @@ -260,4 +274,3 @@ loop: return NULL; } - diff --git a/vm/Thread.c b/vm/Thread.c index ee195eb6c..be3e95229 100644 --- a/vm/Thread.c +++ b/vm/Thread.c @@ -13,10 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + /* * Thread support. */ #include "Dalvik.h" +#include "native/SystemThread.h" #include "utils/threads.h" // need Android thread priorities @@ -26,6 +28,9 @@ #include #include #include +#include + +#include #if defined(HAVE_PRCTL) #include @@ -233,6 +238,16 @@ static void threadExitCheck(void* arg); static void waitForThreadSuspend(Thread* self, Thread* thread); static int getThreadPriorityFromSystem(void); +/* + * The JIT needs to know if any thread is suspended. We do this by + * maintaining a global sum of all threads' suspend counts. All suspendCount + * updates should go through this after aquiring threadSuspendCountLock. + */ +static inline void dvmAddToThreadSuspendCount(int *pSuspendCount, int delta) +{ + *pSuspendCount += delta; + gDvm.sumThreadSuspendCount += delta; +} /* * Initialize thread list and main thread's environment. We need to set @@ -398,8 +413,12 @@ bool dvmThreadObjStartup(void) void dvmThreadShutdown(void) { if (gDvm.threadList != NULL) { - assert(gDvm.threadList->next == NULL); - assert(gDvm.threadList->prev == NULL); + /* + * If we walk through the thread list and try to free the + * lingering thread structures (which should only be for daemon + * threads), the daemon threads may crash if they execute before + * the process dies. Let them leak. + */ freeThread(gDvm.threadList); gDvm.threadList = NULL; } @@ -448,6 +467,11 @@ static inline void unlockThreadSuspendCount(void) * We don't have to check to see if we should be suspended once we have * the lock. Nobody can suspend all threads without holding the thread list * lock while they do it, so by definition there isn't a GC in progress. + * + * TODO: consider checking for suspend after acquiring the lock, and + * backing off if set. As stated above, it can't happen during normal + * execution, but it *can* happen during shutdown when daemon threads + * are being suspended. */ void dvmLockThreadList(Thread* self) { @@ -460,7 +484,7 @@ void dvmLockThreadList(Thread* self) oldStatus = self->status; self->status = THREAD_VMWAIT; } else { - /* happens for JNI AttachCurrentThread [not anymore?] */ + /* happens during VM shutdown */ //LOGW("NULL self in dvmLockThreadList\n"); oldStatus = -1; // shut up gcc } @@ -510,7 +534,7 @@ static void lockThreadSuspend(const char* who, SuspendCause why) u8 startWhen = 0; // init req'd to placate gcc int sleepIter = 0; int cc; - + do { cc = pthread_mutex_trylock(&gDvm._threadSuspendLock); if (cc != 0) { @@ -519,8 +543,8 @@ static void lockThreadSuspend(const char* who, SuspendCause why) * Could be that a resume-all is in progress, and something * grabbed the CPU when the wakeup was broadcast. The thread * performing the resume hasn't had a chance to release the - * thread suspend lock. (Should no longer be an issue -- - * we now release before broadcast.) + * thread suspend lock. (We release before the broadcast, + * so this should be a narrow window.) * * Could be we hit the window as a suspend was started, * and the lock has been grabbed but the suspend counts @@ -580,19 +604,24 @@ static inline void unlockThreadSuspend(void) * * It's possible for this function to get called after a failed * initialization, so be careful with assumptions about the environment. + * + * This will be called from whatever thread calls DestroyJavaVM, usually + * but not necessarily the main thread. It's likely, but not guaranteed, + * that the current thread has already been cleaned up. */ void dvmSlayDaemons(void) { - Thread* self = dvmThreadSelf(); + Thread* self = dvmThreadSelf(); // may be null Thread* target; - Thread* nextTarget; - - if (self == NULL) - return; + int threadId = 0; + bool doWait = false; //dvmEnterCritical(self); dvmLockThreadList(self); + if (self != NULL) + threadId = self->threadId; + target = gDvm.threadList; while (target != NULL) { if (target == self) { @@ -603,27 +632,95 @@ void dvmSlayDaemons(void) if (!dvmGetFieldBoolean(target->threadObj, gDvm.offJavaLangThread_daemon)) { + /* should never happen; suspend it with the rest */ LOGW("threadid=%d: non-daemon id=%d still running at shutdown?!\n", - self->threadId, target->threadId); - target = target->next; - continue; + threadId, target->threadId); } - LOGI("threadid=%d: killing leftover daemon threadid=%d [TODO]\n", - self->threadId, target->threadId); - // TODO: suspend and/or kill the thread - // (at the very least, we can "rescind their JNI privileges") + char* threadName = dvmGetThreadName(target); + LOGD("threadid=%d: suspending daemon id=%d name='%s'\n", + threadId, target->threadId, threadName); + free(threadName); - /* remove from list */ - nextTarget = target->next; - unlinkThread(target); + /* mark as suspended */ + lockThreadSuspendCount(); + dvmAddToThreadSuspendCount(&target->suspendCount, 1); + unlockThreadSuspendCount(); + doWait = true; + + target = target->next; + } + + //dvmDumpAllThreads(false); + + /* + * Unlock the thread list, relocking it later if necessary. It's + * possible a thread is in VMWAIT after calling dvmLockThreadList, + * and that function *doesn't* check for pending suspend after + * acquiring the lock. We want to let them finish their business + * and see the pending suspend before we continue here. + * + * There's no guarantee of mutex fairness, so this might not work. + * (The alternative is to have dvmLockThreadList check for suspend + * after acquiring the lock and back off, something we should consider.) + */ + dvmUnlockThreadList(); + + if (doWait) { + usleep(200 * 1000); + + dvmLockThreadList(self); + + /* + * Sleep for a bit until the threads have suspended. We're trying + * to exit, so don't wait for too long. + */ + int i; + for (i = 0; i < 10; i++) { + bool allSuspended = true; + + target = gDvm.threadList; + while (target != NULL) { + if (target == self) { + target = target->next; + continue; + } + + if (target->status == THREAD_RUNNING && !target->isSuspended) { + LOGD("threadid=%d not ready yet\n", target->threadId); + allSuspended = false; + break; + } + + target = target->next; + } + + if (allSuspended) { + LOGD("threadid=%d: all daemons have suspended\n", threadId); + break; + } else { + LOGD("threadid=%d: waiting for daemons to suspend\n", threadId); + } + + usleep(200 * 1000); + } + dvmUnlockThreadList(); + } +#if 0 /* bad things happen if they come out of JNI or "spuriously" wake up */ + /* + * Abandon the threads and recover their resources. + */ + target = gDvm.threadList; + while (target != NULL) { + Thread* nextTarget = target->next; + unlinkThread(target); freeThread(target); target = nextTarget; } +#endif - dvmUnlockThreadList(); - //dvmExitCritical(self); + //dvmDumpAllThreads(true); } @@ -886,15 +983,22 @@ static bool prepareThread(Thread* thread) /* * Initialize our reference tracking tables. * - * The JNI local ref table *must* be fixed-size because we keep pointers - * into the table in our stack frames. - * * Most threads won't use jniMonitorRefTable, so we clear out the * structure but don't call the init function (which allocs storage). */ +#ifdef USE_INDIRECT_REF + if (!dvmInitIndirectRefTable(&thread->jniLocalRefTable, + kJniLocalRefMin, kJniLocalRefMax, kIndirectKindLocal)) + return false; +#else + /* + * The JNI local ref table *must* be fixed-size because we keep pointers + * into the table in our stack frames. + */ if (!dvmInitReferenceTable(&thread->jniLocalRefTable, kJniLocalRefMax, kJniLocalRefMax)) return false; +#endif if (!dvmInitReferenceTable(&thread->internalLocalRefTable, kInternalRefDefault, kInternalRefMax)) return false; @@ -948,7 +1052,11 @@ static void freeThread(Thread* thread) #endif } +#ifdef USE_INDIRECT_REF + dvmClearIndirectRefTable(&thread->jniLocalRefTable); +#else dvmClearReferenceTable(&thread->jniLocalRefTable); +#endif dvmClearReferenceTable(&thread->internalLocalRefTable); if (&thread->jniMonitorRefTable.table != NULL) dvmClearReferenceTable(&thread->jniMonitorRefTable); @@ -995,6 +1103,12 @@ static void setThreadSelf(Thread* thread) * This is mainly of use to ensure that we don't leak resources if, for * example, a thread attaches itself to us with AttachCurrentThread and * then exits without notifying the VM. + * + * We could do the detach here instead of aborting, but this will lead to + * portability problems. Other implementations do not do this check and + * will simply be unaware that the thread has exited, leading to resource + * leaks (and, if this is a non-daemon thread, an infinite hang when the + * VM tries to shut down). */ static void threadExitCheck(void* arg) { @@ -1004,7 +1118,6 @@ static void threadExitCheck(void* arg) assert(thread != NULL); if (thread->status != THREAD_ZOMBIE) { - /* TODO: instead of failing, we could call dvmDetachCurrentThread() */ LOGE("Native thread exited without telling us\n"); dvmAbort(); } @@ -1185,10 +1298,18 @@ bool dvmCreateInterpThread(Object* threadObj, int reqStackSize) assert(threadObj != NULL); if(gDvm.zygote) { - dvmThrowException("Ljava/lang/IllegalStateException;", - "No new threads in -Xzygote mode"); + // Allow the sampling profiler thread. We shut it down before forking. + StringObject* nameStr = (StringObject*) dvmGetFieldObject(threadObj, + gDvm.offJavaLangThread_name); + char* threadName = dvmCreateCstrFromString(nameStr); + bool profilerThread = strcmp(threadName, "SamplingProfiler") == 0; + free(threadName); + if (!profilerThread) { + dvmThrowException("Ljava/lang/IllegalStateException;", + "No new threads in -Xzygote mode"); - goto fail; + goto fail; + } } self = dvmThreadSelf(); @@ -1470,7 +1591,7 @@ static void* interpThreadStart(void* arg) * setPriority(), and then starts the thread. We could manage this with * a "needs priority update" flag to avoid the redundant call. */ - int priority = dvmGetFieldBoolean(self->threadObj, + int priority = dvmGetFieldInt(self->threadObj, gDvm.offJavaLangThread_priority); dvmChangeThreadPriority(self, priority); @@ -1562,6 +1683,12 @@ static void threadExitUncaughtException(Thread* self, Object* group) } bail: +#if defined(WITH_JIT) + /* Remove this thread's suspendCount from global suspendCount sum */ + lockThreadSuspendCount(); + dvmAddToThreadSuspendCount(&self->suspendCount, -self->suspendCount); + unlockThreadSuspendCount(); +#endif dvmReleaseTrackedAlloc(exception, self); } @@ -2116,6 +2243,9 @@ void dvmDetachCurrentThread(void) dvmUnlockThreadList(); setThreadSelf(NULL); + + dvmDetachSystemThread(self); + freeThread(self); } @@ -2138,7 +2268,7 @@ void dvmSuspendThread(Thread* thread) //assert(thread->handle != dvmJdwpGetDebugThread(gDvm.jdwpState)); lockThreadSuspendCount(); - thread->suspendCount++; + dvmAddToThreadSuspendCount(&thread->suspendCount, 1); thread->dbgSuspendCount++; LOG_THREAD("threadid=%d: suspend++, now=%d\n", @@ -2167,7 +2297,7 @@ void dvmResumeThread(Thread* thread) lockThreadSuspendCount(); if (thread->suspendCount > 0) { - thread->suspendCount--; + dvmAddToThreadSuspendCount(&thread->suspendCount, -1); thread->dbgSuspendCount--; } else { LOG_THREAD("threadid=%d: suspendCount already zero\n", @@ -2205,7 +2335,7 @@ void dvmSuspendSelf(bool jdwpActivity) * though. */ lockThreadSuspendCount(); - self->suspendCount++; + dvmAddToThreadSuspendCount(&self->suspendCount, 1); self->dbgSuspendCount++; /* @@ -2300,7 +2430,7 @@ static void dumpWedgedThread(Thread* thread) char proc[100]; sprintf(proc, "/proc/%d/exe", getpid()); int len; - + len = readlink(proc, exePath, sizeof(exePath)-1); exePath[len] = '\0'; } @@ -2343,29 +2473,83 @@ static void dumpWedgedThread(Thread* thread) * * TODO: track basic stats about time required to suspend VM. */ +#define FIRST_SLEEP (250*1000) /* 0.25s */ +#define MORE_SLEEP (750*1000) /* 0.75s */ static void waitForThreadSuspend(Thread* self, Thread* thread) { const int kMaxRetries = 10; - const int kSpinSleepTime = 750*1000; /* 0.75s */ + int spinSleepTime = FIRST_SLEEP; bool complained = false; + bool needPriorityReset = false; + int savedThreadPrio = -500; int sleepIter = 0; int retryCount = 0; u8 startWhen = 0; // init req'd to placate gcc + u8 firstStartWhen = 0; while (thread->status == THREAD_RUNNING && !thread->isSuspended) { - if (sleepIter == 0) // get current time on first iteration + if (sleepIter == 0) { // get current time on first iteration startWhen = dvmGetRelativeTimeUsec(); + if (firstStartWhen == 0) // first iteration of first attempt + firstStartWhen = startWhen; + + /* + * After waiting for a bit, check to see if the target thread is + * running at a reduced priority. If so, bump it up temporarily + * to give it more CPU time. + * + * getpriority() returns the "nice" value, so larger numbers + * indicate lower priority. + * + * (Not currently changing the cgroup. Wasn't necessary in some + * simple experiments.) + */ + if (retryCount == 2) { + assert(thread->systemTid != 0); + errno = 0; + int threadPrio = getpriority(PRIO_PROCESS, thread->systemTid); + if (errno == 0 && threadPrio > 0) { + const int kHigher = 0; + if (setpriority(PRIO_PROCESS, thread->systemTid, kHigher) < 0) + { + LOGW("Couldn't raise priority on tid %d to %d\n", + thread->systemTid, kHigher); + } else { + savedThreadPrio = threadPrio; + needPriorityReset = true; + LOGD("Temporarily raising priority on tid %d (%d -> %d)\n", + thread->systemTid, threadPrio, kHigher); + } + } + } + } - if (!dvmIterativeSleep(sleepIter++, kSpinSleepTime, startWhen)) { - LOGW("threadid=%d (h=%d): spin on suspend threadid=%d (handle=%d)\n", - self->threadId, (int)self->handle, +#if defined (WITH_JIT) + /* + * If we're still waiting after the first timeout, + * unchain all translations. + */ + if (gDvmJit.pJitEntryTable && retryCount > 0) { + LOGD("JIT unchain all attempt #%d",retryCount); + dvmJitUnchainAll(); + } +#endif + + /* + * Sleep briefly. This returns false if we've exceeded the total + * time limit for this round of sleeping. + */ + if (!dvmIterativeSleep(sleepIter++, spinSleepTime, startWhen)) { + LOGW("threadid=%d: spin on suspend #%d threadid=%d (h=%d)\n", + self->threadId, retryCount, thread->threadId, (int)thread->handle); dumpWedgedThread(thread); complained = true; // keep going; could be slow due to valgrind sleepIter = 0; + spinSleepTime = MORE_SLEEP; if (retryCount++ == kMaxRetries) { LOGE("threadid=%d: stuck on threadid=%d, giving up\n", @@ -2377,9 +2561,20 @@ static void waitForThreadSuspend(Thread* self, Thread* thread) } if (complained) { - LOGW("threadid=%d: spin on suspend resolved\n", self->threadId); + LOGW("threadid=%d: spin on suspend resolved in %lld msec\n", + self->threadId, + (dvmGetRelativeTimeUsec() - firstStartWhen) / 1000); //dvmDumpThread(thread, false); /* suspended, so dump is safe */ } + if (needPriorityReset) { + if (setpriority(PRIO_PROCESS, thread->systemTid, savedThreadPrio) < 0) { + LOGW("NOTE: couldn't reset priority on thread %d to %d\n", + thread->systemTid, savedThreadPrio); + } else { + LOGV("Restored priority on %d to %d\n", + thread->systemTid, savedThreadPrio); + } + } } /* @@ -2444,7 +2639,7 @@ void dvmSuspendAllThreads(SuspendCause why) thread->handle == dvmJdwpGetDebugThread(gDvm.jdwpState)) continue; - thread->suspendCount++; + dvmAddToThreadSuspendCount(&thread->suspendCount, 1); if (why == SUSPEND_FOR_DEBUG || why == SUSPEND_FOR_DEBUG_EVENT) thread->dbgSuspendCount++; } @@ -2476,7 +2671,7 @@ void dvmSuspendAllThreads(SuspendCause why) /* wait for the other thread to see the pending suspend */ waitForThreadSuspend(self, thread); - LOG_THREAD("threadid=%d: threadid=%d status=%d c=%d dc=%d isSusp=%d\n", + LOG_THREAD("threadid=%d: threadid=%d status=%d c=%d dc=%d isSusp=%d\n", self->threadId, thread->threadId, thread->status, thread->suspendCount, thread->dbgSuspendCount, thread->isSuspended); @@ -2521,7 +2716,7 @@ void dvmResumeAllThreads(SuspendCause why) } if (thread->suspendCount > 0) { - thread->suspendCount--; + dvmAddToThreadSuspendCount(&thread->suspendCount, -1); if (why == SUSPEND_FOR_DEBUG || why == SUSPEND_FOR_DEBUG_EVENT) thread->dbgSuspendCount--; } else { @@ -2612,7 +2807,8 @@ void dvmUndoDebuggerSuspensions(void) } assert(thread->suspendCount >= thread->dbgSuspendCount); - thread->suspendCount -= thread->dbgSuspendCount; + dvmAddToThreadSuspendCount(&thread->suspendCount, + -thread->dbgSuspendCount); thread->dbgSuspendCount = 0; } unlockThreadSuspendCount(); @@ -2827,6 +3023,23 @@ Thread* dvmGetThreadFromThreadObject(Object* vmThreadObj) int vmData; vmData = dvmGetFieldInt(vmThreadObj, gDvm.offJavaLangVMThread_vmData); + + if (false) { + Thread* thread = gDvm.threadList; + while (thread != NULL) { + if ((Thread*)vmData == thread) + break; + + thread = thread->next; + } + + if (thread == NULL) { + LOGW("WARNING: vmThreadObj=%p has thread=%p, not in thread list\n", + vmThreadObj, (Thread*)vmData); + vmData = 0; + } + } + return (Thread*) vmData; } @@ -2850,41 +3063,6 @@ static const int kNiceValues[10] = { ANDROID_PRIORITY_URGENT_DISPLAY /* 10 (MAX_PRIORITY) */ }; -/* - * Change the scheduler cgroup of a pid - */ -int dvmChangeThreadSchedulerGroup(const char *cgroup) -{ -#ifdef HAVE_ANDROID_OS - FILE *fp; - char path[255]; - int rc; - - sprintf(path, "/dev/cpuctl/%s/tasks", (cgroup ? cgroup : "")); - - if (!(fp = fopen(path, "w"))) { -#if ENABLE_CGROUP_ERR_LOGGING - LOGW("Unable to open %s (%s)\n", path, strerror(errno)); -#endif - return -errno; - } - - rc = fprintf(fp, "0"); - fclose(fp); - - if (rc < 0) { -#if ENABLE_CGROUP_ERR_LOGGING - LOGW("Unable to move pid %d to cgroup %s (%s)\n", getpid(), - (cgroup ? cgroup : ""), strerror(errno)); -#endif - } - - return (rc < 0) ? errno : 0; -#else // HAVE_ANDROID_OS - return 0; -#endif -} - /* * Change the priority of a system thread to match that of the Thread object. * @@ -2902,10 +3080,10 @@ void dvmChangeThreadPriority(Thread* thread, int newPriority) } newNice = kNiceValues[newPriority-1]; - if (newPriority >= ANDROID_PRIORITY_BACKGROUND) { - dvmChangeThreadSchedulerGroup("bg_non_interactive"); + if (newNice >= ANDROID_PRIORITY_BACKGROUND) { + set_sched_policy(dvmGetSysThreadId(), SP_BACKGROUND); } else if (getpriority(PRIO_PROCESS, pid) >= ANDROID_PRIORITY_BACKGROUND) { - dvmChangeThreadSchedulerGroup(NULL); + set_sched_policy(dvmGetSysThreadId(), SP_FOREGROUND); } if (setpriority(PRIO_PROCESS, pid, newNice) != 0) { @@ -2980,6 +3158,56 @@ void dvmDumpThread(Thread* thread, bool isRunning) dvmDumpThreadEx(&target, thread, isRunning); } +/* + * Try to get the scheduler group. + * + * The data from /proc//cgroup looks like: + * 2:cpu:/bg_non_interactive + * + * We return the part after the "/", which will be an empty string for + * the default cgroup. If the string is longer than "bufLen", the string + * will be truncated. + */ +static bool getSchedulerGroup(Thread* thread, char* buf, size_t bufLen) +{ +#ifdef HAVE_ANDROID_OS + char pathBuf[32]; + char readBuf[256]; + ssize_t count; + int fd; + + snprintf(pathBuf, sizeof(pathBuf), "/proc/%d/cgroup", thread->systemTid); + if ((fd = open(pathBuf, O_RDONLY)) < 0) { + LOGV("open(%s) failed: %s\n", pathBuf, strerror(errno)); + return false; + } + + count = read(fd, readBuf, sizeof(readBuf)); + if (count <= 0) { + LOGV("read(%s) failed (%d): %s\n", + pathBuf, (int) count, strerror(errno)); + close(fd); + return false; + } + close(fd); + + readBuf[--count] = '\0'; /* remove the '\n', now count==strlen */ + + char* cp = strchr(readBuf, '/'); + if (cp == NULL) { + readBuf[sizeof(readBuf)-1] = '\0'; + LOGV("no '/' in '%s' (file=%s count=%d)\n", + readBuf, pathBuf, (int) count); + return false; + } + + memcpy(buf, cp+1, count); /* count-1 for cp+1, count+1 for NUL */ + return true; +#else + return false; +#endif +} + /* * Print information about the specified thread. * @@ -3000,6 +3228,7 @@ void dvmDumpThreadEx(const DebugOutputTarget* target, Thread* thread, StringObject* nameStr; char* threadName = NULL; char* groupName = NULL; + char schedulerGroupBuf[32]; bool isDaemon; int priority; // java.lang.Thread priority int policy; // pthread policy @@ -3022,6 +3251,12 @@ void dvmDumpThreadEx(const DebugOutputTarget* target, Thread* thread, policy = -1; sp.sched_priority = -1; } + if (!getSchedulerGroup(thread, schedulerGroupBuf,sizeof(schedulerGroupBuf))) + { + strcpy(schedulerGroupBuf, "unknown"); + } else if (schedulerGroupBuf[0] == '\0') { + strcpy(schedulerGroupBuf, "default"); + } /* a null value for group is not expected, but deal with it anyway */ groupObj = (Object*) dvmGetFieldObject(threadObj, @@ -3049,9 +3284,9 @@ void dvmDumpThreadEx(const DebugOutputTarget* target, Thread* thread, groupName, thread->suspendCount, thread->dbgSuspendCount, thread->isSuspended ? 'Y' : 'N', thread->threadObj, thread); dvmPrintDebugMessage(target, - " | sysTid=%d nice=%d sched=%d/%d handle=%d\n", + " | sysTid=%d nice=%d sched=%d/%d cgrp=%s handle=%d\n", thread->systemTid, getpriority(PRIO_PROCESS, thread->systemTid), - policy, sp.sched_priority, (int)thread->handle); + policy, sp.sched_priority, schedulerGroupBuf, (int)thread->handle); #ifdef WITH_MONITOR_TRACKING if (!isRunning) { @@ -3274,9 +3509,15 @@ LockedObjectData* dvmFindInMonitorList(const Thread* self, const Object* obj) * GC helper functions */ +/* + * Add the contents of the registers from the interpreted call stack. + */ static void gcScanInterpStackReferences(Thread *thread) { const u4 *framePtr; +#if WITH_EXTRA_GC_CHECKS > 1 + bool first = true; +#endif framePtr = (const u4 *)thread->curFrame; while (framePtr != NULL) { @@ -3285,27 +3526,182 @@ static void gcScanInterpStackReferences(Thread *thread) saveArea = SAVEAREA_FROM_FP(framePtr); method = saveArea->method; - if (method != NULL) { + if (method != NULL && !dvmIsNativeMethod(method)) { #ifdef COUNT_PRECISE_METHODS /* the GC is running, so no lock required */ - if (!dvmIsNativeMethod(method)) { - if (dvmPointerSetAddEntry(gDvm.preciseMethods, method)) - LOGI("Added %s.%s %p\n", - method->clazz->descriptor, method->name, method); + if (dvmPointerSetAddEntry(gDvm.preciseMethods, method)) + LOGI("PGC: added %s.%s %p\n", + method->clazz->descriptor, method->name, method); +#endif +#if WITH_EXTRA_GC_CHECKS > 1 + /* + * May also want to enable the memset() in the "invokeMethod" + * goto target in the portable interpreter. That sets the stack + * to a pattern that makes referring to uninitialized data + * very obvious. + */ + + if (first) { + /* + * First frame, isn't native, check the "alternate" saved PC + * as a sanity check. + * + * It seems like we could check the second frame if the first + * is native, since the PCs should be the same. It turns out + * this doesn't always work. The problem is that we could + * have calls in the sequence: + * interp method #2 + * native method + * interp method #1 + * + * and then GC while in the native method after returning + * from interp method #2. The currentPc on the stack is + * for interp method #1, but thread->currentPc2 is still + * set for the last thing interp method #2 did. + * + * This can also happen in normal execution: + * - sget-object on not-yet-loaded class + * - class init updates currentPc2 + * - static field init is handled by parsing annotations; + * static String init requires creation of a String object, + * which can cause a GC + * + * Essentially, any pattern that involves executing + * interpreted code and then causes an allocation without + * executing instructions in the original method will hit + * this. These are rare enough that the test still has + * some value. + */ + if (saveArea->xtra.currentPc != thread->currentPc2) { + LOGW("PGC: savedPC(%p) != current PC(%p), %s.%s ins=%p\n", + saveArea->xtra.currentPc, thread->currentPc2, + method->clazz->descriptor, method->name, method->insns); + if (saveArea->xtra.currentPc != NULL) + LOGE(" pc inst = 0x%04x\n", *saveArea->xtra.currentPc); + if (thread->currentPc2 != NULL) + LOGE(" pc2 inst = 0x%04x\n", *thread->currentPc2); + dvmDumpThread(thread, false); + } + } else { + /* + * It's unusual, but not impossible, for a non-first frame + * to be at something other than a method invocation. For + * example, if we do a new-instance on a nonexistent class, + * we'll have a lot of class loader activity on the stack + * above the frame with the "new" operation. Could also + * happen while we initialize a Throwable when an instruction + * fails. + * + * So there's not much we can do here to verify the PC, + * except to verify that it's a GC point. + */ } + assert(saveArea->xtra.currentPc != NULL); #endif + + const RegisterMap* pMap; + const u1* regVector; int i; - for (i = method->registersSize - 1; i >= 0; i--) { - u4 rval = *framePtr++; -//TODO: wrap markifobject in a macro that does pointer checks - if (rval != 0 && (rval & 0x3) == 0) { - dvmMarkIfObject((Object *)rval); + + Method* nonConstMethod = (Method*) method; // quiet gcc + pMap = dvmGetExpandedRegisterMap(nonConstMethod); + if (pMap != NULL) { + /* found map, get registers for this address */ + int addr = saveArea->xtra.currentPc - method->insns; + regVector = dvmRegisterMapGetLine(pMap, addr); + if (regVector == NULL) { + LOGW("PGC: map but no entry for %s.%s addr=0x%04x\n", + method->clazz->descriptor, method->name, addr); + } else { + LOGV("PGC: found map for %s.%s 0x%04x (t=%d)\n", + method->clazz->descriptor, method->name, addr, + thread->threadId); + } + } else { + /* + * No map found. If precise GC is disabled this is + * expected -- we don't create pointers to the map data even + * if it's present -- but if it's enabled it means we're + * unexpectedly falling back on a conservative scan, so it's + * worth yelling a little. + */ + if (gDvm.preciseGc) { + LOGVV("PGC: no map for %s.%s\n", + method->clazz->descriptor, method->name); } + regVector = NULL; + } + + if (regVector == NULL) { + /* conservative scan */ + for (i = method->registersSize - 1; i >= 0; i--) { + u4 rval = *framePtr++; + if (rval != 0 && (rval & 0x3) == 0) { + dvmMarkIfObject((Object *)rval); + } + } + } else { + /* + * Precise scan. v0 is at the lowest address on the + * interpreted stack, and is the first bit in the register + * vector, so we can walk through the register map and + * memory in the same direction. + * + * A '1' bit indicates a live reference. + */ + u2 bits = 1 << 1; + for (i = method->registersSize - 1; i >= 0; i--) { + u4 rval = *framePtr++; + + bits >>= 1; + if (bits == 1) { + /* set bit 9 so we can tell when we're empty */ + bits = *regVector++ | 0x0100; + LOGVV("loaded bits: 0x%02x\n", bits & 0xff); + } + + if (rval != 0 && (bits & 0x01) != 0) { + /* + * Non-null, register marked as live reference. This + * should always be a valid object. + */ +#if WITH_EXTRA_GC_CHECKS > 0 + if ((rval & 0x3) != 0 || + !dvmIsValidObject((Object*) rval)) + { + /* this is very bad */ + LOGE("PGC: invalid ref in reg %d: 0x%08x\n", + method->registersSize-1 - i, rval); + } else +#endif + { + dvmMarkObjectNonNull((Object *)rval); + } + } else { + /* + * Null or non-reference, do nothing at all. + */ +#if WITH_EXTRA_GC_CHECKS > 1 + if (dvmIsValidObject((Object*) rval)) { + /* this is normal, but we feel chatty */ + LOGD("PGC: ignoring valid ref in reg %d: 0x%08x\n", + method->registersSize-1 - i, rval); + } +#endif + } + } + dvmReleaseRegisterMapLine(pMap, regVector); } } - /* else this is a break frame; nothing to mark. + /* else this is a break frame and there is nothing to mark, or + * this is a native method and the registers are just the "ins", + * copied from various registers in the caller's set. */ +#if WITH_EXTRA_GC_CHECKS > 1 + first = false; +#endif + /* Don't fall into an infinite loop if things get corrupted. */ assert((uintptr_t)saveArea->prevFrame > (uintptr_t)framePtr || @@ -3331,6 +3727,20 @@ static void gcScanReferenceTable(ReferenceTable *refTable) } } +static void gcScanIndirectRefTable(IndirectRefTable* pRefTable) +{ + Object** op = pRefTable->table; + int numEntries = dvmIndirectRefTableEntries(pRefTable); + int i; + + for (i = 0; i < numEntries; i++) { + Object* obj = *op; + if (obj != NULL) + dvmMarkObjectNonNull(obj); + op++; + } +} + /* * Scan a Thread and mark any objects it references. */ @@ -3361,7 +3771,11 @@ static void gcScanThread(Thread *thread) HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_JNI_LOCAL, thread->threadId); +#ifdef USE_INDIRECT_REF + gcScanIndirectRefTable(&thread->jniLocalRefTable); +#else gcScanReferenceTable(&thread->jniLocalRefTable); +#endif if (thread->jniMonitorRefTable.table != NULL) { HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_JNI_MONITOR, thread->threadId); @@ -3403,11 +3817,10 @@ void dvmGcScanRootThreadGroups() * through the actual ThreadGroups, but it should be * equivalent. * - * This assumes that the ThreadGroup class object is in + * This assumes that the ThreadGroup class object is in * the root set, which should always be true; it's * loaded by the built-in class loader, which is part * of the root set. */ gcScanAllThreads(); } - diff --git a/vm/Thread.h b/vm/Thread.h index 9c15f9e16..1bb531409 100644 --- a/vm/Thread.h +++ b/vm/Thread.h @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + /* * VM thread support. */ @@ -22,7 +23,7 @@ #include "jni.h" #if defined(CHECK_MUTEX) && !defined(__USE_UNIX98) -/* Linux lacks this unless you #define __USE_UNIX98 */ +/* glibc lacks this unless you #define __USE_UNIX98 */ int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type); enum { PTHREAD_MUTEX_ERRORCHECK = PTHREAD_MUTEX_ERRORCHECK_NP }; #endif @@ -67,6 +68,7 @@ void dvmThreadShutdown(void); void dvmSlayDaemons(void); +#define kJniLocalRefMin 32 #define kJniLocalRefMax 512 /* arbitrary; should be plenty */ #define kInternalRefDefault 32 /* equally arbitrary */ #define kInternalRefMax 4096 /* mainly a sanity check */ @@ -75,6 +77,11 @@ void dvmSlayDaemons(void); #define kDefaultStackSize (12*1024) /* three 4K pages */ #define kMaxStackSize (256*1024 + STACK_OVERFLOW_RESERVE) +/* + * System thread state. See native/SystemThread.h. + */ +typedef struct SystemThread SystemThread; + /* * Our per-thread data. * @@ -88,7 +95,7 @@ typedef struct Thread { * Thread's current status. Can only be changed by the thread itself * (i.e. don't mess with this from other threads). */ - ThreadStatus status; + volatile ThreadStatus status; /* * This is the number of times the thread has been suspended. When the @@ -151,7 +158,11 @@ typedef struct Thread { ReferenceTable internalLocalRefTable; /* JNI local reference tracking */ +#ifdef USE_INDIRECT_REF + IndirectRefTable jniLocalRefTable; +#else ReferenceTable jniLocalRefTable; +#endif /* JNI native monitor reference tracking (initialized on first use) */ ReferenceTable jniMonitorRefTable; @@ -203,6 +214,14 @@ typedef struct Thread { #ifdef WITH_JNI_STACK_CHECK u4 stackCrc; #endif + +#if WITH_EXTRA_GC_CHECKS > 1 + /* PC, saved on every instruction; redundant with StackSaveArea */ + const u2* currentPc2; +#endif + + /* system thread state */ + SystemThread* systemThread; } Thread; /* start point for an internal thread; mimics pthread args */ @@ -250,6 +269,9 @@ typedef enum SuspendCause { SUSPEND_FOR_DEBUG_EVENT, SUSPEND_FOR_STACK_DUMP, SUSPEND_FOR_DEX_OPT, +#if defined(WITH_JIT) + SUSPEND_FOR_JIT, +#endif } SuspendCause; void dvmSuspendThread(Thread* thread); void dvmSuspendSelf(bool jdwpActivity); @@ -277,12 +299,11 @@ void dvmWaitForSuspend(Thread* thread); bool dvmCheckSuspendPending(Thread* self); /* - * Fast test for use in the interpreter. If our suspend count is nonzero, - * do a more rigorous evaluation. + * Fast test for use in the interpreter. Returns "true" if our suspend + * count is nonzero. */ -INLINE void dvmCheckSuspendQuick(Thread* self) { - if (self->suspendCount != 0) - dvmCheckSuspendPending(self); +INLINE bool dvmCheckSuspendQuick(Thread* self) { + return (self->suspendCount != 0); } /* @@ -390,24 +411,18 @@ char* dvmGetThreadName(Thread* thread); * thread is part of the GC's root set. */ bool dvmIsOnThreadList(const Thread* thread); - + /* * Get/set the JNIEnv field. */ INLINE JNIEnv* dvmGetThreadJNIEnv(Thread* self) { return self->jniEnv; } INLINE void dvmSetThreadJNIEnv(Thread* self, JNIEnv* env) { self->jniEnv = env;} -/* - * Change the scheduler group of the current process - */ -int dvmChangeThreadSchedulerGroup(const char *group); - /* * Update the priority value of the underlying pthread. */ void dvmChangeThreadPriority(Thread* thread, int newPriority); - /* * Debug: dump information about a single thread. */ @@ -421,7 +436,6 @@ void dvmDumpThreadEx(const DebugOutputTarget* target, Thread* thread, void dvmDumpAllThreads(bool grabLock); void dvmDumpAllThreadsEx(const DebugOutputTarget* target, bool grabLock); - #ifdef WITH_MONITOR_TRACKING /* * Track locks held by the current thread, along with the stack trace at diff --git a/vm/UtfString.c b/vm/UtfString.c index a5e0b35a1..dfb76bc85 100644 --- a/vm/UtfString.c +++ b/vm/UtfString.c @@ -434,6 +434,15 @@ int dvmStringLen(StringObject* jstr) return dvmGetFieldInt((Object*) jstr, gDvm.offJavaLangString_count); } +/* + * Get the char[] object from the String. + */ +ArrayObject* dvmStringCharArray(StringObject* jstr) +{ + return (ArrayObject*) dvmGetFieldObject((Object*) jstr, + gDvm.offJavaLangString_value); +} + /* * Get the string's data. */ diff --git a/vm/UtfString.h b/vm/UtfString.h index 5ca2ce634..ca500a7b7 100644 --- a/vm/UtfString.h +++ b/vm/UtfString.h @@ -99,6 +99,11 @@ int dvmStringUtf8ByteLen(StringObject* jstr); */ int dvmStringLen(StringObject* jstr); +/* + * Get the char[] object from the String. + */ +ArrayObject* dvmStringCharArray(StringObject* jstr); + /* * Get a pointer to the Unicode data. */ diff --git a/vm/alloc/Alloc.c b/vm/alloc/Alloc.c index e24741351..0961520e4 100644 --- a/vm/alloc/Alloc.c +++ b/vm/alloc/Alloc.c @@ -68,25 +68,27 @@ bool dvmGcPreZygoteFork(void) } /* - * Create a "stock instance" of an exception class. These won't have - * useful stack traces in them, but they can be thrown when everything - * else is not working in a container class. + * Create a "stock instance" of an exception class. */ -static Object* createStockException(const char* descriptor) +static Object* createStockException(const char* descriptor, const char* msg) { + Thread* self = dvmThreadSelf(); + StringObject* msgStr = NULL; ClassObject* clazz; Method* init; Object* obj; + /* find class, initialize if necessary */ clazz = dvmFindSystemClass(descriptor); if (clazz == NULL) { LOGE("Unable to find %s\n", descriptor); return NULL; } - init = dvmFindDirectMethodByDescriptor(clazz, "", "()V"); + init = dvmFindDirectMethodByDescriptor(clazz, "", + "(Ljava/lang/String;)V"); if (init == NULL) { - LOGE("Unable to find nullary constructor for %s\n", descriptor); + LOGE("Unable to find String-arg constructor for %s\n", descriptor); return NULL; } @@ -94,30 +96,56 @@ static Object* createStockException(const char* descriptor) if (obj == NULL) return NULL; - Thread* self = dvmThreadSelf(); + if (msg == NULL) { + msgStr = NULL; + } else { + msgStr = dvmCreateStringFromCstr(msg, ALLOC_DEFAULT); + if (msgStr == NULL) { + LOGW("Could not allocate message string \"%s\"\n", msg); + dvmReleaseTrackedAlloc(obj, self); + return NULL; + } + } + JValue unused; - dvmCallMethod(self, init, obj, &unused); - if (dvmCheckException(self)) + dvmCallMethod(self, init, obj, &unused, msgStr); + if (dvmCheckException(self)) { + dvmReleaseTrackedAlloc((Object*) msgStr, self); + dvmReleaseTrackedAlloc(obj, self); return NULL; + } + dvmReleaseTrackedAlloc((Object*) msgStr, self); // okay if msgStr NULL return obj; } /* - * "Late" initialization. We had to defer this until we were able to - * interpret code. + * Create some "stock" exceptions. These can be thrown when the system is + * too screwed up to allocate and initialize anything, or when we don't + * need a meaningful stack trace. + * + * We can't do this during the initial startup because we need to execute + * the constructors. */ -bool dvmGcLateInit(void) +bool dvmCreateStockExceptions(void) { /* * Pre-allocate some throwables. These need to be explicitly added - * to the root set by the GC. + * to the GC's root set (see dvmHeapMarkRootSet()). */ - gDvm.outOfMemoryObj = createStockException("Ljava/lang/OutOfMemoryError;"); + gDvm.outOfMemoryObj = createStockException("Ljava/lang/OutOfMemoryError;", + "[memory exhausted]"); dvmReleaseTrackedAlloc(gDvm.outOfMemoryObj, NULL); - gDvm.internalErrorObj = createStockException("Ljava/lang/InternalError;"); + gDvm.internalErrorObj = createStockException("Ljava/lang/InternalError;", + "[pre-allocated]"); dvmReleaseTrackedAlloc(gDvm.internalErrorObj, NULL); - if (gDvm.outOfMemoryObj == NULL || gDvm.internalErrorObj == NULL) { + gDvm.noClassDefFoundErrorObj = + createStockException("Ljava/lang/NoClassDefFoundError;", NULL); + dvmReleaseTrackedAlloc(gDvm.noClassDefFoundErrorObj, NULL); + + if (gDvm.outOfMemoryObj == NULL || gDvm.internalErrorObj == NULL || + gDvm.noClassDefFoundErrorObj == NULL) + { LOGW("Unable to create stock exceptions\n"); return false; } diff --git a/vm/alloc/Alloc.h b/vm/alloc/Alloc.h index 0489db7d4..facc753d1 100644 --- a/vm/alloc/Alloc.h +++ b/vm/alloc/Alloc.h @@ -25,9 +25,9 @@ * Initialization. */ bool dvmGcStartup(void); +bool dvmCreateStockExceptions(void); bool dvmGcStartupAfterZygote(void); void dvmGcShutdown(void); -bool dvmGcLateInit(void); /* * Do any last-minute preparation before we call fork() for the first time. @@ -133,7 +133,7 @@ INLINE int dvmValidateObject(Object* obj) } #ifdef WITH_EXTRA_OBJECT_VALIDATION if (!dvmIsValidObject(obj)) { - //abort(); + dvmAbort(); dvmThrowException("Ljava/lang/InternalError;", "VM detected invalid object ptr"); return false; @@ -142,7 +142,7 @@ INLINE int dvmValidateObject(Object* obj) #ifndef NDEBUG /* check for heap corruption */ if (obj->clazz == NULL || ((u4) obj->clazz) <= 65536) { - abort(); + dvmAbort(); dvmThrowException("Ljava/lang/InternalError;", "VM detected invalid object class ptr"); return false; diff --git a/vm/alloc/DdmHeap.c b/vm/alloc/DdmHeap.c index 78da6cd7e..f21a8752d 100644 --- a/vm/alloc/DdmHeap.c +++ b/vm/alloc/DdmHeap.c @@ -224,10 +224,18 @@ heap_chunk_callback(const void *chunkptr, size_t chunklen, * allocation units used by the chunk. */ { + size_t needed = (((chunklen/ALLOCATION_UNIT_SIZE + 255) / 256) * 2); size_t bytesLeft = ctx->bufLen - (size_t)(ctx->p - ctx->buf); - if (bytesLeft < (((chunklen/ALLOCATION_UNIT_SIZE + 255) / 256) * 2)) { + if (bytesLeft < needed) { flush_hpsg_chunk(ctx); } + + bytesLeft = ctx->bufLen - (size_t)(ctx->p - ctx->buf); + if (bytesLeft < needed) { + LOGW("chunk is too big to transmit (chunklen=%zd, %zd bytes)\n", + chunklen, needed); + return; + } } //TODO: notice when there's a gap and start a new heap, or at least a new range. @@ -345,7 +353,12 @@ enum HpsgWhat { HPSG_WHAT_DISTINCT_OBJECTS = 1, }; -#define HPSx_CHUNK_SIZE (4096 - 16) +/* + * Maximum chunk size. Obtain this from the formula: + * + * (((maximum_heap_size / ALLOCATION_UNIT_SIZE) + 255) / 256) * 2 + */ +#define HPSx_CHUNK_SIZE (16384 - 16) void dlmalloc_walk_heap(void(*)(const void*, size_t, const void*, size_t, void*),void*); diff --git a/vm/alloc/Heap.c b/vm/alloc/Heap.c index e58a8a8c8..17d6a5cb1 100644 --- a/vm/alloc/Heap.c +++ b/vm/alloc/Heap.c @@ -27,6 +27,8 @@ #include "utils/threads.h" // need Android thread priorities #define kInvalidPriority 10000 +#include + #include #include #include @@ -148,15 +150,21 @@ void dvmHeapShutdown() /* * We've been asked to allocate something we can't, e.g. an array so - * large that (length * elementWidth) is larger than 2^31. We want to - * throw an OutOfMemoryError, but doing so implies that certain other - * actions have taken place (like clearing soft references). + * large that (length * elementWidth) is larger than 2^31. + * + * _The Java Programming Language_, 4th edition, says, "you can be sure + * that all SoftReferences to softly reachable objects will be cleared + * before an OutOfMemoryError is thrown." * - * TODO: for now we just throw an InternalError. + * It's unclear whether that holds for all situations where an OOM can + * be thrown, or just in the context of an allocation that fails due + * to lack of heap space. For simplicity we just throw the exception. + * + * (OOM due to actually running out of space is handled elsewhere.) */ void dvmThrowBadAllocException(const char* msg) { - dvmThrowException("Ljava/lang/InternalError;", msg); + dvmThrowException("Ljava/lang/OutOfMemoryError;", msg); } /* @@ -174,6 +182,7 @@ bool dvmLockHeap() if (self != NULL) { oldStatus = dvmChangeStatus(self, THREAD_VMWAIT); } else { + LOGI("ODD: waiting on heap lock, no self\n"); oldStatus = -1; // shut up gcc } @@ -776,7 +785,7 @@ void dvmCollectGarbageInternal(bool collectSoftReferences) */ if (priorityResult >= ANDROID_PRIORITY_BACKGROUND) { - dvmChangeThreadSchedulerGroup(NULL); + set_sched_policy(dvmGetSysThreadId(), SP_FOREGROUND); } if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_NORMAL) != 0) { @@ -867,7 +876,10 @@ void dvmCollectGarbageInternal(bool collectSoftReferences) /* Set up the marking context. */ - dvmHeapBeginMarkStep(); + if (!dvmHeapBeginMarkStep()) { + LOGE_HEAP("dvmHeapBeginMarkStep failed; aborting\n"); + dvmAbort(); + } /* Mark the set of objects that are strongly reachable from the roots. */ @@ -1026,7 +1038,7 @@ void dvmCollectGarbageInternal(bool collectSoftReferences) } if (oldThreadPriority >= ANDROID_PRIORITY_BACKGROUND) { - dvmChangeThreadSchedulerGroup("bg_non_interactive"); + set_sched_policy(dvmGetSysThreadId(), SP_BACKGROUND); } } gcElapsedTime = (dvmGetRelativeTimeUsec() - gcHeap->gcStartTime) / 1000; diff --git a/vm/alloc/HeapDebug.c b/vm/alloc/HeapDebug.c index fc3655f1c..54b5cb620 100644 --- a/vm/alloc/HeapDebug.c +++ b/vm/alloc/HeapDebug.c @@ -15,7 +15,7 @@ */ #include -#include +#include #include "Dalvik.h" #include "HeapInternal.h" diff --git a/vm/alloc/HeapSource.c b/vm/alloc/HeapSource.c index d97290f32..830e5d727 100644 --- a/vm/alloc/HeapSource.c +++ b/vm/alloc/HeapSource.c @@ -786,6 +786,82 @@ dvmHeapSourceFree(void *ptr) } } +/* + * Frees the first numPtrs objects in the ptrs list. The list must + * contain addresses all in the same mspace, and must be in increasing + * order. This implies that there are no duplicates, and no entries + * are NULL. + */ +void +dvmHeapSourceFreeList(size_t numPtrs, void **ptrs) +{ + Heap *heap; + + HS_BOILERPLATE(); + + if (numPtrs == 0) { + return; + } + + assert(ptrs != NULL); + assert(*ptrs != NULL); + heap = ptr2heap(gHs, *ptrs); + if (heap != NULL) { + mspace *msp = heap->msp; + // Calling mspace_free on shared heaps disrupts sharing too + // much. For heap[0] -- the 'active heap' -- we call + // mspace_free, but on the other heaps we only do some + // accounting. + if (heap == gHs->heaps) { + // mspace_merge_objects takes two allocated objects, and + // if the second immediately follows the first, will merge + // them, returning a larger object occupying the same + // memory. This is a local operation, and doesn't require + // dlmalloc to manipulate any freelists. It's pretty + // inexpensive compared to free(). + + // ptrs is an array of objects all in memory order, and if + // client code has been allocating lots of short-lived + // objects, this is likely to contain runs of objects all + // now garbage, and thus highly amenable to this optimization. + + // Unroll the 0th iteration around the loop below, + // countFree ptrs[0] and initializing merged. + assert(ptrs[0] != NULL); + assert(ptr2heap(gHs, ptrs[0]) == heap); + countFree(heap, ptrs[0], true); + void *merged = ptrs[0]; + + size_t i; + for (i = 1; i < numPtrs; i++) { + assert(merged != NULL); + assert(ptrs[i] != NULL); + assert((intptr_t)merged < (intptr_t)ptrs[i]); + assert(ptr2heap(gHs, ptrs[i]) == heap); + countFree(heap, ptrs[i], true); + // Try to merge. If it works, merged now includes the + // memory of ptrs[i]. If it doesn't, free merged, and + // see if ptrs[i] starts a new run of adjacent + // objects to merge. + if (mspace_merge_objects(msp, merged, ptrs[i]) == NULL) { + mspace_free(msp, merged); + merged = ptrs[i]; + } + } + assert(merged != NULL); + mspace_free(msp, merged); + } else { + // This is not an 'active heap'. Only do the accounting. + size_t i; + for (i = 0; i < numPtrs; i++) { + assert(ptrs[i] != NULL); + assert(ptr2heap(gHs, ptrs[i]) == heap); + countFree(heap, ptrs[i], true); + } + } + } +} + /* * Returns true iff was allocated from the heap source. */ diff --git a/vm/alloc/HeapSource.h b/vm/alloc/HeapSource.h index 3007d4fc1..fdaf119f4 100644 --- a/vm/alloc/HeapSource.h +++ b/vm/alloc/HeapSource.h @@ -104,6 +104,14 @@ void *dvmHeapSourceAllocAndGrow(size_t n); */ void dvmHeapSourceFree(void *ptr); +/* + * Frees the first numPtrs objects in the ptrs list. The list must + * contain addresses all in the same mspace, and must be in increasing + * order. This implies that there are no duplicates, and no entries + * are NULL. + */ +void dvmHeapSourceFreeList(size_t numPtrs, void **ptrs); + /* * Returns true iff was allocated from the heap source. */ diff --git a/vm/alloc/HeapWorker.c b/vm/alloc/HeapWorker.c index b4a2d0ea5..e7a4d0375 100644 --- a/vm/alloc/HeapWorker.c +++ b/vm/alloc/HeapWorker.c @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + /* * An async worker thread to handle certain heap operations that * need to be done in a separate thread to avoid synchronization @@ -342,12 +343,20 @@ static void* heapWorkerThreadStart(void* arg) /* Trim the heap if we were asked to. */ trimtime = gDvm.gcHeap->heapWorkerNextTrim; if (trimtime.tv_sec != 0 && trimtime.tv_nsec != 0) { - struct timeval now; + struct timespec now; + +#ifdef HAVE_TIMEDWAIT_MONOTONIC + clock_gettime(CLOCK_MONOTONIC, &now); // relative time +#else + struct timeval tvnow; + gettimeofday(&tvnow, NULL); // absolute time + now.tv_sec = tvnow.tv_sec; + now.tv_nsec = tvnow.tv_usec * 1000; +#endif - gettimeofday(&now, NULL); if (trimtime.tv_sec < now.tv_sec || (trimtime.tv_sec == now.tv_sec && - trimtime.tv_nsec <= now.tv_usec * 1000)) + trimtime.tv_nsec <= now.tv_nsec)) { size_t madvisedSizes[HEAP_SOURCE_MAX_HEAP_COUNT]; @@ -377,8 +386,13 @@ static void* heapWorkerThreadStart(void* arg) /* sleep until signaled */ if (timedwait) { +#ifdef HAVE_TIMEDWAIT_MONOTONIC + cc = pthread_cond_timedwait_monotonic(&gDvm.heapWorkerCond, + &gDvm.heapWorkerLock, &trimtime); +#else cc = pthread_cond_timedwait(&gDvm.heapWorkerCond, &gDvm.heapWorkerLock, &trimtime); +#endif assert(cc == 0 || cc == ETIMEDOUT || cc == EINTR); } else { cc = pthread_cond_wait(&gDvm.heapWorkerCond, &gDvm.heapWorkerLock); @@ -494,9 +508,14 @@ void dvmScheduleHeapSourceTrim(size_t timeoutSec) } else { struct timeval now; +#ifdef HAVE_TIMEDWAIT_MONOTONIC + clock_gettime(CLOCK_MONOTONIC, &timeout); + timeout.tv_sec += timeoutSec; +#else gettimeofday(&now, NULL); timeout.tv_sec = now.tv_sec + timeoutSec; timeout.tv_nsec = now.tv_usec * 1000; +#endif dvmSignalHeapWorker(false); } gcHeap->heapWorkerNextTrim = timeout; diff --git a/vm/alloc/MarkSweep.c b/vm/alloc/MarkSweep.c index a0601d78b..09cc25fc1 100644 --- a/vm/alloc/MarkSweep.c +++ b/vm/alloc/MarkSweep.c @@ -15,6 +15,7 @@ */ #include "Dalvik.h" +#include "alloc/clz.h" #include "alloc/HeapBitmap.h" #include "alloc/HeapInternal.h" #include "alloc/HeapSource.h" @@ -22,6 +23,7 @@ #include // for ULONG_MAX #include // for madvise(), mmap() #include +#include #define GC_DEBUG_PARANOID 2 #define GC_DEBUG_BASIC 1 @@ -92,7 +94,7 @@ createMarkStack(GcMarkStack *stack) { const Object **limit; size_t size; - int fd; + int fd, err; /* Create a stack big enough for the worst possible case, * where the heap is perfectly full of the smallest object. @@ -104,14 +106,17 @@ createMarkStack(GcMarkStack *stack) size = ALIGN_UP_TO_PAGE_SIZE(size); fd = ashmem_create_region("dalvik-heap-markstack", size); if (fd < 0) { - LOGE_GC("Could not create %d-byte ashmem mark stack\n", size); + LOGE_GC("Could not create %d-byte ashmem mark stack: %s\n", + size, strerror(errno)); return false; } limit = (const Object **)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); + err = errno; close(fd); if (limit == MAP_FAILED) { - LOGE_GC("Could not mmap %d-byte ashmem mark stack\n", size); + LOGE_GC("Could not mmap %d-byte ashmem mark stack: %s\n", + size, strerror(err)); return false; } @@ -374,6 +379,7 @@ void dvmHeapMarkRootSet() LOG_SCAN("special objects\n"); dvmMarkObjectNonNull(gDvm.outOfMemoryObj); dvmMarkObjectNonNull(gDvm.internalErrorObj); + dvmMarkObjectNonNull(gDvm.noClassDefFoundErrorObj); //TODO: scan object references sitting in gDvm; use pointer begin & end HPROF_CLEAR_GC_SCAN_STATE(); @@ -430,30 +436,39 @@ static void scanStaticFields(const ClassObject *clazz, GcMarkContext *ctx) static void scanInstanceFields(const DataObject *obj, ClassObject *clazz, GcMarkContext *ctx) { -//TODO: Optimize this by avoiding walking the superclass chain - while (clazz != NULL) { - InstField *f; - int i; + if (false && clazz->refOffsets != CLASS_WALK_SUPER) { + unsigned int refOffsets = clazz->refOffsets; + while (refOffsets != 0) { + const int rshift = CLZ(refOffsets); + refOffsets &= ~(CLASS_HIGH_BIT >> rshift); + markObject(dvmGetFieldObject((Object*)obj, + CLASS_OFFSET_FROM_CLZ(rshift)), ctx); + } + } else { + while (clazz != NULL) { + InstField *f; + int i; - /* All of the fields that contain object references - * are guaranteed to be at the beginning of the ifields list. - */ - f = clazz->ifields; - for (i = 0; i < clazz->ifieldRefCount; i++) { - /* Mark the array or object reference. - * May be NULL. - * - * Note that, per the comment on struct InstField, - * f->byteOffset is the offset from the beginning of - * obj, not the offset into obj->instanceData. + /* All of the fields that contain object references + * are guaranteed to be at the beginning of the ifields list. */ - markObject(dvmGetFieldObject((Object*)obj, f->byteOffset), ctx); - f++; - } + f = clazz->ifields; + for (i = 0; i < clazz->ifieldRefCount; i++) { + /* Mark the array or object reference. + * May be NULL. + * + * Note that, per the comment on struct InstField, + * f->byteOffset is the offset from the beginning of + * obj, not the offset into obj->instanceData. + */ + markObject(dvmGetFieldObject((Object*)obj, f->byteOffset), ctx); + f++; + } - /* This will be NULL when we hit java.lang.Object - */ - clazz = clazz->super; + /* This will be NULL when we hit java.lang.Object + */ + clazz = clazz->super; + } } } @@ -520,6 +535,17 @@ static void scanObject(const Object *obj, GcMarkContext *ctx) } #endif +#if WITH_OBJECT_HEADERS + if (ptr2chunk(obj)->scanGeneration == gGeneration) { + LOGE("object 0x%08x was already scanned this generation\n", + (uintptr_t)obj); + dvmAbort(); + } + ptr2chunk(obj)->oldScanGeneration = ptr2chunk(obj)->scanGeneration; + ptr2chunk(obj)->scanGeneration = gGeneration; + ptr2chunk(obj)->scanCount++; +#endif + /* Get and mark the class object for this particular instance. */ clazz = obj->clazz; @@ -540,16 +566,9 @@ static void scanObject(const Object *obj, GcMarkContext *ctx) */ return; } + #if WITH_OBJECT_HEADERS gMarkParent = obj; - if (ptr2chunk(obj)->scanGeneration == gGeneration) { - LOGE("object 0x%08x was already scanned this generation\n", - (uintptr_t)obj); - dvmAbort(); - } - ptr2chunk(obj)->oldScanGeneration = ptr2chunk(obj)->scanGeneration; - ptr2chunk(obj)->scanGeneration = gGeneration; - ptr2chunk(obj)->scanCount++; #endif assert(dvmIsValidObject((Object *)clazz)); @@ -1194,6 +1213,7 @@ sweepBitmapCallback(size_t numPtrs, void **ptrs, const void *finger, void *arg) { const ClassObject *const classJavaLangClass = gDvm.classJavaLangClass; size_t i; + void **origPtrs = ptrs; for (i = 0; i < numPtrs; i++) { DvmHeapChunk *hc; @@ -1256,11 +1276,10 @@ sweepBitmapCallback(size_t numPtrs, void **ptrs, const void *finger, void *arg) #endif } #endif - -//TODO: provide a heapsource function that takes a list of pointers to free -// and call it outside of this loop. - dvmHeapSourceFree(hc); } + // TODO: dvmHeapSourceFreeList has a loop, just like the above + // does. Consider collapsing the two loops to save overhead. + dvmHeapSourceFreeList(numPtrs, origPtrs); return true; } diff --git a/vm/analysis/CodeVerify.c b/vm/analysis/CodeVerify.c index 8c5e8d2be..f945f2388 100644 --- a/vm/analysis/CodeVerify.c +++ b/vm/analysis/CodeVerify.c @@ -72,9 +72,9 @@ int gDvm__gcSimpleData = 0; static inline bool doVerboseLogging(const Method* meth) { return false; /* COMMENT OUT to enable verbose debugging */ - const char* cd = "Lop_lshr;"; - const char* mn = "test"; - const char* sg = "(II)J"; + const char* cd = "Landroid/net/http/Request;"; + const char* mn = "readResponse"; + const char* sg = "(Landroid/net/http/AndroidHttpClientConnection;)V"; return (strcmp(meth->clazz->descriptor, cd) == 0 && dvmCompareNameDescriptorAndMethod(mn, sg, meth) == 0); } @@ -116,12 +116,13 @@ typedef struct RegisterTable { static void checkMergeTab(void); static bool isInitMethod(const Method* meth); static RegType getInvocationThis(const RegType* insnRegs,\ - const int insnRegCount, const DecodedInstruction* pDecInsn, bool* pOkay); + const int insnRegCount, const DecodedInstruction* pDecInsn, + VerifyError* pFailure); static void verifyRegisterType(const RegType* insnRegs, const int insnRegCount,\ - u4 vsrc, RegType checkType, bool* pOkay); -static bool doCodeVerification(const Method* meth, InsnFlags* insnFlags,\ + u4 vsrc, RegType checkType, VerifyError* pFailure); +static bool doCodeVerification(Method* meth, InsnFlags* insnFlags,\ RegisterTable* regTable, UninitInstanceMap* uninitMap); -static bool verifyInstruction(const Method* meth, InsnFlags* insnFlags,\ +static bool verifyInstruction(Method* meth, InsnFlags* insnFlags,\ RegisterTable* regTable, RegType* workRegs, int insnIdx, UninitInstanceMap* uninitMap, int* pStartGuess); static ClassObject* findCommonSuperclass(ClassObject* c1, ClassObject* c2); @@ -327,7 +328,7 @@ static bool checkFieldArrayStore1nr(RegType instrType, RegType targetType) */ static RegType primitiveTypeToRegType(PrimitiveType primType) { - struct { + static const struct { RegType regType; /* type equivalent */ PrimitiveType primType; /* verification */ } convTab[] = { @@ -545,9 +546,11 @@ static bool isClassInitMethod(const Method* meth) /* * Look up a class reference given as a simple string descriptor. + * + * If we can't find it, return a generic substitute when possible. */ static ClassObject* lookupClassByDescriptor(const Method* meth, - const char* pDescriptor, bool* pOkay) + const char* pDescriptor, VerifyError* pFailure) { /* * The javac compiler occasionally puts references to nonexistent @@ -585,7 +588,7 @@ static ClassObject* lookupClassByDescriptor(const Method* meth, if (pDescriptor[1] != 'L' && pDescriptor[1] != '[') { LOG_VFY("VFY: invalid char in signature in '%s'\n", pDescriptor); - *pOkay = false; + *pFailure = VERIFY_ERROR_GENERIC; } /* @@ -607,17 +610,17 @@ static ClassObject* lookupClassByDescriptor(const Method* meth, } else { /* We are looking at a primitive type. */ LOG_VFY("VFY: invalid char in signature in '%s'\n", pDescriptor); - *pOkay = false; + *pFailure = VERIFY_ERROR_GENERIC; } if (clazz == NULL) { - *pOkay = false; + *pFailure = VERIFY_ERROR_GENERIC; } } if (dvmIsPrimitiveClass(clazz)) { LOG_VFY("VFY: invalid use of primitive type '%s'\n", pDescriptor); - *pOkay = false; + *pFailure = VERIFY_ERROR_GENERIC; clazz = NULL; } @@ -634,7 +637,7 @@ static ClassObject* lookupClassByDescriptor(const Method* meth, * NOTE: this is also expected to verify the signature. */ static ClassObject* lookupSignatureClass(const Method* meth, const char** pSig, - bool* pOkay) + VerifyError* pFailure) { const char* sig = *pSig; const char* endp = sig; @@ -645,7 +648,7 @@ static ClassObject* lookupSignatureClass(const Method* meth, const char** pSig, ; if (*endp != ';') { LOG_VFY("VFY: bad signature component '%s' (missing ';')\n", sig); - *pOkay = false; + *pFailure = VERIFY_ERROR_GENERIC; return NULL; } @@ -657,7 +660,7 @@ static ClassObject* lookupSignatureClass(const Method* meth, const char** pSig, *pSig = endp - 1; /* - 1 so that *pSig points at, not past, the ';' */ - return lookupClassByDescriptor(meth, typeStr, pOkay); + return lookupClassByDescriptor(meth, typeStr, pFailure); } /* @@ -669,7 +672,7 @@ static ClassObject* lookupSignatureClass(const Method* meth, const char** pSig, * NOTE: this is also expected to verify the signature. */ static ClassObject* lookupSignatureArrayClass(const Method* meth, - const char** pSig, bool* pOkay) + const char** pSig, VerifyError* pFailure) { const char* sig = *pSig; const char* endp = sig; @@ -685,7 +688,7 @@ static ClassObject* lookupSignatureArrayClass(const Method* meth, ; if (*endp != ';') { LOG_VFY("VFY: bad signature component '%s' (missing ';')\n", sig); - *pOkay = false; + *pFailure = VERIFY_ERROR_GENERIC; return NULL; } } @@ -697,7 +700,7 @@ static ClassObject* lookupSignatureArrayClass(const Method* meth, *pSig = endp; - return lookupClassByDescriptor(meth, typeStr, pOkay); + return lookupClassByDescriptor(meth, typeStr, pFailure); } /* @@ -713,7 +716,7 @@ static bool setTypesFromSignature(const Method* meth, RegType* regTypes, { DexParameterIterator iterator; int actualArgs, expectedArgs, argStart; - bool okay = true; + VerifyError failure = VERIFY_ERROR_NONE; dexParameterIteratorInit(&iterator, &meth->prototype); argStart = meth->registersSize - meth->insSize; @@ -768,8 +771,8 @@ static bool setTypesFromSignature(const Method* meth, RegType* regTypes, */ { ClassObject* clazz = - lookupClassByDescriptor(meth, descriptor, &okay); - if (!okay) + lookupClassByDescriptor(meth, descriptor, &failure); + if (!VERIFY_OK(failure)) goto bad_sig; regTypes[argStart + actualArgs] = regTypeFromClass(clazz); } @@ -926,10 +929,10 @@ static RegType getMethodReturnType(const Method* meth) case 'L': case '[': { - bool okay = true; + VerifyError failure = VERIFY_ERROR_NONE; ClassObject* clazz = - lookupClassByDescriptor(meth, descriptor, &okay); - assert(okay); + lookupClassByDescriptor(meth, descriptor, &failure); + assert(VERIFY_OK(failure)); type = regTypeFromClass(clazz); } break; @@ -1012,13 +1015,13 @@ static RegType primSigCharToRegType(char sigChar) * Widening conversions on integers and references are allowed, but * narrowing conversions are not. * - * Returns the resolved method on success, NULL (and sets "*pOkay" to "false") - * on failure. + * Returns the resolved method on success, NULL on failure (with *pFailure + * set appropriately). */ static Method* verifyInvocationArgs(const Method* meth, const RegType* insnRegs, const int insnRegCount, const DecodedInstruction* pDecInsn, UninitInstanceMap* uninitMap, MethodType methodType, bool isRange, - bool isSuper, bool* pOkay) + bool isSuper, VerifyError* pFailure) { Method* resMethod; char* sigOriginal = NULL; @@ -1030,7 +1033,8 @@ static Method* verifyInvocationArgs(const Method* meth, const RegType* insnRegs, if (methodType == METHOD_INTERFACE) { resMethod = dvmOptResolveInterfaceMethod(meth->clazz, pDecInsn->vB); } else { - resMethod = dvmOptResolveMethod(meth->clazz, pDecInsn->vB, methodType); + resMethod = dvmOptResolveMethod(meth->clazz, pDecInsn->vB, methodType, + pFailure); } if (resMethod == NULL) { /* failed; print a meaningful failure message */ @@ -1051,7 +1055,7 @@ static Method* verifyInvocationArgs(const Method* meth, const RegType* insnRegs, //char* curMethodDesc = // dexProtoCopyMethodDescriptor(&meth->prototype); - LOGE("Could not find method %s.%s, referenced from " + LOGI("Could not find method %s.%s, referenced from " "method %s.%s\n", dotMissingClass, methodName/*, methodDesc*/, dotMethClass, meth->name/*, curMethodDesc*/); @@ -1065,6 +1069,8 @@ static Method* verifyInvocationArgs(const Method* meth, const RegType* insnRegs, dvmMethodTypeStr(methodType), pDecInsn->vB, classDescriptor, methodName, methodDesc); free(methodDesc); + if (VERIFY_OK(*pFailure)) /* not set for interface resolve */ + *pFailure = VERIFY_ERROR_NO_METHOD; goto fail; } @@ -1095,6 +1101,7 @@ static Method* verifyInvocationArgs(const Method* meth, const RegType* insnRegs, (super == NULL) ? "-" : super->descriptor, resMethod->name, desc); free(desc); + *pFailure = VERIFY_ERROR_NO_METHOD; goto fail; } } @@ -1135,8 +1142,8 @@ static Method* verifyInvocationArgs(const Method* meth, const RegType* insnRegs, RegType actualArgType; actualArgType = getInvocationThis(insnRegs, insnRegCount, pDecInsn, - pOkay); - if (!*pOkay) + pFailure); + if (!VERIFY_OK(*pFailure)) goto fail; if (regTypeIsUninitReference(actualArgType) && resMethod->name[0] != '<') @@ -1176,12 +1183,12 @@ static Method* verifyInvocationArgs(const Method* meth, const RegType* insnRegs, switch (*sig) { case 'L': { - ClassObject* clazz = lookupSignatureClass(meth, &sig, pOkay); - if (!*pOkay) + ClassObject* clazz = lookupSignatureClass(meth, &sig, pFailure); + if (!VERIFY_OK(*pFailure)) goto bad_sig; verifyRegisterType(insnRegs, insnRegCount, getReg, - regTypeFromClass(clazz), pOkay); - if (!*pOkay) { + regTypeFromClass(clazz), pFailure); + if (!VERIFY_OK(*pFailure)) { LOG_VFY("VFY: bad arg %d (into %s)\n", actualArgs, clazz->descriptor); goto bad_sig; @@ -1192,12 +1199,12 @@ static Method* verifyInvocationArgs(const Method* meth, const RegType* insnRegs, case '[': { ClassObject* clazz = - lookupSignatureArrayClass(meth, &sig, pOkay); - if (!*pOkay) + lookupSignatureArrayClass(meth, &sig, pFailure); + if (!VERIFY_OK(*pFailure)) goto bad_sig; verifyRegisterType(insnRegs, insnRegCount, getReg, - regTypeFromClass(clazz), pOkay); - if (!*pOkay) { + regTypeFromClass(clazz), pFailure); + if (!VERIFY_OK(*pFailure)) { LOG_VFY("VFY: bad arg %d (into %s)\n", actualArgs, clazz->descriptor); goto bad_sig; @@ -1207,42 +1214,42 @@ static Method* verifyInvocationArgs(const Method* meth, const RegType* insnRegs, break; case 'Z': verifyRegisterType(insnRegs, insnRegCount, getReg, - kRegTypeBoolean, pOkay); + kRegTypeBoolean, pFailure); actualArgs++; break; case 'C': verifyRegisterType(insnRegs, insnRegCount, getReg, - kRegTypeChar, pOkay); + kRegTypeChar, pFailure); actualArgs++; break; case 'B': verifyRegisterType(insnRegs, insnRegCount, getReg, - kRegTypeByte, pOkay); + kRegTypeByte, pFailure); actualArgs++; break; case 'I': verifyRegisterType(insnRegs, insnRegCount, getReg, - kRegTypeInteger, pOkay); + kRegTypeInteger, pFailure); actualArgs++; break; case 'S': verifyRegisterType(insnRegs, insnRegCount, getReg, - kRegTypeShort, pOkay); + kRegTypeShort, pFailure); actualArgs++; break; case 'F': verifyRegisterType(insnRegs, insnRegCount, getReg, - kRegTypeFloat, pOkay); + kRegTypeFloat, pFailure); actualArgs++; break; case 'D': verifyRegisterType(insnRegs, insnRegCount, getReg, - kRegTypeDoubleLo, pOkay); + kRegTypeDoubleLo, pFailure); actualArgs += 2; break; case 'J': verifyRegisterType(insnRegs, insnRegCount, getReg, - kRegTypeLongLo, pOkay); + kRegTypeLongLo, pFailure); actualArgs += 2; break; default: @@ -1272,13 +1279,14 @@ bad_sig: if (resMethod != NULL) { char* desc = dexProtoCopyMethodDescriptor(&resMethod->prototype); LOG_VFY("VFY: rejecting call to %s.%s %s\n", - resMethod->clazz->descriptor, resMethod->name, desc); + resMethod->clazz->descriptor, resMethod->name, desc); free(desc); } fail: free(sigOriginal); - *pOkay = false; + if (*pFailure == VERIFY_ERROR_NONE) + *pFailure = VERIFY_ERROR_GENERIC; return NULL; } @@ -1324,15 +1332,15 @@ static ClassObject* getFieldClass(const Method* meth, const Field* field) /* * Get the type of register N, verifying that the register is valid. * - * Sets "*pOkay" to false if the register number is out of range. + * Sets "*pFailure" appropriately if the register number is out of range. */ static inline RegType getRegisterType(const RegType* insnRegs, - const int insnRegCount, u4 vsrc, bool* pOkay) + const int insnRegCount, u4 vsrc, VerifyError* pFailure) { RegType type; if (vsrc >= (u4) insnRegCount) { - *pOkay = false; + *pFailure = VERIFY_ERROR_GENERIC; return kRegTypeUnknown; } else { return insnRegs[vsrc]; @@ -1341,21 +1349,21 @@ static inline RegType getRegisterType(const RegType* insnRegs, /* * Get the value from a register, and cast it to a ClassObject. Sets - * "pOkay" to false if something fails. + * "*pFailure" if something fails. * * This fails if the register holds an uninitialized class. * * If the register holds kRegTypeZero, this returns a NULL pointer. */ static ClassObject* getClassFromRegister(const RegType* insnRegs, - const int insnRegCount, u4 vsrc, bool* pOkay) + const int insnRegCount, u4 vsrc, VerifyError* pFailure) { ClassObject* clazz = NULL; RegType type; /* get the element type of the array held in vsrc */ - type = getRegisterType(insnRegs, insnRegCount, vsrc, pOkay); - if (!*pOkay) + type = getRegisterType(insnRegs, insnRegCount, vsrc, pFailure); + if (!VERIFY_OK(*pFailure)) goto bail; /* if "always zero", we allow it to fail at runtime */ @@ -1365,12 +1373,12 @@ static ClassObject* getClassFromRegister(const RegType* insnRegs, if (!regTypeIsReference(type)) { LOG_VFY("VFY: tried to get class from non-ref register v%d (type=%d)\n", vsrc, type); - *pOkay = false; + *pFailure = VERIFY_ERROR_GENERIC; goto bail; } if (regTypeIsUninitReference(type)) { LOG_VFY("VFY: register %u holds uninitialized reference\n", vsrc); - *pOkay = false; + *pFailure = VERIFY_ERROR_GENERIC; goto bail; } @@ -1391,27 +1399,28 @@ bail: * and then return vC. */ static RegType getInvocationThis(const RegType* insnRegs, - const int insnRegCount, const DecodedInstruction* pDecInsn, bool* pOkay) + const int insnRegCount, const DecodedInstruction* pDecInsn, + VerifyError* pFailure) { RegType thisType = kRegTypeUnknown; if (pDecInsn->vA < 1) { LOG_VFY("VFY: invoke lacks 'this'\n"); - *pOkay = false; + *pFailure = VERIFY_ERROR_GENERIC; goto bail; } /* get the element type of the array held in vsrc */ - thisType = getRegisterType(insnRegs, insnRegCount, pDecInsn->vC, pOkay); - if (!*pOkay) { - LOG_VFY("VFY: failed to get this from register %u\n", pDecInsn->vC); + thisType = getRegisterType(insnRegs, insnRegCount, pDecInsn->vC, pFailure); + if (!VERIFY_OK(*pFailure)) { + LOG_VFY("VFY: failed to get 'this' from register %u\n", pDecInsn->vC); goto bail; } if (!regTypeIsReference(thisType)) { LOG_VFY("VFY: tried to get class from non-ref register v%d (type=%d)\n", pDecInsn->vC, thisType); - *pOkay = false; + *pFailure = VERIFY_ERROR_GENERIC; goto bail; } @@ -1424,10 +1433,10 @@ bail: * "newType" is the "Lo" part of a 64-bit value, register N+1 will be * set to "newType+1". * - * Sets "*pOkay" to false if the register number is out of range. + * Sets "*pFailure" if the register number is out of range. */ static void setRegisterType(RegType* insnRegs, const int insnRegCount, - u4 vdst, RegType newType, bool* pOkay) + u4 vdst, RegType newType, VerifyError* pFailure) { //LOGD("set-reg v%u = %d\n", vdst, newType); switch (newType) { @@ -1443,7 +1452,7 @@ static void setRegisterType(RegType* insnRegs, const int insnRegCount, case kRegTypeFloat: case kRegTypeZero: if (vdst >= (u4) insnRegCount) { - *pOkay = false; + *pFailure = VERIFY_ERROR_GENERIC; } else { insnRegs[vdst] = newType; } @@ -1451,7 +1460,7 @@ static void setRegisterType(RegType* insnRegs, const int insnRegCount, case kRegTypeLongLo: case kRegTypeDoubleLo: if (vdst+1 >= (u4) insnRegCount) { - *pOkay = false; + *pFailure = VERIFY_ERROR_GENERIC; } else { insnRegs[vdst] = newType; insnRegs[vdst+1] = newType+1; @@ -1460,14 +1469,14 @@ static void setRegisterType(RegType* insnRegs, const int insnRegCount, case kRegTypeLongHi: case kRegTypeDoubleHi: /* should never set these explicitly */ - *pOkay = false; + *pFailure = VERIFY_ERROR_GENERIC; break; case kRegTypeUninit: default: if (regTypeIsReference(newType)) { if (vdst >= (u4) insnRegCount) { - *pOkay = false; + *pFailure = VERIFY_ERROR_GENERIC; break; } insnRegs[vdst] = newType; @@ -1490,7 +1499,7 @@ static void setRegisterType(RegType* insnRegs, const int insnRegCount, case kRegTypeConflict: // should only be set during a merge LOG_VFY("Unexpected set type %d\n", newType); assert(false); - *pOkay = false; + *pFailure = VERIFY_ERROR_GENERIC; break; } } @@ -1509,10 +1518,10 @@ static void setRegisterType(RegType* insnRegs, const int insnRegCount, * interface, verify that the register implements checkType. */ static void verifyRegisterType(const RegType* insnRegs, const int insnRegCount, - u4 vsrc, RegType checkType, bool* pOkay) + u4 vsrc, RegType checkType, VerifyError* pFailure) { if (vsrc >= (u4) insnRegCount) { - *pOkay = false; + *pFailure = VERIFY_ERROR_GENERIC; return; } @@ -1531,7 +1540,7 @@ static void verifyRegisterType(const RegType* insnRegs, const int insnRegCount, if (!canConvertTo1nr(srcType, checkType)) { LOG_VFY("VFY: register1 v%u type %d, wanted %d\n", vsrc, srcType, checkType); - *pOkay = false; + *pFailure = VERIFY_ERROR_GENERIC; } break; case kRegTypeLongLo: @@ -1539,15 +1548,15 @@ static void verifyRegisterType(const RegType* insnRegs, const int insnRegCount, if (vsrc+1 >= (u4) insnRegCount) { LOG_VFY("VFY: register2 v%u out of range (%d)\n", vsrc, insnRegCount); - *pOkay = false; + *pFailure = VERIFY_ERROR_GENERIC; } else if (insnRegs[vsrc+1] != srcType+1) { LOG_VFY("VFY: register2 v%u-%u values %d,%d\n", vsrc, vsrc+1, insnRegs[vsrc], insnRegs[vsrc+1]); - *pOkay = false; + *pFailure = VERIFY_ERROR_GENERIC; } else if (!canConvertTo2(srcType, checkType)) { LOG_VFY("VFY: register2 v%u type %d, wanted %d\n", vsrc, srcType, checkType); - *pOkay = false; + *pFailure = VERIFY_ERROR_GENERIC; } break; @@ -1559,7 +1568,7 @@ static void verifyRegisterType(const RegType* insnRegs, const int insnRegCount, case kRegTypeConflict: /* should never be checking for these explicitly */ assert(false); - *pOkay = false; + *pFailure = VERIFY_ERROR_GENERIC; return; case kRegTypeUninit: default: @@ -1567,23 +1576,23 @@ static void verifyRegisterType(const RegType* insnRegs, const int insnRegCount, if (!regTypeIsReference(checkType)) { LOG_VFY("VFY: unexpected check type %d\n", checkType); assert(false); - *pOkay = false; + *pFailure = VERIFY_ERROR_GENERIC; break; } if (regTypeIsUninitReference(checkType)) { LOG_VFY("VFY: uninitialized ref not expected as reg check\n"); - *pOkay = false; + *pFailure = VERIFY_ERROR_GENERIC; break; } /* make sure srcType is initialized reference or always-NULL */ if (!regTypeIsReference(srcType)) { LOG_VFY("VFY: register1 v%u type %d, wanted ref\n", vsrc, srcType); - *pOkay = false; + *pFailure = VERIFY_ERROR_GENERIC; break; } if (regTypeIsUninitReference(srcType)) { LOG_VFY("VFY: register1 v%u holds uninitialized ref\n", vsrc); - *pOkay = false; + *pFailure = VERIFY_ERROR_GENERIC; break; } /* if the register isn't Zero, make sure it's an instance of check */ @@ -1605,14 +1614,14 @@ static void verifyRegisterType(const RegType* insnRegs, const int insnRegCount, { LOG_VFY("VFY: %s does not implement %s\n", srcClass->descriptor, checkClass->descriptor); - *pOkay = false; + *pFailure = VERIFY_ERROR_GENERIC; } */ } else { if (!dvmInstanceof(srcClass, checkClass)) { LOG_VFY("VFY: %s is not instance of %s\n", srcClass->descriptor, checkClass->descriptor); - *pOkay = false; + *pFailure = VERIFY_ERROR_GENERIC; } } } @@ -1625,10 +1634,10 @@ static void verifyRegisterType(const RegType* insnRegs, const int insnRegCount, * "insnRegCount" to encompass the result register. */ static void setResultRegisterType(RegType* insnRegs, const int insnRegCount, - RegType newType, bool* pOkay) + RegType newType, VerifyError* pFailure) { setRegisterType(insnRegs, insnRegCount + kExtraRegs, - RESULT_REGISTER(insnRegCount), newType, pOkay); + RESULT_REGISTER(insnRegCount), newType, pFailure); } @@ -1639,7 +1648,7 @@ static void setResultRegisterType(RegType* insnRegs, const int insnRegCount, * must be marked as initialized. */ static void markRefsAsInitialized(RegType* insnRegs, int insnRegCount, - UninitInstanceMap* uninitMap, RegType uninitType, bool* pOkay) + UninitInstanceMap* uninitMap, RegType uninitType, VerifyError* pFailure) { ClassObject* clazz; RegType initType; @@ -1649,7 +1658,7 @@ static void markRefsAsInitialized(RegType* insnRegs, int insnRegCount, if (clazz == NULL) { LOGE("VFY: unable to find type=0x%x (idx=%d)\n", uninitType, regTypeToUninitIndex(uninitType)); - *pOkay = false; + *pFailure = VERIFY_ERROR_GENERIC; return; } initType = regTypeFromClass(clazz); @@ -1748,9 +1757,10 @@ typedef enum TypeCategory { * * For category 2 values, "type" must be the "low" half of the value. * - * Sets "*pOkay" to false if not. + * Sets "*pFailure" if something looks wrong. */ -static void checkTypeCategory(RegType type, TypeCategory cat, bool* pOkay) +static void checkTypeCategory(RegType type, TypeCategory cat, + VerifyError* pFailure) { switch (cat) { case kTypeCategory1nr: @@ -1767,7 +1777,7 @@ static void checkTypeCategory(RegType type, TypeCategory cat, bool* pOkay) case kRegTypeInteger: break; default: - *pOkay = false; + *pFailure = VERIFY_ERROR_GENERIC; break; } break; @@ -1778,19 +1788,19 @@ static void checkTypeCategory(RegType type, TypeCategory cat, bool* pOkay) case kRegTypeDoubleLo: break; default: - *pOkay = false; + *pFailure = VERIFY_ERROR_GENERIC; break; } break; case kTypeCategoryRef: if (type != kRegTypeZero && !regTypeIsReference(type)) - *pOkay = false; + *pFailure = VERIFY_ERROR_GENERIC; break; default: assert(false); - *pOkay = false; + *pFailure = VERIFY_ERROR_GENERIC; break; } } @@ -1802,10 +1812,10 @@ static void checkTypeCategory(RegType type, TypeCategory cat, bool* pOkay) * Does not verify that "typel" is in fact the low part of a 64-bit * register pair. */ -static void checkWidePair(RegType typel, RegType typeh, bool* pOkay) +static void checkWidePair(RegType typel, RegType typeh, VerifyError* pFailure) { if ((typeh != typel+1)) - *pOkay = false; + *pFailure = VERIFY_ERROR_GENERIC; } /* @@ -1816,15 +1826,15 @@ static void checkWidePair(RegType typel, RegType typeh, bool* pOkay) * "vsrc" values are checked against this. */ static void copyRegister1(RegType* insnRegs, int insnRegCount, u4 vdst, - u4 vsrc, TypeCategory cat, bool* pOkay) + u4 vsrc, TypeCategory cat, VerifyError* pFailure) { - RegType type = getRegisterType(insnRegs, insnRegCount, vsrc, pOkay); - if (*pOkay) - checkTypeCategory(type, cat, pOkay); - if (*pOkay) - setRegisterType(insnRegs, insnRegCount, vdst, type, pOkay); + RegType type = getRegisterType(insnRegs, insnRegCount, vsrc, pFailure); + if (VERIFY_OK(*pFailure)) + checkTypeCategory(type, cat, pFailure); + if (VERIFY_OK(*pFailure)) + setRegisterType(insnRegs, insnRegCount, vdst, type, pFailure); - if (!*pOkay) { + if (!VERIFY_OK(*pFailure)) { LOG_VFY("VFY: copy1 v%u<-v%u type=%d cat=%d\n", vdst, vsrc, type, cat); } } @@ -1834,18 +1844,18 @@ static void copyRegister1(RegType* insnRegs, int insnRegCount, u4 vdst, * "vsrc" to "vdst". This copies both halves of the register. */ static void copyRegister2(RegType* insnRegs, int insnRegCount, u4 vdst, - u4 vsrc, bool* pOkay) + u4 vsrc, VerifyError* pFailure) { - RegType typel = getRegisterType(insnRegs, insnRegCount, vsrc, pOkay); - RegType typeh = getRegisterType(insnRegs, insnRegCount, vsrc+1, pOkay); - if (*pOkay) { - checkTypeCategory(typel, kTypeCategory2, pOkay); - checkWidePair(typel, typeh, pOkay); + RegType typel = getRegisterType(insnRegs, insnRegCount, vsrc, pFailure); + RegType typeh = getRegisterType(insnRegs, insnRegCount, vsrc+1, pFailure); + if (VERIFY_OK(*pFailure)) { + checkTypeCategory(typel, kTypeCategory2, pFailure); + checkWidePair(typel, typeh, pFailure); } - if (*pOkay) - setRegisterType(insnRegs, insnRegCount, vdst, typel, pOkay); + if (VERIFY_OK(*pFailure)) + setRegisterType(insnRegs, insnRegCount, vdst, typel, pFailure); - if (!*pOkay) { + if (!VERIFY_OK(*pFailure)) { LOG_VFY("VFY: copy2 v%u<-v%u type=%d/%d\n", vdst, vsrc, typel, typeh); } } @@ -1858,21 +1868,21 @@ static void copyRegister2(RegType* insnRegs, int insnRegCount, u4 vdst, * because that would affect the test on "vdst" as well. */ static void copyResultRegister1(RegType* insnRegs, const int insnRegCount, - u4 vdst, TypeCategory cat, bool* pOkay) + u4 vdst, TypeCategory cat, VerifyError* pFailure) { RegType type; u4 vsrc; vsrc = RESULT_REGISTER(insnRegCount); - type = getRegisterType(insnRegs, insnRegCount + kExtraRegs, vsrc, pOkay); - if (*pOkay) - checkTypeCategory(type, cat, pOkay); - if (*pOkay) { - setRegisterType(insnRegs, insnRegCount, vdst, type, pOkay); + type = getRegisterType(insnRegs, insnRegCount + kExtraRegs, vsrc, pFailure); + if (VERIFY_OK(*pFailure)) + checkTypeCategory(type, cat, pFailure); + if (VERIFY_OK(*pFailure)) { + setRegisterType(insnRegs, insnRegCount, vdst, type, pFailure); insnRegs[vsrc] = kRegTypeUnknown; } - if (!*pOkay) { + if (!VERIFY_OK(*pFailure)) { LOG_VFY("VFY: copyRes1 v%u<-v%u cat=%d type=%d\n", vdst, vsrc, cat, type); } @@ -1886,25 +1896,27 @@ static void copyResultRegister1(RegType* insnRegs, const int insnRegCount, * because that would affect the test on "vdst" as well. */ static void copyResultRegister2(RegType* insnRegs, const int insnRegCount, - u4 vdst, bool* pOkay) + u4 vdst, VerifyError* pFailure) { RegType typel, typeh; u4 vsrc; vsrc = RESULT_REGISTER(insnRegCount); - typel = getRegisterType(insnRegs, insnRegCount + kExtraRegs, vsrc, pOkay); - typeh = getRegisterType(insnRegs, insnRegCount + kExtraRegs, vsrc+1, pOkay); - if (*pOkay) { - checkTypeCategory(typel, kTypeCategory2, pOkay); - checkWidePair(typel, typeh, pOkay); + typel = getRegisterType(insnRegs, insnRegCount + kExtraRegs, vsrc, + pFailure); + typeh = getRegisterType(insnRegs, insnRegCount + kExtraRegs, vsrc+1, + pFailure); + if (VERIFY_OK(*pFailure)) { + checkTypeCategory(typel, kTypeCategory2, pFailure); + checkWidePair(typel, typeh, pFailure); } - if (*pOkay) { - setRegisterType(insnRegs, insnRegCount, vdst, typel, pOkay); + if (VERIFY_OK(*pFailure)) { + setRegisterType(insnRegs, insnRegCount, vdst, typel, pFailure); insnRegs[vsrc] = kRegTypeUnknown; insnRegs[vsrc+1] = kRegTypeUnknown; } - if (!*pOkay) { + if (!VERIFY_OK(*pFailure)) { LOG_VFY("VFY: copyRes2 v%u<-v%u type=%d/%d\n", vdst, vsrc, typel, typeh); } @@ -1916,10 +1928,10 @@ static void copyResultRegister2(RegType* insnRegs, const int insnRegCount, */ static void checkUnop(RegType* insnRegs, const int insnRegCount, DecodedInstruction* pDecInsn, RegType dstType, RegType srcType, - bool* pOkay) + VerifyError* pFailure) { - verifyRegisterType(insnRegs, insnRegCount, pDecInsn->vB, srcType, pOkay); - setRegisterType(insnRegs, insnRegCount, pDecInsn->vA, dstType, pOkay); + verifyRegisterType(insnRegs, insnRegCount, pDecInsn->vB, srcType, pFailure); + setRegisterType(insnRegs, insnRegCount, pDecInsn->vA, dstType, pFailure); } /* @@ -1929,6 +1941,13 @@ static void checkUnop(RegType* insnRegs, const int insnRegCount, * * Assumes we've already validated reg1/reg2. * + * TODO: consider generalizing this. The key principle is that the + * result of a bitwise operation can only be as wide as the widest of + * the operands. You can safely AND/OR/XOR two chars together and know + * you still have a char, so it's reasonable for the compiler or "dx" + * to skip the int-to-char instruction. (We need to do this for boolean + * because there is no int-to-boolean operation.) + * * Returns true if both args are Boolean, Zero, or One. */ static bool upcastBooleanOp(RegType* insnRegs, const int insnRegCount, @@ -1958,10 +1977,10 @@ static bool upcastBooleanOp(RegType* insnRegs, const int insnRegCount, */ static void checkLitop(RegType* insnRegs, const int insnRegCount, DecodedInstruction* pDecInsn, RegType dstType, RegType srcType, - bool checkBooleanOp, bool* pOkay) + bool checkBooleanOp, VerifyError* pFailure) { - verifyRegisterType(insnRegs, insnRegCount, pDecInsn->vB, srcType, pOkay); - if (*pOkay && checkBooleanOp) { + verifyRegisterType(insnRegs, insnRegCount, pDecInsn->vB, srcType, pFailure); + if (VERIFY_OK(*pFailure) && checkBooleanOp) { assert(dstType == kRegTypeInteger); /* check vB with the call, then check the constant manually */ if (upcastBooleanOp(insnRegs, insnRegCount, pDecInsn->vB, pDecInsn->vB) @@ -1970,7 +1989,7 @@ static void checkLitop(RegType* insnRegs, const int insnRegCount, dstType = kRegTypeBoolean; } } - setRegisterType(insnRegs, insnRegCount, pDecInsn->vA, dstType, pOkay); + setRegisterType(insnRegs, insnRegCount, pDecInsn->vA, dstType, pFailure); } /* @@ -1980,16 +1999,18 @@ static void checkLitop(RegType* insnRegs, const int insnRegCount, */ static void checkBinop(RegType* insnRegs, const int insnRegCount, DecodedInstruction* pDecInsn, RegType dstType, RegType srcType1, - RegType srcType2, bool checkBooleanOp, bool* pOkay) + RegType srcType2, bool checkBooleanOp, VerifyError* pFailure) { - verifyRegisterType(insnRegs, insnRegCount, pDecInsn->vB, srcType1, pOkay); - verifyRegisterType(insnRegs, insnRegCount, pDecInsn->vC, srcType2, pOkay); - if (*pOkay && checkBooleanOp) { + verifyRegisterType(insnRegs, insnRegCount, pDecInsn->vB, srcType1, + pFailure); + verifyRegisterType(insnRegs, insnRegCount, pDecInsn->vC, srcType2, + pFailure); + if (VERIFY_OK(*pFailure) && checkBooleanOp) { assert(dstType == kRegTypeInteger); if (upcastBooleanOp(insnRegs, insnRegCount, pDecInsn->vB, pDecInsn->vC)) dstType = kRegTypeBoolean; } - setRegisterType(insnRegs, insnRegCount, pDecInsn->vA, dstType, pOkay); + setRegisterType(insnRegs, insnRegCount, pDecInsn->vA, dstType, pFailure); } /* @@ -1998,16 +2019,135 @@ static void checkBinop(RegType* insnRegs, const int insnRegCount, */ static void checkBinop2addr(RegType* insnRegs, const int insnRegCount, DecodedInstruction* pDecInsn, RegType dstType, RegType srcType1, - RegType srcType2, bool checkBooleanOp, bool* pOkay) + RegType srcType2, bool checkBooleanOp, VerifyError* pFailure) { - verifyRegisterType(insnRegs, insnRegCount, pDecInsn->vA, srcType1, pOkay); - verifyRegisterType(insnRegs, insnRegCount, pDecInsn->vB, srcType2, pOkay); - if (*pOkay && checkBooleanOp) { + verifyRegisterType(insnRegs, insnRegCount, pDecInsn->vA, srcType1, + pFailure); + verifyRegisterType(insnRegs, insnRegCount, pDecInsn->vB, srcType2, + pFailure); + if (VERIFY_OK(*pFailure) && checkBooleanOp) { assert(dstType == kRegTypeInteger); if (upcastBooleanOp(insnRegs, insnRegCount, pDecInsn->vA, pDecInsn->vB)) dstType = kRegTypeBoolean; } - setRegisterType(insnRegs, insnRegCount, pDecInsn->vA, dstType, pOkay); + setRegisterType(insnRegs, insnRegCount, pDecInsn->vA, dstType, pFailure); +} + +/* + * Treat right-shifting as a narrowing conversion when possible. + * + * For example, right-shifting an int 24 times results in a value that can + * be treated as a byte. + * + * Things get interesting when contemplating sign extension. Right- + * shifting an integer by 16 yields a value that can be represented in a + * "short" but not a "char", but an unsigned right shift by 16 yields a + * value that belongs in a char rather than a short. (Consider what would + * happen if the result of the shift were cast to a char or short and then + * cast back to an int. If sign extension, or the lack thereof, causes + * a change in the 32-bit representation, then the conversion was lossy.) + * + * A signed right shift by 17 on an integer results in a short. An unsigned + * right shfit by 17 on an integer results in a posshort, which can be + * assigned to a short or a char. + * + * An unsigned right shift on a short can actually expand the result into + * a 32-bit integer. For example, 0xfffff123 >>> 8 becomes 0x00fffff1, + * which can't be represented in anything smaller than an int. + * + * javac does not generate code that takes advantage of this, but some + * of the code optimizers do. It's generally a peephole optimization + * that replaces a particular sequence, e.g. (bipush 24, ishr, i2b) is + * replaced by (bipush 24, ishr). Knowing that shifting a short 8 times + * to the right yields a byte is really more than we need to handle the + * code that's out there, but support is not much more complex than just + * handling integer. + * + * Right-shifting never yields a boolean value. + * + * Returns the new register type. + */ +static RegType adjustForRightShift(RegType* workRegs, const int insnRegCount, + int reg, unsigned int shiftCount, bool isUnsignedShift, + VerifyError* pFailure) +{ + RegType srcType = getRegisterType(workRegs, insnRegCount, reg, pFailure); + RegType newType; + + /* no-op */ + if (shiftCount == 0) + return srcType; + + /* safe defaults */ + if (isUnsignedShift) + newType = kRegTypeInteger; + else + newType = srcType; + + if (shiftCount >= 32) { + LOG_VFY("Got unexpectedly large shift count %u\n", shiftCount); + /* fail? */ + return newType; + } + + switch (srcType) { + case kRegTypeInteger: /* 32-bit signed value */ + case kRegTypeFloat: /* (allowed; treat same as int) */ + if (isUnsignedShift) { + if (shiftCount > 24) + newType = kRegTypePosByte; + else if (shiftCount >= 16) + newType = kRegTypeChar; + } else { + if (shiftCount >= 24) + newType = kRegTypeByte; + else if (shiftCount >= 16) + newType = kRegTypeShort; + } + break; + case kRegTypeShort: /* 16-bit signed value */ + if (isUnsignedShift) { + /* default (kRegTypeInteger) is correct */ + } else { + if (shiftCount >= 8) + newType = kRegTypeByte; + } + break; + case kRegTypePosShort: /* 15-bit unsigned value */ + if (shiftCount >= 8) + newType = kRegTypePosByte; + break; + case kRegTypeChar: /* 16-bit unsigned value */ + if (shiftCount > 8) + newType = kRegTypePosByte; + break; + case kRegTypeByte: /* 8-bit signed value */ + /* defaults (u=kRegTypeInteger / s=srcType) are correct */ + break; + case kRegTypePosByte: /* 7-bit unsigned value */ + /* always use newType=srcType */ + newType = srcType; + break; + case kRegTypeZero: /* 1-bit unsigned value */ + case kRegTypeOne: + case kRegTypeBoolean: + /* unnecessary? */ + newType = kRegTypeZero; + break; + default: + /* long, double, references; shouldn't be here! */ + assert(false); + break; + } + + if (newType != srcType) { + LOGVV("narrowing: %d(%d) --> %d to %d\n", + shiftCount, isUnsignedShift, srcType, newType); + } else { + LOGVV("not narrowed: %d(%d) --> %d\n", + shiftCount, isUnsignedShift, srcType); + } + return newType; } @@ -2329,28 +2469,28 @@ static void updateRegisters(const Method* meth, InsnFlags* insnFlags, * allow it to be uninitialized if this is an "" method and the field * is declared within the "objType" class. * - * Returns an InstField on success, returns NULL and sets "*pOkay" to false + * Returns an InstField on success, returns NULL and sets "*pFailure" * on failure. */ static InstField* getInstField(const Method* meth, const UninitInstanceMap* uninitMap, RegType objType, int fieldIdx, - bool* pOkay) + VerifyError* pFailure) { InstField* instField = NULL; ClassObject* objClass; bool mustBeLocal = false; if (!regTypeIsReference(objType)) { - LOG_VFY("VFY: attempt to access field of non-reference type %d\n", + LOG_VFY("VFY: attempt to access field in non-reference type %d\n", objType); - *pOkay = false; + *pFailure = VERIFY_ERROR_GENERIC; goto bail; } - instField = dvmOptResolveInstField(meth->clazz, fieldIdx); + instField = dvmOptResolveInstField(meth->clazz, fieldIdx, pFailure); if (instField == NULL) { LOG_VFY("VFY: unable to resolve instance field %u\n", fieldIdx); - *pOkay = false; + assert(!VERIFY_OK(*pFailure)); goto bail; } @@ -2367,7 +2507,7 @@ static InstField* getInstField(const Method* meth, if (regTypeIsUninitReference(objType)) { if (!isInitMethod(meth) || meth->clazz != objClass) { LOG_VFY("VFY: attempt to access field via uninitialized ref\n"); - *pOkay = false; + *pFailure = VERIFY_ERROR_GENERIC; goto bail; } mustBeLocal = true; @@ -2377,7 +2517,7 @@ static InstField* getInstField(const Method* meth, LOG_VFY("VFY: invalid field access (field %s.%s, through %s ref)\n", instField->field.clazz->descriptor, instField->field.name, objClass->descriptor); - *pOkay = false; + *pFailure = VERIFY_ERROR_NO_FIELD; goto bail; } @@ -2388,7 +2528,7 @@ static InstField* getInstField(const Method* meth, { LOG_VFY("VFY: invalid constructor field access (field %s in %s)\n", instField->field.name, objClass->descriptor); - *pOkay = false; + *pFailure = VERIFY_ERROR_GENERIC; goto bail; } } @@ -2400,15 +2540,15 @@ bail: /* * Look up a static field. * - * Returns a StaticField on success, returns NULL and sets "*pOkay" to false + * Returns a StaticField on success, returns NULL and sets "*pFailure" * on failure. */ static StaticField* getStaticField(const Method* meth, int fieldIdx, - bool* pOkay) + VerifyError* pFailure) { StaticField* staticField; - staticField = dvmOptResolveStaticField(meth->clazz, fieldIdx); + staticField = dvmOptResolveStaticField(meth->clazz, fieldIdx, pFailure); if (staticField == NULL) { DexFile* pDexFile = meth->clazz->pDvmDex->pDexFile; const DexFieldId* pFieldId; @@ -2418,8 +2558,7 @@ static StaticField* getStaticField(const Method* meth, int fieldIdx, LOG_VFY("VFY: unable to resolve static field %u (%s) in %s\n", fieldIdx, dexStringById(pDexFile, pFieldId->nameIdx), dexStringByTypeIdx(pDexFile, pFieldId->classIdx)); - - *pOkay = false; + assert(!VERIFY_OK(*pFailure)); goto bail; } @@ -2431,10 +2570,10 @@ bail: * If "field" is marked "final", make sure this is the either * or as appropriate. * - * Sets "*pOkay" to false on failure. + * Sets "*pFailure" on failure. */ static void checkFinalFieldAccess(const Method* meth, const Field* field, - bool* pOkay) + VerifyError* pFailure) { if (!dvmIsFinalField(field)) return; @@ -2443,21 +2582,15 @@ static void checkFinalFieldAccess(const Method* meth, const Field* field, if (meth->clazz != field->clazz) { LOG_VFY_METH(meth, "VFY: can't modify final field %s.%s\n", field->clazz->descriptor, field->name); - *pOkay = false; + *pFailure = VERIFY_ERROR_ACCESS_FIELD; return; } /* - * The EMMA code coverage tool generates a static method that - * modifies a private static final field. The method is only - * called by , so the code is reasonable if not quite - * kosher. (Attempting to *compile* code that does something - * like that will earn you a quick thumbs-down from javac.) - * - * The verifier in another popular VM doesn't complain about this, - * so we're going to allow classes to modify their own static - * final fields outside of class initializers. Further testing - * showed that modifications to instance fields are also allowed. + * The VM spec descriptions of putfield and putstatic say that + * IllegalAccessError is only thrown when the instructions appear + * outside the declaring class. Our earlier attempts to restrict + * final field modification to constructors are, therefore, wrong. */ #if 0 /* make sure we're in the right kind of constructor */ @@ -2465,13 +2598,13 @@ static void checkFinalFieldAccess(const Method* meth, const Field* field, if (!isClassInitMethod(meth)) { LOG_VFY_METH(meth, "VFY: can't modify final static field outside \n"); - *pOkay = false; + *pFailure = VERIFY_ERROR_GENERIC; } } else { if (!isInitMethod(meth)) { LOG_VFY_METH(meth, "VFY: can't modify final field outside \n"); - *pOkay = false; + *pFailure = VERIFY_ERROR_GENERIC; } } #endif @@ -2480,19 +2613,19 @@ static void checkFinalFieldAccess(const Method* meth, const Field* field, /* * Make sure that the register type is suitable for use as an array index. * - * Sets "*pOkay" to false if not. + * Sets "*pFailure" if not. */ static void checkArrayIndexType(const Method* meth, RegType regType, - bool* pOkay) + VerifyError* pFailure) { - if (*pOkay) { + if (VERIFY_OK(*pFailure)) { /* * The 1nr types are interchangeable at this level. We could * do something special if we can definitively identify it as a * float, but there's no real value in doing so. */ - checkTypeCategory(regType, kTypeCategory1nr, pOkay); - if (!*pOkay) { + checkTypeCategory(regType, kTypeCategory1nr, pFailure); + if (!VERIFY_OK(*pFailure)) { LOG_VFY_METH(meth, "Invalid reg type for array index (%d)\n", regType); } @@ -2560,11 +2693,14 @@ static bool checkMoveException(const Method* meth, int insnIdx, * Returns NULL if no matching exception handler can be found, or if the * exception is not a subclass of Throwable. */ -static ClassObject* getCaughtExceptionType(const Method* meth, int insnIdx) +static ClassObject* getCaughtExceptionType(const Method* meth, int insnIdx, + VerifyError* pFailure) { + VerifyError localFailure; const DexCode* pCode; DexFile* pDexFile; ClassObject* commonSuper = NULL; + bool foundPossibleHandler = false; u4 handlersSize; u4 offset; u4 i; @@ -2593,16 +2729,22 @@ static ClassObject* getCaughtExceptionType(const Method* meth, int insnIdx) if (handler->address == (u4) insnIdx) { ClassObject* clazz; + foundPossibleHandler = true; if (handler->typeIdx == kDexNoIndex) clazz = gDvm.classJavaLangThrowable; else - clazz = dvmOptResolveClass(meth->clazz, handler->typeIdx); + clazz = dvmOptResolveClass(meth->clazz, handler->typeIdx, + &localFailure); if (clazz == NULL) { LOG_VFY("VFY: unable to resolve exception class %u (%s)\n", handler->typeIdx, dexStringByTypeIdx(pDexFile, handler->typeIdx)); + /* TODO: do we want to keep going? If we don't fail + * this we run the risk of having a non-Throwable + * introduced at runtime. However, that won't pass + * an instanceof test, so is essentially harmless. */ } else { if (commonSuper == NULL) commonSuper = clazz; @@ -2616,8 +2758,12 @@ static ClassObject* getCaughtExceptionType(const Method* meth, int insnIdx) } if (commonSuper == NULL) { + /* no catch blocks, or no catches with classes we can find */ LOG_VFY_METH(meth, "VFY: unable to find exception handler at addr 0x%x\n", insnIdx); + *pFailure = VERIFY_ERROR_GENERIC; + } else { + // TODO: verify the class is an instance of Throwable? } return commonSuper; @@ -2732,7 +2878,7 @@ static bool initRegisterTable(const Method* meth, const InsnFlags* insnFlags, static void verifyFilledNewArrayRegs(const Method* meth, const RegType* insnRegs, const int insnRegCount, const DecodedInstruction* pDecInsn, ClassObject* resClass, bool isRange, - bool* pOkay) + VerifyError* pFailure) { u4 argCount = pDecInsn->vA; RegType expectedType; @@ -2761,8 +2907,9 @@ static void verifyFilledNewArrayRegs(const Method* meth, else getReg = pDecInsn->arg[ui]; - verifyRegisterType(insnRegs, insnRegCount, getReg, expectedType, pOkay); - if (!*pOkay) { + verifyRegisterType(insnRegs, insnRegCount, getReg, expectedType, + pFailure); + if (!VERIFY_OK(*pFailure)) { LOG_VFY("VFY: filled-new-array arg %u(%u) not valid\n", ui, getReg); return; } @@ -2770,6 +2917,128 @@ static void verifyFilledNewArrayRegs(const Method* meth, } +/* + * Replace an instruction with "throw-verification-error". This allows us to + * defer error reporting until the code path is first used. + * + * The throw-verification-error instruction requires two code units. Some + * of the replaced instructions require three; the third code unit will + * receive a "nop". The instruction's length will be left unchanged + * in "insnFlags". + * + * IMPORTANT: this may replace meth->insns with a pointer to a new copy of + * the instructions. + * + * Returns "true" on success. + */ +static bool replaceFailingInstruction(Method* meth, InsnFlags* insnFlags, + int insnIdx, VerifyError failure) +{ + VerifyErrorRefType refType; + const u2* oldInsns = meth->insns + insnIdx; + u2 oldInsn = *oldInsns; + bool result = false; + + dvmMakeCodeReadWrite(meth); + + //LOGD(" was 0x%04x\n", oldInsn); + u2* newInsns = (u2*) meth->insns + insnIdx; + + /* + * Generate the new instruction out of the old. + * + * First, make sure this is an instruction we're expecting to stomp on. + */ + switch (oldInsn & 0xff) { + case OP_CONST_CLASS: // insn[1] == class ref, 2 bytes + case OP_CHECK_CAST: + case OP_INSTANCE_OF: + case OP_NEW_INSTANCE: + case OP_NEW_ARRAY: + case OP_FILLED_NEW_ARRAY: // insn[1] == class ref, 3 bytes + case OP_FILLED_NEW_ARRAY_RANGE: + refType = VERIFY_ERROR_REF_CLASS; + break; + + case OP_IGET: // insn[1] == field ref, 2 bytes + case OP_IGET_BOOLEAN: + case OP_IGET_BYTE: + case OP_IGET_CHAR: + case OP_IGET_SHORT: + case OP_IGET_WIDE: + case OP_IGET_OBJECT: + case OP_IPUT: + case OP_IPUT_BOOLEAN: + case OP_IPUT_BYTE: + case OP_IPUT_CHAR: + case OP_IPUT_SHORT: + case OP_IPUT_WIDE: + case OP_IPUT_OBJECT: + case OP_SGET: + case OP_SGET_BOOLEAN: + case OP_SGET_BYTE: + case OP_SGET_CHAR: + case OP_SGET_SHORT: + case OP_SGET_WIDE: + case OP_SGET_OBJECT: + case OP_SPUT: + case OP_SPUT_BOOLEAN: + case OP_SPUT_BYTE: + case OP_SPUT_CHAR: + case OP_SPUT_SHORT: + case OP_SPUT_WIDE: + case OP_SPUT_OBJECT: + refType = VERIFY_ERROR_REF_FIELD; + break; + + case OP_INVOKE_VIRTUAL: // insn[1] == method ref, 3 bytes + case OP_INVOKE_VIRTUAL_RANGE: + case OP_INVOKE_SUPER: + case OP_INVOKE_SUPER_RANGE: + case OP_INVOKE_DIRECT: + case OP_INVOKE_DIRECT_RANGE: + case OP_INVOKE_STATIC: + case OP_INVOKE_STATIC_RANGE: + case OP_INVOKE_INTERFACE: + case OP_INVOKE_INTERFACE_RANGE: + refType = VERIFY_ERROR_REF_METHOD; + break; + + default: + /* could handle this in a generic way, but this is probably safer */ + LOG_VFY("GLITCH: verifier asked to replace opcode 0x%02x\n", + oldInsn & 0xff); + goto bail; + } + + /* write a NOP over the third code unit, if necessary */ + int width = dvmInsnGetWidth(insnFlags, insnIdx); + switch (width) { + case 2: + /* nothing to do */ + break; + case 3: + newInsns[2] = OP_NOP; + break; + default: + /* whoops */ + LOGE("ERROR: stomped a %d-unit instruction with a verifier error\n", + width); + dvmAbort(); + } + + /* encode the opcode, with the failure code in the high byte */ + newInsns[0] = OP_THROW_VERIFICATION_ERROR | + (failure << 8) | (refType << (8 + kVerifyErrorRefTypeShift)); + + result = true; + +bail: + dvmMakeCodeReadOnly(meth); + return result; +} + + /* * =========================================================================== * Entry point and driver loop @@ -2779,7 +3048,7 @@ static void verifyFilledNewArrayRegs(const Method* meth, /* * Entry point for the detailed code-flow analysis. */ -bool dvmVerifyCodeFlow(const Method* meth, InsnFlags* insnFlags, +bool dvmVerifyCodeFlow(Method* meth, InsnFlags* insnFlags, UninitInstanceMap* uninitMap) { bool result = false; @@ -2806,9 +3075,13 @@ bool dvmVerifyCodeFlow(const Method* meth, InsnFlags* insnFlags, if (gDvm.classJavaLangString == NULL) gDvm.classJavaLangString = dvmFindSystemClassNoInit("Ljava/lang/String;"); - if (gDvm.classJavaLangThrowable == NULL) + if (gDvm.classJavaLangThrowable == NULL) { gDvm.classJavaLangThrowable = dvmFindSystemClassNoInit("Ljava/lang/Throwable;"); + gDvm.offJavaLangThrowable_cause = + dvmFindFieldOffset(gDvm.classJavaLangThrowable, + "cause", "Ljava/lang/Throwable;"); + } if (gDvm.classJavaLangObject == NULL) gDvm.classJavaLangObject = dvmFindSystemClassNoInit("Ljava/lang/Object;"); @@ -2929,7 +3202,7 @@ bail: * instruction if a register contains an uninitialized instance created * by that same instrutcion. */ -static bool doCodeVerification(const Method* meth, InsnFlags* insnFlags, +static bool doCodeVerification(Method* meth, InsnFlags* insnFlags, RegisterTable* regTable, UninitInstanceMap* uninitMap) { const int insnsSize = dvmGetMethodInsnsSize(meth); @@ -3087,10 +3360,14 @@ static bool doCodeVerification(const Method* meth, InsnFlags* insnFlags, dvmInsnSetChanged(insnFlags, insnIdx, false); } - if (DEAD_CODE_SCAN) { + if (DEAD_CODE_SCAN && !IS_METHOD_FLAG_SET(meth, METHOD_ISWRITABLE)) { /* - * Scan for dead code. There's nothing "evil" about dead code, but it - * indicates a flaw somewhere down the line, possibly in the verifier. + * Scan for dead code. There's nothing "evil" about dead code + * (besides the wasted space), but it indicates a flaw somewhere + * down the line, possibly in the verifier. + * + * If we've rewritten "always throw" instructions into the stream, + * we are almost certainly going to have some dead code. */ int deadStart = -1; for (insnIdx = 0; insnIdx < insnsSize; @@ -3156,8 +3433,11 @@ bail: * if execution at that point needs to be (re-)evaluated. Register changes * are merged into "regTypes" at the target addresses. Does not set or * clear any other flags in "insnFlags". + * + * This may alter meth->insns if we need to replace an instruction with + * throw-verification-error. */ -static bool verifyInstruction(const Method* meth, InsnFlags* insnFlags, +static bool verifyInstruction(Method* meth, InsnFlags* insnFlags, RegisterTable* regTable, RegType* workRegs, int insnIdx, UninitInstanceMap* uninitMap, int* pStartGuess) { @@ -3194,14 +3474,14 @@ static bool verifyInstruction(const Method* meth, InsnFlags* insnFlags, RegType tmpType; DecodedInstruction decInsn; bool justSetResult = false; - bool okay = true; + VerifyError failure = VERIFY_ERROR_NONE; #ifndef NDEBUG memset(&decInsn, 0x81, sizeof(decInsn)); #endif dexDecodeInstruction(gDvm.instrFormat, insns, &decInsn); - const int nextFlags = dexGetInstrFlags(gDvm.instrFlags, decInsn.opCode); + int nextFlags = dexGetInstrFlags(gDvm.instrFlags, decInsn.opCode); /* * Make a copy of the previous register state. If the instruction @@ -3229,7 +3509,7 @@ static bool verifyInstruction(const Method* meth, InsnFlags* insnFlags, */ if (decInsn.vA != 0) { LOG_VFY("VFY: encountered data table in instruction stream\n"); - okay = false; + failure = VERIFY_ERROR_GENERIC; } break; @@ -3237,18 +3517,18 @@ static bool verifyInstruction(const Method* meth, InsnFlags* insnFlags, case OP_MOVE_FROM16: case OP_MOVE_16: copyRegister1(workRegs, insnRegCount, decInsn.vA, decInsn.vB, - kTypeCategory1nr, &okay); + kTypeCategory1nr, &failure); break; case OP_MOVE_WIDE: case OP_MOVE_WIDE_FROM16: case OP_MOVE_WIDE_16: - copyRegister2(workRegs, insnRegCount, decInsn.vA, decInsn.vB, &okay); + copyRegister2(workRegs, insnRegCount, decInsn.vA, decInsn.vB, &failure); break; case OP_MOVE_OBJECT: case OP_MOVE_OBJECT_FROM16: case OP_MOVE_OBJECT_16: copyRegister1(workRegs, insnRegCount, decInsn.vA, decInsn.vB, - kTypeCategoryRef, &okay); + kTypeCategoryRef, &failure); break; /* @@ -3264,14 +3544,14 @@ static bool verifyInstruction(const Method* meth, InsnFlags* insnFlags, */ case OP_MOVE_RESULT: copyResultRegister1(workRegs, insnRegCount, decInsn.vA, - kTypeCategory1nr, &okay); + kTypeCategory1nr, &failure); break; case OP_MOVE_RESULT_WIDE: - copyResultRegister2(workRegs, insnRegCount, decInsn.vA, &okay); + copyResultRegister2(workRegs, insnRegCount, decInsn.vA, &failure); break; case OP_MOVE_RESULT_OBJECT: copyResultRegister1(workRegs, insnRegCount, decInsn.vA, - kTypeCategoryRef, &okay); + kTypeCategoryRef, &failure); break; case OP_MOVE_EXCEPTION: @@ -3284,71 +3564,75 @@ static bool verifyInstruction(const Method* meth, InsnFlags* insnFlags, * "resClass" will hold the closest common superclass of all * exceptions that can be handled here. */ - resClass = getCaughtExceptionType(meth, insnIdx); + resClass = getCaughtExceptionType(meth, insnIdx, &failure); if (resClass == NULL) { - okay = false; + assert(!VERIFY_OK(failure)); } else { setRegisterType(workRegs, insnRegCount, decInsn.vA, - regTypeFromClass(resClass), &okay); + regTypeFromClass(resClass), &failure); } break; case OP_RETURN_VOID: - okay = checkConstructorReturn(meth, workRegs, insnRegCount); - if (okay && getMethodReturnType(meth) != kRegTypeUnknown) { + if (!checkConstructorReturn(meth, workRegs, insnRegCount)) { + failure = VERIFY_ERROR_GENERIC; + } else if (getMethodReturnType(meth) != kRegTypeUnknown) { LOG_VFY("VFY: return-void not expected\n"); - okay = false; + failure = VERIFY_ERROR_GENERIC; } break; case OP_RETURN: - okay = checkConstructorReturn(meth, workRegs, insnRegCount); - if (okay) { + if (!checkConstructorReturn(meth, workRegs, insnRegCount)) { + failure = VERIFY_ERROR_GENERIC; + } else { /* check the method signature */ RegType returnType = getMethodReturnType(meth); - checkTypeCategory(returnType, kTypeCategory1nr, &okay); - if (!okay) + checkTypeCategory(returnType, kTypeCategory1nr, &failure); + if (!VERIFY_OK(failure)) LOG_VFY("VFY: return-32 not expected\n"); /* check the register contents */ returnType = getRegisterType(workRegs, insnRegCount, decInsn.vA, - &okay); - checkTypeCategory(returnType, kTypeCategory1nr, &okay); - if (!okay) + &failure); + checkTypeCategory(returnType, kTypeCategory1nr, &failure); + if (!VERIFY_OK(failure)) LOG_VFY("VFY: return-32 on invalid register v%d\n", decInsn.vA); } break; case OP_RETURN_WIDE: - okay = checkConstructorReturn(meth, workRegs, insnRegCount); - if (okay) { + if (!checkConstructorReturn(meth, workRegs, insnRegCount)) { + failure = VERIFY_ERROR_GENERIC; + } else { RegType returnType, returnTypeHi; /* check the method signature */ returnType = getMethodReturnType(meth); - checkTypeCategory(returnType, kTypeCategory2, &okay); - if (!okay) + checkTypeCategory(returnType, kTypeCategory2, &failure); + if (!VERIFY_OK(failure)) LOG_VFY("VFY: return-wide not expected\n"); /* check the register contents */ returnType = getRegisterType(workRegs, insnRegCount, decInsn.vA, - &okay); + &failure); returnTypeHi = getRegisterType(workRegs, insnRegCount, - decInsn.vA +1, &okay); - if (okay) { - checkTypeCategory(returnType, kTypeCategory2, &okay); - checkWidePair(returnType, returnTypeHi, &okay); + decInsn.vA +1, &failure); + if (VERIFY_OK(failure)) { + checkTypeCategory(returnType, kTypeCategory2, &failure); + checkWidePair(returnType, returnTypeHi, &failure); } - if (!okay) { + if (!VERIFY_OK(failure)) { LOG_VFY("VFY: return-wide on invalid register pair v%d\n", decInsn.vA); } } break; case OP_RETURN_OBJECT: - okay = checkConstructorReturn(meth, workRegs, insnRegCount); - if (okay) { + if (!checkConstructorReturn(meth, workRegs, insnRegCount)) { + failure = VERIFY_ERROR_GENERIC; + } else { RegType returnType = getMethodReturnType(meth); - checkTypeCategory(returnType, kTypeCategoryRef, &okay); - if (!okay) { + checkTypeCategory(returnType, kTypeCategoryRef, &failure); + if (!VERIFY_OK(failure)) { LOG_VFY("VFY: return-object not expected\n"); break; } @@ -3371,16 +3655,17 @@ static bool verifyInstruction(const Method* meth, InsnFlags* insnFlags, declClass = regTypeInitializedReferenceToClass(returnType); resClass = getClassFromRegister(workRegs, insnRegCount, - decInsn.vA, &okay); - if (!okay) + decInsn.vA, &failure); + if (!VERIFY_OK(failure)) break; if (resClass != NULL) { if (!dvmIsInterfaceClass(declClass) && !dvmInstanceof(resClass, declClass)) { - LOG_VFY("VFY: returning %s, declared %s\n", - resClass->descriptor, declClass->descriptor); - okay = false; + LOG_VFY("VFY: returning %s (cl=%p), declared %s (cl=%p)\n", + resClass->descriptor, resClass->classLoader, + declClass->descriptor, declClass->classLoader); + failure = VERIFY_ERROR_GENERIC; break; } } @@ -3392,12 +3677,12 @@ static bool verifyInstruction(const Method* meth, InsnFlags* insnFlags, case OP_CONST: /* could be boolean, int, float, or a null reference */ setRegisterType(workRegs, insnRegCount, decInsn.vA, - dvmDetermineCat1Const((s4)decInsn.vB), &okay); + dvmDetermineCat1Const((s4)decInsn.vB), &failure); break; case OP_CONST_HIGH16: /* could be boolean, int, float, or a null reference */ setRegisterType(workRegs, insnRegCount, decInsn.vA, - dvmDetermineCat1Const((s4) decInsn.vB << 16), &okay); + dvmDetermineCat1Const((s4) decInsn.vB << 16), &failure); break; case OP_CONST_WIDE_16: case OP_CONST_WIDE_32: @@ -3405,36 +3690,38 @@ static bool verifyInstruction(const Method* meth, InsnFlags* insnFlags, case OP_CONST_WIDE_HIGH16: /* could be long or double; default to long and allow conversion */ setRegisterType(workRegs, insnRegCount, decInsn.vA, - kRegTypeLongLo, &okay); + kRegTypeLongLo, &failure); break; case OP_CONST_STRING: case OP_CONST_STRING_JUMBO: assert(gDvm.classJavaLangString != NULL); setRegisterType(workRegs, insnRegCount, decInsn.vA, - regTypeFromClass(gDvm.classJavaLangString), &okay); + regTypeFromClass(gDvm.classJavaLangString), &failure); break; case OP_CONST_CLASS: assert(gDvm.classJavaLangClass != NULL); /* make sure we can resolve the class; access check is important */ - resClass = dvmOptResolveClass(meth->clazz, decInsn.vB); + resClass = dvmOptResolveClass(meth->clazz, decInsn.vB, &failure); if (resClass == NULL) { const char* badClassDesc = dexStringByTypeIdx(pDexFile, decInsn.vB); dvmLogUnableToResolveClass(badClassDesc, meth); LOG_VFY("VFY: unable to resolve const-class %d (%s) in %s\n", decInsn.vB, badClassDesc, meth->clazz->descriptor); - okay = false; + assert(failure != VERIFY_ERROR_GENERIC); } else { setRegisterType(workRegs, insnRegCount, decInsn.vA, - regTypeFromClass(gDvm.classJavaLangClass), &okay); + regTypeFromClass(gDvm.classJavaLangClass), &failure); } break; case OP_MONITOR_ENTER: case OP_MONITOR_EXIT: - tmpType = getRegisterType(workRegs, insnRegCount, decInsn.vA, &okay); - if (okay && !regTypeIsReference(tmpType)) { - LOG_VFY("VFY: monitor op on non-object\n"); - okay = false; + tmpType = getRegisterType(workRegs, insnRegCount, decInsn.vA, &failure); + if (VERIFY_OK(failure)) { + if (!regTypeIsReference(tmpType)) { + LOG_VFY("VFY: monitor op on non-object\n"); + failure = VERIFY_ERROR_GENERIC; + } } break; @@ -3447,86 +3734,88 @@ static bool verifyInstruction(const Method* meth, InsnFlags* insnFlags, * If it fails, an exception is thrown, which we deal with later * by ignoring the update to decInsn.vA when branching to a handler. */ - resClass = dvmOptResolveClass(meth->clazz, decInsn.vB); + resClass = dvmOptResolveClass(meth->clazz, decInsn.vB, &failure); if (resClass == NULL) { const char* badClassDesc = dexStringByTypeIdx(pDexFile, decInsn.vB); dvmLogUnableToResolveClass(badClassDesc, meth); LOG_VFY("VFY: unable to resolve check-cast %d (%s) in %s\n", decInsn.vB, badClassDesc, meth->clazz->descriptor); - okay = false; + assert(failure != VERIFY_ERROR_GENERIC); } else { RegType origType; origType = getRegisterType(workRegs, insnRegCount, decInsn.vA, - &okay); - if (!okay) + &failure); + if (!VERIFY_OK(failure)) break; if (!regTypeIsReference(origType)) { LOG_VFY("VFY: check-cast on non-reference in v%u\n",decInsn.vA); - okay = false; + failure = VERIFY_ERROR_GENERIC; break; } setRegisterType(workRegs, insnRegCount, decInsn.vA, - regTypeFromClass(resClass), &okay); + regTypeFromClass(resClass), &failure); } break; case OP_INSTANCE_OF: /* make sure we're checking a reference type */ - tmpType = getRegisterType(workRegs, insnRegCount, decInsn.vB, &okay); - if (!okay) + tmpType = getRegisterType(workRegs, insnRegCount, decInsn.vB, &failure); + if (!VERIFY_OK(failure)) break; if (!regTypeIsReference(tmpType)) { LOG_VFY("VFY: vB not a reference (%d)\n", tmpType); - okay = false; + failure = VERIFY_ERROR_GENERIC; break; } /* make sure we can resolve the class; access check is important */ - resClass = dvmOptResolveClass(meth->clazz, decInsn.vC); + resClass = dvmOptResolveClass(meth->clazz, decInsn.vC, &failure); if (resClass == NULL) { const char* badClassDesc = dexStringByTypeIdx(pDexFile, decInsn.vC); dvmLogUnableToResolveClass(badClassDesc, meth); LOG_VFY("VFY: unable to resolve instanceof %d (%s) in %s\n", decInsn.vC, badClassDesc, meth->clazz->descriptor); - okay = false; + assert(failure != VERIFY_ERROR_GENERIC); } else { /* result is boolean */ setRegisterType(workRegs, insnRegCount, decInsn.vA, - kRegTypeBoolean, &okay); + kRegTypeBoolean, &failure); } break; case OP_ARRAY_LENGTH: resClass = getClassFromRegister(workRegs, insnRegCount, - decInsn.vB, &okay); - if (!okay) + decInsn.vB, &failure); + if (!VERIFY_OK(failure)) break; if (resClass != NULL && !dvmIsArrayClass(resClass)) { LOG_VFY("VFY: array-length on non-array\n"); - okay = false; + failure = VERIFY_ERROR_GENERIC; break; } setRegisterType(workRegs, insnRegCount, decInsn.vA, kRegTypeInteger, - &okay); + &failure); break; case OP_NEW_INSTANCE: - /* - * We can check for interface and abstract classes here, but we - * can't reject them. We can ask the optimizer to replace the - * instructions with a magic "always throw InstantiationError" - * instruction. (Not enough bytes to sub in a method call.) - */ - resClass = dvmOptResolveClass(meth->clazz, decInsn.vB); + resClass = dvmOptResolveClass(meth->clazz, decInsn.vB, &failure); if (resClass == NULL) { const char* badClassDesc = dexStringByTypeIdx(pDexFile, decInsn.vB); dvmLogUnableToResolveClass(badClassDesc, meth); LOG_VFY("VFY: unable to resolve new-instance %d (%s) in %s\n", decInsn.vB, badClassDesc, meth->clazz->descriptor); - okay = false; + assert(failure != VERIFY_ERROR_GENERIC); } else { RegType uninitType; + /* can't create an instance of an interface or abstract class */ + if (dvmIsAbstractClass(resClass) || dvmIsInterfaceClass(resClass)) { + LOG_VFY("VFY: new-instance on interface or abstract class %s\n", + resClass->descriptor); + failure = VERIFY_ERROR_INSTANTIATION; + break; + } + /* add resolved class to uninit map if not already there */ int uidx = dvmSetUninitInstance(uninitMap, insnIdx, resClass); assert(uidx >= 0); @@ -3541,50 +3830,50 @@ static bool verifyInstruction(const Method* meth, InsnFlags* insnFlags, /* add the new uninitialized reference to the register ste */ setRegisterType(workRegs, insnRegCount, decInsn.vA, - uninitType, &okay); + uninitType, &failure); } break; case OP_NEW_ARRAY: - resClass = dvmOptResolveClass(meth->clazz, decInsn.vC); + resClass = dvmOptResolveClass(meth->clazz, decInsn.vC, &failure); if (resClass == NULL) { const char* badClassDesc = dexStringByTypeIdx(pDexFile, decInsn.vC); dvmLogUnableToResolveClass(badClassDesc, meth); LOG_VFY("VFY: unable to resolve new-array %d (%s) in %s\n", decInsn.vC, badClassDesc, meth->clazz->descriptor); - okay = false; + assert(failure != VERIFY_ERROR_GENERIC); } else if (!dvmIsArrayClass(resClass)) { LOG_VFY("VFY: new-array on non-array class\n"); - okay = false; + failure = VERIFY_ERROR_GENERIC; } else { /* make sure "size" register is valid type */ verifyRegisterType(workRegs, insnRegCount, decInsn.vB, - kRegTypeInteger, &okay); + kRegTypeInteger, &failure); /* set register type to array class */ setRegisterType(workRegs, insnRegCount, decInsn.vA, - regTypeFromClass(resClass), &okay); + regTypeFromClass(resClass), &failure); } break; case OP_FILLED_NEW_ARRAY: case OP_FILLED_NEW_ARRAY_RANGE: - resClass = dvmOptResolveClass(meth->clazz, decInsn.vB); + resClass = dvmOptResolveClass(meth->clazz, decInsn.vB, &failure); if (resClass == NULL) { const char* badClassDesc = dexStringByTypeIdx(pDexFile, decInsn.vB); dvmLogUnableToResolveClass(badClassDesc, meth); LOG_VFY("VFY: unable to resolve filled-array %d (%s) in %s\n", decInsn.vB, badClassDesc, meth->clazz->descriptor); - okay = false; + assert(failure != VERIFY_ERROR_GENERIC); } else if (!dvmIsArrayClass(resClass)) { LOG_VFY("VFY: filled-new-array on non-array class\n"); - okay = false; + failure = VERIFY_ERROR_GENERIC; } else { bool isRange = (decInsn.opCode == OP_FILLED_NEW_ARRAY_RANGE); /* check the arguments to the instruction */ verifyFilledNewArrayRegs(meth, workRegs, insnRegCount, &decInsn, - resClass, isRange, &okay); + resClass, isRange, &failure); /* filled-array result goes into "result" register */ setResultRegisterType(workRegs, insnRegCount, - regTypeFromClass(resClass), &okay); + regTypeFromClass(resClass), &failure); justSetResult = true; } break; @@ -3592,38 +3881,38 @@ static bool verifyInstruction(const Method* meth, InsnFlags* insnFlags, case OP_CMPL_FLOAT: case OP_CMPG_FLOAT: verifyRegisterType(workRegs, insnRegCount, decInsn.vB, kRegTypeFloat, - &okay); + &failure); verifyRegisterType(workRegs, insnRegCount, decInsn.vC, kRegTypeFloat, - &okay); + &failure); setRegisterType(workRegs, insnRegCount, decInsn.vA, kRegTypeBoolean, - &okay); + &failure); break; case OP_CMPL_DOUBLE: case OP_CMPG_DOUBLE: verifyRegisterType(workRegs, insnRegCount, decInsn.vB, kRegTypeDoubleLo, - &okay); + &failure); verifyRegisterType(workRegs, insnRegCount, decInsn.vC, kRegTypeDoubleLo, - &okay); + &failure); setRegisterType(workRegs, insnRegCount, decInsn.vA, kRegTypeBoolean, - &okay); + &failure); break; case OP_CMP_LONG: verifyRegisterType(workRegs, insnRegCount, decInsn.vB, kRegTypeLongLo, - &okay); + &failure); verifyRegisterType(workRegs, insnRegCount, decInsn.vC, kRegTypeLongLo, - &okay); + &failure); setRegisterType(workRegs, insnRegCount, decInsn.vA, kRegTypeBoolean, - &okay); + &failure); break; case OP_THROW: resClass = getClassFromRegister(workRegs, insnRegCount, - decInsn.vA, &okay); - if (okay && resClass != NULL) { + decInsn.vA, &failure); + if (VERIFY_OK(failure) && resClass != NULL) { if (!dvmInstanceof(resClass, gDvm.classJavaLangThrowable)) { LOG_VFY("VFY: thrown class %s not instanceof Throwable\n", resClass->descriptor); - okay = false; + failure = VERIFY_ERROR_GENERIC; } } break; @@ -3638,7 +3927,7 @@ static bool verifyInstruction(const Method* meth, InsnFlags* insnFlags, case OP_SPARSE_SWITCH: /* verify that vAA is an integer, or can be converted to one */ verifyRegisterType(workRegs, insnRegCount, decInsn.vA, - kRegTypeInteger, &okay); + kRegTypeInteger, &failure); break; case OP_FILL_ARRAY_DATA: @@ -3649,8 +3938,8 @@ static bool verifyInstruction(const Method* meth, InsnFlags* insnFlags, /* Similar to the verification done for APUT */ resClass = getClassFromRegister(workRegs, insnRegCount, - decInsn.vA, &okay); - if (!okay) + decInsn.vA, &failure); + if (!VERIFY_OK(failure)) break; /* resClass can be null if the reg type is Zero */ @@ -3663,7 +3952,7 @@ static bool verifyInstruction(const Method* meth, InsnFlags* insnFlags, { LOG_VFY("VFY: invalid fill-array-data on %s\n", resClass->descriptor); - okay = false; + failure = VERIFY_ERROR_GENERIC; break; } @@ -3678,7 +3967,7 @@ static bool verifyInstruction(const Method* meth, InsnFlags* insnFlags, arrayData = insns + (insns[1] | (((s4)insns[2]) << 16)); if (arrayData[0] != kArrayDataSignature) { LOG_VFY("VFY: invalid magic for array-data\n"); - okay = false; + failure = VERIFY_ERROR_GENERIC; break; } @@ -3712,7 +4001,7 @@ static bool verifyInstruction(const Method* meth, InsnFlags* insnFlags, if (arrayData[1] != elemWidth) { LOG_VFY("VFY: array-data size mismatch (%d vs %d)\n", arrayData[1], elemWidth); - okay = false; + failure = VERIFY_ERROR_GENERIC; } } break; @@ -3723,9 +4012,11 @@ static bool verifyInstruction(const Method* meth, InsnFlags* insnFlags, RegType type1, type2; bool tmpResult; - type1 = getRegisterType(workRegs, insnRegCount, decInsn.vA, &okay); - type2 = getRegisterType(workRegs, insnRegCount, decInsn.vB, &okay); - if (!okay) + type1 = getRegisterType(workRegs, insnRegCount, decInsn.vA, + &failure); + type2 = getRegisterType(workRegs, insnRegCount, decInsn.vB, + &failure); + if (!VERIFY_OK(failure)) break; /* both references? */ @@ -3733,9 +4024,9 @@ static bool verifyInstruction(const Method* meth, InsnFlags* insnFlags, break; /* both category-1nr? */ - checkTypeCategory(type1, kTypeCategory1nr, &okay); - checkTypeCategory(type2, kTypeCategory1nr, &okay); - if (!okay) { + checkTypeCategory(type1, kTypeCategory1nr, &failure); + checkTypeCategory(type2, kTypeCategory1nr, &failure); + if (!VERIFY_OK(failure)) { LOG_VFY("VFY: args to if-eq/if-ne must both be refs or cat1\n"); break; } @@ -3745,43 +4036,43 @@ static bool verifyInstruction(const Method* meth, InsnFlags* insnFlags, case OP_IF_GE: case OP_IF_GT: case OP_IF_LE: - tmpType = getRegisterType(workRegs, insnRegCount, decInsn.vA, &okay); - if (!okay) + tmpType = getRegisterType(workRegs, insnRegCount, decInsn.vA, &failure); + if (!VERIFY_OK(failure)) break; - checkTypeCategory(tmpType, kTypeCategory1nr, &okay); - if (!okay) { + checkTypeCategory(tmpType, kTypeCategory1nr, &failure); + if (!VERIFY_OK(failure)) { LOG_VFY("VFY: args to 'if' must be cat-1nr\n"); break; } - tmpType = getRegisterType(workRegs, insnRegCount, decInsn.vB,&okay); - if (!okay) + tmpType = getRegisterType(workRegs, insnRegCount, decInsn.vB, &failure); + if (!VERIFY_OK(failure)) break; - checkTypeCategory(tmpType, kTypeCategory1nr, &okay); - if (!okay) { + checkTypeCategory(tmpType, kTypeCategory1nr, &failure); + if (!VERIFY_OK(failure)) { LOG_VFY("VFY: args to 'if' must be cat-1nr\n"); break; } break; case OP_IF_EQZ: case OP_IF_NEZ: - tmpType = getRegisterType(workRegs, insnRegCount, decInsn.vA, &okay); - if (!okay) + tmpType = getRegisterType(workRegs, insnRegCount, decInsn.vA, &failure); + if (!VERIFY_OK(failure)) break; if (regTypeIsReference(tmpType)) break; - checkTypeCategory(tmpType, kTypeCategory1nr, &okay); - if (!okay) + checkTypeCategory(tmpType, kTypeCategory1nr, &failure); + if (!VERIFY_OK(failure)) LOG_VFY("VFY: expected cat-1 arg to if\n"); break; case OP_IF_LTZ: case OP_IF_GEZ: case OP_IF_GTZ: case OP_IF_LEZ: - tmpType = getRegisterType(workRegs, insnRegCount, decInsn.vA, &okay); - if (!okay) + tmpType = getRegisterType(workRegs, insnRegCount, decInsn.vA, &failure); + if (!VERIFY_OK(failure)) break; - checkTypeCategory(tmpType, kTypeCategory1nr, &okay); - if (!okay) + checkTypeCategory(tmpType, kTypeCategory1nr, &failure); + if (!VERIFY_OK(failure)) LOG_VFY("VFY: expected cat-1 arg to if\n"); break; @@ -3805,14 +4096,14 @@ aget_1nr_common: RegType srcType, indexType; indexType = getRegisterType(workRegs, insnRegCount, decInsn.vC, - &okay); - checkArrayIndexType(meth, indexType, &okay); - if (!okay) + &failure); + checkArrayIndexType(meth, indexType, &failure); + if (!VERIFY_OK(failure)) break; resClass = getClassFromRegister(workRegs, insnRegCount, - decInsn.vB, &okay); - if (!okay) + decInsn.vB, &failure); + if (!VERIFY_OK(failure)) break; if (resClass != NULL) { /* verify the class */ @@ -3821,7 +4112,7 @@ aget_1nr_common: { LOG_VFY("VFY: invalid aget-1nr target %s\n", resClass->descriptor); - okay = false; + failure = VERIFY_ERROR_GENERIC; break; } @@ -3833,13 +4124,13 @@ aget_1nr_common: LOG_VFY("VFY: invalid aget-1nr, array type=%d with" " inst type=%d (on %s)\n", srcType, tmpType, resClass->descriptor); - okay = false; + failure = VERIFY_ERROR_GENERIC; break; } } setRegisterType(workRegs, insnRegCount, decInsn.vA, - tmpType, &okay); + tmpType, &failure); } break; @@ -3848,14 +4139,14 @@ aget_1nr_common: RegType dstType, indexType; indexType = getRegisterType(workRegs, insnRegCount, decInsn.vC, - &okay); - checkArrayIndexType(meth, indexType, &okay); - if (!okay) + &failure); + checkArrayIndexType(meth, indexType, &failure); + if (!VERIFY_OK(failure)) break; resClass = getClassFromRegister(workRegs, insnRegCount, - decInsn.vB, &okay); - if (!okay) + decInsn.vB, &failure); + if (!VERIFY_OK(failure)) break; if (resClass != NULL) { /* verify the class */ @@ -3864,7 +4155,7 @@ aget_1nr_common: { LOG_VFY("VFY: invalid aget-wide target %s\n", resClass->descriptor); - okay = false; + failure = VERIFY_ERROR_GENERIC; break; } @@ -3880,7 +4171,7 @@ aget_1nr_common: LOG_VFY("VFY: invalid aget-wide on %s\n", resClass->descriptor); dstType = kRegTypeUnknown; - okay = false; + failure = VERIFY_ERROR_GENERIC; break; } } else { @@ -3893,7 +4184,7 @@ aget_1nr_common: dstType = kRegTypeLongLo; } setRegisterType(workRegs, insnRegCount, decInsn.vA, - dstType, &okay); + dstType, &failure); } break; @@ -3902,15 +4193,15 @@ aget_1nr_common: RegType dstType, indexType; indexType = getRegisterType(workRegs, insnRegCount, decInsn.vC, - &okay); - checkArrayIndexType(meth, indexType, &okay); - if (!okay) + &failure); + checkArrayIndexType(meth, indexType, &failure); + if (!VERIFY_OK(failure)) break; /* get the class of the array we're pulling an object from */ resClass = getClassFromRegister(workRegs, insnRegCount, - decInsn.vB, &okay); - if (!okay) + decInsn.vB, &failure); + if (!VERIFY_OK(failure)) break; if (resClass != NULL) { ClassObject* elementClass; @@ -3918,7 +4209,7 @@ aget_1nr_common: assert(resClass != NULL); if (!dvmIsArrayClass(resClass)) { LOG_VFY("VFY: aget-object on non-array class\n"); - okay = false; + failure = VERIFY_ERROR_GENERIC; break; } assert(resClass->elementClass != NULL); @@ -3938,7 +4229,7 @@ aget_1nr_common: } else { LOG_VFY("VFY: aget-object on non-ref array class (%s)\n", resClass->descriptor); - okay = false; + failure = VERIFY_ERROR_GENERIC; break; } @@ -3954,7 +4245,7 @@ aget_1nr_common: dstType = kRegTypeZero; } setRegisterType(workRegs, insnRegCount, decInsn.vA, - dstType, &okay); + dstType, &failure); } break; case OP_APUT: @@ -3977,24 +4268,24 @@ aput_1nr_common: RegType srcType, dstType, indexType; indexType = getRegisterType(workRegs, insnRegCount, decInsn.vC, - &okay); - checkArrayIndexType(meth, indexType, &okay); - if (!okay) + &failure); + checkArrayIndexType(meth, indexType, &failure); + if (!VERIFY_OK(failure)) break; /* make sure the source register has the correct type */ srcType = getRegisterType(workRegs, insnRegCount, decInsn.vA, - &okay); + &failure); if (!canConvertTo1nr(srcType, tmpType)) { LOG_VFY("VFY: invalid reg type %d on aput instr (need %d)\n", srcType, tmpType); - okay = false; + failure = VERIFY_ERROR_GENERIC; break; } resClass = getClassFromRegister(workRegs, insnRegCount, - decInsn.vB, &okay); - if (!okay) + decInsn.vB, &failure); + if (!VERIFY_OK(failure)) break; /* resClass can be null if the reg type is Zero */ @@ -4005,7 +4296,7 @@ aput_1nr_common: resClass->elementClass->primitiveType == PRIM_NOT) { LOG_VFY("VFY: invalid aput-1nr on %s\n", resClass->descriptor); - okay = false; + failure = VERIFY_ERROR_GENERIC; break; } @@ -4017,31 +4308,31 @@ aput_1nr_common: if (!checkFieldArrayStore1nr(tmpType, dstType)) { LOG_VFY("VFY: invalid aput-1nr on %s (inst=%d dst=%d)\n", resClass->descriptor, tmpType, dstType); - okay = false; + failure = VERIFY_ERROR_GENERIC; break; } } break; case OP_APUT_WIDE: tmpType = getRegisterType(workRegs, insnRegCount, decInsn.vC, - &okay); - checkArrayIndexType(meth, tmpType, &okay); - if (!okay) + &failure); + checkArrayIndexType(meth, tmpType, &failure); + if (!VERIFY_OK(failure)) break; - tmpType = getRegisterType(workRegs, insnRegCount, decInsn.vA, &okay); - if (okay) { + tmpType = getRegisterType(workRegs, insnRegCount, decInsn.vA, &failure); + if (VERIFY_OK(failure)) { RegType typeHi = - getRegisterType(workRegs, insnRegCount, decInsn.vA+1, &okay); - checkTypeCategory(tmpType, kTypeCategory2, &okay); - checkWidePair(tmpType, typeHi, &okay); + getRegisterType(workRegs, insnRegCount, decInsn.vA+1, &failure); + checkTypeCategory(tmpType, kTypeCategory2, &failure); + checkWidePair(tmpType, typeHi, &failure); } - if (!okay) + if (!VERIFY_OK(failure)) break; resClass = getClassFromRegister(workRegs, insnRegCount, - decInsn.vB, &okay); - if (!okay) + decInsn.vB, &failure); + if (!VERIFY_OK(failure)) break; if (resClass != NULL) { /* verify the class and try to refine "dstType" */ @@ -4050,7 +4341,7 @@ aput_1nr_common: { LOG_VFY("VFY: invalid aput-wide on %s\n", resClass->descriptor); - okay = false; + failure = VERIFY_ERROR_GENERIC; break; } @@ -4062,22 +4353,22 @@ aput_1nr_common: default: LOG_VFY("VFY: invalid aput-wide on %s\n", resClass->descriptor); - okay = false; + failure = VERIFY_ERROR_GENERIC; break; } } break; case OP_APUT_OBJECT: tmpType = getRegisterType(workRegs, insnRegCount, decInsn.vC, - &okay); - checkArrayIndexType(meth, tmpType, &okay); - if (!okay) + &failure); + checkArrayIndexType(meth, tmpType, &failure); + if (!VERIFY_OK(failure)) break; /* get the ref we're storing; Zero is okay, Uninit is not */ resClass = getClassFromRegister(workRegs, insnRegCount, - decInsn.vA, &okay); - if (!okay) + decInsn.vA, &failure); + if (!VERIFY_OK(failure)) break; if (resClass != NULL) { ClassObject* arrayClass; @@ -4089,14 +4380,14 @@ aput_1nr_common: * null pointer exception). */ arrayClass = getClassFromRegister(workRegs, insnRegCount, - decInsn.vB, &okay); + decInsn.vB, &failure); if (arrayClass != NULL) { /* see if the array holds a compatible type */ if (!dvmIsArrayClass(arrayClass)) { LOG_VFY("VFY: invalid aput-object on %s\n", arrayClass->descriptor); - okay = false; + failure = VERIFY_ERROR_GENERIC; break; } @@ -4121,7 +4412,7 @@ aput_1nr_common: if (elementClass->primitiveType != PRIM_NOT) { LOG_VFY("VFY: invalid aput-object of %s into %s\n", resClass->descriptor, arrayClass->descriptor); - okay = false; + failure = VERIFY_ERROR_GENERIC; break; } } @@ -4150,12 +4441,12 @@ iget_1nr_common: RegType objType, fieldType; objType = getRegisterType(workRegs, insnRegCount, decInsn.vB, - &okay); - if (!okay) + &failure); + if (!VERIFY_OK(failure)) break; instField = getInstField(meth, uninitMap, objType, decInsn.vC, - &okay); - if (!okay) + &failure); + if (!VERIFY_OK(failure)) break; /* make sure the field's type is compatible with expectation */ @@ -4166,11 +4457,12 @@ iget_1nr_common: LOG_VFY("VFY: invalid iget-1nr of %s.%s (inst=%d field=%d)\n", instField->field.clazz->descriptor, instField->field.name, tmpType, fieldType); - okay = false; + failure = VERIFY_ERROR_GENERIC; break; } - setRegisterType(workRegs, insnRegCount, decInsn.vA, tmpType, &okay); + setRegisterType(workRegs, insnRegCount, decInsn.vA, tmpType, + &failure); } break; case OP_IGET_WIDE: @@ -4181,12 +4473,12 @@ iget_1nr_common: RegType objType; objType = getRegisterType(workRegs, insnRegCount, decInsn.vB, - &okay); - if (!okay) + &failure); + if (!VERIFY_OK(failure)) break; instField = getInstField(meth, uninitMap, objType, decInsn.vC, - &okay); - if (!okay) + &failure); + if (!VERIFY_OK(failure)) break; /* check the type, which should be prim */ switch (instField->field.signature[0]) { @@ -4201,12 +4493,12 @@ iget_1nr_common: instField->field.clazz->descriptor, instField->field.name); dstType = kRegTypeUnknown; - okay = false; + failure = VERIFY_ERROR_GENERIC; break; } - if (okay) { + if (VERIFY_OK(failure)) { setRegisterType(workRegs, insnRegCount, decInsn.vA, - dstType, &okay); + dstType, &failure); } } break; @@ -4217,25 +4509,25 @@ iget_1nr_common: RegType objType; objType = getRegisterType(workRegs, insnRegCount, decInsn.vB, - &okay); - if (!okay) + &failure); + if (!VERIFY_OK(failure)) break; instField = getInstField(meth, uninitMap, objType, decInsn.vC, - &okay); - if (!okay) + &failure); + if (!VERIFY_OK(failure)) break; fieldClass = getFieldClass(meth, &instField->field); if (fieldClass == NULL) { /* class not found or primitive type */ LOG_VFY("VFY: unable to recover field class from '%s'\n", instField->field.signature); - okay = false; + failure = VERIFY_ERROR_GENERIC; break; } - if (okay) { + if (VERIFY_OK(failure)) { assert(!dvmIsPrimitiveClass(fieldClass)); setRegisterType(workRegs, insnRegCount, decInsn.vA, - regTypeFromClass(fieldClass), &okay); + regTypeFromClass(fieldClass), &failure); } } break; @@ -4260,26 +4552,34 @@ iput_1nr_common: ClassObject* fieldClass; InstField* instField; - /* make sure the source register has the correct type */ srcType = getRegisterType(workRegs, insnRegCount, decInsn.vA, - &okay); + &failure); + + /* + * javac generates synthetic functions that write byte values + * into boolean fields. + */ + if (tmpType == kRegTypeBoolean && srcType == kRegTypeByte) + srcType = kRegTypeBoolean; + + /* make sure the source register has the correct type */ if (!canConvertTo1nr(srcType, tmpType)) { LOG_VFY("VFY: invalid reg type %d on iput instr (need %d)\n", srcType, tmpType); - okay = false; + failure = VERIFY_ERROR_GENERIC; break; } objType = getRegisterType(workRegs, insnRegCount, decInsn.vB, - &okay); - if (!okay) + &failure); + if (!VERIFY_OK(failure)) break; instField = getInstField(meth, uninitMap, objType, decInsn.vC, - &okay); - if (!okay) + &failure); + if (!VERIFY_OK(failure)) break; - checkFinalFieldAccess(meth, &instField->field, &okay); - if (!okay) + checkFinalFieldAccess(meth, &instField->field, &failure); + if (!VERIFY_OK(failure)) break; /* get type of field we're storing into */ @@ -4290,34 +4590,34 @@ iput_1nr_common: LOG_VFY("VFY: invalid iput-1nr of %s.%s (inst=%d field=%d)\n", instField->field.clazz->descriptor, instField->field.name, tmpType, fieldType); - okay = false; + failure = VERIFY_ERROR_GENERIC; break; } } break; case OP_IPUT_WIDE: - tmpType = getRegisterType(workRegs, insnRegCount, decInsn.vA, &okay); - if (okay) { + tmpType = getRegisterType(workRegs, insnRegCount, decInsn.vA, &failure); + if (VERIFY_OK(failure)) { RegType typeHi = - getRegisterType(workRegs, insnRegCount, decInsn.vA+1, &okay); - checkTypeCategory(tmpType, kTypeCategory2, &okay); - checkWidePair(tmpType, typeHi, &okay); + getRegisterType(workRegs, insnRegCount, decInsn.vA+1, &failure); + checkTypeCategory(tmpType, kTypeCategory2, &failure); + checkWidePair(tmpType, typeHi, &failure); } - if (okay) { + if (VERIFY_OK(failure)) { ClassObject* fieldClass; InstField* instField; RegType objType; objType = getRegisterType(workRegs, insnRegCount, decInsn.vB, - &okay); - if (!okay) + &failure); + if (!VERIFY_OK(failure)) break; instField = getInstField(meth, uninitMap, objType, decInsn.vC, - &okay); - if (!okay) + &failure); + if (!VERIFY_OK(failure)) break; - checkFinalFieldAccess(meth, &instField->field, &okay); - if (!okay) + checkFinalFieldAccess(meth, &instField->field, &failure); + if (!VERIFY_OK(failure)) break; /* check the type, which should be prim */ @@ -4330,7 +4630,7 @@ iput_1nr_common: LOG_VFY("VFY: invalid iput-wide of %s.%s\n", instField->field.clazz->descriptor, instField->field.name); - okay = false; + failure = VERIFY_ERROR_GENERIC; break; } } @@ -4343,34 +4643,34 @@ iput_1nr_common: RegType objType, valueType; objType = getRegisterType(workRegs, insnRegCount, decInsn.vB, - &okay); - if (!okay) + &failure); + if (!VERIFY_OK(failure)) break; instField = getInstField(meth, uninitMap, objType, decInsn.vC, - &okay); - if (!okay) + &failure); + if (!VERIFY_OK(failure)) break; - checkFinalFieldAccess(meth, &instField->field, &okay); - if (!okay) + checkFinalFieldAccess(meth, &instField->field, &failure); + if (!VERIFY_OK(failure)) break; fieldClass = getFieldClass(meth, &instField->field); if (fieldClass == NULL) { LOG_VFY("VFY: unable to recover field class from '%s'\n", instField->field.signature); - okay = false; + failure = VERIFY_ERROR_GENERIC; break; } valueType = getRegisterType(workRegs, insnRegCount, decInsn.vA, - &okay); - if (!okay) + &failure); + if (!VERIFY_OK(failure)) break; if (!regTypeIsReference(valueType)) { LOG_VFY("VFY: storing non-ref v%d into ref field '%s' (%s)\n", decInsn.vA, instField->field.name, fieldClass->descriptor); - okay = false; + failure = VERIFY_ERROR_GENERIC; break; } if (valueType != kRegTypeZero) { @@ -4378,7 +4678,7 @@ iput_1nr_common: if (valueClass == NULL) { LOG_VFY("VFY: storing uninit ref v%d into ref field\n", decInsn.vA); - okay = false; + failure = VERIFY_ERROR_GENERIC; break; } /* allow if field is any interface or field is base class */ @@ -4389,7 +4689,7 @@ iput_1nr_common: valueClass->descriptor, fieldClass->descriptor, instField->field.clazz->descriptor, instField->field.name); - okay = false; + failure = VERIFY_ERROR_GENERIC; break; } } @@ -4416,8 +4716,8 @@ sget_1nr_common: StaticField* staticField; RegType fieldType; - staticField = getStaticField(meth, decInsn.vB, &okay); - if (!okay) + staticField = getStaticField(meth, decInsn.vB, &failure); + if (!VERIFY_OK(failure)) break; /* @@ -4433,11 +4733,12 @@ sget_1nr_common: LOG_VFY("VFY: invalid sget-1nr of %s.%s (inst=%d actual=%d)\n", staticField->field.clazz->descriptor, staticField->field.name, tmpType, fieldType); - okay = false; + failure = VERIFY_ERROR_GENERIC; break; } - setRegisterType(workRegs, insnRegCount, decInsn.vA, tmpType, &okay); + setRegisterType(workRegs, insnRegCount, decInsn.vA, tmpType, + &failure); } break; case OP_SGET_WIDE: @@ -4445,8 +4746,8 @@ sget_1nr_common: StaticField* staticField; RegType dstType; - staticField = getStaticField(meth, decInsn.vB, &okay); - if (!okay) + staticField = getStaticField(meth, decInsn.vB, &failure); + if (!VERIFY_OK(failure)) break; /* check the type, which should be prim */ switch (staticField->field.signature[0]) { @@ -4461,12 +4762,12 @@ sget_1nr_common: staticField->field.clazz->descriptor, staticField->field.name); dstType = kRegTypeUnknown; - okay = false; + failure = VERIFY_ERROR_GENERIC; break; } - if (okay) { + if (VERIFY_OK(failure)) { setRegisterType(workRegs, insnRegCount, decInsn.vA, - dstType, &okay); + dstType, &failure); } } break; @@ -4475,23 +4776,23 @@ sget_1nr_common: StaticField* staticField; ClassObject* fieldClass; - staticField = getStaticField(meth, decInsn.vB, &okay); - if (!okay) + staticField = getStaticField(meth, decInsn.vB, &failure); + if (!VERIFY_OK(failure)) break; fieldClass = getFieldClass(meth, &staticField->field); if (fieldClass == NULL) { LOG_VFY("VFY: unable to recover field class from '%s'\n", staticField->field.signature); - okay = false; + failure = VERIFY_ERROR_GENERIC; break; } if (dvmIsPrimitiveClass(fieldClass)) { LOG_VFY("VFY: attempt to get prim field with sget-object\n"); - okay = false; + failure = VERIFY_ERROR_GENERIC; break; } setRegisterType(workRegs, insnRegCount, decInsn.vA, - regTypeFromClass(fieldClass), &okay); + regTypeFromClass(fieldClass), &failure); } break; case OP_SPUT: @@ -4514,21 +4815,29 @@ sput_1nr_common: RegType srcType, fieldType; StaticField* staticField; - /* make sure the source register has the correct type */ srcType = getRegisterType(workRegs, insnRegCount, decInsn.vA, - &okay); + &failure); + + /* + * javac generates synthetic functions that write byte values + * into boolean fields. + */ + if (tmpType == kRegTypeBoolean && srcType == kRegTypeByte) + srcType = kRegTypeBoolean; + + /* make sure the source register has the correct type */ if (!canConvertTo1nr(srcType, tmpType)) { - LOG_VFY("VFY: invalid reg type %d on iput instr (need %d)\n", + LOG_VFY("VFY: invalid reg type %d on sput instr (need %d)\n", srcType, tmpType); - okay = false; + failure = VERIFY_ERROR_GENERIC; break; } - staticField = getStaticField(meth, decInsn.vB, &okay); - if (!okay) + staticField = getStaticField(meth, decInsn.vB, &failure); + if (!VERIFY_OK(failure)) break; - checkFinalFieldAccess(meth, &staticField->field, &okay); - if (!okay) + checkFinalFieldAccess(meth, &staticField->field, &failure); + if (!VERIFY_OK(failure)) break; /* @@ -4543,27 +4852,27 @@ sput_1nr_common: LOG_VFY("VFY: invalid sput-1nr of %s.%s (inst=%d actual=%d)\n", staticField->field.clazz->descriptor, staticField->field.name, tmpType, fieldType); - okay = false; + failure = VERIFY_ERROR_GENERIC; break; } } break; case OP_SPUT_WIDE: - tmpType = getRegisterType(workRegs, insnRegCount, decInsn.vA, &okay); - if (okay) { + tmpType = getRegisterType(workRegs, insnRegCount, decInsn.vA, &failure); + if (VERIFY_OK(failure)) { RegType typeHi = - getRegisterType(workRegs, insnRegCount, decInsn.vA+1, &okay); - checkTypeCategory(tmpType, kTypeCategory2, &okay); - checkWidePair(tmpType, typeHi, &okay); + getRegisterType(workRegs, insnRegCount, decInsn.vA+1, &failure); + checkTypeCategory(tmpType, kTypeCategory2, &failure); + checkWidePair(tmpType, typeHi, &failure); } - if (okay) { + if (VERIFY_OK(failure)) { StaticField* staticField; - staticField = getStaticField(meth, decInsn.vB, &okay); - if (!okay) + staticField = getStaticField(meth, decInsn.vB, &failure); + if (!VERIFY_OK(failure)) break; - checkFinalFieldAccess(meth, &staticField->field, &okay); - if (!okay) + checkFinalFieldAccess(meth, &staticField->field, &failure); + if (!VERIFY_OK(failure)) break; /* check the type, which should be prim */ @@ -4576,7 +4885,7 @@ sput_1nr_common: LOG_VFY("VFY: invalid sput-wide of %s.%s\n", staticField->field.clazz->descriptor, staticField->field.name); - okay = false; + failure = VERIFY_ERROR_GENERIC; break; } } @@ -4588,30 +4897,30 @@ sput_1nr_common: StaticField* staticField; RegType valueType; - staticField = getStaticField(meth, decInsn.vB, &okay); - if (!okay) + staticField = getStaticField(meth, decInsn.vB, &failure); + if (!VERIFY_OK(failure)) break; - checkFinalFieldAccess(meth, &staticField->field, &okay); - if (!okay) + checkFinalFieldAccess(meth, &staticField->field, &failure); + if (!VERIFY_OK(failure)) break; fieldClass = getFieldClass(meth, &staticField->field); if (fieldClass == NULL) { LOG_VFY("VFY: unable to recover field class from '%s'\n", staticField->field.signature); - okay = false; + failure = VERIFY_ERROR_GENERIC; break; } valueType = getRegisterType(workRegs, insnRegCount, decInsn.vA, - &okay); - if (!okay) + &failure); + if (!VERIFY_OK(failure)) break; if (!regTypeIsReference(valueType)) { LOG_VFY("VFY: storing non-ref v%d into ref field '%s' (%s)\n", decInsn.vA, staticField->field.name, fieldClass->descriptor); - okay = false; + failure = VERIFY_ERROR_GENERIC; break; } if (valueType != kRegTypeZero) { @@ -4619,7 +4928,7 @@ sput_1nr_common: if (valueClass == NULL) { LOG_VFY("VFY: storing uninit ref v%d into ref field\n", decInsn.vA); - okay = false; + failure = VERIFY_ERROR_GENERIC; break; } /* allow if field is any interface or field is base class */ @@ -4630,7 +4939,7 @@ sput_1nr_common: valueClass->descriptor, fieldClass->descriptor, staticField->field.clazz->descriptor, staticField->field.name); - okay = false; + failure = VERIFY_ERROR_GENERIC; break; } } @@ -4654,11 +4963,11 @@ sput_1nr_common: calledMethod = verifyInvocationArgs(meth, workRegs, insnRegCount, &decInsn, uninitMap, METHOD_VIRTUAL, isRange, - isSuper, &okay); - if (!okay) + isSuper, &failure); + if (!VERIFY_OK(failure)) break; returnType = getMethodReturnType(calledMethod); - setResultRegisterType(workRegs, insnRegCount, returnType, &okay); + setResultRegisterType(workRegs, insnRegCount, returnType, &failure); justSetResult = true; } break; @@ -4672,8 +4981,8 @@ sput_1nr_common: isRange = (decInsn.opCode == OP_INVOKE_DIRECT_RANGE); calledMethod = verifyInvocationArgs(meth, workRegs, insnRegCount, &decInsn, uninitMap, METHOD_DIRECT, isRange, - false, &okay); - if (!okay) + false, &failure); + if (!VERIFY_OK(failure)) break; /* @@ -4688,14 +4997,14 @@ sput_1nr_common: if (isInitMethod(calledMethod)) { RegType thisType; thisType = getInvocationThis(workRegs, insnRegCount, - &decInsn, &okay); - if (!okay) + &decInsn, &failure); + if (!VERIFY_OK(failure)) break; /* no null refs allowed (?) */ if (thisType == kRegTypeZero) { LOG_VFY("VFY: unable to initialize null ref\n"); - okay = false; + failure = VERIFY_ERROR_GENERIC; break; } @@ -4709,20 +5018,20 @@ sput_1nr_common: if (thisClass != meth->clazz) { LOG_VFY("VFY: invoke-direct on super only " "allowed for 'this' in "); - okay = false; + failure = VERIFY_ERROR_GENERIC; break; } } else if (calledMethod->clazz != thisClass) { LOG_VFY("VFY: invoke-direct must be on current " "class or super\n"); - okay = false; + failure = VERIFY_ERROR_GENERIC; break; } /* arg must be an uninitialized reference */ if (!regTypeIsUninitReference(thisType)) { LOG_VFY("VFY: can only initialize the uninitialized\n"); - okay = false; + failure = VERIFY_ERROR_GENERIC; break; } @@ -4734,13 +5043,13 @@ sput_1nr_common: */ int uidx = regTypeToUninitIndex(thisType); markRefsAsInitialized(workRegs, insnRegCount, uninitMap, - thisType, &okay); - if (!okay) + thisType, &failure); + if (!VERIFY_OK(failure)) break; } returnType = getMethodReturnType(calledMethod); setResultRegisterType(workRegs, insnRegCount, - returnType, &okay); + returnType, &failure); justSetResult = true; } break; @@ -4754,12 +5063,12 @@ sput_1nr_common: isRange = (decInsn.opCode == OP_INVOKE_STATIC_RANGE); calledMethod = verifyInvocationArgs(meth, workRegs, insnRegCount, &decInsn, uninitMap, METHOD_STATIC, isRange, - false, &okay); - if (!okay) + false, &failure); + if (!VERIFY_OK(failure)) break; returnType = getMethodReturnType(calledMethod); - setResultRegisterType(workRegs, insnRegCount, returnType, &okay); + setResultRegisterType(workRegs, insnRegCount, returnType, &failure); justSetResult = true; } break; @@ -4773,8 +5082,8 @@ sput_1nr_common: isRange = (decInsn.opCode == OP_INVOKE_INTERFACE_RANGE); absMethod = verifyInvocationArgs(meth, workRegs, insnRegCount, &decInsn, uninitMap, METHOD_INTERFACE, isRange, - false, &okay); - if (!okay) + false, &failure); + if (!VERIFY_OK(failure)) break; #if 0 /* can't do this here, fails on dalvik test 052-verifier-fun */ @@ -4784,8 +5093,8 @@ sput_1nr_common: * interface classes, this might have reduced to Object. */ thisType = getInvocationThis(workRegs, insnRegCount, - &decInsn, &okay); - if (!okay) + &decInsn, &failure); + if (!VERIFY_OK(failure)) break; if (thisType == kRegTypeZero) { @@ -4796,7 +5105,7 @@ sput_1nr_common: thisClass = regTypeInitializedReferenceToClass(thisType); if (thisClass == NULL) { LOG_VFY("VFY: interface call on uninitialized\n"); - okay = false; + failure = VERIFY_ERROR_GENERIC; break; } @@ -4812,7 +5121,7 @@ sput_1nr_common: { LOG_VFY("VFY: unable to match absMethod '%s' with %s interfaces\n", absMethod->name, thisClass->descriptor); - okay = false; + failure = VERIFY_ERROR_GENERIC; break; } } @@ -4824,7 +5133,7 @@ sput_1nr_common: * in the abstract method, so we're good. */ returnType = getMethodReturnType(absMethod); - setResultRegisterType(workRegs, insnRegCount, returnType, &okay); + setResultRegisterType(workRegs, insnRegCount, returnType, &failure); justSetResult = true; } break; @@ -4832,80 +5141,80 @@ sput_1nr_common: case OP_NEG_INT: case OP_NOT_INT: checkUnop(workRegs, insnRegCount, &decInsn, - kRegTypeInteger, kRegTypeInteger, &okay); + kRegTypeInteger, kRegTypeInteger, &failure); break; case OP_NEG_LONG: case OP_NOT_LONG: checkUnop(workRegs, insnRegCount, &decInsn, - kRegTypeLongLo, kRegTypeLongLo, &okay); + kRegTypeLongLo, kRegTypeLongLo, &failure); break; case OP_NEG_FLOAT: checkUnop(workRegs, insnRegCount, &decInsn, - kRegTypeFloat, kRegTypeFloat, &okay); + kRegTypeFloat, kRegTypeFloat, &failure); break; case OP_NEG_DOUBLE: checkUnop(workRegs, insnRegCount, &decInsn, - kRegTypeDoubleLo, kRegTypeDoubleLo, &okay); + kRegTypeDoubleLo, kRegTypeDoubleLo, &failure); break; case OP_INT_TO_LONG: checkUnop(workRegs, insnRegCount, &decInsn, - kRegTypeLongLo, kRegTypeInteger, &okay); + kRegTypeLongLo, kRegTypeInteger, &failure); break; case OP_INT_TO_FLOAT: checkUnop(workRegs, insnRegCount, &decInsn, - kRegTypeFloat, kRegTypeInteger, &okay); + kRegTypeFloat, kRegTypeInteger, &failure); break; case OP_INT_TO_DOUBLE: checkUnop(workRegs, insnRegCount, &decInsn, - kRegTypeDoubleLo, kRegTypeInteger, &okay); + kRegTypeDoubleLo, kRegTypeInteger, &failure); break; case OP_LONG_TO_INT: checkUnop(workRegs, insnRegCount, &decInsn, - kRegTypeInteger, kRegTypeLongLo, &okay); + kRegTypeInteger, kRegTypeLongLo, &failure); break; case OP_LONG_TO_FLOAT: checkUnop(workRegs, insnRegCount, &decInsn, - kRegTypeFloat, kRegTypeLongLo, &okay); + kRegTypeFloat, kRegTypeLongLo, &failure); break; case OP_LONG_TO_DOUBLE: checkUnop(workRegs, insnRegCount, &decInsn, - kRegTypeDoubleLo, kRegTypeLongLo, &okay); + kRegTypeDoubleLo, kRegTypeLongLo, &failure); break; case OP_FLOAT_TO_INT: checkUnop(workRegs, insnRegCount, &decInsn, - kRegTypeInteger, kRegTypeFloat, &okay); + kRegTypeInteger, kRegTypeFloat, &failure); break; case OP_FLOAT_TO_LONG: checkUnop(workRegs, insnRegCount, &decInsn, - kRegTypeLongLo, kRegTypeFloat, &okay); + kRegTypeLongLo, kRegTypeFloat, &failure); break; case OP_FLOAT_TO_DOUBLE: checkUnop(workRegs, insnRegCount, &decInsn, - kRegTypeDoubleLo, kRegTypeFloat, &okay); + kRegTypeDoubleLo, kRegTypeFloat, &failure); break; case OP_DOUBLE_TO_INT: checkUnop(workRegs, insnRegCount, &decInsn, - kRegTypeInteger, kRegTypeDoubleLo, &okay); + kRegTypeInteger, kRegTypeDoubleLo, &failure); break; case OP_DOUBLE_TO_LONG: checkUnop(workRegs, insnRegCount, &decInsn, - kRegTypeLongLo, kRegTypeDoubleLo, &okay); + kRegTypeLongLo, kRegTypeDoubleLo, &failure); break; case OP_DOUBLE_TO_FLOAT: checkUnop(workRegs, insnRegCount, &decInsn, - kRegTypeFloat, kRegTypeDoubleLo, &okay); + kRegTypeFloat, kRegTypeDoubleLo, &failure); break; case OP_INT_TO_BYTE: checkUnop(workRegs, insnRegCount, &decInsn, - kRegTypeByte, kRegTypeInteger, &okay); + kRegTypeByte, kRegTypeInteger, &failure); break; case OP_INT_TO_CHAR: checkUnop(workRegs, insnRegCount, &decInsn, - kRegTypeChar, kRegTypeInteger, &okay); + kRegTypeChar, kRegTypeInteger, &failure); break; case OP_INT_TO_SHORT: checkUnop(workRegs, insnRegCount, &decInsn, - kRegTypeShort, kRegTypeInteger, &okay); + kRegTypeShort, kRegTypeInteger, &failure); break; case OP_ADD_INT: @@ -4917,13 +5226,13 @@ sput_1nr_common: case OP_SHR_INT: case OP_USHR_INT: checkBinop(workRegs, insnRegCount, &decInsn, - kRegTypeInteger, kRegTypeInteger, kRegTypeInteger, false, &okay); + kRegTypeInteger, kRegTypeInteger, kRegTypeInteger, false, &failure); break; case OP_AND_INT: case OP_OR_INT: case OP_XOR_INT: checkBinop(workRegs, insnRegCount, &decInsn, - kRegTypeInteger, kRegTypeInteger, kRegTypeInteger, true, &okay); + kRegTypeInteger, kRegTypeInteger, kRegTypeInteger, true, &failure); break; case OP_ADD_LONG: case OP_SUB_LONG: @@ -4934,14 +5243,14 @@ sput_1nr_common: case OP_OR_LONG: case OP_XOR_LONG: checkBinop(workRegs, insnRegCount, &decInsn, - kRegTypeLongLo, kRegTypeLongLo, kRegTypeLongLo, false, &okay); + kRegTypeLongLo, kRegTypeLongLo, kRegTypeLongLo, false, &failure); break; case OP_SHL_LONG: case OP_SHR_LONG: case OP_USHR_LONG: /* shift distance is Int, making these different from other binops */ checkBinop(workRegs, insnRegCount, &decInsn, - kRegTypeLongLo, kRegTypeLongLo, kRegTypeInteger, false, &okay); + kRegTypeLongLo, kRegTypeLongLo, kRegTypeInteger, false, &failure); break; case OP_ADD_FLOAT: case OP_SUB_FLOAT: @@ -4949,7 +5258,7 @@ sput_1nr_common: case OP_DIV_FLOAT: case OP_REM_FLOAT: checkBinop(workRegs, insnRegCount, &decInsn, - kRegTypeFloat, kRegTypeFloat, kRegTypeFloat, false, &okay); + kRegTypeFloat, kRegTypeFloat, kRegTypeFloat, false, &failure); break; case OP_ADD_DOUBLE: case OP_SUB_DOUBLE: @@ -4957,7 +5266,8 @@ sput_1nr_common: case OP_DIV_DOUBLE: case OP_REM_DOUBLE: checkBinop(workRegs, insnRegCount, &decInsn, - kRegTypeDoubleLo, kRegTypeDoubleLo, kRegTypeDoubleLo, false, &okay); + kRegTypeDoubleLo, kRegTypeDoubleLo, kRegTypeDoubleLo, false, + &failure); break; case OP_ADD_INT_2ADDR: case OP_SUB_INT_2ADDR: @@ -4967,17 +5277,17 @@ sput_1nr_common: case OP_SHR_INT_2ADDR: case OP_USHR_INT_2ADDR: checkBinop2addr(workRegs, insnRegCount, &decInsn, - kRegTypeInteger, kRegTypeInteger, kRegTypeInteger, false, &okay); + kRegTypeInteger, kRegTypeInteger, kRegTypeInteger, false, &failure); break; case OP_AND_INT_2ADDR: case OP_OR_INT_2ADDR: case OP_XOR_INT_2ADDR: checkBinop2addr(workRegs, insnRegCount, &decInsn, - kRegTypeInteger, kRegTypeInteger, kRegTypeInteger, true, &okay); + kRegTypeInteger, kRegTypeInteger, kRegTypeInteger, true, &failure); break; case OP_DIV_INT_2ADDR: checkBinop2addr(workRegs, insnRegCount, &decInsn, - kRegTypeInteger, kRegTypeInteger, kRegTypeInteger, false, &okay); + kRegTypeInteger, kRegTypeInteger, kRegTypeInteger, false, &failure); break; case OP_ADD_LONG_2ADDR: case OP_SUB_LONG_2ADDR: @@ -4988,13 +5298,13 @@ sput_1nr_common: case OP_OR_LONG_2ADDR: case OP_XOR_LONG_2ADDR: checkBinop2addr(workRegs, insnRegCount, &decInsn, - kRegTypeLongLo, kRegTypeLongLo, kRegTypeLongLo, false, &okay); + kRegTypeLongLo, kRegTypeLongLo, kRegTypeLongLo, false, &failure); break; case OP_SHL_LONG_2ADDR: case OP_SHR_LONG_2ADDR: case OP_USHR_LONG_2ADDR: checkBinop2addr(workRegs, insnRegCount, &decInsn, - kRegTypeLongLo, kRegTypeLongLo, kRegTypeInteger, false, &okay); + kRegTypeLongLo, kRegTypeLongLo, kRegTypeInteger, false, &failure); break; case OP_ADD_FLOAT_2ADDR: case OP_SUB_FLOAT_2ADDR: @@ -5002,7 +5312,7 @@ sput_1nr_common: case OP_DIV_FLOAT_2ADDR: case OP_REM_FLOAT_2ADDR: checkBinop2addr(workRegs, insnRegCount, &decInsn, - kRegTypeFloat, kRegTypeFloat, kRegTypeFloat, false, &okay); + kRegTypeFloat, kRegTypeFloat, kRegTypeFloat, false, &failure); break; case OP_ADD_DOUBLE_2ADDR: case OP_SUB_DOUBLE_2ADDR: @@ -5010,7 +5320,8 @@ sput_1nr_common: case OP_DIV_DOUBLE_2ADDR: case OP_REM_DOUBLE_2ADDR: checkBinop2addr(workRegs, insnRegCount, &decInsn, - kRegTypeDoubleLo, kRegTypeDoubleLo, kRegTypeDoubleLo, false, &okay); + kRegTypeDoubleLo, kRegTypeDoubleLo, kRegTypeDoubleLo, false, + &failure); break; case OP_ADD_INT_LIT16: case OP_RSUB_INT: @@ -5018,13 +5329,13 @@ sput_1nr_common: case OP_DIV_INT_LIT16: case OP_REM_INT_LIT16: checkLitop(workRegs, insnRegCount, &decInsn, - kRegTypeInteger, kRegTypeInteger, false, &okay); + kRegTypeInteger, kRegTypeInteger, false, &failure); break; case OP_AND_INT_LIT16: case OP_OR_INT_LIT16: case OP_XOR_INT_LIT16: checkLitop(workRegs, insnRegCount, &decInsn, - kRegTypeInteger, kRegTypeInteger, true, &okay); + kRegTypeInteger, kRegTypeInteger, true, &failure); break; case OP_ADD_INT_LIT8: case OP_RSUB_INT_LIT8: @@ -5032,18 +5343,35 @@ sput_1nr_common: case OP_DIV_INT_LIT8: case OP_REM_INT_LIT8: case OP_SHL_INT_LIT8: + checkLitop(workRegs, insnRegCount, &decInsn, + kRegTypeInteger, kRegTypeInteger, false, &failure); + break; case OP_SHR_INT_LIT8: + tmpType = adjustForRightShift(workRegs, insnRegCount, + decInsn.vB, decInsn.vC, false, &failure); + checkLitop(workRegs, insnRegCount, &decInsn, + tmpType, kRegTypeInteger, false, &failure); + break; case OP_USHR_INT_LIT8: + tmpType = adjustForRightShift(workRegs, insnRegCount, + decInsn.vB, decInsn.vC, true, &failure); checkLitop(workRegs, insnRegCount, &decInsn, - kRegTypeInteger, kRegTypeInteger, false, &okay); + tmpType, kRegTypeInteger, false, &failure); break; case OP_AND_INT_LIT8: case OP_OR_INT_LIT8: case OP_XOR_INT_LIT8: checkLitop(workRegs, insnRegCount, &decInsn, - kRegTypeInteger, kRegTypeInteger, true, &okay); + kRegTypeInteger, kRegTypeInteger, true, &failure); break; + /* + * This falls into the general category of "optimized" instructions, + * which don't generally appear during verification. Because it's + * inserted in the course of verification, we can expect to see it here. + */ + case OP_THROW_VERIFICATION_ERROR: + break; /* * Verifying "quickened" instructions is tricky, because we have @@ -5089,7 +5417,7 @@ sput_1nr_common: case OP_INVOKE_VIRTUAL_QUICK_RANGE: case OP_INVOKE_SUPER_QUICK: case OP_INVOKE_SUPER_QUICK_RANGE: - okay = false; + failure = VERIFY_ERROR_GENERIC; break; /* these should never appear */ @@ -5112,14 +5440,13 @@ sput_1nr_common: case OP_UNUSED_EA: case OP_UNUSED_EB: case OP_UNUSED_EC: - case OP_UNUSED_ED: case OP_UNUSED_EF: case OP_UNUSED_F1: case OP_UNUSED_FC: case OP_UNUSED_FD: case OP_UNUSED_FE: case OP_UNUSED_FF: - okay = false; + failure = VERIFY_ERROR_GENERIC; break; /* @@ -5128,10 +5455,28 @@ sput_1nr_common: */ } - if (!okay) { - LOG_VFY_METH(meth, "VFY: rejecting opcode 0x%02x at 0x%04x\n", - decInsn.opCode, insnIdx); - goto bail; + if (!VERIFY_OK(failure)) { + if (failure == VERIFY_ERROR_GENERIC || gDvm.optimizing) { + /* immediate failure, reject class */ + LOG_VFY_METH(meth, "VFY: rejecting opcode 0x%02x at 0x%04x\n", + decInsn.opCode, insnIdx); + goto bail; + } else { + /* replace opcode and continue on */ + LOGD("VFY: replacing opcode 0x%02x at 0x%04x\n", + decInsn.opCode, insnIdx); + if (!replaceFailingInstruction(meth, insnFlags, insnIdx, failure)) { + LOG_VFY_METH(meth, "VFY: rejecting opcode 0x%02x at 0x%04x\n", + decInsn.opCode, insnIdx); + goto bail; + } + /* IMPORTANT: meth->insns may have been changed */ + insns = meth->insns + insnIdx; + + /* continue on as if we just handled a throw-verification-error */ + failure = VERIFY_ERROR_NONE; + nextFlags = kInstrCanThrow; + } } /* @@ -5206,6 +5551,7 @@ sput_1nr_common: if (!checkMoveException(meth, insnIdx+branchTarget, "branch")) goto bail; + /* update branch target, set "changed" if appropriate */ updateRegisters(meth, insnFlags, regTable, insnIdx+branchTarget, workRegs); } @@ -5297,6 +5643,7 @@ bail: return result; } + /* * callback function used in dumpRegTypes to print local vars * valid at a given address. diff --git a/vm/analysis/CodeVerify.h b/vm/analysis/CodeVerify.h index 0cd463895..1b93655e1 100644 --- a/vm/analysis/CodeVerify.h +++ b/vm/analysis/CodeVerify.h @@ -198,10 +198,10 @@ INLINE bool dvmInsnIsGcPoint(const InsnFlags* insnFlags, int addr) { return (insnFlags[addr] & kInsnFlagGcPoint) != 0; } INLINE void dvmInsnSetGcPoint(InsnFlags* insnFlags, int addr, - bool isBranch) + bool isGcPoint) { - assert(isBranch); - //if (isBranch) + assert(isGcPoint); + //if (isGcPoint) insnFlags[addr] |= kInsnFlagGcPoint; //else // insnFlags[addr] &= ~kInsnFlagGcPoint; @@ -259,7 +259,7 @@ void dvmFreeUninitInstanceMap(UninitInstanceMap* uninitMap); * Verify bytecode in "meth". "insnFlags" should be populated with * instruction widths and "in try" flags. */ -bool dvmVerifyCodeFlow(const Method* meth, InsnFlags* insnFlags, +bool dvmVerifyCodeFlow(Method* meth, InsnFlags* insnFlags, UninitInstanceMap* uninitMap); #endif /*_DALVIK_CODEVERIFY*/ diff --git a/vm/analysis/DexOptimize.c b/vm/analysis/DexOptimize.c index d086b998f..b3e2d40c1 100644 --- a/vm/analysis/DexOptimize.c +++ b/vm/analysis/DexOptimize.c @@ -25,6 +25,7 @@ #include "Dalvik.h" #include "libdex/InstrUtils.h" #include "libdex/OptInvocation.h" +#include "analysis/RegisterMap.h" #include @@ -50,7 +51,7 @@ typedef struct InlineSub { /* fwd */ static int writeDependencies(int fd, u4 modWhen, u4 crc); static bool writeAuxData(int fd, const DexClassLookup* pClassLookup,\ - const IndexMapSet* pIndexMapSet); + const IndexMapSet* pIndexMapSet, const RegisterMapBuilder* pRegMapBuilder); static void logFailedWrite(size_t expected, ssize_t actual, const char* msg, int err); @@ -365,6 +366,9 @@ bool dvmOptimizeDexFile(int fd, off_t dexOffset, long dexLength, char* androidRoot; int flags; + /* change process groups, so we don't clash with ProcessManager */ + setpgid(0, 0); + /* full path to optimizer */ androidRoot = getenv("ANDROID_ROOT"); if (androidRoot == NULL) { @@ -506,6 +510,7 @@ bool dvmContinueOptimization(int fd, off_t dexOffset, long dexLength, { DexClassLookup* pClassLookup = NULL; IndexMapSet* pIndexMapSet = NULL; + RegisterMapBuilder* pRegMapBuilder = NULL; bool doVerify, doOpt; u4 headerFlags = 0; @@ -566,6 +571,13 @@ bool dvmContinueOptimization(int fd, off_t dexOffset, long dexLength, * Rewrite the file. Byte reordering, structure realigning, * class verification, and bytecode optimization are all performed * here. + * + * In theory the file could change size and bits could shift around. + * In practice this would be annoying to deal with, so the file + * layout is designed so that it can always be rewritten in place. + * + * This sets "headerFlags" and creates the class lookup table as + * part of doing the processing. */ success = rewriteDex(((u1*) mapAddr) + dexOffset, dexLength, doVerify, doOpt, &headerFlags, &pClassLookup); @@ -576,6 +588,7 @@ bool dvmContinueOptimization(int fd, off_t dexOffset, long dexLength, if (dvmDexFileOpenPartial(dexAddr, dexLength, &pDvmDex) != 0) { LOGE("Unable to create DexFile\n"); + success = false; } else { /* * If configured to do so, scan the instructions, looking @@ -586,8 +599,20 @@ bool dvmContinueOptimization(int fd, off_t dexOffset, long dexLength, */ pIndexMapSet = dvmRewriteConstants(pDvmDex); - updateChecksum(dexAddr, dexLength, - (DexHeader*) pDvmDex->pHeader); + /* + * If configured to do so, generate a full set of register + * maps for all verified classes. + */ + if (gDvm.generateRegisterMaps) { + pRegMapBuilder = dvmGenerateRegisterMaps(pDvmDex); + if (pRegMapBuilder == NULL) { + LOGE("Failed generating register maps\n"); + success = false; + } + } + + DexHeader* pHeader = (DexHeader*)pDvmDex->pHeader; + updateChecksum(dexAddr, dexLength, pHeader); dvmDexFileFree(pDvmDex); } @@ -640,8 +665,7 @@ bool dvmContinueOptimization(int fd, off_t dexOffset, long dexLength, goto bail; } - - /* compute deps length, and adjust aux start for 64-bit alignment */ + /* compute deps length, then adjust aux start for 64-bit alignment */ auxOffset = lseek(fd, 0, SEEK_END); depsLength = auxOffset - depsOffset; @@ -656,7 +680,7 @@ bool dvmContinueOptimization(int fd, off_t dexOffset, long dexLength, /* * Append any auxillary pre-computed data structures. */ - if (!writeAuxData(fd, pClassLookup, pIndexMapSet)) { + if (!writeAuxData(fd, pClassLookup, pIndexMapSet, pRegMapBuilder)) { LOGW("Failed writing aux data\n"); goto bail; } @@ -692,8 +716,11 @@ bool dvmContinueOptimization(int fd, off_t dexOffset, long dexLength, LOGV("Successfully wrote DEX header\n"); result = true; + //dvmRegisterMapDumpStats(); + bail: dvmFreeIndexMapSet(pIndexMapSet); + dvmFreeRegisterMapBuilder(pRegMapBuilder); free(pClassLookup); return result; } @@ -888,7 +915,8 @@ bool dvmCheckOptHeaderAndDependencies(int fd, bool sourceAvail, u4 modWhen, } val = read4LE(&ptr); if (val != DALVIK_VM_BUILD) { - LOGI("DexOpt: VM build mismatch (%d vs %d)\n", val, DALVIK_VM_BUILD); + LOGD("DexOpt: VM build version mismatch (%d vs %d)\n", + val, DALVIK_VM_BUILD); goto bail; } @@ -1085,19 +1113,28 @@ static bool writeChunk(int fd, u4 type, const void* data, size_t size) * so it can be used directly when the file is mapped for reading. */ static bool writeAuxData(int fd, const DexClassLookup* pClassLookup, - const IndexMapSet* pIndexMapSet) + const IndexMapSet* pIndexMapSet, const RegisterMapBuilder* pRegMapBuilder) { /* pre-computed class lookup hash table */ - if (!writeChunk(fd, (u4) kDexChunkClassLookup, pClassLookup, - pClassLookup->size)) + if (!writeChunk(fd, (u4) kDexChunkClassLookup, + pClassLookup, pClassLookup->size)) { return false; } /* remapped constants (optional) */ if (pIndexMapSet != NULL) { - if (!writeChunk(fd, pIndexMapSet->chunkType, pIndexMapSet->chunkData, - pIndexMapSet->chunkDataLen)) + if (!writeChunk(fd, pIndexMapSet->chunkType, + pIndexMapSet->chunkData, pIndexMapSet->chunkDataLen)) + { + return false; + } + } + + /* register maps (optional) */ + if (pRegMapBuilder != NULL) { + if (!writeChunk(fd, (u4) kDexChunkRegisterMaps, + pRegMapBuilder->data, pRegMapBuilder->size)) { return false; } @@ -1622,8 +1659,11 @@ static void untweakLoader(ClassObject* referrer, ClassObject* resClass) * file. * * Exceptions caused by failures are cleared before returning. + * + * On failure, returns NULL, and sets *pFailure if pFailure is not NULL. */ -ClassObject* dvmOptResolveClass(ClassObject* referrer, u4 classIdx) +ClassObject* dvmOptResolveClass(ClassObject* referrer, u4 classIdx, + VerifyError* pFailure) { DvmDex* pDvmDex = referrer->pDvmDex; ClassObject* resClass; @@ -1645,6 +1685,23 @@ ClassObject* dvmOptResolveClass(ClassObject* referrer, u4 classIdx) LOGV("DexOpt: class %d (%s) not found\n", classIdx, dexStringByTypeIdx(pDvmDex->pDexFile, classIdx)); + if (pFailure != NULL) { + /* dig through the wrappers to find the original failure */ + Object* excep = dvmGetException(dvmThreadSelf()); + while (true) { + Object* cause = dvmGetExceptionCause(excep); + if (cause == NULL) + break; + excep = cause; + } + if (strcmp(excep->clazz->descriptor, + "Ljava/lang/IncompatibleClassChangeError;") == 0) + { + *pFailure = VERIFY_ERROR_CLASS_CHANGE; + } else { + *pFailure = VERIFY_ERROR_NO_CLASS; + } + } dvmClearOptException(dvmThreadSelf()); return NULL; } @@ -1659,6 +1716,8 @@ ClassObject* dvmOptResolveClass(ClassObject* referrer, u4 classIdx) if (IS_CLASS_FLAG_SET(resClass, CLASS_MULTIPLE_DEFS)) { LOGI("DexOpt: not resolving ambiguous class '%s'\n", resClass->descriptor); + if (pFailure != NULL) + *pFailure = VERIFY_ERROR_NO_CLASS; return NULL; } @@ -1669,6 +1728,8 @@ ClassObject* dvmOptResolveClass(ClassObject* referrer, u4 classIdx) if (!allowed) { LOGW("DexOpt: resolve class illegal access: %s -> %s\n", referrer->descriptor, resClass->descriptor); + if (pFailure != NULL) + *pFailure = VERIFY_ERROR_ACCESS_CLASS; return NULL; } @@ -1677,8 +1738,11 @@ ClassObject* dvmOptResolveClass(ClassObject* referrer, u4 classIdx) /* * Alternate version of dvmResolveInstField(). + * + * On failure, returns NULL, and sets *pFailure if pFailure is not NULL. */ -InstField* dvmOptResolveInstField(ClassObject* referrer, u4 ifieldIdx) +InstField* dvmOptResolveInstField(ClassObject* referrer, u4 ifieldIdx, + VerifyError* pFailure) { DvmDex* pDvmDex = referrer->pDvmDex; InstField* resField; @@ -1693,20 +1757,31 @@ InstField* dvmOptResolveInstField(ClassObject* referrer, u4 ifieldIdx) /* * Find the field's class. */ - resClass = dvmOptResolveClass(referrer, pFieldId->classIdx); + resClass = dvmOptResolveClass(referrer, pFieldId->classIdx, pFailure); if (resClass == NULL) { //dvmClearOptException(dvmThreadSelf()); assert(!dvmCheckException(dvmThreadSelf())); + if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); } return NULL; } - resField = dvmFindInstanceFieldHier(resClass, + resField = (InstField*)dvmFindFieldHier(resClass, dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx), dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx)); if (resField == NULL) { LOGD("DexOpt: couldn't find field %s.%s\n", resClass->descriptor, dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx)); + if (pFailure != NULL) + *pFailure = VERIFY_ERROR_NO_FIELD; + return NULL; + } + if (dvmIsStaticField(&resField->field)) { + LOGD("DexOpt: wanted instance, got static for field %s.%s\n", + resClass->descriptor, + dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx)); + if (pFailure != NULL) + *pFailure = VERIFY_ERROR_CLASS_CHANGE; return NULL; } @@ -1724,6 +1799,8 @@ InstField* dvmOptResolveInstField(ClassObject* referrer, u4 ifieldIdx) LOGI("DexOpt: access denied from %s to field %s.%s\n", referrer->descriptor, resField->field.clazz->descriptor, resField->field.name); + if (pFailure != NULL) + *pFailure = VERIFY_ERROR_ACCESS_FIELD; return NULL; } @@ -1734,8 +1811,11 @@ InstField* dvmOptResolveInstField(ClassObject* referrer, u4 ifieldIdx) * Alternate version of dvmResolveStaticField(). * * Does not force initialization of the resolved field's class. + * + * On failure, returns NULL, and sets *pFailure if pFailure is not NULL. */ -StaticField* dvmOptResolveStaticField(ClassObject* referrer, u4 sfieldIdx) +StaticField* dvmOptResolveStaticField(ClassObject* referrer, u4 sfieldIdx, + VerifyError* pFailure) { DvmDex* pDvmDex = referrer->pDvmDex; StaticField* resField; @@ -1750,18 +1830,29 @@ StaticField* dvmOptResolveStaticField(ClassObject* referrer, u4 sfieldIdx) /* * Find the field's class. */ - resClass = dvmOptResolveClass(referrer, pFieldId->classIdx); + resClass = dvmOptResolveClass(referrer, pFieldId->classIdx, pFailure); if (resClass == NULL) { //dvmClearOptException(dvmThreadSelf()); assert(!dvmCheckException(dvmThreadSelf())); + if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); } return NULL; } - resField = dvmFindStaticFieldHier(resClass, + resField = (StaticField*)dvmFindFieldHier(resClass, dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx), dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx)); if (resField == NULL) { LOGD("DexOpt: couldn't find static field\n"); + if (pFailure != NULL) + *pFailure = VERIFY_ERROR_NO_FIELD; + return NULL; + } + if (!dvmIsStaticField(&resField->field)) { + LOGD("DexOpt: wanted static, got instance for field %s.%s\n", + resClass->descriptor, + dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx)); + if (pFailure != NULL) + *pFailure = VERIFY_ERROR_CLASS_CHANGE; return NULL; } @@ -1784,6 +1875,8 @@ StaticField* dvmOptResolveStaticField(ClassObject* referrer, u4 sfieldIdx) LOGI("DexOpt: access denied from %s to field %s.%s\n", referrer->descriptor, resField->field.clazz->descriptor, resField->field.name); + if (pFailure != NULL) + *pFailure = VERIFY_ERROR_ACCESS_FIELD; return NULL; } @@ -1809,7 +1902,7 @@ static void rewriteInstField(Method* method, u2* insns, OpCode newOpc) InstField* field; int byteOffset; - field = dvmOptResolveInstField(clazz, fieldIdx); + field = dvmOptResolveInstField(clazz, fieldIdx, NULL); if (field == NULL) { LOGI("DexOpt: unable to optimize field ref 0x%04x at 0x%02x in %s.%s\n", fieldIdx, (int) (insns - method->insns), clazz->descriptor, @@ -1833,14 +1926,18 @@ static void rewriteInstField(Method* method, u2* insns, OpCode newOpc) * Alternate version of dvmResolveMethod(). * * Doesn't throw exceptions, and checks access on every lookup. + * + * On failure, returns NULL, and sets *pFailure if pFailure is not NULL. */ Method* dvmOptResolveMethod(ClassObject* referrer, u4 methodIdx, - MethodType methodType) + MethodType methodType, VerifyError* pFailure) { DvmDex* pDvmDex = referrer->pDvmDex; Method* resMethod; - assert(methodType != METHOD_INTERFACE); + assert(methodType == METHOD_DIRECT || + methodType == METHOD_VIRTUAL || + methodType == METHOD_STATIC); LOGVV("--- resolving method %u (referrer=%s)\n", methodIdx, referrer->descriptor); @@ -1852,16 +1949,22 @@ Method* dvmOptResolveMethod(ClassObject* referrer, u4 methodIdx, pMethodId = dexGetMethodId(pDvmDex->pDexFile, methodIdx); - resClass = dvmOptResolveClass(referrer, pMethodId->classIdx); + resClass = dvmOptResolveClass(referrer, pMethodId->classIdx, pFailure); if (resClass == NULL) { - /* can't find the class that the method is a part of */ + /* + * Can't find the class that the method is a part of, or don't + * have permission to access the class. + */ LOGV("DexOpt: can't find called method's class (?.%s)\n", dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx)); + if (pFailure != NULL) { assert(!VERIFY_OK(*pFailure)); } return NULL; } if (dvmIsInterfaceClass(resClass)) { /* method is part of an interface; this is wrong method for that */ LOGW("DexOpt: method is in an interface\n"); + if (pFailure != NULL) + *pFailure = VERIFY_ERROR_GENERIC; return NULL; } @@ -1876,25 +1979,44 @@ Method* dvmOptResolveMethod(ClassObject* referrer, u4 methodIdx, if (methodType == METHOD_DIRECT) { resMethod = dvmFindDirectMethod(resClass, dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx), &proto); - } else if (methodType == METHOD_STATIC) { - resMethod = dvmFindDirectMethodHier(resClass, - dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx), &proto); } else { - resMethod = dvmFindVirtualMethodHier(resClass, + /* METHOD_STATIC or METHOD_VIRTUAL */ + resMethod = dvmFindMethodHier(resClass, dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx), &proto); } if (resMethod == NULL) { LOGV("DexOpt: couldn't find method '%s'\n", dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx)); + if (pFailure != NULL) + *pFailure = VERIFY_ERROR_NO_METHOD; return NULL; } + if (methodType == METHOD_STATIC) { + if (!dvmIsStaticMethod(resMethod)) { + LOGD("DexOpt: wanted static, got instance for method %s.%s\n", + resClass->descriptor, resMethod->name); + if (pFailure != NULL) + *pFailure = VERIFY_ERROR_CLASS_CHANGE; + return NULL; + } + } else if (methodType == METHOD_VIRTUAL) { + if (dvmIsStaticMethod(resMethod)) { + LOGD("DexOpt: wanted instance, got static for method %s.%s\n", + resClass->descriptor, resMethod->name); + if (pFailure != NULL) + *pFailure = VERIFY_ERROR_CLASS_CHANGE; + return NULL; + } + } /* see if this is a pure-abstract method */ if (dvmIsAbstractMethod(resMethod) && !dvmIsAbstractClass(resClass)) { LOGW("DexOpt: pure-abstract method '%s' in %s\n", dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx), resClass->descriptor); + if (pFailure != NULL) + *pFailure = VERIFY_ERROR_GENERIC; return NULL; } @@ -1925,6 +2047,8 @@ Method* dvmOptResolveMethod(ClassObject* referrer, u4 methodIdx, referrer->descriptor); free(desc); } + if (pFailure != NULL) + *pFailure = VERIFY_ERROR_ACCESS_METHOD; return NULL; } @@ -1945,7 +2069,7 @@ static bool rewriteVirtualInvoke(Method* method, u2* insns, OpCode newOpc) Method* baseMethod; u2 methodIdx = insns[1]; - baseMethod = dvmOptResolveMethod(clazz, methodIdx, METHOD_VIRTUAL); + baseMethod = dvmOptResolveMethod(clazz, methodIdx, METHOD_VIRTUAL, NULL); if (baseMethod == NULL) { LOGD("DexOpt: unable to optimize virt call 0x%04x at 0x%02x in %s.%s\n", methodIdx, @@ -1989,7 +2113,7 @@ static bool rewriteDirectInvoke(Method* method, u2* insns) Method* calledMethod; u2 methodIdx = insns[1]; - calledMethod = dvmOptResolveMethod(clazz, methodIdx, METHOD_DIRECT); + calledMethod = dvmOptResolveMethod(clazz, methodIdx, METHOD_DIRECT, NULL); if (calledMethod == NULL) { LOGD("DexOpt: unable to opt direct call 0x%04x at 0x%02x in %s.%s\n", methodIdx, @@ -2021,6 +2145,8 @@ static bool rewriteDirectInvoke(Method* method, u2* insns) /* * Resolve an interface method reference. * + * No method access check here -- interface methods are always public. + * * Returns NULL if the method was not found. Does not throw an exception. */ Method* dvmOptResolveInterfaceMethod(ClassObject* referrer, u4 methodIdx) @@ -2039,7 +2165,7 @@ Method* dvmOptResolveInterfaceMethod(ClassObject* referrer, u4 methodIdx) pMethodId = dexGetMethodId(pDvmDex->pDexFile, methodIdx); - resClass = dvmOptResolveClass(referrer, pMethodId->classIdx); + resClass = dvmOptResolveClass(referrer, pMethodId->classIdx, NULL); if (resClass == NULL) { /* can't find the class that the method is a part of */ dvmClearOptException(dvmThreadSelf()); @@ -2115,7 +2241,7 @@ static bool rewriteExecuteInline(Method* method, u2* insns, //return false; - calledMethod = dvmOptResolveMethod(clazz, methodIdx, methodType); + calledMethod = dvmOptResolveMethod(clazz, methodIdx, methodType, NULL); if (calledMethod == NULL) { LOGV("+++ DexOpt inline: can't find %d\n", methodIdx); return false; diff --git a/vm/analysis/DexOptimize.h b/vm/analysis/DexOptimize.h index 01aa8288a..afcb3cba2 100644 --- a/vm/analysis/DexOptimize.h +++ b/vm/analysis/DexOptimize.h @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + /* * DEX optimization declarations. */ @@ -35,6 +36,40 @@ enum DexoptFlags { DEXOPT_GEN_REGISTER_MAPS = 1, /* generate register maps during verify */ }; +/* + * An enumeration of problems that can turn up during verification. + */ +typedef enum VerifyError { + VERIFY_ERROR_NONE = 0, /* no error; must be zero */ + VERIFY_ERROR_GENERIC, /* VerifyError */ + + VERIFY_ERROR_NO_CLASS, /* NoClassDefFoundError */ + VERIFY_ERROR_NO_FIELD, /* NoSuchFieldError */ + VERIFY_ERROR_NO_METHOD, /* NoSuchMethodError */ + VERIFY_ERROR_ACCESS_CLASS, /* IllegalAccessError */ + VERIFY_ERROR_ACCESS_FIELD, /* IllegalAccessError */ + VERIFY_ERROR_ACCESS_METHOD, /* IllegalAccessError */ + VERIFY_ERROR_CLASS_CHANGE, /* IncompatibleClassChangeError */ + VERIFY_ERROR_INSTANTIATION, /* InstantiationError */ +} VerifyError; + +/* + * Identifies the type of reference in the instruction that generated the + * verify error (e.g. VERIFY_ERROR_ACCESS_CLASS could come from a method, + * field, or class reference). + * + * This must fit in two bits. + */ +typedef enum VerifyErrorRefType { + VERIFY_ERROR_REF_CLASS = 0, + VERIFY_ERROR_REF_FIELD = 1, + VERIFY_ERROR_REF_METHOD = 2, +} VerifyErrorRefType; + +#define kVerifyErrorRefTypeShift 6 + +#define VERIFY_OK(_failure) ((_failure) == VERIFY_ERROR_NONE) + /* * Given the full path to a DEX or Jar file, and (if appropriate) the name * within the Jar, open the optimized version from the cache. @@ -81,11 +116,14 @@ bool dvmContinueOptimization(int fd, off_t dexOffset, long dexLength, * Abbreviated resolution functions, for use by optimization and verification * code. */ -ClassObject* dvmOptResolveClass(ClassObject* referrer, u4 classIdx); +ClassObject* dvmOptResolveClass(ClassObject* referrer, u4 classIdx, + VerifyError* pFailure); Method* dvmOptResolveMethod(ClassObject* referrer, u4 methodIdx, - MethodType methodType); + MethodType methodType, VerifyError* pFailure); Method* dvmOptResolveInterfaceMethod(ClassObject* referrer, u4 methodIdx); -InstField* dvmOptResolveInstField(ClassObject* referrer, u4 ifieldIdx); -StaticField* dvmOptResolveStaticField(ClassObject* referrer, u4 sfieldIdx); +InstField* dvmOptResolveInstField(ClassObject* referrer, u4 ifieldIdx, + VerifyError* pFailure); +StaticField* dvmOptResolveStaticField(ClassObject* referrer, u4 sfieldIdx, + VerifyError* pFailure); #endif /*_DALVIK_DEXOPTIMIZE*/ diff --git a/vm/analysis/DexVerify.c b/vm/analysis/DexVerify.c index 354d68f11..10251db9a 100644 --- a/vm/analysis/DexVerify.c +++ b/vm/analysis/DexVerify.c @@ -36,11 +36,18 @@ bool dvmVerificationStartup(void) gDvm.instrWidth = dexCreateInstrWidthTable(); gDvm.instrFormat = dexCreateInstrFormatTable(); gDvm.instrFlags = dexCreateInstrFlagsTable(); - return (gDvm.instrWidth != NULL && gDvm.instrFormat!= NULL); + if (gDvm.instrWidth == NULL || gDvm.instrFormat == NULL || + gDvm.instrFlags == NULL) + { + LOGE("Unable to create instruction tables\n"); + return false; + } + + return true; } /* - * Initialize some things we need for verification. + * Free up some things we needed for verification. */ void dvmVerificationShutdown(void) { @@ -533,15 +540,36 @@ static bool verifyInstructions(const Method* meth, InsnFlags* insnFlags, dvmInsnSetBranchTarget(insnFlags, 0, true); for (i = 0; i < insnCount; /**/) { - static int gcMask = kInstrCanBranch | kInstrCanSwitch | + /* + * These types of instructions can be GC points. To support precise + * GC, all such instructions must export the PC in the interpreter, + * or the GC won't be able to identify the current PC for the thread. + */ + static const int gcMask = kInstrCanBranch | kInstrCanSwitch | kInstrCanThrow | kInstrCanReturn; + int width = dvmInsnGetWidth(insnFlags, i); OpCode opcode = *insns & 0xff; InstructionFlags opFlags = dexGetInstrFlags(gDvm.instrFlags, opcode); int offset, absOffset; - if ((opFlags & gcMask) != 0) - dvmInsnSetGcPoint(insnFlags, i, true); + if ((opFlags & gcMask) != 0) { + /* + * This instruction is probably a GC point. Branch instructions + * only qualify if they go backward, so we need to check the + * offset. + */ + int offset = -1; + bool unused; + if (dvmGetBranchTarget(meth, insnFlags, i, &offset, &unused)) { + if (offset < 0) { + dvmInsnSetGcPoint(insnFlags, i, true); + } + } else { + /* not a branch target */ + dvmInsnSetGcPoint(insnFlags, i, true); + } + } switch (opcode) { case OP_NOP: diff --git a/vm/analysis/RegisterMap.c b/vm/analysis/RegisterMap.c index b02874a75..0e3445ac2 100644 --- a/vm/analysis/RegisterMap.c +++ b/vm/analysis/RegisterMap.c @@ -14,8 +14,6 @@ * limitations under the License. */ -// ** UNDER CONSTRUCTION ** - /* * This code generate "register maps" for Dalvik bytecode. In a stack-based * VM we might call these "stack maps". They are used to increase the @@ -27,64 +25,154 @@ #include "analysis/RegisterMap.h" #include "libdex/DexCatch.h" #include "libdex/InstrUtils.h" +#include "libdex/Leb128.h" #include +/* double-check the compression */ +#define REGISTER_MAP_VERIFY false + +/* verbose logging */ +#define REGISTER_MAP_VERBOSE false + + +// fwd +static void outputTypeVector(const RegType* regs, int insnRegCount, u1* data); +static bool verifyMap(VerifierData* vdata, const RegisterMap* pMap); +static int compareMaps(const RegisterMap* pMap1, const RegisterMap* pMap2); + +static void computeMapStats(RegisterMap* pMap, const Method* method); +static RegisterMap* compressMapDifferential(const RegisterMap* pMap,\ + const Method* meth); +static RegisterMap* uncompressMapDifferential(const RegisterMap* pMap); + +//#define REGISTER_MAP_STATS +#ifdef REGISTER_MAP_STATS /* -Notes on just-in-time RegisterMap generation + * Generate some statistics on the register maps we create and use. + */ +#define kMaxGcPointGap 50 +#define kUpdatePosnMinRegs 24 +#define kNumUpdatePosns 8 +#define kMaxDiffBits 20 +typedef struct MapStats { + /* + * Buckets measuring the distance between GC points. This tells us how + * many bits we need to encode the advancing program counter. We ignore + * some of the "long tail" entries. + */ + int gcPointGap[kMaxGcPointGap]; -Generating RegisterMap tables as part of verification is convenient because -we generate most of what we need to know as part of doing the verify. -The negative aspect of doing it this way is that we must store the -result in the DEX file (if we're verifying ahead of time) or in memory -(if verifying during class load) for every concrete non-native method, -even if we never actually need the map during a GC. + /* + * Number of gaps. Equal to (number of gcPoints - number of methods), + * since the computation isn't including the initial gap. + */ + int gcGapCount; -A simple but compact encoding of register map data increases the size of -optimized DEX files by about 25%, so size considerations are important. + /* + * Number of gaps. + */ + int totalGcPointCount; -We can instead generate the RegisterMap at the point where it is needed. -In a typical application we only need to convert about 2% of the loaded -methods, and we can generate type-precise roots reasonably quickly because -(a) we know the method has already been verified and hence can make a -lot of assumptions, and (b) we don't care what type of object a register -holds, just whether or not it holds a reference, and hence can skip a -lot of class resolution gymnastics. + /* + * For larger methods (>= 24 registers), measure in which octant register + * updates occur. This should help us understand whether register + * changes tend to cluster in the low regs even for large methods. + */ + int updatePosn[kNumUpdatePosns]; -There are a couple of problems with this approach however. First, to -get good performance we really want an implementation that is largely -independent from the verifier, which means some duplication of effort. -Second, we're dealing with post-dexopt code, which contains "quickened" -instructions. We can't process those without either tracking type -information (which slows us down) or storing additional data in the DEX -file that allows us to reconstruct the original instructions (adds ~5% -to the size of the ODEX). + /* + * For all methods, count up the number of changes to registers < 16 + * and >= 16. + */ + int updateLT16; + int updateGE16; + /* + * Histogram of the number of bits that differ between adjacent entries. + */ + int numDiffBits[kMaxDiffBits]; -Implementation notes... -Both type-precise and live-precise information can be generated knowing -only whether or not a register holds a reference. We don't need to -know what kind of reference or whether the object has been initialized. -Not only can we skip many of the fancy steps in the verifier, we can -initialize from simpler sources, e.g. the initial registers and return -type are set from the "shorty" signature rather than the full signature. + /* + * Track the number of expanded maps, and the heap space required to + * hold them. + */ + int numExpandedMaps; + int totalExpandedMapSize; +} MapStats; +#endif -The short-term storage needs for just-in-time register map generation can -be much lower because we can use a 1-byte SRegType instead of a 4-byte -RegType. On the other hand, if we're not doing type-precise analysis -in the verifier we only need to store register contents at every branch -target, rather than every GC point (which are much more frequent). +/* + * Prepare some things. + */ +bool dvmRegisterMapStartup(void) +{ +#ifdef REGISTER_MAP_STATS + MapStats* pStats = calloc(1, sizeof(MapStats)); + gDvm.registerMapStats = pStats; +#endif + return true; +} -Whether it happens in the verifier or independently, because this is done -with native heap allocations that may be difficult to return to the system, -an effort should be made to minimize memory use. -*/ +/* + * Clean up. + */ +void dvmRegisterMapShutdown(void) +{ +#ifdef REGISTER_MAP_STATS + free(gDvm.registerMapStats); +#endif +} -// fwd -static void outputTypeVector(const RegType* regs, int insnRegCount, u1* data); -static bool verifyMap(VerifierData* vdata, const RegisterMap* pMap); +/* + * Write stats to log file. + */ +void dvmRegisterMapDumpStats(void) +{ +#ifdef REGISTER_MAP_STATS + MapStats* pStats = (MapStats*) gDvm.registerMapStats; + int i, end; + + for (end = kMaxGcPointGap-1; end >= 0; end--) { + if (pStats->gcPointGap[end] != 0) + break; + } + + LOGI("Register Map gcPointGap stats (diff count=%d, total=%d):\n", + pStats->gcGapCount, pStats->totalGcPointCount); + assert(pStats->gcPointGap[0] == 0); + for (i = 1; i <= end; i++) { + LOGI(" %2d %d\n", i, pStats->gcPointGap[i]); + } + + + for (end = kMaxDiffBits-1; end >= 0; end--) { + if (pStats->numDiffBits[end] != 0) + break; + } + + LOGI("Register Map bit difference stats:\n"); + for (i = 0; i <= end; i++) { + LOGI(" %2d %d\n", i, pStats->numDiffBits[i]); + } + + + LOGI("Register Map update position stats (lt16=%d ge16=%d):\n", + pStats->updateLT16, pStats->updateGE16); + for (i = 0; i < kNumUpdatePosns; i++) { + LOGI(" %2d %d\n", i, pStats->updatePosn[i]); + } +#endif +} + + +/* + * =========================================================================== + * Map generation + * =========================================================================== + */ /* * Generate the register map for a method that has just been verified @@ -97,6 +185,7 @@ static bool verifyMap(VerifierData* vdata, const RegisterMap* pMap); */ RegisterMap* dvmGenerateRegisterMapV(VerifierData* vdata) { + static const int kHeaderSize = offsetof(RegisterMap, data); RegisterMap* pMap = NULL; RegisterMap* pResult = NULL; RegisterMapFormat format; @@ -105,12 +194,25 @@ RegisterMap* dvmGenerateRegisterMapV(VerifierData* vdata) int i, bytesForAddr, gcPointCount; int bufSize; + if (vdata->method->registersSize >= 2048) { + LOGE("ERROR: register map can't handle %d registers\n", + vdata->method->registersSize); + goto bail; + } regWidth = (vdata->method->registersSize + 7) / 8; + + /* + * Decide if we need 8 or 16 bits to hold the address. Strictly speaking + * we only need 16 bits if we actually encode an address >= 256 -- if + * the method has a section at the end without GC points (e.g. array + * data) we don't need to count it. The situation is unusual, and + * detecting it requires scanning the entire method, so we don't bother. + */ if (vdata->insnsSize < 256) { - format = kFormatCompact8; + format = kRegMapFormatCompact8; bytesForAddr = 1; } else { - format = kFormatCompact16; + format = kRegMapFormatCompact16; bytesForAddr = 2; } @@ -135,17 +237,17 @@ RegisterMap* dvmGenerateRegisterMapV(VerifierData* vdata) /* * Allocate a buffer to hold the map data. */ - bufSize = offsetof(RegisterMap, data); - bufSize += gcPointCount * (bytesForAddr + regWidth); + bufSize = kHeaderSize + gcPointCount * (bytesForAddr + regWidth); - LOGD("+++ grm: %s.%s (adr=%d gpc=%d rwd=%d bsz=%d)\n", + LOGV("+++ grm: %s.%s (adr=%d gpc=%d rwd=%d bsz=%d)\n", vdata->method->clazz->descriptor, vdata->method->name, bytesForAddr, gcPointCount, regWidth, bufSize); pMap = (RegisterMap*) malloc(bufSize); - pMap->format = format; - pMap->regWidth = regWidth; - pMap->numEntries = gcPointCount; + dvmRegisterMapSetFormat(pMap, format); + dvmRegisterMapSetOnHeap(pMap, true); + dvmRegisterMapSetRegWidth(pMap, regWidth); + dvmRegisterMapSetNumEntries(pMap, gcPointCount); /* * Populate it. @@ -154,9 +256,9 @@ RegisterMap* dvmGenerateRegisterMapV(VerifierData* vdata) for (i = 0; i < vdata->insnsSize; i++) { if (dvmInsnIsGcPoint(vdata->insnFlags, i)) { assert(vdata->addrRegs[i] != NULL); - if (format == kFormatCompact8) { + if (format == kRegMapFormatCompact8) { *mapData++ = i; - } else /*kFormatCompact16*/ { + } else /*kRegMapFormatCompact16*/ { *mapData++ = i & 0xff; *mapData++ = i >> 8; } @@ -165,14 +267,68 @@ RegisterMap* dvmGenerateRegisterMapV(VerifierData* vdata) } } - LOGI("mapData=%p pMap=%p bufSize=%d\n", mapData, pMap, bufSize); + LOGV("mapData=%p pMap=%p bufSize=%d\n", mapData, pMap, bufSize); assert(mapData - (const u1*) pMap == bufSize); -#if 1 - if (!verifyMap(vdata, pMap)) + if (REGISTER_MAP_VERIFY && !verifyMap(vdata, pMap)) goto bail; +#ifdef REGISTER_MAP_STATS + computeMapStats(pMap, vdata->method); #endif + /* + * Try to compress the map. + */ + RegisterMap* pCompMap; + + pCompMap = compressMapDifferential(pMap, vdata->method); + if (pCompMap != NULL) { + if (REGISTER_MAP_VERIFY) { + /* + * Expand the compressed map we just created, and compare it + * to the original. Abort the VM if it doesn't match up. + */ + RegisterMap* pUncompMap; + pUncompMap = uncompressMapDifferential(pCompMap); + if (pUncompMap == NULL) { + LOGE("Map failed to uncompress - %s.%s\n", + vdata->method->clazz->descriptor, + vdata->method->name); + free(pCompMap); + /* bad - compression is broken or we're out of memory */ + dvmAbort(); + } else { + if (compareMaps(pMap, pUncompMap) != 0) { + LOGE("Map comparison failed - %s.%s\n", + vdata->method->clazz->descriptor, + vdata->method->name); + free(pCompMap); + /* bad - compression is broken */ + dvmAbort(); + } + + /* verify succeeded */ + free(pUncompMap); + } + } + + if (REGISTER_MAP_VERBOSE) { + LOGD("Good compress on %s.%s\n", + vdata->method->clazz->descriptor, + vdata->method->name); + } + free(pMap); + pMap = pCompMap; + } else { + if (REGISTER_MAP_VERBOSE) { + LOGD("Unable to compress %s.%s (ent=%d rw=%d)\n", + vdata->method->clazz->descriptor, + vdata->method->name, + dvmRegisterMapGetNumEntries(pMap), + dvmRegisterMapGetRegWidth(pMap)); + } + } + pResult = pMap; bail: @@ -187,6 +343,7 @@ void dvmFreeRegisterMap(RegisterMap* pMap) if (pMap == NULL) return; + assert(dvmRegisterMapGetOnHeap(pMap)); free(pMap); } @@ -231,40 +388,134 @@ static void outputTypeVector(const RegType* regs, int insnRegCount, u1* data) } } +/* + * Print the map as a series of binary strings. + * + * Pass in method->registersSize if known, or -1 if not. + */ +static void dumpRegisterMap(const RegisterMap* pMap, int registersSize) +{ + const u1* rawMap = pMap->data; + const RegisterMapFormat format = dvmRegisterMapGetFormat(pMap); + const int numEntries = dvmRegisterMapGetNumEntries(pMap); + const int regWidth = dvmRegisterMapGetRegWidth(pMap); + int addrWidth; + + switch (format) { + case kRegMapFormatCompact8: + addrWidth = 1; + break; + case kRegMapFormatCompact16: + addrWidth = 2; + break; + default: + /* can't happen */ + LOGE("Can only dump Compact8 / Compact16 maps (not %d)\n", format); + return; + } + + if (registersSize < 0) + registersSize = 8 * regWidth; + assert(registersSize <= regWidth * 8); + + int ent; + for (ent = 0; ent < numEntries; ent++) { + int i, addr; + + addr = *rawMap++; + if (addrWidth > 1) + addr |= (*rawMap++) << 8; + + const u1* dataStart = rawMap; + u1 val = 0; + + /* create binary string */ + char outBuf[registersSize +1]; + for (i = 0; i < registersSize; i++) { + val >>= 1; + if ((i & 0x07) == 0) + val = *rawMap++; + + outBuf[i] = '0' + (val & 0x01); + } + outBuf[i] = '\0'; + + /* back up and create hex dump */ + char hexBuf[regWidth * 3 +1]; + char* cp = hexBuf; + rawMap = dataStart; + for (i = 0; i < regWidth; i++) { + sprintf(cp, " %02x", *rawMap++); + cp += 3; + } + hexBuf[i * 3] = '\0'; + + LOGD(" %04x %s %s\n", addr, outBuf, hexBuf); + } +} + /* * Double-check the map. * * We run through all of the data in the map, and compare it to the original. + * Only works on uncompressed data. */ static bool verifyMap(VerifierData* vdata, const RegisterMap* pMap) { - const u1* data = pMap->data; + const u1* rawMap = pMap->data; + const RegisterMapFormat format = dvmRegisterMapGetFormat(pMap); + const int numEntries = dvmRegisterMapGetNumEntries(pMap); int ent; + bool dumpMap = false; + + if (false) { + const char* cd = "Landroid/net/http/Request;"; + const char* mn = "readResponse"; + const char* sg = "(Landroid/net/http/AndroidHttpClientConnection;)V"; + if (strcmp(vdata->method->clazz->descriptor, cd) == 0 && + strcmp(vdata->method->name, mn) == 0) + { + char* desc; + desc = dexProtoCopyMethodDescriptor(&vdata->method->prototype); + LOGI("Map for %s.%s %s\n", vdata->method->clazz->descriptor, + vdata->method->name, desc); + free(desc); + + dumpMap = true; + } + } + + if ((vdata->method->registersSize + 7) / 8 != pMap->regWidth) { + LOGE("GLITCH: registersSize=%d, regWidth=%d\n", + vdata->method->registersSize, pMap->regWidth); + return false; + } - for (ent = 0; ent < pMap->numEntries; ent++) { + for (ent = 0; ent < numEntries; ent++) { int addr; - switch (pMap->format) { - case kFormatCompact8: - addr = *data++; + switch (format) { + case kRegMapFormatCompact8: + addr = *rawMap++; break; - case kFormatCompact16: - addr = *data++; - addr |= (*data++) << 8; + case kRegMapFormatCompact16: + addr = *rawMap++; + addr |= (*rawMap++) << 8; break; default: /* shouldn't happen */ - LOGE("GLITCH: bad format (%d)", pMap->format); + LOGE("GLITCH: bad format (%d)", format); dvmAbort(); } + const u1* dataStart = rawMap; const RegType* regs = vdata->addrRegs[addr]; if (regs == NULL) { LOGE("GLITCH: addr %d has no data\n", addr); return false; } - u1 val; + u1 val = 0; int i; for (i = 0; i < vdata->method->registersSize; i++) { @@ -273,7 +524,7 @@ static bool verifyMap(VerifierData* vdata, const RegisterMap* pMap) val >>= 1; if ((i & 0x07) == 0) { /* load next byte of data */ - val = *data++; + val = *rawMap++; } bitIsRef = val & 0x01; @@ -288,32 +539,1363 @@ static bool verifyMap(VerifierData* vdata, const RegisterMap* pMap) } } - /* print the map as a binary string */ - if (false) { - char outBuf[vdata->method->registersSize +1]; - for (i = 0; i < vdata->method->registersSize; i++) { - if (isReferenceType(regs[i])) { - outBuf[i] = '1'; - } else { - outBuf[i] = '0'; - } - } - outBuf[i] = '\0'; - LOGD(" %04d %s\n", addr, outBuf); - } + /* rawMap now points to the address field of the next entry */ } + if (dumpMap) + dumpRegisterMap(pMap, vdata->method->registersSize); + return true; } /* * =========================================================================== - * Just-in-time generation + * DEX generation & parsing * =========================================================================== */ -#if 0 /* incomplete implementation; may be removed entirely in the future */ +/* + * Advance "ptr" to ensure 32-bit alignment. + */ +static inline u1* align32(u1* ptr) +{ + return (u1*) (((int) ptr + 3) & ~0x03); +} + +/* + * Compute the size, in bytes, of a register map. + */ +static size_t computeRegisterMapSize(const RegisterMap* pMap) +{ + static const int kHeaderSize = offsetof(RegisterMap, data); + u1 format = dvmRegisterMapGetFormat(pMap); + u2 numEntries = dvmRegisterMapGetNumEntries(pMap); + + assert(pMap != NULL); + + switch (format) { + case kRegMapFormatNone: + return 1; + case kRegMapFormatCompact8: + return kHeaderSize + (1 + pMap->regWidth) * numEntries; + case kRegMapFormatCompact16: + return kHeaderSize + (2 + pMap->regWidth) * numEntries; + case kRegMapFormatDifferential: + { + /* kHeaderSize + decoded ULEB128 length */ + const u1* ptr = pMap->data; + int len = readUnsignedLeb128(&ptr); + return len + (ptr - (u1*) pMap); + } + default: + LOGE("Bad register map format %d\n", format); + dvmAbort(); + return 0; + } +} + +/* + * Output the map for a single method, if it has one. + * + * Abstract and native methods have no map. All others are expected to + * have one, since we know the class verified successfully. + * + * This strips the "allocated on heap" flag from the format byte, so that + * direct-mapped maps are correctly identified as such. + */ +static bool writeMapForMethod(const Method* meth, u1** pPtr) +{ + if (meth->registerMap == NULL) { + if (!dvmIsAbstractMethod(meth) && !dvmIsNativeMethod(meth)) { + LOGW("Warning: no map available for %s.%s\n", + meth->clazz->descriptor, meth->name); + /* weird, but keep going */ + } + *(*pPtr)++ = kRegMapFormatNone; + return true; + } + + /* serialize map into the buffer */ + size_t mapSize = computeRegisterMapSize(meth->registerMap); + memcpy(*pPtr, meth->registerMap, mapSize); + + /* strip the "on heap" flag out of the format byte, which is always first */ + assert(**pPtr == meth->registerMap->format); + **pPtr &= ~(kRegMapFormatOnHeap); + + *pPtr += mapSize; + + return true; +} + +/* + * Write maps for all methods in the specified class to the buffer, which + * can hold at most "length" bytes. "*pPtr" will be advanced past the end + * of the data we write. + */ +static bool writeMapsAllMethods(DvmDex* pDvmDex, const ClassObject* clazz, + u1** pPtr, size_t length) +{ + RegisterMapMethodPool* pMethodPool; + u1* ptr = *pPtr; + int i, methodCount; + + /* artificial limit */ + if (clazz->virtualMethodCount + clazz->directMethodCount >= 65536) { + LOGE("Too many methods in %s\n", clazz->descriptor); + return false; + } + + pMethodPool = (RegisterMapMethodPool*) ptr; + ptr += offsetof(RegisterMapMethodPool, methodData); + methodCount = 0; + + /* + * Run through all methods, direct then virtual. The class loader will + * traverse them in the same order. (We could split them into two + * distinct pieces, but there doesn't appear to be any value in doing + * so other than that it makes class loading slightly less fragile.) + * + * The class loader won't know about miranda methods at the point + * where it parses this, so we omit those. + * + * TODO: consider omitting all native/abstract definitions. Should be + * safe, though we lose the ability to sanity-check against the + * method counts in the DEX file. + */ + for (i = 0; i < clazz->directMethodCount; i++) { + const Method* meth = &clazz->directMethods[i]; + if (dvmIsMirandaMethod(meth)) + continue; + if (!writeMapForMethod(&clazz->directMethods[i], &ptr)) { + return false; + } + methodCount++; + //ptr = align32(ptr); + } + + for (i = 0; i < clazz->virtualMethodCount; i++) { + const Method* meth = &clazz->virtualMethods[i]; + if (dvmIsMirandaMethod(meth)) + continue; + if (!writeMapForMethod(&clazz->virtualMethods[i], &ptr)) { + return false; + } + methodCount++; + //ptr = align32(ptr); + } + + pMethodPool->methodCount = methodCount; + + *pPtr = ptr; + return true; +} + +/* + * Write maps for all classes to the specified buffer, which can hold at + * most "length" bytes. + * + * Returns the actual length used, or 0 on failure. + */ +static size_t writeMapsAllClasses(DvmDex* pDvmDex, u1* basePtr, size_t length) +{ + DexFile* pDexFile = pDvmDex->pDexFile; + u4 count = pDexFile->pHeader->classDefsSize; + RegisterMapClassPool* pClassPool; + u4* offsetTable; + u1* ptr = basePtr; + u4 idx; + + assert(gDvm.optimizing); + + pClassPool = (RegisterMapClassPool*) ptr; + ptr += offsetof(RegisterMapClassPool, classDataOffset); + offsetTable = (u4*) ptr; + ptr += count * sizeof(u4); + + pClassPool->numClasses = count; + + /* + * We want an entry for every class, loaded or not. + */ + for (idx = 0; idx < count; idx++) { + const DexClassDef* pClassDef; + const char* classDescriptor; + ClassObject* clazz; + + pClassDef = dexGetClassDef(pDexFile, idx); + classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx); + + /* + * All classes have been loaded into the bootstrap class loader. + * If we can find it, and it was successfully pre-verified, we + * run through its methods and add the register maps. + * + * If it wasn't pre-verified then we know it can't have any + * register maps. Classes that can't be loaded or failed + * verification get an empty slot in the index. + */ + clazz = NULL; + if ((pClassDef->accessFlags & CLASS_ISPREVERIFIED) != 0) + clazz = dvmLookupClass(classDescriptor, NULL, false); + + if (clazz != NULL) { + offsetTable[idx] = ptr - basePtr; + LOGVV("%d -> offset %d (%p-%p)\n", + idx, offsetTable[idx], ptr, basePtr); + + if (!writeMapsAllMethods(pDvmDex, clazz, &ptr, + length - (ptr - basePtr))) + { + return 0; + } + + ptr = align32(ptr); + LOGVV("Size %s (%d+%d methods): %d\n", clazz->descriptor, + clazz->directMethodCount, clazz->virtualMethodCount, + (ptr - basePtr) - offsetTable[idx]); + } else { + LOGV("%4d NOT mapadding '%s'\n", idx, classDescriptor); + assert(offsetTable[idx] == 0); + } + } + + if (ptr - basePtr >= (int)length) { + /* a bit late */ + LOGE("Buffer overrun\n"); + dvmAbort(); + } + + return ptr - basePtr; +} + +/* + * Generate a register map set for all verified classes in "pDvmDex". + */ +RegisterMapBuilder* dvmGenerateRegisterMaps(DvmDex* pDvmDex) +{ + RegisterMapBuilder* pBuilder; + + pBuilder = (RegisterMapBuilder*) calloc(1, sizeof(RegisterMapBuilder)); + if (pBuilder == NULL) + return NULL; + + /* + * We have a couple of options here: + * (1) Compute the size of the output, and malloc a buffer. + * (2) Create a "large-enough" anonymous mmap region. + * + * The nice thing about option #2 is that we don't have to traverse + * all of the classes and methods twice. The risk is that we might + * not make the region large enough. Since the pages aren't mapped + * until used we can allocate a semi-absurd amount of memory without + * worrying about the effect on the rest of the system. + * + * The basic encoding on the largest jar file requires about 1MB of + * storage. We map out 4MB here. (TODO: guarantee that the last + * page of the mapping is marked invalid, so we reliably fail if + * we overrun.) + */ + if (sysCreatePrivateMap(4 * 1024 * 1024, &pBuilder->memMap) != 0) { + free(pBuilder); + return NULL; + } + + /* + * Create the maps. + */ + size_t actual = writeMapsAllClasses(pDvmDex, (u1*)pBuilder->memMap.addr, + pBuilder->memMap.length); + if (actual == 0) { + dvmFreeRegisterMapBuilder(pBuilder); + return NULL; + } + + LOGV("TOTAL size of register maps: %d\n", actual); + + pBuilder->data = pBuilder->memMap.addr; + pBuilder->size = actual; + return pBuilder; +} + +/* + * Free the builder. + */ +void dvmFreeRegisterMapBuilder(RegisterMapBuilder* pBuilder) +{ + if (pBuilder == NULL) + return; + + sysReleaseShmem(&pBuilder->memMap); + free(pBuilder); +} + + +/* + * Find the data for the specified class. + * + * If there's no register map data, or none for this class, we return NULL. + */ +const void* dvmRegisterMapGetClassData(const DexFile* pDexFile, u4 classIdx, + u4* pNumMaps) +{ + const RegisterMapClassPool* pClassPool; + const RegisterMapMethodPool* pMethodPool; + + pClassPool = (const RegisterMapClassPool*) pDexFile->pRegisterMapPool; + if (pClassPool == NULL) + return NULL; + + if (classIdx >= pClassPool->numClasses) { + LOGE("bad class index (%d vs %d)\n", classIdx, pClassPool->numClasses); + dvmAbort(); + } + + u4 classOffset = pClassPool->classDataOffset[classIdx]; + if (classOffset == 0) { + LOGV("+++ no map for classIdx=%d\n", classIdx); + return NULL; + } + + pMethodPool = + (const RegisterMapMethodPool*) (((u1*) pClassPool) + classOffset); + if (pNumMaps != NULL) + *pNumMaps = pMethodPool->methodCount; + return pMethodPool->methodData; +} + +/* + * This advances "*pPtr" and returns its original value. + */ +const RegisterMap* dvmRegisterMapGetNext(const void** pPtr) +{ + const RegisterMap* pMap = *pPtr; + + *pPtr = /*align32*/(((u1*) pMap) + computeRegisterMapSize(pMap)); + LOGVV("getNext: %p -> %p (f=0x%x w=%d e=%d)\n", + pMap, *pPtr, pMap->format, pMap->regWidth, + dvmRegisterMapGetNumEntries(pMap)); + return pMap; +} + + +/* + * =========================================================================== + * Utility functions + * =========================================================================== + */ + +/* + * Return the data for the specified address, or NULL if not found. + * + * The result must be released with dvmReleaseRegisterMapLine(). + */ +const u1* dvmRegisterMapGetLine(const RegisterMap* pMap, int addr) +{ + int addrWidth, lineWidth; + u1 format = dvmRegisterMapGetFormat(pMap); + u2 numEntries = dvmRegisterMapGetNumEntries(pMap); + + assert(numEntries > 0); + + switch (format) { + case kRegMapFormatNone: + return NULL; + case kRegMapFormatCompact8: + addrWidth = 1; + break; + case kRegMapFormatCompact16: + addrWidth = 2; + break; + default: + LOGE("Unknown format %d\n", format); + dvmAbort(); + return NULL; + } + + lineWidth = addrWidth + pMap->regWidth; + + /* + * Find the appropriate entry. Many maps are very small, some are very + * large. + */ + static const int kSearchThreshold = 8; + const u1* data = NULL; + int lineAddr; + + if (numEntries < kSearchThreshold) { + int i; + data = pMap->data; + for (i = numEntries; i > 0; i--) { + lineAddr = data[0]; + if (addrWidth > 1) + lineAddr |= data[1] << 8; + if (lineAddr == addr) + return data + addrWidth; + + data += lineWidth; + } + } else { + int hi, lo, mid; + + lo = 0; + hi = numEntries -1; + + while (hi >= lo) { + mid = (hi + lo) / 2; + data = pMap->data + lineWidth * mid; + + lineAddr = data[0]; + if (addrWidth > 1) + lineAddr |= data[1] << 8; + + if (addr > lineAddr) { + lo = mid + 1; + } else if (addr < lineAddr) { + hi = mid - 1; + } else { + return data + addrWidth; + } + } + } + + assert(data == pMap->data + lineWidth * numEntries); + return NULL; +} + +/* + * Compare two register maps. + * + * Returns 0 if they're equal, nonzero if not. + */ +static int compareMaps(const RegisterMap* pMap1, const RegisterMap* pMap2) +{ + size_t size1, size2; + + size1 = computeRegisterMapSize(pMap1); + size2 = computeRegisterMapSize(pMap2); + if (size1 != size2) { + LOGI("compareMaps: size mismatch (%zd vs %zd)\n", size1, size2); + return -1; + } + + if (memcmp(pMap1, pMap2, size1) != 0) { + LOGI("compareMaps: content mismatch\n"); + return -1; + } + + return 0; +} + + +/* + * Get the expanded form of the register map associated with the method. + * + * If the map is already in one of the uncompressed formats, we return + * immediately. Otherwise, we expand the map and replace method's register + * map pointer, freeing it if it was allocated on the heap. + * + * NOTE: this function is not synchronized; external locking is mandatory + * (unless we're in the zygote, where single-threaded access is guaranteed). + */ +const RegisterMap* dvmGetExpandedRegisterMap0(Method* method) +{ + const RegisterMap* curMap = method->registerMap; + RegisterMap* newMap; + + if (curMap == NULL) + return NULL; + + /* sanity check to ensure this isn't called w/o external locking */ + /* (if we use this at a time other than during GC, fix/remove this test) */ + if (true) { + if (!gDvm.zygote && pthread_mutex_trylock(&gDvm.gcHeapLock) == 0) { + LOGE("GLITCH: dvmGetExpandedRegisterMap not called at GC time\n"); + dvmAbort(); + } + } + + RegisterMapFormat format = dvmRegisterMapGetFormat(curMap); + switch (format) { + case kRegMapFormatCompact8: + case kRegMapFormatCompact16: + if (REGISTER_MAP_VERBOSE) { + if (dvmRegisterMapGetOnHeap(curMap)) { + LOGD("RegMap: already expanded: %s.%s\n", + method->clazz->descriptor, method->name); + } else { + LOGD("RegMap: stored w/o compression: %s.%s\n", + method->clazz->descriptor, method->name); + } + } + return curMap; + case kRegMapFormatDifferential: + newMap = uncompressMapDifferential(curMap); + break; + default: + LOGE("Unknown format %d in dvmGetExpandedRegisterMap\n", format); + dvmAbort(); + } + + if (newMap == NULL) { + LOGE("Map failed to uncompress (fmt=%d) %s.%s\n", + format, method->clazz->descriptor, method->name); + return NULL; + } + +#ifdef REGISTER_MAP_STATS + /* + * Gather and display some stats. + */ + { + MapStats* pStats = (MapStats*) gDvm.registerMapStats; + pStats->numExpandedMaps++; + pStats->totalExpandedMapSize += computeRegisterMapSize(newMap); + LOGD("RMAP: count=%d size=%d\n", + pStats->numExpandedMaps, pStats->totalExpandedMapSize); + } +#endif + + IF_LOGV() { + char* desc = dexProtoCopyMethodDescriptor(&method->prototype); + LOGV("Expanding map -> %s.%s:%s\n", + method->clazz->descriptor, method->name, desc); + free(desc); + } + + /* + * Update method, and free compressed map if it was sitting on the heap. + */ + dvmSetRegisterMap(method, newMap); + + if (dvmRegisterMapGetOnHeap(curMap)) + dvmFreeRegisterMap((RegisterMap*) curMap); + + return newMap; +} + + +/* + * =========================================================================== + * Map compression + * =========================================================================== + */ + +/* +Notes on map compression + +The idea is to create a compressed form that will be uncompressed before +use, with the output possibly saved in a cache. This means we can use an +approach that is unsuited for random access if we choose. + +In the event that a map simply does not work with our compression scheme, +it's reasonable to store the map without compression. In the future we +may want to have more than one compression scheme, and try each in turn, +retaining the best. (We certainly want to keep the uncompressed form if it +turns out to be smaller or even slightly larger than the compressed form.) + +Each entry consists of an address and a bit vector. Adjacent entries are +strongly correlated, suggesting differential encoding. + + +Ideally we would avoid outputting adjacent entries with identical +bit vectors. However, the register values at a given address do not +imply anything about the set of valid registers at subsequent addresses. +We therefore cannot omit an entry. + + If the thread stack has a PC at an address without a corresponding + entry in the register map, we must conservatively scan the registers in + that thread. This can happen when single-stepping in the debugger, + because the debugger is allowed to invoke arbitrary methods when + a thread is stopped at a breakpoint. If we can guarantee that a GC + thread scan will never happen while the debugger has that thread stopped, + then we can lift this restriction and simply omit entries that don't + change the bit vector from its previous state. + +Each entry advances the address value by at least 1 (measured in 16-bit +"code units"). Looking at the bootclasspath entries, advancing by 2 units +is most common. Advances by 1 unit are far less common than advances by +2 units, but more common than 5, and things fall off rapidly. Gaps of +up to 220 code units appear in some computationally intensive bits of code, +but are exceedingly rare. + +If we sum up the number of transitions in a couple of ranges in framework.jar: + [1,4]: 188998 of 218922 gaps (86.3%) + [1,7]: 211647 of 218922 gaps (96.7%) +Using a 3-bit delta, with one value reserved as an escape code, should +yield good results for the address. + +These results would change dramatically if we reduced the set of GC +points by e.g. removing instructions like integer divide that are only +present because they can throw and cause an allocation. + +We also need to include an "initial gap", because the first few instructions +in a method may not be GC points. + + +By observation, many entries simply repeat the previous bit vector, or +change only one or two bits. (This is with type-precise information; +the rate of change of bits will be different if live-precise information +is factored in). + +Looking again at adjacent entries in framework.jar: + 0 bits changed: 63.0% + 1 bit changed: 32.2% +After that it falls off rapidly, e.g. the number of entries with 2 bits +changed is usually less than 1/10th of the number of entries with 1 bit +changed. A solution that allows us to encode 0- or 1- bit changes +efficiently will do well. + +We still need to handle cases where a large number of bits change. We +probably want a way to drop in a full copy of the bit vector when it's +smaller than the representation of multiple bit changes. + + +The bit-change information can be encoded as an index that tells the +decoder to toggle the state. We want to encode the index in as few bits +as possible, but we need to allow for fairly wide vectors (e.g. we have a +method with 175 registers). We can deal with this in a couple of ways: +(1) use an encoding that assumes few registers and has an escape code +for larger numbers of registers; or (2) use different encodings based +on how many total registers the method has. The choice depends to some +extent on whether methods with large numbers of registers tend to modify +the first 16 regs more often than the others. + +The last N registers hold method arguments. If the bytecode is expected +to be examined in a debugger, "dx" ensures that the contents of these +registers won't change. Depending upon the encoding format, we may be +able to take advantage of this. We still have to encode the initial +state, but we know we'll never have to output a bit change for the last +N registers. + +Considering only methods with 16 or more registers, the "target octant" +for register changes looks like this: + [ 43.1%, 16.4%, 6.5%, 6.2%, 7.4%, 8.8%, 9.7%, 1.8% ] +As expected, there are fewer changes at the end of the list where the +arguments are kept, and more changes at the start of the list because +register values smaller than 16 can be used in compact Dalvik instructions +and hence are favored for frequently-used values. In general, the first +octant is considerably more active than later entries, the last octant +is much less active, and the rest are all about the same. + +Looking at all bit changes in all methods, 94% are to registers 0-15. The +encoding will benefit greatly by favoring the low-numbered registers. + + +Some of the smaller methods have identical maps, and space could be +saved by simply including a pointer to an earlier definition. This would +be best accomplished by specifying a "pointer" format value, followed by +a 3-byte (or ULEB128) offset. Implementing this would probably involve +generating a hash value for each register map and maintaining a hash table. + +In some cases there are repeating patterns in the bit vector that aren't +adjacent. These could benefit from a dictionary encoding. This doesn't +really become useful until the methods reach a certain size though, +and managing the dictionary may incur more overhead than we want. + +Large maps can be compressed significantly. The trouble is that, when +we need to use them, we have to uncompress them onto the heap. We may +get a better trade-off between storage size and heap usage by refusing to +compress large maps, so that they can be memory mapped and used directly. +(OTOH, only about 2% of the maps will ever actually be used.) + + +----- differential format ----- + +// common header ++00 1B format ++01 1B regWidth ++02 2B numEntries (little-endian) ++04 nB length in bytes of the data that follows, in ULEB128 format + (not strictly necessary; allows determination of size w/o full parse) ++05+ 1B initial address (0-127), high bit set if max addr >= 256 ++06+ nB initial value for bit vector + +// for each entry ++00: CCCCBAAA + + AAA: address difference. Values from 0 to 6 indicate an increment of 1 + to 7. A value of 7 indicates that the address difference is large, + and the next byte is a ULEB128-encoded difference value. + + B: determines the meaning of CCCC. + + CCCC: if B is 0, this is the number of the bit to toggle (0-15). + If B is 1, this is a count of the number of changed bits (1-14). A value + of 0 means that no bits were changed, and a value of 15 indicates + that enough bits were changed that it required less space to output + the entire bit vector. + ++01: (optional) ULEB128-encoded address difference + ++01+: (optional) one or more ULEB128-encoded bit numbers, OR the entire + bit vector. + +The most common situation is an entry whose address has changed by 2-4 +code units, has no changes or just a single bit change, and the changed +register is less than 16. We should therefore be able to encode a large +number of entries with a single byte, which is half the size of the +Compact8 encoding method. +*/ + +/* + * Compute some stats on an uncompressed register map. + */ +static void computeMapStats(RegisterMap* pMap, const Method* method) +{ +#ifdef REGISTER_MAP_STATS + MapStats* pStats = (MapStats*) gDvm.registerMapStats; + const u1 format = dvmRegisterMapGetFormat(pMap); + const u2 numEntries = dvmRegisterMapGetNumEntries(pMap); + const u1* rawMap = pMap->data; + const u1* prevData = NULL; + int ent, addr, prevAddr = -1; + + for (ent = 0; ent < numEntries; ent++) { + switch (format) { + case kRegMapFormatCompact8: + addr = *rawMap++; + break; + case kRegMapFormatCompact16: + addr = *rawMap++; + addr |= (*rawMap++) << 8; + break; + default: + /* shouldn't happen */ + LOGE("GLITCH: bad format (%d)", format); + dvmAbort(); + } + + const u1* dataStart = rawMap; + + pStats->totalGcPointCount++; + + /* + * Gather "gap size" stats, i.e. the difference in addresses between + * successive GC points. + */ + if (prevData != NULL) { + assert(prevAddr >= 0); + int addrDiff = addr - prevAddr; + + if (addrDiff < 0) { + LOGE("GLITCH: address went backward (0x%04x->0x%04x, %s.%s)\n", + prevAddr, addr, method->clazz->descriptor, method->name); + } else if (addrDiff > kMaxGcPointGap) { + if (REGISTER_MAP_VERBOSE) { + LOGI("HEY: addrDiff is %d, max %d (0x%04x->0x%04x %s.%s)\n", + addrDiff, kMaxGcPointGap, prevAddr, addr, + method->clazz->descriptor, method->name); + } + /* skip this one */ + } else { + pStats->gcPointGap[addrDiff]++; + } + pStats->gcGapCount++; + + + /* + * Compare bit vectors in adjacent entries. We want to count + * up the number of bits that differ (to see if we frequently + * change 0 or 1 bits) and get a sense for which part of the + * vector changes the most often (near the start, middle, end). + * + * We only do the vector position quantization if we have at + * least 16 registers in the method. + */ + int numDiff = 0; + float div = (float) kNumUpdatePosns / method->registersSize; + int regByte; + for (regByte = 0; regByte < pMap->regWidth; regByte++) { + int prev, cur, bit; + + prev = prevData[regByte]; + cur = dataStart[regByte]; + + for (bit = 0; bit < 8; bit++) { + if (((prev >> bit) & 1) != ((cur >> bit) & 1)) { + numDiff++; + + int bitNum = regByte * 8 + bit; + + if (bitNum < 16) + pStats->updateLT16++; + else + pStats->updateGE16++; + + if (method->registersSize < 16) + continue; + + if (bitNum >= method->registersSize) { + /* stuff off the end should be zero in both */ + LOGE("WEIRD: bit=%d (%d/%d), prev=%02x cur=%02x\n", + bit, regByte, method->registersSize, + prev, cur); + assert(false); + } + int idx = (int) (bitNum * div); + if (!(idx >= 0 && idx < kNumUpdatePosns)) { + LOGE("FAIL: bitNum=%d (of %d) div=%.3f idx=%d\n", + bitNum, method->registersSize, div, idx); + assert(false); + } + pStats->updatePosn[idx]++; + } + } + } + + if (numDiff > kMaxDiffBits) { + if (REGISTER_MAP_VERBOSE) { + LOGI("WOW: numDiff is %d, max %d\n", numDiff, kMaxDiffBits); + } + } else { + pStats->numDiffBits[numDiff]++; + } + } + + /* advance to start of next line */ + rawMap += pMap->regWidth; + + prevAddr = addr; + prevData = dataStart; + } +#endif +} + + +/* + * Compute the difference between two bit vectors. + * + * If "lebOutBuf" is non-NULL, we output the bit indices in ULEB128 format + * as we go. Otherwise, we just generate the various counts. + * + * The bit vectors are compared byte-by-byte, so any unused bits at the + * end must be zero. + * + * Returns the number of bytes required to hold the ULEB128 output. + * + * If "pFirstBitChanged" or "pNumBitsChanged" are non-NULL, they will + * receive the index of the first changed bit and the number of changed + * bits, respectively. + */ +static int computeBitDiff(const u1* bits1, const u1* bits2, int byteWidth, + int* pFirstBitChanged, int* pNumBitsChanged, u1* lebOutBuf) +{ + int numBitsChanged = 0; + int firstBitChanged = -1; + int lebSize = 0; + int byteNum; + + /* + * Run through the vectors, first comparing them at the byte level. This + * will yield a fairly quick result if nothing has changed between them. + */ + for (byteNum = 0; byteNum < byteWidth; byteNum++) { + u1 byte1 = *bits1++; + u1 byte2 = *bits2++; + if (byte1 != byte2) { + /* + * Walk through the byte, identifying the changed bits. + */ + int bitNum; + for (bitNum = 0; bitNum < 8; bitNum++) { + if (((byte1 >> bitNum) & 0x01) != ((byte2 >> bitNum) & 0x01)) { + int bitOffset = (byteNum << 3) + bitNum; + + if (firstBitChanged < 0) + firstBitChanged = bitOffset; + numBitsChanged++; + + if (lebOutBuf == NULL) { + lebSize += unsignedLeb128Size(bitOffset); + } else { + u1* curBuf = lebOutBuf; + lebOutBuf = writeUnsignedLeb128(lebOutBuf, bitOffset); + lebSize += lebOutBuf - curBuf; + } + } + } + } + } + + if (numBitsChanged > 0) + assert(firstBitChanged >= 0); + + if (pFirstBitChanged != NULL) + *pFirstBitChanged = firstBitChanged; + if (pNumBitsChanged != NULL) + *pNumBitsChanged = numBitsChanged; + + return lebSize; +} + +/* + * Compress the register map with differential encoding. + * + * "meth" is only needed for debug output. + * + * On success, returns a newly-allocated RegisterMap. If the map is not + * compatible for some reason, or fails to get smaller, this will return NULL. + */ +static RegisterMap* compressMapDifferential(const RegisterMap* pMap, + const Method* meth) +{ + RegisterMap* pNewMap = NULL; + int origSize = computeRegisterMapSize(pMap); + u1* tmpBuf = NULL; + u1* tmpPtr; + int addrWidth, regWidth, numEntries; + bool debug = false; + + if (false && + strcmp(meth->clazz->descriptor, "Landroid/text/StaticLayout;") == 0 && + strcmp(meth->name, "generate") == 0) + { + debug = true; + } + + u1 format = dvmRegisterMapGetFormat(pMap); + switch (format) { + case kRegMapFormatCompact8: + addrWidth = 1; + break; + case kRegMapFormatCompact16: + addrWidth = 2; + break; + default: + LOGE("ERROR: can't compress map with format=%d\n", format); + goto bail; + } + + regWidth = dvmRegisterMapGetRegWidth(pMap); + numEntries = dvmRegisterMapGetNumEntries(pMap); + + if (debug) { + LOGI("COMPRESS: %s.%s aw=%d rw=%d ne=%d\n", + meth->clazz->descriptor, meth->name, + addrWidth, regWidth, numEntries); + dumpRegisterMap(pMap, -1); + } + + if (numEntries <= 1) { + LOGV("Can't compress map with 0 or 1 entries\n"); + goto bail; + } + + /* + * We don't know how large the compressed data will be. It's possible + * for it to expand and become larger than the original. The header + * itself is variable-sized, so we generate everything into a temporary + * buffer and then copy it to form-fitting storage once we know how big + * it will be (and that it's smaller than the original). + * + * If we use a size that is equal to the size of the input map plus + * a value longer than a single entry can possibly expand to, we need + * only check for overflow at the end of each entry. The worst case + * for a single line is (1 + + ). + * Addresses are 16 bits, so that's (1 + 3 + regWidth). + * + * The initial address offset and bit vector will take up less than + * or equal to the amount of space required when uncompressed -- large + * initial offsets are rejected. + */ + tmpBuf = (u1*) malloc(origSize + (1 + 3 + regWidth)); + if (tmpBuf == NULL) + goto bail; + + tmpPtr = tmpBuf; + + const u1* mapData = pMap->data; + const u1* prevBits; + u2 addr, prevAddr; + + addr = *mapData++; + if (addrWidth > 1) + addr |= (*mapData++) << 8; + + if (addr >= 128) { + LOGV("Can't compress map with starting address >= 128\n"); + goto bail; + } + + /* + * Start by writing the initial address and bit vector data. The high + * bit of the initial address is used to indicate the required address + * width (which the decoder can't otherwise determine without parsing + * the compressed data). + */ + *tmpPtr++ = addr | (addrWidth > 1 ? 0x80 : 0x00); + memcpy(tmpPtr, mapData, regWidth); + + prevBits = mapData; + prevAddr = addr; + + tmpPtr += regWidth; + mapData += regWidth; + + /* + * Loop over all following entries. + */ + int entry; + for (entry = 1; entry < numEntries; entry++) { + int addrDiff; + u1 key; + + /* + * Pull out the address and figure out how to encode it. + */ + addr = *mapData++; + if (addrWidth > 1) + addr |= (*mapData++) << 8; + + if (debug) + LOGI(" addr=0x%04x ent=%d (aw=%d)\n", addr, entry, addrWidth); + + addrDiff = addr - prevAddr; + assert(addrDiff > 0); + if (addrDiff < 8) { + /* small difference, encode in 3 bits */ + key = addrDiff -1; /* set 00000AAA */ + if (debug) + LOGI(" : small %d, key=0x%02x\n", addrDiff, key); + } else { + /* large difference, output escape code */ + key = 0x07; /* escape code for AAA */ + if (debug) + LOGI(" : large %d, key=0x%02x\n", addrDiff, key); + } + + int numBitsChanged, firstBitChanged, lebSize; + + lebSize = computeBitDiff(prevBits, mapData, regWidth, + &firstBitChanged, &numBitsChanged, NULL); + + if (debug) { + LOGI(" : diff fbc=%d nbc=%d ls=%d (rw=%d)\n", + firstBitChanged, numBitsChanged, lebSize, regWidth); + } + + if (numBitsChanged == 0) { + /* set B to 1 and CCCC to zero to indicate no bits were changed */ + key |= 0x08; + if (debug) LOGI(" : no bits changed\n"); + } else if (numBitsChanged == 1 && firstBitChanged < 16) { + /* set B to 0 and CCCC to the index of the changed bit */ + key |= firstBitChanged << 4; + if (debug) LOGI(" : 1 low bit changed\n"); + } else if (numBitsChanged < 15 && lebSize < regWidth) { + /* set B to 1 and CCCC to the number of bits */ + key |= 0x08 | (numBitsChanged << 4); + if (debug) LOGI(" : some bits changed\n"); + } else { + /* set B to 1 and CCCC to 0x0f so we store the entire vector */ + key |= 0x08 | 0xf0; + if (debug) LOGI(" : encode original\n"); + } + + /* + * Encode output. Start with the key, follow with the address + * diff (if it didn't fit in 3 bits), then the changed bit info. + */ + *tmpPtr++ = key; + if ((key & 0x07) == 0x07) + tmpPtr = writeUnsignedLeb128(tmpPtr, addrDiff); + + if ((key & 0x08) != 0) { + int bitCount = key >> 4; + if (bitCount == 0) { + /* nothing changed, no additional output required */ + } else if (bitCount == 15) { + /* full vector is most compact representation */ + memcpy(tmpPtr, mapData, regWidth); + tmpPtr += regWidth; + } else { + /* write bit indices in LEB128 format */ + (void) computeBitDiff(prevBits, mapData, regWidth, + NULL, NULL, tmpPtr); + tmpPtr += lebSize; + } + } else { + /* single-bit changed, value encoded in key byte */ + } + + prevBits = mapData; + prevAddr = addr; + mapData += regWidth; + + /* + * See if we've run past the original size. + */ + if (tmpPtr - tmpBuf >= origSize) { + if (debug) { + LOGD("Compressed size >= original (%d vs %d): %s.%s\n", + tmpPtr - tmpBuf, origSize, + meth->clazz->descriptor, meth->name); + } + goto bail; + } + } + + /* + * Create a RegisterMap with the contents. + * + * TODO: consider using a threshold other than merely ">=". We would + * get poorer compression but potentially use less native heap space. + */ + static const int kHeaderSize = offsetof(RegisterMap, data); + int newDataSize = tmpPtr - tmpBuf; + int newMapSize; + + newMapSize = kHeaderSize + unsignedLeb128Size(newDataSize) + newDataSize; + if (newMapSize >= origSize) { + if (debug) { + LOGD("Final comp size >= original (%d vs %d): %s.%s\n", + newMapSize, origSize, meth->clazz->descriptor, meth->name); + } + goto bail; + } + + pNewMap = (RegisterMap*) malloc(newMapSize); + if (pNewMap == NULL) + goto bail; + dvmRegisterMapSetFormat(pNewMap, kRegMapFormatDifferential); + dvmRegisterMapSetOnHeap(pNewMap, true); + dvmRegisterMapSetRegWidth(pNewMap, regWidth); + dvmRegisterMapSetNumEntries(pNewMap, numEntries); + + tmpPtr = pNewMap->data; + tmpPtr = writeUnsignedLeb128(tmpPtr, newDataSize); + memcpy(tmpPtr, tmpBuf, newDataSize); + + if (REGISTER_MAP_VERBOSE) { + LOGD("Compression successful (%d -> %d) from aw=%d rw=%d ne=%d\n", + computeRegisterMapSize(pMap), computeRegisterMapSize(pNewMap), + addrWidth, regWidth, numEntries); + } + +bail: + free(tmpBuf); + return pNewMap; +} + +/* + * Toggle the value of the "idx"th bit in "ptr". + */ +static inline void toggleBit(u1* ptr, int idx) +{ + ptr[idx >> 3] ^= 1 << (idx & 0x07); +} + +/* + * Expand a compressed map to an uncompressed form. + * + * Returns a newly-allocated RegisterMap on success, or NULL on failure. + * + * TODO: consider using the linear allocator or a custom allocator with + * LRU replacement for these instead of the native heap. + */ +static RegisterMap* uncompressMapDifferential(const RegisterMap* pMap) +{ + RegisterMap* pNewMap = NULL; + static const int kHeaderSize = offsetof(RegisterMap, data); + u1 format = dvmRegisterMapGetFormat(pMap); + RegisterMapFormat newFormat; + int regWidth, numEntries, newAddrWidth, newMapSize; + + if (format != kRegMapFormatDifferential) { + LOGE("Not differential (%d)\n", format); + goto bail; + } + + regWidth = dvmRegisterMapGetRegWidth(pMap); + numEntries = dvmRegisterMapGetNumEntries(pMap); + + /* get the data size; we can check this at the end */ + const u1* srcPtr = pMap->data; + int expectedSrcLen = readUnsignedLeb128(&srcPtr); + const u1* srcStart = srcPtr; + + /* get the initial address and the 16-bit address flag */ + int addr = *srcPtr & 0x7f; + if ((*srcPtr & 0x80) == 0) { + newFormat = kRegMapFormatCompact8; + newAddrWidth = 1; + } else { + newFormat = kRegMapFormatCompact16; + newAddrWidth = 2; + } + srcPtr++; + + /* now we know enough to allocate the new map */ + if (REGISTER_MAP_VERBOSE) { + LOGI("Expanding to map aw=%d rw=%d ne=%d\n", + newAddrWidth, regWidth, numEntries); + } + newMapSize = kHeaderSize + (newAddrWidth + regWidth) * numEntries; + pNewMap = (RegisterMap*) malloc(newMapSize); + if (pNewMap == NULL) + goto bail; + + dvmRegisterMapSetFormat(pNewMap, newFormat); + dvmRegisterMapSetOnHeap(pNewMap, true); + dvmRegisterMapSetRegWidth(pNewMap, regWidth); + dvmRegisterMapSetNumEntries(pNewMap, numEntries); + + /* + * Write the start address and initial bits to the new map. + */ + u1* dstPtr = pNewMap->data; + + *dstPtr++ = addr & 0xff; + if (newAddrWidth > 1) + *dstPtr++ = (u1) (addr >> 8); + + memcpy(dstPtr, srcPtr, regWidth); + + int prevAddr = addr; + const u1* prevBits = dstPtr; /* point at uncompressed data */ + + dstPtr += regWidth; + srcPtr += regWidth; + + /* + * Walk through, uncompressing one line at a time. + */ + int entry; + for (entry = 1; entry < numEntries; entry++) { + int addrDiff; + u1 key; + + key = *srcPtr++; + + /* get the address */ + if ((key & 0x07) == 7) { + /* address diff follows in ULEB128 */ + addrDiff = readUnsignedLeb128(&srcPtr); + } else { + addrDiff = (key & 0x07) +1; + } + + addr = prevAddr + addrDiff; + *dstPtr++ = addr & 0xff; + if (newAddrWidth > 1) + *dstPtr++ = (u1) (addr >> 8); + + /* unpack the bits */ + if ((key & 0x08) != 0) { + int bitCount = (key >> 4); + if (bitCount == 0) { + /* no bits changed, just copy previous */ + memcpy(dstPtr, prevBits, regWidth); + } else if (bitCount == 15) { + /* full copy of bit vector is present; ignore prevBits */ + memcpy(dstPtr, srcPtr, regWidth); + srcPtr += regWidth; + } else { + /* copy previous bits and modify listed indices */ + memcpy(dstPtr, prevBits, regWidth); + while (bitCount--) { + int bitIndex = readUnsignedLeb128(&srcPtr); + toggleBit(dstPtr, bitIndex); + } + } + } else { + /* copy previous bits and modify the specified one */ + memcpy(dstPtr, prevBits, regWidth); + + /* one bit, from 0-15 inclusive, was changed */ + toggleBit(dstPtr, key >> 4); + } + + prevAddr = addr; + prevBits = dstPtr; + dstPtr += regWidth; + } + + if (dstPtr - (u1*) pNewMap != newMapSize) { + LOGE("ERROR: output %d bytes, expected %d\n", + dstPtr - (u1*) pNewMap, newMapSize); + goto bail; + } + + if (srcPtr - srcStart != expectedSrcLen) { + LOGE("ERROR: consumed %d bytes, expected %d\n", + srcPtr - srcStart, expectedSrcLen); + goto bail; + } + + if (REGISTER_MAP_VERBOSE) { + LOGD("Expansion successful (%d -> %d)\n", + computeRegisterMapSize(pMap), computeRegisterMapSize(pNewMap)); + } + + return pNewMap; + +bail: + free(pNewMap); + return NULL; +} + + +/* + * =========================================================================== + * Just-in-time generation + * =========================================================================== + */ + +#if 0 /* incomplete implementation; may be removed entirely in the future */ + +/* +Notes on just-in-time RegisterMap generation + +Generating RegisterMap tables as part of verification is convenient because +we generate most of what we need to know as part of doing the verify. +The negative aspect of doing it this way is that we must store the +result in the DEX file (if we're verifying ahead of time) or in memory +(if verifying during class load) for every concrete non-native method, +even if we never actually need the map during a GC. + +A simple but compact encoding of register map data increases the size of +optimized DEX files by about 25%, so size considerations are important. + +We can instead generate the RegisterMap at the point where it is needed. +In a typical application we only need to convert about 2% of the loaded +methods, and we can generate type-precise roots reasonably quickly because +(a) we know the method has already been verified and hence can make a +lot of assumptions, and (b) we don't care what type of object a register +holds, just whether or not it holds a reference, and hence can skip a +lot of class resolution gymnastics. + +There are a couple of problems with this approach however. First, to +get good performance we really want an implementation that is largely +independent from the verifier, which means some duplication of effort. +Second, we're dealing with post-dexopt code, which contains "quickened" +instructions. We can't process those without either tracking type +information (which slows us down) or storing additional data in the DEX +file that allows us to reconstruct the original instructions (adds ~5% +to the size of the ODEX). + + +Implementation notes... + +Both type-precise and live-precise information can be generated knowing +only whether or not a register holds a reference. We don't need to +know what kind of reference or whether the object has been initialized. +Not only can we skip many of the fancy steps in the verifier, we can +initialize from simpler sources, e.g. the initial registers and return +type are set from the "shorty" signature rather than the full signature. + +The short-term storage needs for just-in-time register map generation can +be much lower because we can use a 1-byte SRegType instead of a 4-byte +RegType. On the other hand, if we're not doing type-precise analysis +in the verifier we only need to store register contents at every branch +target, rather than every GC point (which are much more frequent). + +Whether it happens in the verifier or independently, because this is done +with native heap allocations that may be difficult to return to the system, +an effort should be made to minimize memory use. +*/ /* * This is like RegType in the verifier, but simplified. It holds a value diff --git a/vm/analysis/RegisterMap.h b/vm/analysis/RegisterMap.h index 2a890e777..dc17b1d3f 100644 --- a/vm/analysis/RegisterMap.h +++ b/vm/analysis/RegisterMap.h @@ -14,38 +14,221 @@ * limitations under the License. */ -// ** UNDER CONSTRUCTION ** - /* * Declaration of register map data structure and related functions. + * + * These structures should be treated as opaque through most of the VM. */ #ifndef _DALVIK_REGISTERMAP #define _DALVIK_REGISTERMAP +#include "analysis/VerifySubs.h" +#include "analysis/CodeVerify.h" + /* * Format enumeration for RegisterMap data area. */ typedef enum RegisterMapFormat { - kFormatUnknown = 0, - kFormatCompact8, /* compact layout, 8-bit addresses */ - kFormatCompact16, /* compact layout, 16-bit addresses */ - // TODO: compressed stream + kRegMapFormatUnknown = 0, + kRegMapFormatNone, /* indicates no map data follows */ + kRegMapFormatCompact8, /* compact layout, 8-bit addresses */ + kRegMapFormatCompact16, /* compact layout, 16-bit addresses */ + kRegMapFormatDifferential, /* compressed, differential encoding */ + + kRegMapFormatOnHeap = 0x80, /* bit flag, indicates allocation on heap */ } RegisterMapFormat; /* * This is a single variable-size structure. It may be allocated on the * heap or mapped out of a (post-dexopt) DEX file. + * + * 32-bit alignment of the structure is NOT guaranteed. This makes it a + * little awkward to deal with as a structure; to avoid accidents we use + * only byte types. Multi-byte values are little-endian. + * + * Size of (format==FormatNone): 1 byte + * Size of (format==FormatCompact8): 4 + (1 + regWidth) * numEntries + * Size of (format==FormatCompact16): 4 + (2 + regWidth) * numEntries */ struct RegisterMap { /* header */ - u1 format; /* enum RegisterMapFormat */ + u1 format; /* enum RegisterMapFormat; MUST be first entry */ u1 regWidth; /* bytes per register line, 1+ */ - u2 numEntries; /* number of entries */ + u1 numEntries[2]; /* number of entries */ - /* data starts here; no alignment guarantees made */ + /* raw data starts here; need not be aligned */ u1 data[1]; }; +bool dvmRegisterMapStartup(void); +void dvmRegisterMapShutdown(void); + +/* + * Get the format. + */ +INLINE RegisterMapFormat dvmRegisterMapGetFormat(const RegisterMap* pMap) { + return pMap->format & ~(kRegMapFormatOnHeap); +} + +/* + * Set the format. + */ +INLINE void dvmRegisterMapSetFormat(RegisterMap* pMap, RegisterMapFormat format) +{ + pMap->format &= kRegMapFormatOnHeap; + pMap->format |= format; +} + +/* + * Get the "on heap" flag. + */ +INLINE bool dvmRegisterMapGetOnHeap(const RegisterMap* pMap) { + return (pMap->format & kRegMapFormatOnHeap) != 0; +} + +/* + * Get the register bit vector width, in bytes. + */ +INLINE u1 dvmRegisterMapGetRegWidth(const RegisterMap* pMap) { + return pMap->regWidth; +} + +/* + * Set the register bit vector width, in bytes. + */ +INLINE void dvmRegisterMapSetRegWidth(RegisterMap* pMap, int regWidth) { + pMap->regWidth = regWidth; +} + +/* + * Set the "on heap" flag. + */ +INLINE void dvmRegisterMapSetOnHeap(RegisterMap* pMap, bool val) { + if (val) + pMap->format |= kRegMapFormatOnHeap; + else + pMap->format &= ~(kRegMapFormatOnHeap); +} + +/* + * Get the number of entries in this map. + */ +INLINE u2 dvmRegisterMapGetNumEntries(const RegisterMap* pMap) { + return pMap->numEntries[0] | (pMap->numEntries[1] << 8); +} + +/* + * Set the number of entries in this map. + */ +INLINE void dvmRegisterMapSetNumEntries(RegisterMap* pMap, u2 numEntries) { + pMap->numEntries[0] = (u1) numEntries; + pMap->numEntries[1] = numEntries >> 8; +} + +/* + * Retrieve the bit vector for the specified address. This is a pointer + * to the bit data from an uncompressed map, or to a temporary copy of + * data from a compressed map. + * + * The caller must call dvmReleaseRegisterMapLine() with the result. + * + * Returns NULL if not found. + */ +const u1* dvmRegisterMapGetLine(const RegisterMap* pMap, int addr); + +/* + * Release "data". + * + * If "pMap" points to a compressed map from which we have expanded a + * single line onto the heap, this will free "data"; otherwise, it does + * nothing. + * + * TODO: decide if this is still a useful concept. + */ +INLINE void dvmReleaseRegisterMapLine(const RegisterMap* pMap, const u1* data) +{} + + +/* + * A pool of register maps for methods associated with a single class. + * + * Each entry is a 4-byte method index followed by the 32-bit-aligned + * RegisterMap. The size of the RegisterMap is determined by parsing + * the map. The lack of an index reduces random access speed, but we + * should be doing that rarely (during class load) and it saves space. + * + * These structures are 32-bit aligned. + */ +typedef struct RegisterMapMethodPool { + u2 methodCount; /* chiefly used as a sanity check */ + + /* stream of per-method data starts here */ + u4 methodData[1]; +} RegisterMapMethodPool; + +/* + * Header for the memory-mapped RegisterMap pool in the DEX file. + * + * The classDataOffset table provides offsets from the start of the + * RegisterMapPool structure. There is one entry per class (including + * interfaces, which can have static initializers). + * + * The offset points to a RegisterMapMethodPool. + * + * These structures are 32-bit aligned. + */ +typedef struct RegisterMapClassPool { + u4 numClasses; + + /* offset table starts here, 32-bit aligned; offset==0 means no data */ + u4 classDataOffset[1]; +} RegisterMapClassPool; + +/* + * Find the register maps for this class. (Used during class loading.) + * If "pNumMaps" is non-NULL, it will return the number of maps in the set. + * + * Returns NULL if none is available. + */ +const void* dvmRegisterMapGetClassData(const DexFile* pDexFile, u4 classIdx, + u4* pNumMaps); + +/* + * Get the register map for the next method. "*pPtr" will be advanced past + * the end of the map. (Used during class loading.) + * + * This should initially be called with the result from + * dvmRegisterMapGetClassData(). + */ +const RegisterMap* dvmRegisterMapGetNext(const void** pPtr); + +/* + * This holds some meta-data while we construct the set of register maps + * for a DEX file. + * + * In particular, it keeps track of our temporary mmap region so we can + * free it later. + */ +typedef struct RegisterMapBuilder { + /* public */ + void* data; + size_t size; + + /* private */ + MemMapping memMap; +} RegisterMapBuilder; + +/* + * Generate a register map set for all verified classes in "pDvmDex". + */ +RegisterMapBuilder* dvmGenerateRegisterMaps(DvmDex* pDvmDex); + +/* + * Free the builder. + */ +void dvmFreeRegisterMapBuilder(RegisterMapBuilder* pBuilder); + + /* * Generate the register map for a previously-verified method. * @@ -97,4 +280,31 @@ typedef struct VerifierData { */ RegisterMap* dvmGenerateRegisterMapV(VerifierData* vdata); +/* + * Get the expanded form of the register map associated with the specified + * method. May update method->registerMap, possibly freeing the previous + * map. + * + * Returns NULL on failure (e.g. unable to expand map). + * + * NOTE: this function is not synchronized; external locking is mandatory. + * (This is expected to be called at GC time.) + */ +const RegisterMap* dvmGetExpandedRegisterMap0(Method* method); +INLINE const RegisterMap* dvmGetExpandedRegisterMap(Method* method) +{ + const RegisterMap* curMap = method->registerMap; + if (curMap == NULL) + return NULL; + RegisterMapFormat format = dvmRegisterMapGetFormat(curMap); + if (format == kRegMapFormatCompact8 || format == kRegMapFormatCompact16) { + return curMap; + } else { + return dvmGetExpandedRegisterMap0(method); + } +} + +/* dump stats gathered during register map creation process */ +void dvmRegisterMapDumpStats(void); + #endif /*_DALVIK_REGISTERMAP*/ diff --git a/vm/analysis/VerifySubs.c b/vm/analysis/VerifySubs.c index 8dcc6f8dc..39d6dc848 100644 --- a/vm/analysis/VerifySubs.c +++ b/vm/analysis/VerifySubs.c @@ -65,6 +65,8 @@ bool dvmComputeCodeWidths(const Method* meth, InsnFlags* insnFlags, if (width == 0) { LOG_VFY_METH(meth, "VFY: invalid post-opt instruction (0x%x)\n", instr); + LOGI("### instr=%d width=%d table=%d\n", + instr, width, dexGetInstrWidthAbs(gDvm.instrWidth, instr)); goto bail; } if (width < 0 || width > 5) { diff --git a/vm/analysis/VerifySubs.h b/vm/analysis/VerifySubs.h index 4d5b57ce8..a87c6f154 100644 --- a/vm/analysis/VerifySubs.h +++ b/vm/analysis/VerifySubs.h @@ -57,7 +57,11 @@ bool dvmCheckBranchTarget(const Method* meth, InsnFlags* insnFlags, #define LOG_VFY_METH(_meth, ...) dvmLogVerifyFailure(_meth, __VA_ARGS__) /* log verification failure with optional method info */ -void dvmLogVerifyFailure(const Method* meth, const char* format, ...); +void dvmLogVerifyFailure(const Method* meth, const char* format, ...) +#if defined(__GNUC__) + __attribute__ ((format(printf, 2, 3))) +#endif + ; /* log verification failure due to resolution trouble */ void dvmLogUnableToResolveClass(const char* missingClassDescr, diff --git a/vm/arch/arm/CallEABI.S b/vm/arch/arm/CallEABI.S index a98473fa2..9717e3b3d 100644 --- a/vm/arch/arm/CallEABI.S +++ b/vm/arch/arm/CallEABI.S @@ -154,7 +154,7 @@ DBG bne dvmAbort subeq r3, r3, #1 @ Do we have arg padding flags in "argInfo"? (just need to check hi bit) - teqs r2, #0 + teq r2, #0 bmi .Lno_arg_info /* @@ -239,7 +239,12 @@ DBG strcs ip, [r8] @ DEBUG DEBUG @ call the method ldr ip, [r4, #8] @ func +#ifdef __ARM_HAVE_BLX blx ip +#else + mov lr, pc + bx ip +#endif @ We're back, result is in r0 or (for long/double) r0-r1. @ diff --git a/vm/arch/sh/CallSH4ABI.S b/vm/arch/sh/CallSH4ABI.S new file mode 100644 index 000000000..f8b2ddc0b --- /dev/null +++ b/vm/arch/sh/CallSH4ABI.S @@ -0,0 +1,400 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * Invoking JNI native method via SH4 ABI. + * This inplementation follows the spec found in following URL. + * http://www.ecos.sourceware.org/docs-1.3.1/ref/gnupro-ref/sh/SH_ch01.html#pgfId-461254 + + * This version supports SH4A little endian. + */ + .text + .align 4 + .type dvmPlatformInvoke, #function + .globl dvmPlatformInvoke + +/* + * @param r4 void* pEnv (used as scrach after invoking method) + * @param r5 ClassObject* clazz + * @param r6 int argInfo + * @param r7 int argc + * @param r15[0] const u4 * argv + * @param r15[1] const char * shorty + * @param r15[2] void * func + * @param r15[3] JValue * pReturn + * + * @remark r0,r1 Scratch before invoking method. + * Return value after invoking method. + * @remark r2 shorty pointer + * @remark r3 argv pointer before invoking method. + * pReturn after invoking method. + * @remark r8-11 Don't touch. + * @remark r12 status of r5-7 + * @remark r13 status of fr4-11 + * @remark r14 Keep stack pointer. + */ +dvmPlatformInvoke: + ## save preserved regsiters + mov.l r14, @-r15 + mov r15, r14 + add #4, r14 /* r14 = original r15 = stack pointer */ + mov.l r13, @-r15 + mov.l r12, @-r15 + sts.l pr, @-r15 + + # fetch arguments + mov.l @r14, r3 /* argv */ + mov.l @(4,r14), r2 /* shorty for argumnets */ + mov #1, r0 /* shorty's 1st byte specify ret value type. */ + add r0, r2 + +### initialize local variables + + ## r12 ... status of r6, and r7 + ## bit 1 << 0 : if r6 is available, it contains 1. + ## bit 1 << 1 : if r7 is available, it contains 1. + ## Note : r4 is always used to pass pEnv. + ## r5 is always used for clazz or object + mov #3, r12 /* b0000-0111 : r5-7 avialble. */ + + ## r13 ... status of fr4-fr11 + ## bit 1 << 0 : if fr4 is available, it contains 1. + ## bit 1 << 1 : if fr5 is available, it contains 1. + ## ... + ## bit 1 << 7 : if fr11 is available, it contains 1. + mov #0xFF, r13 /* b1111-1111 : fr4-11 avialble. */ + +### put arguments + + ## ... keep pEnv in r4 as is. + + ## check clazz + mov #0, r0 + cmp/eq r0, r5 + bf arg_loop /* if r5 has clazz, keep it as is */ + mov.l @r3+, r5 /* put object arg in r5 */ + + ## other args +arg_loop: +one_arg_handled: + mov.b @r2+, r0 + cmp/eq #0, r0 /* if (*shorty == '\0) */ + bf process_one_arg + bra arg_end /* no argument left */ + nop + +process_one_arg: + + ## check arg type + + cmp/eq #'F', r0 + bt jfloat_arg + + cmp/eq #'D', r0 + bt jdouble_arg + + cmp/eq #'J', r0 + bt jlong_arg + + ## other 32bit arg types + mov r12, r0 + cmp/eq #0, r0 + bt put_32bit_on_stack /* r6-7 not available */ + + tst #1, r0 + bt j32_arg_1 + mov.l @r3+, r6 /* put one arg in r6 */ + mov #1, r0 /* r6 is not available now. */ + not r0, r0 + and r0, r12 + bra one_arg_handled + nop +j32_arg_1: + tst #2, r0 + bt j32_arg_fatal_error + mov.l @r3+, r7 /* put one arg in r7 */ + mov #2, r0 /* r7 is not available now. */ + not r0, r0 + and r0, r12 + bra one_arg_handled + nop + +j32_arg_fatal_error: + bra j32_arg_fatal_error + nop + +jlong_arg: + mov r12, r0 + cmp/eq #0, r0 + bt put_64bit_on_stack /* r6-7 not available */ + + and #3, r0 + cmp/eq #3, r0 + bf put_64bit_on_stack /* consequent two registers not available. */ + mov.l @r3+, r6 /* put one arg in r6 and r7 */ + mov.l @r3+, r7 + mov #3, r0 /* r6 and r7 are not available now. */ + not r0, r0 + and r0, r12 + bra one_arg_handled + nop + + # utility routines are placed here make short range jumps available. +put_32bit_on_stack: + mov.l @r3+, r0 + mov.l r0, @-r15 + bra one_arg_handled + nop + +put_64bit_on_stack: + mov.l @r3+, r0 + mov.l r0, @-r15 /* Pay attention that the endianness is */ + mov.l @r3+, r0 /* once reversed. It is corrected when the */ + mov.l r0, @-r15 /* arguments on stack are revesred before */ + bra one_arg_handled /* jni call */ + nop + +jdouble_arg: + mov r13, r0 + cmp/eq #0, r0 + bt put_64bit_on_stack /* fr4-11 not available */ + + and #3, r0 + cmp/eq #3, r0 + bf jdouble_arg_1 + + fmov.s @r3+, fr5 /* put one arg to drX */ + fmov.s @r3+, fr4 + mov #3, r0 /* fr4-frX not available now. */ + not r0, r0 + and r0, r13 + bra one_arg_handled + nop + +jdouble_arg_1: + mov r13, r0 + and #12, r0 + cmp/eq #12, r0 + bf jdouble_arg_2 + + fmov.s @r3+, fr7 /* put one arg to drX */ + fmov.s @r3+, fr6 + mov #15, r0 /* fr4-frX not available now. */ + not r0, r0 + and r0, r13 + bra one_arg_handled + nop + +jdouble_arg_2: + mov r13, r0 + and #48, r0 + cmp/eq #48, r0 + bf jdouble_arg_3 + fmov.s @r3+, fr9 /* put one arg to drX */ + fmov.s @r3+, fr8 + mov #63, r0 /* fr4-frX not available now. */ + not r0, r0 + and r0, r13 + bra one_arg_handled + nop + +jdouble_arg_3: + mov r13, r0 + and #192, r0 + cmp/eq #192, r0 + bf put_64bit_on_stack + fmov.s @r3+, fr11 /* put one arg to drX */ + fmov.s @r3+, fr10 + mov #0, r13 /* fr4-fr11 all not available now. */ + bra one_arg_handled + nop + +jfloat_arg: + mov r13, r0 + cmp/eq #0, r0 + bt put_32bit_on_stack /* fr4-11 not available */ + + tst #2, r0 + bt jfloat_arg_1 + fmov.s @r3+, fr5 /* put one arg to frX */ + mov #2, r0 /* frX not available now. */ + not r0, r0 + and r0, r13 + bra one_arg_handled + nop + +jfloat_arg_1: + tst #1, r0 + bt jfloat_arg_2 + fmov.s @r3+, fr4 /* put one arg to frX */ + mov #1, r0 /* frX not available now. */ + not r0, r0 + and r0, r13 + bra one_arg_handled + nop + +jfloat_arg_2: + tst #8, r0 + bt jfloat_arg_3 + fmov.s @r3+, fr7 /* put one arg to frX */ + mov #8, r0 /* frX not available now. */ + not r0, r0 + and r0, r13 + bra one_arg_handled + nop + +jfloat_arg_3: + tst #4, r0 + bt jfloat_arg_4 + fmov.s @r3+, fr6 /* put one arg to frX */ + mov #4, r0 /* frX not available now. */ + not r0, r0 + and r0, r13 + bra one_arg_handled + nop + +jfloat_arg_4: + tst #32, r0 + bt jfloat_arg_5 + fmov.s @r3+, fr9 /* put one arg to frX */ + mov #32, r0 /* frX not available now. */ + not r0, r0 + and r0, r13 + bra one_arg_handled + nop + +jfloat_arg_5: + tst #16, r0 + bt jfloat_arg_6 + fmov.s @r3+, fr8 /* put one arg to frX */ + mov #16, r0 /* frX not available now. */ + not r0, r0 + and r0, r13 + bra one_arg_handled + nop + +jfloat_arg_6: + tst #128, r0 + bt jfloat_arg_7 + fmov.s @r3+, fr11 /* put one arg to frX */ + mov #127, r0 /* frX not available now. */ + not r0, r0 + and r0, r13 + bra one_arg_handled + nop + +jfloat_arg_7: + tst #64, r0 + bt jfloat_fatal_error + fmov.s @r3+, fr10 /* put one arg to frX */ + mov #64, r0 /* frX not available now. */ + not r0, r0 + and r0, r13 + bra one_arg_handled + nop + +jfloat_fatal_error: + bra jfloat_fatal_error: + nop + +arg_end: + + +### reverse the variables on stack + mov r14, r12 /* points to first arg on stack */ + add #-20, r12 + mov r15, r13 /* points to last arg on stack */ +arg_rev_loop: + cmp/hs r12, r13 /* When r13 >= r12 (unsigned), 1->T */ + bt arg_rev_end + mov.l @r12, r0 + mov.l @r13, r1 + mov.l r0, @r13 + mov.l r1, @r12 + add #-4, r12 + add #4, r13 + bra arg_rev_loop + nop + +arg_rev_end: + +### invoke the JNI function. + + mov.l @(8,r14), r0 + jsr @r0 + nop + +### pass the return value + + /* + * r0 and r1 keep return value. + */ + + ## fetch data + mov.l @(4,r14), r2 /* reload shorty */ + mov.b @r2, r2 /* first byte specifyes return value type. */ + mov.l @(12,r14), r3 /* pReturn */ + + ## check return value types + + mov #'V', r4 + cmp/eq r4, r2 + bt end + + mov #'F', r4 + cmp/eq r4, r2 + bt jfloat_ret + + mov #'D', r4 + cmp/eq r4, r2 + bt jdouble_ret + + mov #'J', r4 + cmp/eq r4, r2 + bt jlong_ret + + ## fall-through for other 32 bit java types. + + ## load return values +j32_ret: + bra end + mov.l r0, @r3 /* delay slot */ + +jfloat_ret: + bra end + fmov.s fr0, @r3 /* delay slot */ + +jdouble_ret: + fmov.s fr1, @r3 + mov #4, r0 + bra end + fmov.s fr0, @(r0,r3) /* delay slot */ + +jlong_ret: + mov.l r0, @r3 + bra end + mov.l r1, @(4,r3) /* delay slot */ + +end: + ## restore preserved registers + mov r14, r15 + add #-16, r15 + lds.l @r15+, pr + mov.l @r15+, r12 + mov.l @r15+, r13 + mov.l @r15+, r14 + + rts /* dvmPlatformInvoke returns void. */ + nop diff --git a/vm/arch/x86/Call386ABI.S b/vm/arch/x86/Call386ABI.S index 9e32bdd87..c98876c74 100644 --- a/vm/arch/x86/Call386ABI.S +++ b/vm/arch/x86/Call386ABI.S @@ -131,12 +131,32 @@ isClass: /* Is FP? */ cmpl $2,%ebx jle isFP - /* No need to distinguish between 32/64 - just save both */ + cmpl $4,%ebx /* smaller than 32-bits? */ + jg isSmall +storeRetval: + /* Blindly storing 64-bits won't hurt 32-bit case */ movl %eax,(%ecx) movl %edx,4(%ecx) jmp cleanUpAndExit +isSmall: + cmpl $7,%ebx /* S1? */ + jne checkShort + movsbl %al,%eax + movl %eax,(%ecx) + jmp cleanUpAndExit +checkShort: + cmpl $6,%ebx /* U2? */ + jne isSignedShort + movzwl %ax,%eax + movl %eax,(%ecx) + jmp cleanUpAndExit +isSignedShort: + /* Must be S2 */ + movswl %ax,%eax + movl %eax,(%ecx) + jmp cleanUpAndExit isFP: - /* Is single? */ + /* Is Float? */ cmpl $1,%ebx je saveFloat fstpl (%ecx) diff --git a/vm/compiler/Compiler.c b/vm/compiler/Compiler.c new file mode 100644 index 000000000..203a08020 --- /dev/null +++ b/vm/compiler/Compiler.c @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "Dalvik.h" +#include "interp/Jit.h" +#include "CompilerInternals.h" + + +static inline bool workQueueLength(void) +{ + return gDvmJit.compilerQueueLength; +} + +static CompilerWorkOrder workDequeue(void) +{ + assert(gDvmJit.compilerWorkQueue[gDvmJit.compilerWorkDequeueIndex].kind + != kWorkOrderInvalid); + CompilerWorkOrder work = + gDvmJit.compilerWorkQueue[gDvmJit.compilerWorkDequeueIndex]; + gDvmJit.compilerWorkQueue[gDvmJit.compilerWorkDequeueIndex++].kind = + kWorkOrderInvalid; + if (gDvmJit.compilerWorkDequeueIndex == COMPILER_WORK_QUEUE_SIZE) { + gDvmJit.compilerWorkDequeueIndex = 0; + } + gDvmJit.compilerQueueLength--; + + /* Remember the high water mark of the queue length */ + if (gDvmJit.compilerQueueLength > gDvmJit.compilerMaxQueued) + gDvmJit.compilerMaxQueued = gDvmJit.compilerQueueLength; + + return work; +} + +bool dvmCompilerWorkEnqueue(const u2 *pc, WorkOrderKind kind, void* info) +{ + int cc; + int i; + int numWork; + + dvmLockMutex(&gDvmJit.compilerLock); + + /* Queue full */ + if (gDvmJit.compilerQueueLength == COMPILER_WORK_QUEUE_SIZE || + gDvmJit.codeCacheFull == true) { + dvmUnlockMutex(&gDvmJit.compilerLock); + return false; + } + + for (numWork = gDvmJit.compilerQueueLength, + i = gDvmJit.compilerWorkDequeueIndex; + numWork > 0; + numWork--) { + /* Already enqueued */ + if (gDvmJit.compilerWorkQueue[i++].pc == pc) + goto done; + /* Wrap around */ + if (i == COMPILER_WORK_QUEUE_SIZE) + i = 0; + } + + gDvmJit.compilerWorkQueue[gDvmJit.compilerWorkEnqueueIndex].pc = pc; + gDvmJit.compilerWorkQueue[gDvmJit.compilerWorkEnqueueIndex].kind = kind; + gDvmJit.compilerWorkQueue[gDvmJit.compilerWorkEnqueueIndex].info = info; + gDvmJit.compilerWorkEnqueueIndex++; + if (gDvmJit.compilerWorkEnqueueIndex == COMPILER_WORK_QUEUE_SIZE) + gDvmJit.compilerWorkEnqueueIndex = 0; + gDvmJit.compilerQueueLength++; + cc = pthread_cond_signal(&gDvmJit.compilerQueueActivity); + assert(cc == 0); + +done: + dvmUnlockMutex(&gDvmJit.compilerLock); + return true; +} + +/* Block until queue length is 0 */ +void dvmCompilerDrainQueue(void) +{ + dvmLockMutex(&gDvmJit.compilerLock); + while (workQueueLength() != 0 && !gDvmJit.haltCompilerThread) { + pthread_cond_wait(&gDvmJit.compilerQueueEmpty, &gDvmJit.compilerLock); + } + dvmUnlockMutex(&gDvmJit.compilerLock); +} + +static void *compilerThreadStart(void *arg) +{ + dvmLockMutex(&gDvmJit.compilerLock); + /* + * Since the compiler thread will not touch any objects on the heap once + * being created, we just fake its state as VMWAIT so that it can be a + * bit late when there is suspend request pending. + */ + dvmChangeStatus(NULL, THREAD_VMWAIT); + while (!gDvmJit.haltCompilerThread) { + if (workQueueLength() == 0) { + int cc; + cc = pthread_cond_signal(&gDvmJit.compilerQueueEmpty); + assert(cc == 0); + pthread_cond_wait(&gDvmJit.compilerQueueActivity, + &gDvmJit.compilerLock); + continue; + } else { + do { + CompilerWorkOrder work = workDequeue(); + dvmUnlockMutex(&gDvmJit.compilerLock); + /* Check whether there is a suspend request on me */ + dvmCheckSuspendPending(NULL); + /* Is JitTable filling up? */ + if (gDvmJit.jitTableEntriesUsed > + (gDvmJit.jitTableSize - gDvmJit.jitTableSize/4)) { + dvmJitResizeJitTable(gDvmJit.jitTableSize * 2); + } + if (gDvmJit.haltCompilerThread) { + LOGD("Compiler shutdown in progress - discarding request"); + } else { + /* Compilation is successful */ + if (dvmCompilerDoWork(&work)) { + dvmJitSetCodeAddr(work.pc, work.result.codeAddress, + work.result.instructionSet); + } + } + free(work.info); + dvmLockMutex(&gDvmJit.compilerLock); + } while (workQueueLength() != 0); + } + } + pthread_cond_signal(&gDvmJit.compilerQueueEmpty); + dvmUnlockMutex(&gDvmJit.compilerLock); + + LOGD("Compiler thread shutting down\n"); + return NULL; +} + +bool dvmCompilerSetupCodeCache(void) +{ + extern void dvmCompilerTemplateStart(void); + extern void dmvCompilerTemplateEnd(void); + + /* Allocate the code cache */ + gDvmJit.codeCache = mmap(0, CODE_CACHE_SIZE, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (gDvmJit.codeCache == MAP_FAILED) { + LOGE("Failed to create the code cache: %s\n", strerror(errno)); + return false; + } + + /* Copy the template code into the beginning of the code cache */ + int templateSize = (intptr_t) dmvCompilerTemplateEnd - + (intptr_t) dvmCompilerTemplateStart; + memcpy((void *) gDvmJit.codeCache, + (void *) dvmCompilerTemplateStart, + templateSize); + + gDvmJit.templateSize = templateSize; + gDvmJit.codeCacheByteUsed = templateSize; + + /* Flush dcache and invalidate the icache to maintain coherence */ + cacheflush((intptr_t) gDvmJit.codeCache, + (intptr_t) gDvmJit.codeCache + CODE_CACHE_SIZE, 0); + return true; +} + +bool dvmCompilerStartup(void) +{ + /* Make sure the BBType enum is in sane state */ + assert(CHAINING_CELL_NORMAL == 0); + + /* Architecture-specific chores to initialize */ + if (!dvmCompilerArchInit()) + goto fail; + + /* + * Setup the code cache if it is not done so already. For apps it should be + * done by the Zygote already, but for command-line dalvikvm invocation we + * need to do it here. + */ + if (gDvmJit.codeCache == NULL) { + if (!dvmCompilerSetupCodeCache()) + goto fail; + } + + /* Allocate the initial arena block */ + if (dvmCompilerHeapInit() == false) { + goto fail; + } + + dvmInitMutex(&gDvmJit.compilerLock); + pthread_cond_init(&gDvmJit.compilerQueueActivity, NULL); + pthread_cond_init(&gDvmJit.compilerQueueEmpty, NULL); + + dvmLockMutex(&gDvmJit.compilerLock); + + gDvmJit.haltCompilerThread = false; + + /* Reset the work queue */ + memset(gDvmJit.compilerWorkQueue, 0, + sizeof(CompilerWorkOrder) * COMPILER_WORK_QUEUE_SIZE); + gDvmJit.compilerWorkEnqueueIndex = gDvmJit.compilerWorkDequeueIndex = 0; + gDvmJit.compilerQueueLength = 0; + gDvmJit.compilerHighWater = + COMPILER_WORK_QUEUE_SIZE - (COMPILER_WORK_QUEUE_SIZE/4); + + assert(gDvmJit.compilerHighWater < COMPILER_WORK_QUEUE_SIZE); + if (!dvmCreateInternalThread(&gDvmJit.compilerHandle, "Compiler", + compilerThreadStart, NULL)) { + dvmUnlockMutex(&gDvmJit.compilerLock); + goto fail; + } + + /* Track method-level compilation statistics */ + gDvmJit.methodStatsTable = dvmHashTableCreate(32, NULL); + + dvmUnlockMutex(&gDvmJit.compilerLock); + + return true; + +fail: + return false; +} + +void dvmCompilerShutdown(void) +{ + void *threadReturn; + + if (gDvmJit.compilerHandle) { + + gDvmJit.haltCompilerThread = true; + + dvmLockMutex(&gDvmJit.compilerLock); + pthread_cond_signal(&gDvmJit.compilerQueueActivity); + dvmUnlockMutex(&gDvmJit.compilerLock); + + if (pthread_join(gDvmJit.compilerHandle, &threadReturn) != 0) + LOGW("Compiler thread join failed\n"); + else + LOGD("Compiler thread has shut down\n"); + } +} diff --git a/vm/compiler/Compiler.h b/vm/compiler/Compiler.h new file mode 100644 index 000000000..3b7ae542c --- /dev/null +++ b/vm/compiler/Compiler.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _DALVIK_VM_COMPILER +#define _DALVIK_VM_COMPILER + +#define CODE_CACHE_SIZE 1024*1024 +#define MAX_JIT_RUN_LEN 64 +#define COMPILER_WORK_QUEUE_SIZE 100 + +#define COMPILER_TRACED(X) +#define COMPILER_TRACEE(X) +#define COMPILER_TRACE_CHAINING(X) + +typedef enum JitInstructionSetType { + DALVIK_JIT_NONE = 0, + DALVIK_JIT_ARM, + DALVIK_JIT_THUMB, + DALVIK_JIT_THUMB2, + DALVIK_JIT_THUMB2EE, + DALVIK_JIT_X86 +} JitInstructionSetType; + +/* Description of a compiled trace. */ +typedef struct JitTranslationInfo { + void *codeAddress; + JitInstructionSetType instructionSet; +} JitTranslationInfo; + +typedef enum WorkOrderKind { + kWorkOrderInvalid = 0, // Should never see by the backend + kWorkOrderMethod = 1, // Work is to compile a whole method + kWorkOrderTrace = 2, // Work is to compile code fragment(s) +} WorkOrderKind; + +typedef struct CompilerWorkOrder { + const u2* pc; + WorkOrderKind kind; + void* info; + JitTranslationInfo result; +} CompilerWorkOrder; + +typedef enum JitState { + kJitOff = 0, + kJitNormal = 1, // Profiling in mterp or running native + kJitTSelectRequest = 2, // Transition state - start trace selection + kJitTSelect = 3, // Actively selecting trace in dbg interp + kJitTSelectAbort = 4, // Something threw during selection - abort + kJitTSelectEnd = 5, // Done with the trace - wrap it up + kJitSingleStep = 6, // Single step interpretation + kJitSingleStepEnd = 7, // Done with single step, return to mterp +} JitState; + +typedef enum JitHint { + kJitHintNone = 0, + kJitHintTaken = 1, // Last inst in run was taken branch + kJitHintNotTaken = 2, // Last inst in run was not taken branch + kJitHintNoBias = 3, // Last inst in run was unbiased branch +} jitHint; + +/* + * Element of a Jit trace description. Describes a contiguous + * sequence of Dalvik byte codes, the last of which can be + * associated with a hint. + * Dalvik byte code + */ +typedef struct { + u2 startOffset; // Starting offset for trace run + unsigned numInsts:8; // Number of Byte codes in run + unsigned runEnd:1; // Run ends with last byte code + jitHint hint:7; // Hint to apply to final code of run +} JitCodeDesc; + +typedef union { + JitCodeDesc frag; + void* hint; +} JitTraceRun; + +/* + * Trace description as will appear in the translation cache. Note + * flexible array at end, as these will be of variable size. To + * conserve space in the translation cache, total length of JitTraceRun + * array must be recomputed via seqential scan if needed. + */ +typedef struct { + const Method* method; + JitTraceRun trace[]; +} JitTraceDescription; + +typedef struct CompilerMethodStats { + const Method *method; // Used as hash entry signature + int dalvikSize; // # of bytes for dalvik bytecodes + int compiledDalvikSize; // # of compiled dalvik bytecodes + int nativeSize; // # of bytes for produced native code +} CompilerMethodStats; + +bool dvmCompilerSetupCodeCache(void); +bool dvmCompilerArchInit(void); +void dvmCompilerArchDump(void); +bool dvmCompilerStartup(void); +void dvmCompilerShutdown(void); +bool dvmCompilerWorkEnqueue(const u2* pc, WorkOrderKind kind, void* info); +void *dvmCheckCodeCache(void *method); +bool dvmCompileMethod(const Method *method, JitTranslationInfo *info); +bool dvmCompileTrace(JitTraceDescription *trace, int numMaxInsts, + JitTranslationInfo *info); +void dvmCompilerDumpStats(void); +void dvmCompilerDrainQueue(void); +void dvmJitUnchainAll(void); +void dvmCompilerSortAndPrintTraceProfiles(void); + +#endif /* _DALVIK_VM_COMPILER */ diff --git a/vm/compiler/CompilerIR.h b/vm/compiler/CompilerIR.h new file mode 100644 index 000000000..d61d2eeff --- /dev/null +++ b/vm/compiler/CompilerIR.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "codegen/Optimizer.h" + +#ifndef _DALVIK_VM_COMPILER_IR +#define _DALVIK_VM_COMPILER_IR + +typedef enum BBType { + /* For coding convenience reasons chaining cell types should appear first */ + CHAINING_CELL_NORMAL = 0, + CHAINING_CELL_HOT, + CHAINING_CELL_INVOKE_SINGLETON, + CHAINING_CELL_INVOKE_PREDICTED, + CHAINING_CELL_LAST, + DALVIK_BYTECODE, + PC_RECONSTRUCTION, + EXCEPTION_HANDLING, +} BBType; + +typedef struct ChainCellCounts { + union { + u1 count[CHAINING_CELL_LAST]; + u4 dummyForAlignment; + } u; +} ChainCellCounts; + +typedef struct LIR { + int offset; + struct LIR *next; + struct LIR *prev; + struct LIR *target; +} LIR; + +typedef struct MIR { + DecodedInstruction dalvikInsn; + unsigned int width; + unsigned int offset; + struct MIR *prev; + struct MIR *next; +} MIR; + +typedef struct BasicBlock { + int id; + int visited; + unsigned int startOffset; + const Method *containingMethod; // For blocks from the callee + BBType blockType; + bool needFallThroughBranch; // For blocks ended due to length limit + MIR *firstMIRInsn; + MIR *lastMIRInsn; + struct BasicBlock *fallThrough; + struct BasicBlock *taken; + struct BasicBlock *next; // Serial link for book keeping purposes +} BasicBlock; + +typedef struct CompilationUnit { + int numInsts; + int numBlocks; + BasicBlock **blockList; + const Method *method; + const JitTraceDescription *traceDesc; + LIR *firstLIRInsn; + LIR *lastLIRInsn; + LIR *wordList; + LIR *chainCellOffsetLIR; + GrowableList pcReconstructionList; + int headerSize; // bytes before the first code ptr + int dataOffset; // starting offset of literal pool + int totalSize; // header + code size + unsigned char *codeBuffer; + void *baseAddr; + bool printMe; + bool allSingleStep; + bool halveInstCount; + bool executionCount; // Add code to count trace executions + int numChainingCells[CHAINING_CELL_LAST]; + LIR *firstChainingLIR[CHAINING_CELL_LAST]; + RegisterScoreboard registerScoreboard; // Track register dependency + int optRound; // round number to tell an LIR's age + JitInstructionSetType instructionSet; +} CompilationUnit; + +BasicBlock *dvmCompilerNewBB(BBType blockType); + +void dvmCompilerAppendMIR(BasicBlock *bb, MIR *mir); + +void dvmCompilerAppendLIR(CompilationUnit *cUnit, LIR *lir); + +void dvmCompilerInsertLIRBefore(LIR *currentLIR, LIR *newLIR); + +/* Debug Utilities */ +void dvmCompilerDumpCompilationUnit(CompilationUnit *cUnit); + +#endif /* _DALVIK_VM_COMPILER_IR */ diff --git a/vm/compiler/CompilerInternals.h b/vm/compiler/CompilerInternals.h new file mode 100644 index 000000000..410213aac --- /dev/null +++ b/vm/compiler/CompilerInternals.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _DALVIK_VM_COMPILER_INTERNAL +#define _DALVIK_VM_COMPILER_INTERNAL + +#include "Dalvik.h" +#include "CompilerUtility.h" +#include "CompilerIR.h" +#include "codegen/CompilerCodegen.h" +#include "interp/Jit.h" + +#endif /* _DALVIK_VM_COMPILER_INTERNAL */ diff --git a/vm/compiler/CompilerUtility.h b/vm/compiler/CompilerUtility.h new file mode 100644 index 000000000..7b4de1199 --- /dev/null +++ b/vm/compiler/CompilerUtility.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _DALVIK_VM_COMPILER_UTILITY +#define _DALVIK_VM_COMPILER_UTILITY + +#define ARENA_DEFAULT_SIZE 4096 + +/* Allocate the initial memory block for arena-based allocation */ +bool dvmCompilerHeapInit(void); + +typedef struct ArenaMemBlock { + size_t bytesAllocated; + struct ArenaMemBlock *next; + char ptr[0]; +} ArenaMemBlock; + +void *dvmCompilerNew(size_t size, bool zero); + +void dvmCompilerArenaReset(void); + +typedef struct GrowableList { + size_t numAllocated; + size_t numUsed; + void **elemList; +} GrowableList; + +void dvmInitGrowableList(GrowableList *gList, size_t initLength); +void dvmInsertGrowableList(GrowableList *gList, void *elem); + +#endif /* _DALVIK_COMPILER_UTILITY */ diff --git a/vm/compiler/Frontend.c b/vm/compiler/Frontend.c new file mode 100644 index 000000000..66060a0eb --- /dev/null +++ b/vm/compiler/Frontend.c @@ -0,0 +1,752 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Dalvik.h" +#include "libdex/OpCode.h" +#include "dexdump/OpCodeNames.h" +#include "interp/Jit.h" +#include "CompilerInternals.h" + +/* + * Parse an instruction, return the length of the instruction + */ +static inline int parseInsn(const u2 *codePtr, DecodedInstruction *decInsn, + bool printMe) +{ + u2 instr = *codePtr; + OpCode opcode = instr & 0xff; + int insnWidth; + + // Don't parse instruction data + if (opcode == OP_NOP && instr != 0) { + return 0; + } else { + insnWidth = gDvm.instrWidth[opcode]; + if (insnWidth < 0) { + insnWidth = -insnWidth; + } + } + + dexDecodeInstruction(gDvm.instrFormat, codePtr, decInsn); + if (printMe) { + LOGD("%p: %#06x %s\n", codePtr, opcode, getOpcodeName(opcode)); + } + return insnWidth; +} + +/* + * Identify block-ending instructions and collect supplemental information + * regarding the following instructions. + */ +static inline bool findBlockBoundary(const Method *caller, MIR *insn, + unsigned int curOffset, + unsigned int *target, bool *isInvoke, + const Method **callee) +{ + switch (insn->dalvikInsn.opCode) { + /* Target is not compile-time constant */ + case OP_RETURN_VOID: + case OP_RETURN: + case OP_RETURN_WIDE: + case OP_RETURN_OBJECT: + case OP_THROW: + case OP_INVOKE_VIRTUAL: + case OP_INVOKE_VIRTUAL_RANGE: + case OP_INVOKE_INTERFACE: + case OP_INVOKE_INTERFACE_RANGE: + case OP_INVOKE_VIRTUAL_QUICK: + case OP_INVOKE_VIRTUAL_QUICK_RANGE: + *isInvoke = true; + break; + case OP_INVOKE_SUPER: + case OP_INVOKE_SUPER_RANGE: { + int mIndex = caller->clazz->pDvmDex-> + pResMethods[insn->dalvikInsn.vB]->methodIndex; + const Method *calleeMethod = + caller->clazz->super->vtable[mIndex]; + + if (calleeMethod && !dvmIsNativeMethod(calleeMethod)) { + *target = (unsigned int) calleeMethod->insns; + } + *isInvoke = true; + *callee = calleeMethod; + break; + } + case OP_INVOKE_STATIC: + case OP_INVOKE_STATIC_RANGE: { + const Method *calleeMethod = + caller->clazz->pDvmDex->pResMethods[insn->dalvikInsn.vB]; + + if (calleeMethod && !dvmIsNativeMethod(calleeMethod)) { + *target = (unsigned int) calleeMethod->insns; + } + *isInvoke = true; + *callee = calleeMethod; + break; + } + case OP_INVOKE_SUPER_QUICK: + case OP_INVOKE_SUPER_QUICK_RANGE: { + const Method *calleeMethod = + caller->clazz->super->vtable[insn->dalvikInsn.vB]; + + if (calleeMethod && !dvmIsNativeMethod(calleeMethod)) { + *target = (unsigned int) calleeMethod->insns; + } + *isInvoke = true; + *callee = calleeMethod; + break; + } + case OP_INVOKE_DIRECT: + case OP_INVOKE_DIRECT_RANGE: { + const Method *calleeMethod = + caller->clazz->pDvmDex->pResMethods[insn->dalvikInsn.vB]; + if (calleeMethod && !dvmIsNativeMethod(calleeMethod)) { + *target = (unsigned int) calleeMethod->insns; + } + *isInvoke = true; + *callee = calleeMethod; + break; + } + case OP_GOTO: + case OP_GOTO_16: + case OP_GOTO_32: + *target = curOffset + (int) insn->dalvikInsn.vA; + break; + + case OP_IF_EQ: + case OP_IF_NE: + case OP_IF_LT: + case OP_IF_GE: + case OP_IF_GT: + case OP_IF_LE: + *target = curOffset + (int) insn->dalvikInsn.vC; + break; + + case OP_IF_EQZ: + case OP_IF_NEZ: + case OP_IF_LTZ: + case OP_IF_GEZ: + case OP_IF_GTZ: + case OP_IF_LEZ: + *target = curOffset + (int) insn->dalvikInsn.vB; + break; + + default: + return false; + } return true; +} + +/* + * Identify conditional branch instructions + */ +static inline bool isUnconditionalBranch(MIR *insn) +{ + switch (insn->dalvikInsn.opCode) { + case OP_RETURN_VOID: + case OP_RETURN: + case OP_RETURN_WIDE: + case OP_RETURN_OBJECT: + case OP_GOTO: + case OP_GOTO_16: + case OP_GOTO_32: + return true; + default: + return false; + } +} + +/* + * dvmHashTableLookup() callback + */ +static int compareMethod(const CompilerMethodStats *m1, + const CompilerMethodStats *m2) +{ + return (int) m1->method - (int) m2->method; +} + +/* + * Analyze each method whose traces are ever compiled. Collect a variety of + * statistics like the ratio of exercised vs overall code and code bloat + * ratios. + */ +static CompilerMethodStats *analyzeMethodBody(const Method *method) +{ + const DexCode *dexCode = dvmGetMethodCode(method); + const u2 *codePtr = dexCode->insns; + const u2 *codeEnd = dexCode->insns + dexCode->insnsSize; + int insnSize = 0; + int hashValue = dvmComputeUtf8Hash(method->name); + + CompilerMethodStats dummyMethodEntry; // For hash table lookup + CompilerMethodStats *realMethodEntry; // For hash table storage + + /* For lookup only */ + dummyMethodEntry.method = method; + realMethodEntry = dvmHashTableLookup(gDvmJit.methodStatsTable, hashValue, + &dummyMethodEntry, + (HashCompareFunc) compareMethod, + false); + + /* Part of this method has been compiled before - just return the entry */ + if (realMethodEntry != NULL) { + return realMethodEntry; + } + + /* + * First time to compile this method - set up a new entry in the hash table + */ + realMethodEntry = + (CompilerMethodStats *) calloc(1, sizeof(CompilerMethodStats)); + realMethodEntry->method = method; + + dvmHashTableLookup(gDvmJit.methodStatsTable, hashValue, + realMethodEntry, + (HashCompareFunc) compareMethod, + true); + + /* Count the number of instructions */ + while (codePtr < codeEnd) { + DecodedInstruction dalvikInsn; + int width = parseInsn(codePtr, &dalvikInsn, false); + + /* Terminate when the data section is seen */ + if (width == 0) + break; + + insnSize += width; + codePtr += width; + } + + realMethodEntry->dalvikSize = insnSize * 2; + return realMethodEntry; +} + +/* + * Main entry point to start trace compilation. Basic blocks are constructed + * first and they will be passed to the codegen routines to convert Dalvik + * bytecode into machine code. + */ +bool dvmCompileTrace(JitTraceDescription *desc, int numMaxInsts, + JitTranslationInfo *info) +{ + const DexCode *dexCode = dvmGetMethodCode(desc->method); + const JitTraceRun* currRun = &desc->trace[0]; + unsigned int curOffset = currRun->frag.startOffset; + unsigned int numInsts = currRun->frag.numInsts; + const u2 *codePtr = dexCode->insns + curOffset; + int traceSize = 0; // # of half-words + const u2 *startCodePtr = codePtr; + BasicBlock *startBB, *curBB, *lastBB; + int numBlocks = 0; + static int compilationId; + CompilationUnit cUnit; + CompilerMethodStats *methodStats; + + compilationId++; + memset(&cUnit, 0, sizeof(CompilationUnit)); + + /* Locate the entry to store compilation statistics for this method */ + methodStats = analyzeMethodBody(desc->method); + + cUnit.registerScoreboard.nullCheckedRegs = + dvmAllocBitVector(desc->method->registersSize, false); + + /* Initialize the printMe flag */ + cUnit.printMe = gDvmJit.printMe; + + /* Initialize the profile flag */ + cUnit.executionCount = gDvmJit.profile; + + /* Identify traces that we don't want to compile */ + if (gDvmJit.methodTable) { + int len = strlen(desc->method->clazz->descriptor) + + strlen(desc->method->name) + 1; + char *fullSignature = dvmCompilerNew(len, true); + strcpy(fullSignature, desc->method->clazz->descriptor); + strcat(fullSignature, desc->method->name); + + int hashValue = dvmComputeUtf8Hash(fullSignature); + + /* + * Doing three levels of screening to see whether we want to skip + * compiling this method + */ + + /* First, check the full "class;method" signature */ + bool methodFound = + dvmHashTableLookup(gDvmJit.methodTable, hashValue, + fullSignature, (HashCompareFunc) strcmp, + false) != + NULL; + + /* Full signature not found - check the enclosing class */ + if (methodFound == false) { + int hashValue = dvmComputeUtf8Hash(desc->method->clazz->descriptor); + methodFound = + dvmHashTableLookup(gDvmJit.methodTable, hashValue, + (char *) desc->method->clazz->descriptor, + (HashCompareFunc) strcmp, false) != + NULL; + /* Enclosing class not found - check the method name */ + if (methodFound == false) { + int hashValue = dvmComputeUtf8Hash(desc->method->name); + methodFound = + dvmHashTableLookup(gDvmJit.methodTable, hashValue, + (char *) desc->method->name, + (HashCompareFunc) strcmp, false) != + NULL; + } + } + + /* + * Under the following conditions, the trace will be *conservatively* + * compiled by only containing single-step instructions to and from the + * interpreter. + * 1) If includeSelectedMethod == false, the method matches the full or + * partial signature stored in the hash table. + * + * 2) If includeSelectedMethod == true, the method does not match the + * full and partial signature stored in the hash table. + */ + if (gDvmJit.includeSelectedMethod != methodFound) { + cUnit.allSingleStep = true; + } else { + /* Compile the trace as normal */ + + /* Print the method we cherry picked */ + if (gDvmJit.includeSelectedMethod == true) { + cUnit.printMe = true; + } + } + } + + /* Allocate the first basic block */ + lastBB = startBB = curBB = dvmCompilerNewBB(DALVIK_BYTECODE); + curBB->startOffset = curOffset; + curBB->id = numBlocks++; + + if (cUnit.printMe) { + LOGD("--------\nCompiler: Building trace for %s, offset 0x%x\n", + desc->method->name, curOffset); + } + + /* + * Analyze the trace descriptor and include up to the maximal number + * of Dalvik instructions into the IR. + */ + while (1) { + MIR *insn; + int width; + insn = dvmCompilerNew(sizeof(MIR),false); + insn->offset = curOffset; + width = parseInsn(codePtr, &insn->dalvikInsn, cUnit.printMe); + + /* The trace should never incude instruction data */ + assert(width); + insn->width = width; + traceSize += width; + dvmCompilerAppendMIR(curBB, insn); + cUnit.numInsts++; + /* Instruction limit reached - terminate the trace here */ + if (cUnit.numInsts >= numMaxInsts) { + break; + } + if (--numInsts == 0) { + if (currRun->frag.runEnd) { + break; + } else { + curBB = dvmCompilerNewBB(DALVIK_BYTECODE); + lastBB->next = curBB; + lastBB = curBB; + curBB->id = numBlocks++; + currRun++; + curOffset = currRun->frag.startOffset; + numInsts = currRun->frag.numInsts; + curBB->startOffset = curOffset; + codePtr = dexCode->insns + curOffset; + } + } else { + curOffset += width; + codePtr += width; + } + } + + /* Convert # of half-word to bytes */ + methodStats->compiledDalvikSize += traceSize * 2; + + /* + * Now scan basic blocks containing real code to connect the + * taken/fallthrough links. Also create chaining cells for code not included + * in the trace. + */ + for (curBB = startBB; curBB; curBB = curBB->next) { + MIR *lastInsn = curBB->lastMIRInsn; + /* Hit a pseudo block - exit the search now */ + if (lastInsn == NULL) { + break; + } + curOffset = lastInsn->offset; + unsigned int targetOffset = curOffset; + unsigned int fallThroughOffset = curOffset + lastInsn->width; + bool isInvoke = false; + const Method *callee = NULL; + + findBlockBoundary(desc->method, curBB->lastMIRInsn, curOffset, + &targetOffset, &isInvoke, &callee); + + /* Link the taken and fallthrough blocks */ + BasicBlock *searchBB; + + /* No backward branch in the trace - start searching the next BB */ + for (searchBB = curBB->next; searchBB; searchBB = searchBB->next) { + if (targetOffset == searchBB->startOffset) { + curBB->taken = searchBB; + } + if (fallThroughOffset == searchBB->startOffset) { + curBB->fallThrough = searchBB; + } + } + + int flags = dexGetInstrFlags(gDvm.instrFlags, + lastInsn->dalvikInsn.opCode); + + /* + * Some blocks are ended by non-control-flow-change instructions, + * currently only due to trace length constraint. In this case we need + * to generate an explicit branch at the end of the block to jump to + * the chaining cell. + * + * NOTE: INVOKE_DIRECT_EMPTY is actually not an invoke but a nop + */ + curBB->needFallThroughBranch = + ((flags & (kInstrCanBranch | kInstrCanSwitch | kInstrCanReturn | + kInstrInvoke)) == 0) || + (lastInsn->dalvikInsn.opCode == OP_INVOKE_DIRECT_EMPTY); + + + /* Target block not included in the trace */ + if (curBB->taken == NULL && + (isInvoke || (targetOffset != curOffset))) { + BasicBlock *newBB; + if (isInvoke) { + /* Monomorphic callee */ + if (callee) { + newBB = dvmCompilerNewBB(CHAINING_CELL_INVOKE_SINGLETON); + newBB->startOffset = 0; + newBB->containingMethod = callee; + /* Will resolve at runtime */ + } else { + newBB = dvmCompilerNewBB(CHAINING_CELL_INVOKE_PREDICTED); + newBB->startOffset = 0; + } + /* For unconditional branches, request a hot chaining cell */ + } else { + newBB = dvmCompilerNewBB(flags & kInstrUnconditional ? + CHAINING_CELL_HOT : + CHAINING_CELL_NORMAL); + newBB->startOffset = targetOffset; + } + newBB->id = numBlocks++; + curBB->taken = newBB; + lastBB->next = newBB; + lastBB = newBB; + } + + /* Fallthrough block not included in the trace */ + if (!isUnconditionalBranch(lastInsn) && curBB->fallThrough == NULL) { + /* + * If the chaining cell is after an invoke or + * instruction that cannot change the control flow, request a hot + * chaining cell. + */ + if (isInvoke || curBB->needFallThroughBranch) { + lastBB->next = dvmCompilerNewBB(CHAINING_CELL_HOT); + } else { + lastBB->next = dvmCompilerNewBB(CHAINING_CELL_NORMAL); + } + lastBB = lastBB->next; + lastBB->id = numBlocks++; + lastBB->startOffset = fallThroughOffset; + curBB->fallThrough = lastBB; + } + } + + /* Now create a special block to host PC reconstruction code */ + lastBB->next = dvmCompilerNewBB(PC_RECONSTRUCTION); + lastBB = lastBB->next; + lastBB->id = numBlocks++; + + /* And one final block that publishes the PC and raise the exception */ + lastBB->next = dvmCompilerNewBB(EXCEPTION_HANDLING); + lastBB = lastBB->next; + lastBB->id = numBlocks++; + + if (cUnit.printMe) { + LOGD("TRACEINFO (%d): 0x%08x %s%s 0x%x %d of %d, %d blocks", + compilationId, + (intptr_t) desc->method->insns, + desc->method->clazz->descriptor, + desc->method->name, + desc->trace[0].frag.startOffset, + traceSize, + dexCode->insnsSize, + numBlocks); + } + + BasicBlock **blockList; + + cUnit.method = desc->method; + cUnit.traceDesc = desc; + cUnit.numBlocks = numBlocks; + dvmInitGrowableList(&cUnit.pcReconstructionList, 8); + blockList = cUnit.blockList = + dvmCompilerNew(sizeof(BasicBlock *) * numBlocks, true); + + int i; + + for (i = 0, curBB = startBB; i < numBlocks; i++) { + blockList[i] = curBB; + curBB = curBB->next; + } + /* Make sure all blocks are added to the cUnit */ + assert(curBB == NULL); + + if (cUnit.printMe) { + dvmCompilerDumpCompilationUnit(&cUnit); + } + + /* Set the instruction set to use (NOTE: later components may change it) */ + cUnit.instructionSet = dvmCompilerInstructionSet(&cUnit); + + /* Convert MIR to LIR, etc. */ + dvmCompilerMIR2LIR(&cUnit); + + /* Convert LIR into machine code. */ + dvmCompilerAssembleLIR(&cUnit, info); + + if (cUnit.printMe) { + if (cUnit.halveInstCount) { + LOGD("Assembler aborted"); + } else { + dvmCompilerCodegenDump(&cUnit); + } + LOGD("End %s%s, %d Dalvik instructions", + desc->method->clazz->descriptor, desc->method->name, + cUnit.numInsts); + } + + /* Reset the compiler resource pool */ + dvmCompilerArenaReset(); + + /* Free the bit vector tracking null-checked registers */ + dvmFreeBitVector(cUnit.registerScoreboard.nullCheckedRegs); + + if (!cUnit.halveInstCount) { + /* Success */ + methodStats->nativeSize += cUnit.totalSize; + return info->codeAddress != NULL; + + /* Halve the instruction count and retry again */ + } else { + return dvmCompileTrace(desc, cUnit.numInsts / 2, info); + } +} + +/* + * Similar to dvmCompileTrace, but the entity processed here is the whole + * method. + * + * TODO: implementation will be revisited when the trace builder can provide + * whole-method traces. + */ +bool dvmCompileMethod(const Method *method, JitTranslationInfo *info) +{ + const DexCode *dexCode = dvmGetMethodCode(method); + const u2 *codePtr = dexCode->insns; + const u2 *codeEnd = dexCode->insns + dexCode->insnsSize; + int blockID = 0; + unsigned int curOffset = 0; + + BasicBlock *firstBlock = dvmCompilerNewBB(DALVIK_BYTECODE); + firstBlock->id = blockID++; + + /* Allocate the bit-vector to track the beginning of basic blocks */ + BitVector *bbStartAddr = dvmAllocBitVector(dexCode->insnsSize+1, false); + dvmSetBit(bbStartAddr, 0); + + /* + * Sequentially go through every instruction first and put them in a single + * basic block. Identify block boundaries at the mean time. + */ + while (codePtr < codeEnd) { + MIR *insn = dvmCompilerNew(sizeof(MIR), false); + insn->offset = curOffset; + int width = parseInsn(codePtr, &insn->dalvikInsn, false); + bool isInvoke = false; + const Method *callee; + insn->width = width; + + /* Terminate when the data section is seen */ + if (width == 0) + break; + dvmCompilerAppendMIR(firstBlock, insn); + /* + * Check whether this is a block ending instruction and whether it + * suggests the start of a new block + */ + unsigned int target = curOffset; + + /* + * If findBlockBoundary returns true, it means the current instruction + * is terminating the current block. If it is a branch, the target + * address will be recorded in target. + */ + if (findBlockBoundary(method, insn, curOffset, &target, &isInvoke, + &callee)) { + dvmSetBit(bbStartAddr, curOffset + width); + if (target != curOffset) { + dvmSetBit(bbStartAddr, target); + } + } + + codePtr += width; + /* each bit represents 16-bit quantity */ + curOffset += width; + } + + /* + * The number of blocks will be equal to the number of bits set to 1 in the + * bit vector minus 1, because the bit representing the location after the + * last instruction is set to one. + */ + int numBlocks = dvmCountSetBits(bbStartAddr); + if (dvmIsBitSet(bbStartAddr, dexCode->insnsSize)) { + numBlocks--; + } + + CompilationUnit cUnit; + BasicBlock **blockList; + + memset(&cUnit, 0, sizeof(CompilationUnit)); + cUnit.method = method; + blockList = cUnit.blockList = + dvmCompilerNew(sizeof(BasicBlock *) * numBlocks, true); + + /* + * Register the first block onto the list and start split it into block + * boundaries from there. + */ + blockList[0] = firstBlock; + cUnit.numBlocks = 1; + + int i; + for (i = 0; i < numBlocks; i++) { + MIR *insn; + BasicBlock *curBB = blockList[i]; + curOffset = curBB->lastMIRInsn->offset; + + for (insn = curBB->firstMIRInsn->next; insn; insn = insn->next) { + /* Found the beginning of a new block, see if it is created yet */ + if (dvmIsBitSet(bbStartAddr, insn->offset)) { + int j; + for (j = 0; j < cUnit.numBlocks; j++) { + if (blockList[j]->firstMIRInsn->offset == insn->offset) + break; + } + + /* Block not split yet - do it now */ + if (j == cUnit.numBlocks) { + BasicBlock *newBB = dvmCompilerNewBB(DALVIK_BYTECODE); + newBB->id = blockID++; + newBB->firstMIRInsn = insn; + newBB->startOffset = insn->offset; + newBB->lastMIRInsn = curBB->lastMIRInsn; + curBB->lastMIRInsn = insn->prev; + insn->prev->next = NULL; + insn->prev = NULL; + + /* + * If the insn is not an unconditional branch, set up the + * fallthrough link. + */ + if (!isUnconditionalBranch(curBB->lastMIRInsn)) { + curBB->fallThrough = newBB; + } + + /* enqueue the new block */ + blockList[cUnit.numBlocks++] = newBB; + break; + } + } + } + } + + if (numBlocks != cUnit.numBlocks) { + LOGE("Expect %d vs %d basic blocks\n", numBlocks, cUnit.numBlocks); + dvmAbort(); + } + + dvmFreeBitVector(bbStartAddr); + + /* Connect the basic blocks through the taken links */ + for (i = 0; i < numBlocks; i++) { + BasicBlock *curBB = blockList[i]; + MIR *insn = curBB->lastMIRInsn; + unsigned int target = insn->offset; + bool isInvoke; + const Method *callee; + + findBlockBoundary(method, insn, target, &target, &isInvoke, &callee); + + /* Found a block ended on a branch */ + if (target != insn->offset) { + int j; + /* Forward branch */ + if (target > insn->offset) { + j = i + 1; + } else { + /* Backward branch */ + j = 0; + } + for (; j < numBlocks; j++) { + if (blockList[j]->firstMIRInsn->offset == target) { + curBB->taken = blockList[j]; + break; + } + } + + /* Don't create dummy block for the callee yet */ + if (j == numBlocks && !isInvoke) { + LOGE("Target not found for insn %x: expect target %x\n", + curBB->lastMIRInsn->offset, target); + dvmAbort(); + } + } + } + + /* Set the instruction set to use (NOTE: later components may change it) */ + cUnit.instructionSet = dvmCompilerInstructionSet(&cUnit); + + dvmCompilerMIR2LIR(&cUnit); + + dvmCompilerAssembleLIR(&cUnit, info); + + dvmCompilerDumpCompilationUnit(&cUnit); + + dvmCompilerArenaReset(); + + return info->codeAddress != NULL; +} diff --git a/vm/compiler/IntermediateRep.c b/vm/compiler/IntermediateRep.c new file mode 100644 index 000000000..91b7af7ff --- /dev/null +++ b/vm/compiler/IntermediateRep.c @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Dalvik.h" +#include "CompilerInternals.h" + +/* Allocate a new basic block */ +BasicBlock *dvmCompilerNewBB(BBType blockType) +{ + BasicBlock *bb = dvmCompilerNew(sizeof(BasicBlock), true); + bb->blockType = blockType; + return bb; +} + +/* Insert an MIR instruction to the end of a basic block */ +void dvmCompilerAppendMIR(BasicBlock *bb, MIR *mir) +{ + if (bb->firstMIRInsn == NULL) { + assert(bb->firstMIRInsn == NULL); + bb->lastMIRInsn = bb->firstMIRInsn = mir; + mir->prev = mir->next = NULL; + } else { + bb->lastMIRInsn->next = mir; + mir->prev = bb->lastMIRInsn; + mir->next = NULL; + bb->lastMIRInsn = mir; + } +} + +/* + * Append an LIR instruction to the LIR list maintained by a compilation + * unit + */ +void dvmCompilerAppendLIR(CompilationUnit *cUnit, LIR *lir) +{ + if (cUnit->firstLIRInsn == NULL) { + assert(cUnit->lastLIRInsn == NULL); + cUnit->lastLIRInsn = cUnit->firstLIRInsn = lir; + lir->prev = lir->next = NULL; + } else { + cUnit->lastLIRInsn->next = lir; + lir->prev = cUnit->lastLIRInsn; + lir->next = NULL; + cUnit->lastLIRInsn = lir; + } +} + +/* + * Insert an LIR instruction before the current instruction, which cannot be the + * first instruction. + * + * prevLIR <-> newLIR <-> currentLIR + */ +void dvmCompilerInsertLIRBefore(LIR *currentLIR, LIR *newLIR) +{ + if (currentLIR->prev == NULL) + dvmAbort(); + LIR *prevLIR = currentLIR->prev; + + prevLIR->next = newLIR; + newLIR->prev = prevLIR; + newLIR->next = currentLIR; + currentLIR->prev = newLIR; +} diff --git a/vm/compiler/Utility.c b/vm/compiler/Utility.c new file mode 100644 index 000000000..715f750a3 --- /dev/null +++ b/vm/compiler/Utility.c @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Dalvik.h" +#include "CompilerInternals.h" + +static ArenaMemBlock *arenaHead, *currentArena; +static int numArenaBlocks; + +/* Allocate the initial memory block for arena-based allocation */ +bool dvmCompilerHeapInit(void) +{ + assert(arenaHead == NULL); + arenaHead = + (ArenaMemBlock *) malloc(sizeof(ArenaMemBlock) + ARENA_DEFAULT_SIZE); + if (arenaHead == NULL) { + LOGE("No memory left to create compiler heap memory\n"); + return false; + } + currentArena = arenaHead; + currentArena->bytesAllocated = 0; + currentArena->next = NULL; + numArenaBlocks = 1; + + return true; +} + +/* Arena-based malloc for compilation tasks */ +void * dvmCompilerNew(size_t size, bool zero) +{ + size = (size + 3) & ~3; +retry: + /* Normal case - space is available in the current page */ + if (size + currentArena->bytesAllocated <= ARENA_DEFAULT_SIZE) { + void *ptr; + ptr = ¤tArena->ptr[currentArena->bytesAllocated]; + currentArena->bytesAllocated += size; + if (zero) { + memset(ptr, 0, size); + } + return ptr; + } else { + /* + * See if there are previously allocated arena blocks before the last + * reset + */ + if (currentArena->next) { + currentArena = currentArena->next; + goto retry; + } + /* + * If we allocate really large variable-sized data structures that + * could go above the limit we need to enhance the allocation + * mechanism. + */ + if (size > ARENA_DEFAULT_SIZE) { + LOGE("Requesting %d bytes which exceed the maximal size allowed\n", + size); + return NULL; + } + /* Time to allocate a new arena */ + ArenaMemBlock *newArena = (ArenaMemBlock *) + malloc(sizeof(ArenaMemBlock) + ARENA_DEFAULT_SIZE); + newArena->bytesAllocated = 0; + newArena->next = NULL; + currentArena->next = newArena; + currentArena = newArena; + numArenaBlocks++; + LOGD("Total arena pages for JIT: %d", numArenaBlocks); + goto retry; + } + return NULL; +} + +/* Reclaim all the arena blocks allocated so far */ +void dvmCompilerArenaReset(void) +{ + ArenaMemBlock *block; + + for (block = arenaHead; block; block = block->next) { + block->bytesAllocated = 0; + } + currentArena = arenaHead; +} + +/* Growable List initialization */ +void dvmInitGrowableList(GrowableList *gList, size_t initLength) +{ + gList->numAllocated = initLength; + gList->numUsed = 0; + gList->elemList = (void **) dvmCompilerNew(sizeof(void *) * initLength, + true); +} + +/* Expand the capacity of a growable list */ +static void expandGrowableList(GrowableList *gList) +{ + int newLength = gList->numAllocated; + if (newLength < 128) { + newLength <<= 1; + } else { + newLength += 128; + } + void *newArray = dvmCompilerNew(sizeof(void *) * newLength, true); + memcpy(newArray, gList->elemList, sizeof(void *) * gList->numAllocated); + gList->numAllocated = newLength; + gList->elemList = newArray; +} + +/* Insert a new element into the growable list */ +void dvmInsertGrowableList(GrowableList *gList, void *elem) +{ + if (gList->numUsed == gList->numAllocated) { + expandGrowableList(gList); + } + gList->elemList[gList->numUsed++] = elem; +} + +/* Debug Utility - dump a compilation unit */ +void dvmCompilerDumpCompilationUnit(CompilationUnit *cUnit) +{ + int i; + BasicBlock *bb; + LOGD("%d blocks in total\n", cUnit->numBlocks); + + for (i = 0; i < cUnit->numBlocks; i++) { + bb = cUnit->blockList[i]; + LOGD("Block %d (insn %04x - %04x%s)\n", + bb->id, bb->startOffset, + bb->lastMIRInsn ? bb->lastMIRInsn->offset : bb->startOffset, + bb->lastMIRInsn ? "" : " empty"); + if (bb->taken) { + LOGD(" Taken branch: block %d (%04x)\n", + bb->taken->id, bb->taken->startOffset); + } + if (bb->fallThrough) { + LOGD(" Fallthrough : block %d (%04x)\n", + bb->fallThrough->id, bb->fallThrough->startOffset); + } + } +} + +/* + * dvmHashForeach callback. + */ +static int dumpMethodStats(void *compilerMethodStats, void *totalMethodStats) +{ + CompilerMethodStats *methodStats = + (CompilerMethodStats *) compilerMethodStats; + CompilerMethodStats *totalStats = + (CompilerMethodStats *) totalMethodStats; + const Method *method = methodStats->method; + + totalStats->dalvikSize += methodStats->dalvikSize; + totalStats->compiledDalvikSize += methodStats->compiledDalvikSize; + totalStats->nativeSize += methodStats->nativeSize; + + /* Enable the following when fine-tuning the JIT performance */ +#if 0 + int limit = (methodStats->dalvikSize >> 2) * 3; + + /* If over 3/4 of the Dalvik code is compiled, print something */ + if (methodStats->compiledDalvikSize >= limit) { + LOGD("Method stats: %s%s, %d/%d (compiled/total Dalvik), %d (native)", + method->clazz->descriptor, method->name, + methodStats->compiledDalvikSize, + methodStats->dalvikSize, + methodStats->nativeSize); + } +#endif + return 0; +} + +/* + * Dump the current stats of the compiler, including number of bytes used in + * the code cache, arena size, and work queue length, and various JIT stats. + */ +void dvmCompilerDumpStats(void) +{ + CompilerMethodStats totalMethodStats; + + memset(&totalMethodStats, 0, sizeof(CompilerMethodStats)); + LOGD("%d compilations using %d + %d bytes", + gDvmJit.numCompilations, + gDvmJit.templateSize, + gDvmJit.codeCacheByteUsed - gDvmJit.templateSize); + LOGD("Compiler arena uses %d blocks (%d bytes each)", + numArenaBlocks, ARENA_DEFAULT_SIZE); + LOGD("Compiler work queue length is %d/%d", gDvmJit.compilerQueueLength, + gDvmJit.compilerMaxQueued); + dvmJitStats(); + dvmCompilerArchDump(); + dvmHashForeach(gDvmJit.methodStatsTable, dumpMethodStats, + &totalMethodStats); + LOGD("Code size stats: %d/%d (compiled/total Dalvik), %d (native)", + totalMethodStats.compiledDalvikSize, + totalMethodStats.dalvikSize, + totalMethodStats.nativeSize); +} diff --git a/vm/compiler/codegen/CompilerCodegen.h b/vm/compiler/codegen/CompilerCodegen.h new file mode 100644 index 000000000..c9e6bd6df --- /dev/null +++ b/vm/compiler/codegen/CompilerCodegen.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "../CompilerIR.h" + +#ifndef _DALVIK_VM_COMPILERCODEGEN_H_ +#define _DALVIK_VM_COMPILERCODEGEN_H_ + +/* Work unit is architecture dependent */ +bool dvmCompilerDoWork(CompilerWorkOrder *work); + +/* Lower middle-level IR to low-level IR */ +void dvmCompilerMIR2LIR(CompilationUnit *cUnit); + +/* Assemble LIR into machine code */ +void dvmCompilerAssembleLIR(CompilationUnit *cUnit, JitTranslationInfo *info); + +/* Implemented in the codegen//ArchUtility.c */ +void dvmCompilerCodegenDump(CompilationUnit *cUnit); + +/* Implemented in the codegen//Assembler.c */ +void* dvmJitChain(void *tgtAddr, u4* branchAddr); +u4* dvmJitUnchain(void *codeAddr); +void dvmJitUnchainAll(void); + +/* Implemented in codegen///ArchVariant.c */ +JitInstructionSetType dvmCompilerInstructionSet(CompilationUnit *cUnit); + +#endif /* _DALVIK_VM_COMPILERCODEGEN_H_ */ diff --git a/vm/compiler/codegen/Optimizer.h b/vm/compiler/codegen/Optimizer.h new file mode 100644 index 000000000..1a891b140 --- /dev/null +++ b/vm/compiler/codegen/Optimizer.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Dalvik.h" +#include "compiler/CompilerInternals.h" + +#ifndef _DALVIK_VM_COMPILER_OPTIMIZATION_H +#define _DALVIK_VM_COMPILER_OPTIMIZATION_H + +/* Forward declarations */ +struct CompilationUnit; +struct LIR; + +/* + * Data structure tracking the mapping between a Dalvik register (pair) and a + * native register (pair). The idea is to reuse the previously loaded value + * if possible, otherwise to keep the value in a native register as long as + * possible. + */ +typedef struct RegisterScoreboard { + BitVector *nullCheckedRegs; // Track which registers have been null-checked + int liveDalvikReg; // Track which Dalvik register is live + int nativeReg; // And the mapped native register + int nativeRegHi; // And the mapped native register + bool isWide; // Whether a pair of registers are alive +} RegisterScoreboard; + +void dvmCompilerApplyLocalOptimizations(struct CompilationUnit *cUnit, + struct LIR *head, + struct LIR *tail); + +void dvmCompilerApplyGlobalOptimizations(struct CompilationUnit *cUnit); + +#endif /* _DALVIK_VM_COMPILER_OPTIMIZATION_H */ diff --git a/vm/compiler/codegen/arm/ArchUtility.c b/vm/compiler/codegen/arm/ArchUtility.c new file mode 100644 index 000000000..60c5cdb86 --- /dev/null +++ b/vm/compiler/codegen/arm/ArchUtility.c @@ -0,0 +1,264 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "../../CompilerInternals.h" +#include "dexdump/OpCodeNames.h" +#include "ArmLIR.h" + +/* Decode and print a ARM register name */ +static char * decodeRegList(int vector, char *buf) +{ + int i; + bool printed = false; + buf[0] = 0; + for (i = 0; i < 8; i++, vector >>= 1) { + if (vector & 0x1) { + if (printed) { + sprintf(buf + strlen(buf), ", r%d", i); + } else { + printed = true; + sprintf(buf, "r%d", i); + } + } + } + return buf; +} + +static int expandImmediate(int value) +{ + int mode = (value & 0xf00) >> 8; + u4 bits = value & 0xff; + switch(mode) { + case 0: + return bits; + case 1: + return (bits << 16) | bits; + case 2: + return (bits << 24) | (bits << 8); + case 3: + return (bits << 24) | (bits << 16) | (bits << 8) | bits; + default: + break; + } + bits = (bits | 0x80) << 24; + return bits >> (((value & 0xf80) >> 7) - 8); +} + +/* + * Interpret a format string and build a string no longer than size + * See format key in Assemble.c. + */ +static void buildInsnString(char *fmt, ArmLIR *lir, char* buf, + unsigned char *baseAddr, int size) +{ + int i; + char *bufEnd = &buf[size-1]; + char *fmtEnd = &fmt[strlen(fmt)]; + char tbuf[256]; + char nc; + while (fmt < fmtEnd) { + int operand; + if (*fmt == '!') { + fmt++; + assert(fmt < fmtEnd); + nc = *fmt++; + if (nc=='!') { + strcpy(tbuf, "!"); + } else { + assert(fmt < fmtEnd); + assert((unsigned)(nc-'0') < 3); + operand = lir->operands[nc-'0']; + switch(*fmt++) { + case 'm': + operand = expandImmediate(operand); + sprintf(tbuf,"%d [0x%x]", operand, operand); + break; + case 's': + sprintf(tbuf,"s%d",operand & FP_REG_MASK); + break; + case 'S': + sprintf(tbuf,"d%d",(operand & FP_REG_MASK) >> 1); + break; + case 'h': + sprintf(tbuf,"%04x", operand); + break; + case 'M': + case 'd': + sprintf(tbuf,"%d", operand); + break; + case 'D': + sprintf(tbuf,"%d", operand+8); + break; + case 'E': + sprintf(tbuf,"%d", operand*4); + break; + case 'F': + sprintf(tbuf,"%d", operand*2); + break; + case 'c': + switch (operand) { + case ARM_COND_EQ: + strcpy(tbuf, "beq"); + break; + case ARM_COND_NE: + strcpy(tbuf, "bne"); + break; + case ARM_COND_LT: + strcpy(tbuf, "blt"); + break; + case ARM_COND_GE: + strcpy(tbuf, "bge"); + break; + case ARM_COND_GT: + strcpy(tbuf, "bgt"); + break; + case ARM_COND_LE: + strcpy(tbuf, "ble"); + break; + case ARM_COND_CS: + strcpy(tbuf, "bcs"); + break; + case ARM_COND_MI: + strcpy(tbuf, "bmi"); + break; + default: + strcpy(tbuf, ""); + break; + } + break; + case 't': + sprintf(tbuf,"0x%08x", + (int) baseAddr + lir->generic.offset + 4 + + (operand << 1)); + break; + case 'u': { + int offset_1 = lir->operands[0]; + int offset_2 = NEXT_LIR(lir)->operands[0]; + intptr_t target = + ((((intptr_t) baseAddr + lir->generic.offset + 4) & + ~3) + (offset_1 << 21 >> 9) + (offset_2 << 1)) & + 0xfffffffc; + sprintf(tbuf, "%p", (void *) target); + break; + } + + /* Nothing to print for BLX_2 */ + case 'v': + strcpy(tbuf, "see above"); + break; + case 'R': + decodeRegList(operand, tbuf); + break; + default: + strcpy(tbuf,"DecodeError"); + break; + } + if (buf+strlen(tbuf) <= bufEnd) { + strcpy(buf, tbuf); + buf += strlen(tbuf); + } else { + break; + } + } + } else { + *buf++ = *fmt++; + } + if (buf == bufEnd) + break; + } + *buf = 0; +} + +/* Pretty-print a LIR instruction */ +static void dumpLIRInsn(LIR *arg, unsigned char *baseAddr) +{ + ArmLIR *lir = (ArmLIR *) arg; + char buf[256]; + char opName[256]; + int offset = lir->generic.offset; + int dest = lir->operands[0]; + u2 *cPtr = (u2*)baseAddr; + /* Handle pseudo-ops individually, and all regular insns as a group */ + switch(lir->opCode) { + case ARM_PSEUDO_TARGET_LABEL: + break; + case ARM_PSEUDO_CHAINING_CELL_NORMAL: + LOGD("-------- chaining cell (normal): 0x%04x\n", dest); + break; + case ARM_PSEUDO_CHAINING_CELL_HOT: + LOGD("-------- chaining cell (hot): 0x%04x\n", dest); + break; + case ARM_PSEUDO_CHAINING_CELL_INVOKE_PREDICTED: + LOGD("-------- chaining cell (predicted)\n"); + break; + case ARM_PSEUDO_CHAINING_CELL_INVOKE_SINGLETON: + LOGD("-------- chaining cell (invoke singleton): %s/%p\n", + ((Method *)dest)->name, + ((Method *)dest)->insns); + break; + case ARM_PSEUDO_DALVIK_BYTECODE_BOUNDARY: + LOGD("-------- dalvik offset: 0x%04x @ %s\n", dest, + getOpcodeName(lir->operands[1])); + break; + case ARM_PSEUDO_ALIGN4: + LOGD("%p (%04x): .align4\n", baseAddr + offset, offset); + break; + case ARM_PSEUDO_PC_RECONSTRUCTION_CELL: + LOGD("-------- reconstruct dalvik PC : 0x%04x @ +0x%04x\n", dest, + lir->operands[1]); + break; + case ARM_PSEUDO_PC_RECONSTRUCTION_BLOCK_LABEL: + /* Do nothing */ + break; + case ARM_PSEUDO_EH_BLOCK_LABEL: + LOGD("Exception_Handling:\n"); + break; + case ARM_PSEUDO_NORMAL_BLOCK_LABEL: + LOGD("L%#06x:\n", dest); + break; + default: + if (lir->isNop) { + break; + } + buildInsnString(EncodingMap[lir->opCode].name, lir, opName, + baseAddr, 256); + buildInsnString(EncodingMap[lir->opCode].fmt, lir, buf, baseAddr, + 256); + LOGD("%p (%04x): %-8s%s\n", + baseAddr + offset, offset, opName, buf); + break; + } +} + +/* Dump instructions and constant pool contents */ +void dvmCompilerCodegenDump(CompilationUnit *cUnit) +{ + LOGD("Dumping LIR insns\n"); + LIR *lirInsn; + ArmLIR *armLIR; + + LOGD("installed code is at %p\n", cUnit->baseAddr); + LOGD("total size is %d bytes\n", cUnit->totalSize); + for (lirInsn = cUnit->firstLIRInsn; lirInsn; lirInsn = lirInsn->next) { + dumpLIRInsn(lirInsn, cUnit->baseAddr); + } + for (lirInsn = cUnit->wordList; lirInsn; lirInsn = lirInsn->next) { + armLIR = (ArmLIR *) lirInsn; + LOGD("%p (%04x): .word (0x%x)\n", + (char*)cUnit->baseAddr + armLIR->generic.offset, armLIR->generic.offset, + armLIR->operands[0]); + } +} diff --git a/vm/compiler/codegen/arm/ArmLIR.h b/vm/compiler/codegen/arm/ArmLIR.h new file mode 100644 index 000000000..59c752949 --- /dev/null +++ b/vm/compiler/codegen/arm/ArmLIR.h @@ -0,0 +1,395 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Dalvik.h" +#include "compiler/CompilerInternals.h" + +#ifndef _DALVIK_VM_COMPILER_CODEGEN_ARM_ARMLIR_H +#define _DALVIK_VM_COMPILER_CODEGEN_ARM_ARMLIR_H + +/* + * r0, r1, r2, r3 are always scratch + * r4 (rPC) is scratch for Jit, but most be restored when resuming interp + * r5 (rFP) is reserved [holds Dalvik frame pointer] + * r6 (rGLUE) is reserved [holds current &interpState] + * r7 (rINST) is scratch for Jit + * r8 (rIBASE) is scratch for Jit, but must be restored when resuming interp + * r9 is always scratch + * r10 is always scratch + * r11 (fp) used by gcc unless -fomit-frame-pointer set [available for jit?] + * r12 is always scratch + * r13 (sp) is reserved + * r14 (lr) is scratch for Jit + * r15 (pc) is reserved + * + * For Thumb code use: + * r0, r1, r2, r3 to hold operands/results via scoreboard + * r4, r7 for temps + * + * For Thumb2 code use: + * r0, r1, r2, r3, r8, r9, r10, r11 for operands/results via scoreboard + * r4, r7, r14 for temps + * + * When transitioning from code cache to interp: + * restore rIBASE + * restore rPC + * restore r11 (fp)? + * + * Double precision values are stored in consecutive single precision registers + * such that dr0 -> (sr0,sr1), dr1 -> (sr2,sr3) ... dr16 -> (sr30,sr31) + */ + +/* Offset to distingish FP regs */ +#define FP_REG_OFFSET 32 +/* Offset to distinguish DP FP regs */ +#define FP_DOUBLE 64 +/* Reg types */ +#define FPREG(x) ((x & FP_REG_OFFSET) == FP_REG_OFFSET) +#define LOWREG(x) ((x & 0x7) == x) +#define DOUBLEREG(x) ((x & FP_DOUBLE) == FP_DOUBLE) +#define SINGLEREG(x) (FPREG(x) && !DOUBLEREG(x)) +/* Mask to strip off fp flags */ +#define FP_REG_MASK (FP_REG_OFFSET-1) +/* Mask to convert high reg to low for Thumb */ +#define THUMB_REG_MASK 0x7 + + +typedef enum NativeRegisterPool { + r0 = 0, + r1 = 1, + r2 = 2, + r3 = 3, + r4PC = 4, + rFP = 5, + rGLUE = 6, + r7 = 7, + r8 = 8, + r9 = 9, + r10 = 10, + r11 = 11, + r12 = 12, + r13 = 13, + rlr = 14, + rpc = 15, + fr0 = 0 + FP_REG_OFFSET, + fr1 = 1 + FP_REG_OFFSET, + fr2 = 2 + FP_REG_OFFSET, + fr3 = 3 + FP_REG_OFFSET, + fr4 = 4 + FP_REG_OFFSET, + fr5 = 5 + FP_REG_OFFSET, + fr6 = 6 + FP_REG_OFFSET, + fr7 = 7 + FP_REG_OFFSET, + fr8 = 8 + FP_REG_OFFSET, + fr9 = 9 + FP_REG_OFFSET, + fr10 = 10 + FP_REG_OFFSET, + fr11 = 11 + FP_REG_OFFSET, + fr12 = 12 + FP_REG_OFFSET, + fr13 = 13 + FP_REG_OFFSET, + fr14 = 14 + FP_REG_OFFSET, + fr15 = 15 + FP_REG_OFFSET, + fr16 = 16 + FP_REG_OFFSET, + fr17 = 17 + FP_REG_OFFSET, + fr18 = 18 + FP_REG_OFFSET, + fr19 = 19 + FP_REG_OFFSET, + fr20 = 20 + FP_REG_OFFSET, + fr21 = 21 + FP_REG_OFFSET, + fr22 = 22 + FP_REG_OFFSET, + fr23 = 23 + FP_REG_OFFSET, + fr24 = 24 + FP_REG_OFFSET, + fr25 = 25 + FP_REG_OFFSET, + fr26 = 26 + FP_REG_OFFSET, + fr27 = 27 + FP_REG_OFFSET, + fr28 = 28 + FP_REG_OFFSET, + fr29 = 29 + FP_REG_OFFSET, + fr30 = 30 + FP_REG_OFFSET, + fr31 = 31 + FP_REG_OFFSET, + dr0 = fr0 + FP_DOUBLE, + dr1 = fr2 + FP_DOUBLE, + dr2 = fr4 + FP_DOUBLE, + dr3 = fr6 + FP_DOUBLE, + dr4 = fr8 + FP_DOUBLE, + dr5 = fr10 + FP_DOUBLE, + dr6 = fr12 + FP_DOUBLE, + dr7 = fr14 + FP_DOUBLE, + dr8 = fr16 + FP_DOUBLE, + dr9 = fr18 + FP_DOUBLE, + dr10 = fr20 + FP_DOUBLE, + dr11 = fr22 + FP_DOUBLE, + dr12 = fr24 + FP_DOUBLE, + dr13 = fr26 + FP_DOUBLE, + dr14 = fr28 + FP_DOUBLE, + dr15 = fr30 + FP_DOUBLE, +} NativeRegisterPool; + +/* Thumb condition encodings */ +typedef enum ArmConditionCode { + ARM_COND_EQ = 0x0, /* 0000 */ + ARM_COND_NE = 0x1, /* 0001 */ + ARM_COND_LT = 0xb, /* 1011 */ + ARM_COND_GE = 0xa, /* 1010 */ + ARM_COND_GT = 0xc, /* 1100 */ + ARM_COND_LE = 0xd, /* 1101 */ + ARM_COND_CS = 0x2, /* 0010 */ + ARM_COND_MI = 0x4, /* 0100 */ +} ArmConditionCode; + +#define isPseudoOpCode(opCode) ((int)(opCode) < 0) + +/* + * The following enum defines the list of supported Thumb instructions by the + * assembler. Their corresponding snippet positions will be defined in + * Assemble.c. + */ +typedef enum ArmOpCode { + ARM_PSEUDO_TARGET_LABEL = -11, + ARM_PSEUDO_CHAINING_CELL_HOT = -10, + ARM_PSEUDO_CHAINING_CELL_INVOKE_PREDICTED = -9, + ARM_PSEUDO_CHAINING_CELL_INVOKE_SINGLETON = -8, + ARM_PSEUDO_CHAINING_CELL_NORMAL = -7, + ARM_PSEUDO_DALVIK_BYTECODE_BOUNDARY = -6, + ARM_PSEUDO_ALIGN4 = -5, + ARM_PSEUDO_PC_RECONSTRUCTION_CELL = -4, + ARM_PSEUDO_PC_RECONSTRUCTION_BLOCK_LABEL = -3, + ARM_PSEUDO_EH_BLOCK_LABEL = -2, + ARM_PSEUDO_NORMAL_BLOCK_LABEL = -1, + /************************************************************************/ + ARM_16BIT_DATA, /* DATA [0] rd[15..0] */ + THUMB_ADC, /* adc [0100000101] rm[5..3] rd[2..0] */ + THUMB_ADD_RRI3, /* add(1) [0001110] imm_3[8..6] rn[5..3] rd[2..0]*/ + THUMB_ADD_RI8, /* add(2) [00110] rd[10..8] imm_8[7..0] */ + THUMB_ADD_RRR, /* add(3) [0001100] rm[8..6] rn[5..3] rd[2..0] */ + THUMB_ADD_RR_LH, /* add(4) [01000100] H12[01] rm[5..3] rd[2..0] */ + THUMB_ADD_RR_HL, /* add(4) [01001000] H12[10] rm[5..3] rd[2..0] */ + THUMB_ADD_RR_HH, /* add(4) [01001100] H12[11] rm[5..3] rd[2..0] */ + THUMB_ADD_PC_REL, /* add(5) [10100] rd[10..8] imm_8[7..0] */ + THUMB_ADD_SP_REL, /* add(6) [10101] rd[10..8] imm_8[7..0] */ + THUMB_ADD_SPI7, /* add(7) [101100000] imm_7[6..0] */ + THUMB_AND_RR, /* and [0100000000] rm[5..3] rd[2..0] */ + THUMB_ASR, /* asr(1) [00010] imm_5[10..6] rm[5..3] rd[2..0] */ + THUMB_ASRV, /* asr(2) [0100000100] rs[5..3] rd[2..0] */ + THUMB_B_COND, /* b(1) [1101] cond[11..8] offset_8[7..0] */ + THUMB_B_UNCOND, /* b(2) [11100] offset_11[10..0] */ + THUMB_BIC, /* bic [0100001110] rm[5..3] rd[2..0] */ + THUMB_BKPT, /* bkpt [10111110] imm_8[7..0] */ + THUMB_BLX_1, /* blx(1) [111] H[10] offset_11[10..0] */ + THUMB_BLX_2, /* blx(1) [111] H[01] offset_11[10..0] */ + THUMB_BL_1, /* blx(1) [111] H[10] offset_11[10..0] */ + THUMB_BL_2, /* blx(1) [111] H[11] offset_11[10..0] */ + THUMB_BLX_R, /* blx(2) [010001111] H2[6..6] rm[5..3] SBZ[000] */ + THUMB_BX, /* bx [010001110] H2[6..6] rm[5..3] SBZ[000] */ + THUMB_CMN, /* cmn [0100001011] rm[5..3] rd[2..0] */ + THUMB_CMP_RI8, /* cmp(1) [00101] rn[10..8] imm_8[7..0] */ + THUMB_CMP_RR, /* cmp(2) [0100001010] rm[5..3] rd[2..0] */ + THUMB_CMP_LH, /* cmp(3) [01000101] H12[01] rm[5..3] rd[2..0] */ + THUMB_CMP_HL, /* cmp(3) [01000110] H12[10] rm[5..3] rd[2..0] */ + THUMB_CMP_HH, /* cmp(3) [01000111] H12[11] rm[5..3] rd[2..0] */ + THUMB_EOR, /* eor [0100000001] rm[5..3] rd[2..0] */ + THUMB_LDMIA, /* ldmia [11001] rn[10..8] reglist [7..0] */ + THUMB_LDR_RRI5, /* ldr(1) [01101] imm_5[10..6] rn[5..3] rd[2..0] */ + THUMB_LDR_RRR, /* ldr(2) [0101100] rm[8..6] rn[5..3] rd[2..0] */ + THUMB_LDR_PC_REL, /* ldr(3) [01001] rd[10..8] imm_8[7..0] */ + THUMB_LDR_SP_REL, /* ldr(4) [10011] rd[10..8] imm_8[7..0] */ + THUMB_LDRB_RRI5, /* ldrb(1) [01111] imm_5[10..6] rn[5..3] rd[2..0] */ + THUMB_LDRB_RRR, /* ldrb(2) [0101110] rm[8..6] rn[5..3] rd[2..0] */ + THUMB_LDRH_RRI5, /* ldrh(1) [10001] imm_5[10..6] rn[5..3] rd[2..0] */ + THUMB_LDRH_RRR, /* ldrh(2) [0101101] rm[8..6] rn[5..3] rd[2..0] */ + THUMB_LDRSB_RRR, /* ldrsb [0101011] rm[8..6] rn[5..3] rd[2..0] */ + THUMB_LDRSH_RRR, /* ldrsh [0101111] rm[8..6] rn[5..3] rd[2..0] */ + THUMB_LSL, /* lsl(1) [00000] imm_5[10..6] rm[5..3] rd[2..0] */ + THUMB_LSLV, /* lsl(2) [0100000010] rs[5..3] rd[2..0] */ + THUMB_LSR, /* lsr(1) [00001] imm_5[10..6] rm[5..3] rd[2..0] */ + THUMB_LSRV, /* lsr(2) [0100000011] rs[5..3] rd[2..0] */ + THUMB_MOV_IMM, /* mov(1) [00100] rd[10..8] imm_8[7..0] */ + THUMB_MOV_RR, /* mov(2) [0001110000] rn[5..3] rd[2..0] */ + THUMB_MOV_RR_H2H, /* mov(3) [01000111] H12[11] rm[5..3] rd[2..0] */ + THUMB_MOV_RR_H2L, /* mov(3) [01000110] H12[01] rm[5..3] rd[2..0] */ + THUMB_MOV_RR_L2H, /* mov(3) [01000101] H12[10] rm[5..3] rd[2..0] */ + THUMB_MUL, /* mul [0100001101] rm[5..3] rd[2..0] */ + THUMB_MVN, /* mvn [0100001111] rm[5..3] rd[2..0] */ + THUMB_NEG, /* neg [0100001001] rm[5..3] rd[2..0] */ + THUMB_ORR, /* orr [0100001100] rm[5..3] rd[2..0] */ + THUMB_POP, /* pop [1011110] r[8..8] rl[7..0] */ + THUMB_PUSH, /* push [1011010] r[8..8] rl[7..0] */ + THUMB_ROR, /* ror [0100000111] rs[5..3] rd[2..0] */ + THUMB_SBC, /* sbc [0100000110] rm[5..3] rd[2..0] */ + THUMB_STMIA, /* stmia [11000] rn[10..8] reglist [7.. 0] */ + THUMB_STR_RRI5, /* str(1) [01100] imm_5[10..6] rn[5..3] rd[2..0] */ + THUMB_STR_RRR, /* str(2) [0101000] rm[8..6] rn[5..3] rd[2..0] */ + THUMB_STR_SP_REL, /* str(3) [10010] rd[10..8] imm_8[7..0] */ + THUMB_STRB_RRI5, /* strb(1) [01110] imm_5[10..6] rn[5..3] rd[2..0] */ + THUMB_STRB_RRR, /* strb(2) [0101010] rm[8..6] rn[5..3] rd[2..0] */ + THUMB_STRH_RRI5, /* strh(1) [10000] imm_5[10..6] rn[5..3] rd[2..0] */ + THUMB_STRH_RRR, /* strh(2) [0101001] rm[8..6] rn[5..3] rd[2..0] */ + THUMB_SUB_RRI3, /* sub(1) [0001111] imm_3[8..6] rn[5..3] rd[2..0]*/ + THUMB_SUB_RI8, /* sub(2) [00111] rd[10..8] imm_8[7..0] */ + THUMB_SUB_RRR, /* sub(3) [0001101] rm[8..6] rn[5..3] rd[2..0] */ + THUMB_SUB_SPI7, /* sub(4) [101100001] imm_7[6..0] */ + THUMB_SWI, /* swi [11011111] imm_8[7..0] */ + THUMB_TST, /* tst [0100001000] rm[5..3] rn[2..0] */ + THUMB2_VLDRS, /* vldr low sx [111011011001] rn[19..16] rd[15-12] + [1010] imm_8[7..0] */ + THUMB2_VLDRD, /* vldr low dx [111011011001] rn[19..16] rd[15-12] + [1011] imm_8[7..0] */ + THUMB2_VMULS, /* vmul vd, vn, vm [111011100010] rn[19..16] + rd[15-12] [10100000] rm[3..0] */ + THUMB2_VMULD, /* vmul vd, vn, vm [111011100010] rn[19..16] + rd[15-12] [10110000] rm[3..0] */ + THUMB2_VSTRS, /* vstr low sx [111011011000] rn[19..16] rd[15-12] + [1010] imm_8[7..0] */ + THUMB2_VSTRD, /* vstr low dx [111011011000] rn[19..16] rd[15-12] + [1011] imm_8[7..0] */ + THUMB2_VSUBS, /* vsub vd, vn, vm [111011100011] rn[19..16] + rd[15-12] [10100040] rm[3..0] */ + THUMB2_VSUBD, /* vsub vd, vn, vm [111011100011] rn[19..16] + rd[15-12] [10110040] rm[3..0] */ + THUMB2_VADDS, /* vadd vd, vn, vm [111011100011] rn[19..16] + rd[15-12] [10100000] rm[3..0] */ + THUMB2_VADDD, /* vadd vd, vn, vm [111011100011] rn[19..16] + rd[15-12] [10110000] rm[3..0] */ + THUMB2_VDIVS, /* vdiv vd, vn, vm [111011101000] rn[19..16] + rd[15-12] [10100000] rm[3..0] */ + THUMB2_VDIVD, /* vdiv vd, vn, vm [111011101000] rn[19..16] + rd[15-12] [10110000] rm[3..0] */ + THUMB2_VCVTIF, /* vcvt.F32 vd, vm [1110111010111000] vd[15..12] + [10101100] vm[3..0] */ + THUMB2_VCVTID, /* vcvt.F64 vd, vm [1110111010111000] vd[15..12] + [10111100] vm[3..0] */ + THUMB2_VCVTFI, /* vcvt.S32.F32 vd, vm [1110111010111101] vd[15..12] + [10101100] vm[3..0] */ + THUMB2_VCVTDI, /* vcvt.S32.F32 vd, vm [1110111010111101] vd[15..12] + [10111100] vm[3..0] */ + THUMB2_VCVTFD, /* vcvt.F64.F32 vd, vm [1110111010110111] vd[15..12] + [10101100] vm[3..0] */ + THUMB2_VCVTDF, /* vcvt.F32.F64 vd, vm [1110111010110111] vd[15..12] + [10111100] vm[3..0] */ + THUMB2_VSQRTS, /* vsqrt.f32 vd, vm [1110111010110001] vd[15..12] + [10101100] vm[3..0] */ + THUMB2_VSQRTD, /* vsqrt.f64 vd, vm [1110111010110001] vd[15..12] + [10111100] vm[3..0] */ + THUMB2_MOV_IMM_SHIFT, /* mov(T2) rd, # [11110] i [00001001111] + imm3 rd[11..8] imm8 */ + THUMB2_MOV_IMM16, /* mov(T3) rd, # [11110] i [0010100] imm4 [0] + imm3 rd[11..8] imm8 */ + THUMB2_STR_RRI12, /* str(Imm,T3) rd,[rn,#imm12] [111110001100] + rn[19..16] rt[15..12] imm12[11..0] */ + THUMB2_LDR_RRI12, /* str(Imm,T3) rd,[rn,#imm12] [111110001100] + rn[19..16] rt[15..12] imm12[11..0] */ + THUMB2_STR_RRI8_PREDEC, /* str(Imm,T4) rd,[rn,#-imm8] [111110000100] + rn[19..16] rt[15..12] [1100] imm[7..0]*/ + THUMB2_LDR_RRI8_PREDEC, /* ldr(Imm,T4) rd,[rn,#-imm8] [111110000101] + rn[19..16] rt[15..12] [1100] imm[7..0]*/ + THUMB2_CBNZ, /* cbnz rd,
                        Symbol